[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 // This file is part of Moodle - http://moodle.org/ 2 // 3 // Moodle is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // Moodle is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 15 16 /** 17 * Javascript extensions for the External Tool activity editor. 18 * 19 * @package mod 20 * @subpackage lti 21 * @copyright Copyright (c) 2011 Moodlerooms Inc. (http://www.moodlerooms.com) 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 (function(){ 25 var Y; 26 27 M.mod_lti = M.mod_lti || {}; 28 29 M.mod_lti.LTI_SETTING_NEVER = 0; 30 M.mod_lti.LTI_SETTING_ALWAYS = 1; 31 M.mod_lti.LTI_SETTING_DELEGATE = 2; 32 33 M.mod_lti.editor = { 34 init: function(yui3, settings){ 35 if(yui3){ 36 Y = yui3; 37 } 38 39 var self = this; 40 this.settings = Y.JSON.parse(settings); 41 42 this.urlCache = {}; 43 this.toolTypeCache = {}; 44 45 this.addOptGroups(); 46 47 var updateToolMatches = function(){ 48 self.updateAutomaticToolMatch(Y.one('#id_toolurl')); 49 self.updateAutomaticToolMatch(Y.one('#id_securetoolurl')); 50 }; 51 52 var typeSelector = Y.one('#id_typeid'); 53 typeSelector.on('change', function(e){ 54 updateToolMatches(); 55 56 self.toggleEditButtons(); 57 58 if (self.getSelectedToolTypeOption().getAttribute('toolproxy')){ 59 var allowname = Y.one('#id_instructorchoicesendname'); 60 allowname.set('checked', !self.getSelectedToolTypeOption().getAttribute('noname')); 61 62 var allowemail = Y.one('#id_instructorchoicesendemailaddr'); 63 allowemail.set('checked', !self.getSelectedToolTypeOption().getAttribute('noemail')); 64 65 var allowgrades = Y.one('#id_instructorchoiceacceptgrades'); 66 allowgrades.set('checked', !self.getSelectedToolTypeOption().getAttribute('nogrades')); 67 self.toggleGradeSection(); 68 } 69 70 }); 71 72 this.createTypeEditorButtons(); 73 74 this.toggleEditButtons(); 75 76 var textAreas = new Y.NodeList([ 77 Y.one('#id_toolurl'), 78 Y.one('#id_securetoolurl'), 79 Y.one('#id_resourcekey'), 80 Y.one('#id_password') 81 ]); 82 83 var debounce; 84 textAreas.on('keyup', function(e){ 85 clearTimeout(debounce); 86 87 // If no more changes within 2 seconds, look up the matching tool URL 88 debounce = setTimeout(function(){ 89 updateToolMatches(); 90 }, 2000); 91 }); 92 93 var allowgrades = Y.one('#id_instructorchoiceacceptgrades'); 94 allowgrades.on('change', this.toggleGradeSection, this); 95 96 updateToolMatches(); 97 }, 98 99 toggleGradeSection: function(e) { 100 if (e) { 101 e.preventDefault(); 102 } 103 var allowgrades = Y.one('#id_instructorchoiceacceptgrades'); 104 var gradefieldset = Y.one('#id_modstandardgrade'); 105 if (!allowgrades.get('checked')) { 106 gradefieldset.hide(); 107 } else { 108 gradefieldset.show(); 109 } 110 }, 111 112 clearToolCache: function(){ 113 this.urlCache = {}; 114 this.toolTypeCache = {}; 115 }, 116 117 updateAutomaticToolMatch: function(field){ 118 var self = this; 119 120 var toolurl = field; 121 var typeSelector = Y.one('#id_typeid'); 122 123 var id = field.get('id') + '_lti_automatch_tool'; 124 var automatchToolDisplay = Y.one('#' + id); 125 126 if(!automatchToolDisplay){ 127 automatchToolDisplay = Y.Node.create('<span />') 128 .set('id', id) 129 .setStyle('padding-left', '1em'); 130 131 toolurl.insert(automatchToolDisplay, 'after'); 132 } 133 134 var url = toolurl.get('value'); 135 136 // Hide the display if the url box is empty 137 if(!url){ 138 automatchToolDisplay.setStyle('display', 'none'); 139 } else { 140 automatchToolDisplay.set('innerHTML', ''); 141 automatchToolDisplay.setStyle('display', ''); 142 } 143 144 var selectedToolType = parseInt(typeSelector.get('value')); 145 var selectedOption = typeSelector.one('option[value="' + selectedToolType + '"]'); 146 147 // A specific tool type is selected (not "auto") 148 // We still need to check with the server to get privacy settings 149 if(selectedToolType > 0){ 150 // If the entered domain matches the domain of the tool configuration... 151 var domainRegex = /(?:https?:\/\/)?(?:www\.)?([^\/]+)(?:\/|$)/i; 152 var match = domainRegex.exec(url); 153 if(match && match[1] && match[1].toLowerCase() === selectedOption.getAttribute('domain').toLowerCase()){ 154 automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.using_tool_configuration + selectedOption.get('text')); 155 } else { 156 // The entered URL does not match the domain of the tool configuration 157 automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.warning_icon_url + '" />' + M.str.lti.domain_mismatch); 158 } 159 } 160 161 var key = Y.one('#id_resourcekey'); 162 var secret = Y.one('#id_password'); 163 164 // Indicate the tool is manually configured 165 // We still check the Launch URL with the server as course/site tools may override privacy settings 166 if(key.get('value') !== '' && secret.get('value') !== ''){ 167 automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.custom_config); 168 } 169 170 var continuation = function(toolInfo, inputfield){ 171 if (inputfield === undefined || (inputfield.get('id') != 'id_securetoolurl' || inputfield.get('value'))) { 172 self.updatePrivacySettings(toolInfo); 173 } 174 if(toolInfo.toolname){ 175 automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.using_tool_configuration + toolInfo.toolname); 176 } else if(!selectedToolType) { 177 // Inform them custom configuration is in use 178 if(key.get('value') === '' || secret.get('value') === ''){ 179 automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.warning_icon_url + '" />' + M.str.lti.tool_config_not_found); 180 } 181 } 182 }; 183 184 // Cache urls which have already been checked to increase performance 185 // Don't use URL cache if tool type manually selected 186 if(selectedToolType && self.toolTypeCache[selectedToolType]){ 187 return continuation(self.toolTypeCache[selectedToolType]); 188 } else if(self.urlCache[url] && !selectedToolType){ 189 return continuation(self.urlCache[url]); 190 } else if(!selectedToolType && !url) { 191 // No tool type or url set 192 return continuation({}, field); 193 } else { 194 self.findToolByUrl(url, selectedToolType, function(toolInfo){ 195 if(toolInfo){ 196 // Cache the result based on whether the URL or tool type was used to look up the tool 197 if(!selectedToolType){ 198 self.urlCache[url] = toolInfo; 199 } else { 200 self.toolTypeCache[selectedToolType] = toolInfo; 201 } 202 203 Y.one('#id_urlmatchedtypeid').set('value', toolInfo.toolid); 204 205 continuation(toolInfo); 206 } 207 }); 208 } 209 }, 210 211 /** 212 * Updates display of privacy settings to show course / site tool configuration settings. 213 */ 214 updatePrivacySettings: function(toolInfo){ 215 if(!toolInfo || !toolInfo.toolid){ 216 toolInfo = { 217 sendname: M.mod_lti.LTI_SETTING_DELEGATE, 218 sendemailaddr: M.mod_lti.LTI_SETTING_DELEGATE, 219 acceptgrades: M.mod_lti.LTI_SETTING_DELEGATE 220 } 221 } 222 223 var setting, control; 224 225 var privacyControls = { 226 sendname: Y.one('#id_instructorchoicesendname'), 227 sendemailaddr: Y.one('#id_instructorchoicesendemailaddr'), 228 acceptgrades: Y.one('#id_instructorchoiceacceptgrades') 229 }; 230 231 // Store a copy of user entered privacy settings as we may overwrite them 232 if(!this.userPrivacySettings){ 233 this.userPrivacySettings = {}; 234 } 235 236 for(setting in privacyControls){ 237 if(privacyControls.hasOwnProperty(setting)){ 238 control = privacyControls[setting]; 239 240 // Only store the value if it hasn't been forced by the editor 241 if(!control.get('disabled')){ 242 this.userPrivacySettings[setting] = control.get('checked'); 243 } 244 } 245 } 246 247 // Update UI based on course / site tool configuration 248 for(setting in privacyControls){ 249 if(privacyControls.hasOwnProperty(setting)){ 250 var settingValue = toolInfo[setting]; 251 control = privacyControls[setting]; 252 253 if(settingValue == M.mod_lti.LTI_SETTING_NEVER){ 254 control.set('disabled', true); 255 control.set('checked', false); 256 control.set('title', M.str.lti.forced_help); 257 } else if(settingValue == M.mod_lti.LTI_SETTING_ALWAYS){ 258 control.set('disabled', true); 259 control.set('checked', true); 260 control.set('title', M.str.lti.forced_help); 261 } else if(settingValue == M.mod_lti.LTI_SETTING_DELEGATE){ 262 control.set('disabled', false); 263 264 // Get the value out of the stored copy 265 control.set('checked', this.userPrivacySettings[setting]); 266 control.set('title', ''); 267 } 268 } 269 } 270 271 this.toggleGradeSection(); 272 }, 273 274 getSelectedToolTypeOption: function(){ 275 var typeSelector = Y.one('#id_typeid'); 276 277 return typeSelector.one('option[value="' + typeSelector.get('value') + '"]'); 278 }, 279 280 /** 281 * Separate tool listing into option groups. Server-side select control 282 * doesn't seem to support this. 283 */ 284 addOptGroups: function(){ 285 var typeSelector = Y.one('#id_typeid'); 286 287 if(typeSelector.one('option[courseTool=1]')){ 288 // One ore more course tools exist 289 290 var globalGroup = Y.Node.create('<optgroup />') 291 .set('id', 'global_tool_group') 292 .set('label', M.str.lti.global_tool_types); 293 294 var courseGroup = Y.Node.create('<optgroup />') 295 .set('id', 'course_tool_group') 296 .set('label', M.str.lti.course_tool_types); 297 298 var globalOptions = typeSelector.all('option[globalTool=1]').remove().each(function(node){ 299 globalGroup.append(node); 300 }); 301 302 var courseOptions = typeSelector.all('option[courseTool=1]').remove().each(function(node){ 303 courseGroup.append(node); 304 }); 305 306 if(globalOptions.size() > 0){ 307 typeSelector.append(globalGroup); 308 } 309 310 if(courseOptions.size() > 0){ 311 typeSelector.append(courseGroup); 312 } 313 } 314 }, 315 316 /** 317 * Adds buttons for creating, editing, and deleting tool types. 318 * Javascript is a requirement to edit course level tools at this point. 319 */ 320 createTypeEditorButtons: function(){ 321 var self = this; 322 323 var typeSelector = Y.one('#id_typeid'); 324 325 var createIcon = function(id, tooltip, iconUrl){ 326 return Y.Node.create('<a />') 327 .set('id', id) 328 .set('title', tooltip) 329 .setStyle('margin-left', '.5em') 330 .set('href', 'javascript:void(0);') 331 .append(Y.Node.create('<img src="' + iconUrl + '" />')); 332 } 333 334 var addIcon = createIcon('lti_add_tool_type', M.str.lti.addtype, this.settings.add_icon_url); 335 var editIcon = createIcon('lti_edit_tool_type', M.str.lti.edittype, this.settings.edit_icon_url); 336 var deleteIcon = createIcon('lti_delete_tool_type', M.str.lti.deletetype, this.settings.delete_icon_url); 337 338 editIcon.on('click', function(e){ 339 var toolTypeId = typeSelector.get('value'); 340 341 if(self.getSelectedToolTypeOption().getAttribute('editable')){ 342 window.open(self.settings.instructor_tool_type_edit_url + '&action=edit&typeid=' + toolTypeId, 'edit_tool'); 343 } else { 344 alert(M.str.lti.cannot_edit); 345 } 346 }); 347 348 addIcon.on('click', function(e){ 349 window.open(self.settings.instructor_tool_type_edit_url + '&action=add', 'add_tool'); 350 }); 351 352 deleteIcon.on('click', function(e){ 353 var toolTypeId = typeSelector.get('value'); 354 355 if(self.getSelectedToolTypeOption().getAttribute('editable')){ 356 if(confirm(M.str.lti.delete_confirmation)){ 357 self.deleteTool(toolTypeId); 358 } 359 } else { 360 alert(M.str.lti.cannot_delete); 361 } 362 }); 363 364 typeSelector.insert(addIcon, 'after'); 365 addIcon.insert(editIcon, 'after'); 366 editIcon.insert(deleteIcon, 'after'); 367 }, 368 369 toggleEditButtons: function(){ 370 var lti_edit_tool_type = Y.one('#lti_edit_tool_type'); 371 var lti_delete_tool_type = Y.one('#lti_delete_tool_type'); 372 373 // Make the edit / delete icons look enabled / disabled. 374 // Does not work in older browsers, but alerts will catch those cases. 375 if(this.getSelectedToolTypeOption().getAttribute('editable')){ 376 lti_edit_tool_type.setStyle('opacity', '1'); 377 lti_delete_tool_type.setStyle('opacity', '1'); 378 } else { 379 lti_edit_tool_type.setStyle('opacity', '.2'); 380 lti_delete_tool_type.setStyle('opacity', '.2'); 381 } 382 }, 383 384 addToolType: function(toolType){ 385 var typeSelector = Y.one('#id_typeid'); 386 var course_tool_group = Y.one('#course_tool_group'); 387 388 var option = Y.Node.create('<option />') 389 .set('text', toolType.name) 390 .set('value', toolType.id) 391 .set('selected', 'selected') 392 .setAttribute('editable', '1') 393 .setAttribute('courseTool', '1') 394 .setAttribute('domain', toolType.tooldomain); 395 396 if(course_tool_group){ 397 course_tool_group.append(option); 398 } else { 399 typeSelector.append(option); 400 } 401 402 // Adding the new tool may affect which tool gets matched automatically 403 this.clearToolCache(); 404 this.updateAutomaticToolMatch(Y.one('#id_toolurl')); 405 this.updateAutomaticToolMatch(Y.one('#id_securetoolurl')); 406 }, 407 408 updateToolType: function(toolType){ 409 var typeSelector = Y.one('#id_typeid'); 410 411 var option = typeSelector.one('option[value="' + toolType.id + '"]'); 412 option.set('text', toolType.name) 413 .set('domain', toolType.tooldomain); 414 415 // Editing the tool may affect which tool gets matched automatically 416 this.clearToolCache(); 417 this.updateAutomaticToolMatch(Y.one('#id_toolurl')); 418 this.updateAutomaticToolMatch(Y.one('#id_securetoolurl')); 419 }, 420 421 deleteTool: function(toolTypeId){ 422 var self = this; 423 424 Y.io(self.settings.instructor_tool_type_edit_url + '&action=delete&typeid=' + toolTypeId, { 425 on: { 426 success: function(){ 427 self.getSelectedToolTypeOption().remove(); 428 429 // Editing the tool may affect which tool gets matched automatically 430 self.clearToolCache(); 431 self.updateAutomaticToolMatch(Y.one('#id_toolurl')); 432 self.updateAutomaticToolMatch(Y.one('#id_securetoolurl')); 433 }, 434 failure: function(){ 435 436 } 437 } 438 }); 439 }, 440 441 findToolByUrl: function(url, toolId, callback){ 442 var self = this; 443 444 Y.io(self.settings.ajax_url, { 445 data: {action: 'find_tool_config', 446 course: self.settings.courseId, 447 toolurl: url, 448 toolid: toolId || 0 449 }, 450 451 on: { 452 success: function(transactionid, xhr){ 453 var response = xhr.response; 454 455 var toolInfo = Y.JSON.parse(response); 456 457 callback(toolInfo); 458 }, 459 failure: function(){ 460 461 } 462 } 463 }); 464 } 465 466 }; 467 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |