[ Index ] |
PHP Cross Reference of Unnamed Project |
[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.util.get_string('using_tool_configuration', 'lti') + 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.util.get_string('domain_mismatch', 'lti')); 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.util.get_string('custom_config', 'lti')); 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.util.get_string('using_tool_configuration', 'lti') + 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.util.get_string('tool_config_not_found', 'lti')); 180 } 181 } 182 if (toolInfo.cartridge) { 183 automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + 184 '" />' + M.util.get_string('using_tool_cartridge', 'lti')); 185 } 186 }; 187 188 // Cache urls which have already been checked to increase performance 189 // Don't use URL cache if tool type manually selected 190 if(selectedToolType && self.toolTypeCache[selectedToolType]){ 191 return continuation(self.toolTypeCache[selectedToolType]); 192 } else if(self.urlCache[url] && !selectedToolType){ 193 return continuation(self.urlCache[url]); 194 } else if(!selectedToolType && !url) { 195 // No tool type or url set 196 return continuation({}, field); 197 } else { 198 self.findToolByUrl(url, selectedToolType, function(toolInfo){ 199 if(toolInfo){ 200 // Cache the result based on whether the URL or tool type was used to look up the tool 201 if(!selectedToolType){ 202 self.urlCache[url] = toolInfo; 203 } else { 204 self.toolTypeCache[selectedToolType] = toolInfo; 205 } 206 207 Y.one('#id_urlmatchedtypeid').set('value', toolInfo.toolid); 208 209 continuation(toolInfo); 210 } 211 }); 212 } 213 }, 214 215 /** 216 * Updates display of privacy settings to show course / site tool configuration settings. 217 */ 218 updatePrivacySettings: function(toolInfo){ 219 if(!toolInfo || !toolInfo.toolid){ 220 toolInfo = { 221 sendname: M.mod_lti.LTI_SETTING_DELEGATE, 222 sendemailaddr: M.mod_lti.LTI_SETTING_DELEGATE, 223 acceptgrades: M.mod_lti.LTI_SETTING_DELEGATE 224 } 225 } 226 227 var setting, control; 228 229 var privacyControls = { 230 sendname: Y.one('#id_instructorchoicesendname'), 231 sendemailaddr: Y.one('#id_instructorchoicesendemailaddr'), 232 acceptgrades: Y.one('#id_instructorchoiceacceptgrades') 233 }; 234 235 // Store a copy of user entered privacy settings as we may overwrite them 236 if(!this.userPrivacySettings){ 237 this.userPrivacySettings = {}; 238 } 239 240 for(setting in privacyControls){ 241 if(privacyControls.hasOwnProperty(setting)){ 242 control = privacyControls[setting]; 243 244 // Only store the value if it hasn't been forced by the editor 245 if(!control.get('disabled')){ 246 this.userPrivacySettings[setting] = control.get('checked'); 247 } 248 } 249 } 250 251 // Update UI based on course / site tool configuration 252 for(setting in privacyControls){ 253 if(privacyControls.hasOwnProperty(setting)){ 254 var settingValue = toolInfo[setting]; 255 control = privacyControls[setting]; 256 257 if(settingValue == M.mod_lti.LTI_SETTING_NEVER){ 258 control.set('disabled', true); 259 control.set('checked', false); 260 control.set('title', M.util.get_string('forced_help', 'lti')); 261 } else if(settingValue == M.mod_lti.LTI_SETTING_ALWAYS){ 262 control.set('disabled', true); 263 control.set('checked', true); 264 control.set('title', M.util.get_string('forced_help', 'lti')); 265 } else if(settingValue == M.mod_lti.LTI_SETTING_DELEGATE){ 266 control.set('disabled', false); 267 268 // Get the value out of the stored copy 269 control.set('checked', this.userPrivacySettings[setting]); 270 control.set('title', ''); 271 } 272 } 273 } 274 275 this.toggleGradeSection(); 276 }, 277 278 getSelectedToolTypeOption: function(){ 279 var typeSelector = Y.one('#id_typeid'); 280 281 return typeSelector.one('option[value="' + typeSelector.get('value') + '"]'); 282 }, 283 284 /** 285 * Separate tool listing into option groups. Server-side select control 286 * doesn't seem to support this. 287 */ 288 addOptGroups: function(){ 289 var typeSelector = Y.one('#id_typeid'); 290 291 if(typeSelector.one('option[courseTool=1]')){ 292 // One ore more course tools exist 293 294 var globalGroup = Y.Node.create('<optgroup />') 295 .set('id', 'global_tool_group') 296 .set('label', M.util.get_string('global_tool_types', 'lti')); 297 298 var courseGroup = Y.Node.create('<optgroup />') 299 .set('id', 'course_tool_group') 300 .set('label', M.util.get_string('course_tool_types', 'lti')); 301 302 var globalOptions = typeSelector.all('option[globalTool=1]').remove().each(function(node){ 303 globalGroup.append(node); 304 }); 305 306 var courseOptions = typeSelector.all('option[courseTool=1]').remove().each(function(node){ 307 courseGroup.append(node); 308 }); 309 310 if(globalOptions.size() > 0){ 311 typeSelector.append(globalGroup); 312 } 313 314 if(courseOptions.size() > 0){ 315 typeSelector.append(courseGroup); 316 } 317 } 318 }, 319 320 /** 321 * Adds buttons for creating, editing, and deleting tool types. 322 * Javascript is a requirement to edit course level tools at this point. 323 */ 324 createTypeEditorButtons: function(){ 325 var self = this; 326 327 var typeSelector = Y.one('#id_typeid'); 328 329 var createIcon = function(id, tooltip, iconUrl){ 330 return Y.Node.create('<a />') 331 .set('id', id) 332 .set('title', tooltip) 333 .setStyle('margin-left', '.5em') 334 .set('href', 'javascript:void(0);') 335 .append(Y.Node.create('<img src="' + iconUrl + '" />')); 336 } 337 338 var addIcon = createIcon('lti_add_tool_type', M.util.get_string('addtype', 'lti'), this.settings.add_icon_url); 339 var editIcon = createIcon('lti_edit_tool_type', M.util.get_string('edittype', 'lti'), this.settings.edit_icon_url); 340 var deleteIcon = createIcon('lti_delete_tool_type', M.util.get_string('deletetype', 'lti'), this.settings.delete_icon_url); 341 342 editIcon.on('click', function(e){ 343 var toolTypeId = typeSelector.get('value'); 344 345 if(self.getSelectedToolTypeOption().getAttribute('editable')){ 346 window.open(self.settings.instructor_tool_type_edit_url + '&action=edit&typeid=' + toolTypeId, 'edit_tool'); 347 } else { 348 alert(M.util.get_string('cannot_edit', 'lti')); 349 } 350 }); 351 352 addIcon.on('click', function(e){ 353 window.open(self.settings.instructor_tool_type_edit_url + '&action=add', 'add_tool'); 354 }); 355 356 deleteIcon.on('click', function(e){ 357 var toolTypeId = typeSelector.get('value'); 358 359 if(self.getSelectedToolTypeOption().getAttribute('editable')){ 360 if(confirm(M.util.get_string('delete_confirmation', 'lti'))){ 361 self.deleteTool(toolTypeId); 362 } 363 } else { 364 alert(M.util.get_string('cannot_delete', 'lti')); 365 } 366 }); 367 368 typeSelector.insert(addIcon, 'after'); 369 addIcon.insert(editIcon, 'after'); 370 editIcon.insert(deleteIcon, 'after'); 371 }, 372 373 toggleEditButtons: function(){ 374 var lti_edit_tool_type = Y.one('#lti_edit_tool_type'); 375 var lti_delete_tool_type = Y.one('#lti_delete_tool_type'); 376 377 // Make the edit / delete icons look enabled / disabled. 378 // Does not work in older browsers, but alerts will catch those cases. 379 if(this.getSelectedToolTypeOption().getAttribute('editable')){ 380 lti_edit_tool_type.setStyle('opacity', '1'); 381 lti_delete_tool_type.setStyle('opacity', '1'); 382 } else { 383 lti_edit_tool_type.setStyle('opacity', '.2'); 384 lti_delete_tool_type.setStyle('opacity', '.2'); 385 } 386 }, 387 388 addToolType: function(toolType){ 389 var typeSelector = Y.one('#id_typeid'); 390 var course_tool_group = Y.one('#course_tool_group'); 391 392 var option = Y.Node.create('<option />') 393 .set('text', toolType.name) 394 .set('value', toolType.id) 395 .set('selected', 'selected') 396 .setAttribute('editable', '1') 397 .setAttribute('courseTool', '1') 398 .setAttribute('domain', toolType.tooldomain); 399 400 if(course_tool_group){ 401 course_tool_group.append(option); 402 } else { 403 typeSelector.append(option); 404 } 405 406 // Adding the new tool may affect which tool gets matched automatically 407 this.clearToolCache(); 408 this.updateAutomaticToolMatch(Y.one('#id_toolurl')); 409 this.updateAutomaticToolMatch(Y.one('#id_securetoolurl')); 410 this.toggleEditButtons(); 411 412 require(["core/notification"], function (notification) { 413 notification.addNotification({ 414 message: M.util.get_string('tooltypeadded', 'lti'), 415 type: "success" 416 }); 417 }); 418 }, 419 420 updateToolType: function(toolType){ 421 var typeSelector = Y.one('#id_typeid'); 422 423 var option = typeSelector.one('option[value="' + toolType.id + '"]'); 424 option.set('text', toolType.name) 425 .set('domain', toolType.tooldomain); 426 427 // Editing the tool may affect which tool gets matched automatically 428 this.clearToolCache(); 429 this.updateAutomaticToolMatch(Y.one('#id_toolurl')); 430 this.updateAutomaticToolMatch(Y.one('#id_securetoolurl')); 431 432 require(["core/notification"], function (notification) { 433 notification.addNotification({ 434 message: M.util.get_string('tooltypeupdated', 'lti'), 435 type: "success" 436 }); 437 }); 438 }, 439 440 deleteTool: function(toolTypeId){ 441 var self = this; 442 443 Y.io(self.settings.instructor_tool_type_edit_url + '&action=delete&typeid=' + toolTypeId, { 444 on: { 445 success: function(){ 446 self.getSelectedToolTypeOption().remove(); 447 448 // Editing the tool may affect which tool gets matched automatically 449 self.clearToolCache(); 450 self.updateAutomaticToolMatch(Y.one('#id_toolurl')); 451 self.updateAutomaticToolMatch(Y.one('#id_securetoolurl')); 452 453 require(["core/notification"], function (notification) { 454 notification.addNotification({ 455 message: M.util.get_string('tooltypedeleted', 'lti'), 456 type: "success" 457 }); 458 }); 459 }, 460 failure: function(){ 461 require(["core/notification"], function (notification) { 462 notification.addNotification({ 463 message: M.util.get_string('tooltypenotdeleted', 'lti'), 464 type: "problem" 465 }); 466 }); 467 } 468 } 469 }); 470 }, 471 472 findToolByUrl: function(url, toolId, callback){ 473 var self = this; 474 475 Y.io(self.settings.ajax_url, { 476 data: {action: 'find_tool_config', 477 course: self.settings.courseId, 478 toolurl: url, 479 toolid: toolId || 0 480 }, 481 482 on: { 483 success: function(transactionid, xhr){ 484 var response = xhr.response; 485 486 var toolInfo = Y.JSON.parse(response); 487 488 callback(toolInfo); 489 }, 490 failure: function(){ 491 492 } 493 } 494 }); 495 } 496 497 }; 498 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |