Commit | Line | Data |
---|---|---|
61eb12d4 CS |
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 | * MRTODO: Brief description of this file | |
18 | * | |
19 | * @package mod | |
20 | * @subpackage lti | |
21 | * @copyright 2011 onwards MRTODO | |
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
23 | */ | |
6831c7cd CS |
24 | (function(){ |
25 | var Y; | |
e27cb316 | 26 | |
6831c7cd CS |
27 | M.mod_lti = M.mod_lti || {}; |
28 | ||
29 | M.mod_lti.editor = { | |
30 | init: function(yui3, settings){ | |
31 | if(yui3){ | |
32 | Y = yui3; | |
996b0fd9 | 33 | } |
e27cb316 | 34 | |
16e8f130 | 35 | var self = this; |
6831c7cd CS |
36 | this.settings = Y.JSON.parse(settings); |
37 | ||
38 | this.urlCache = {}; | |
39 | ||
40 | this.addOptGroups(); | |
41 | ||
d8d04121 CS |
42 | var updateToolMatches = function(){ |
43 | self.updateAutomaticToolMatch(Y.one('#id_toolurl')); | |
44 | self.updateAutomaticToolMatch(Y.one('#id_securetoolurl')); | |
45 | }; | |
46 | ||
6831c7cd CS |
47 | var typeSelector = Y.one('#id_typeid'); |
48 | typeSelector.on('change', function(e){ | |
d8d04121 | 49 | updateToolMatches(); |
e27cb316 | 50 | |
6831c7cd CS |
51 | self.toggleEditButtons(); |
52 | }); | |
53 | ||
54 | this.createTypeEditorButtons(); | |
55 | ||
56 | this.toggleEditButtons(); | |
e27cb316 | 57 | |
6831c7cd CS |
58 | var textAreas = new Y.NodeList([ |
59 | Y.one('#id_toolurl'), | |
d8d04121 | 60 | Y.one('#id_securetoolurl'), |
6831c7cd CS |
61 | Y.one('#id_resourcekey'), |
62 | Y.one('#id_password') | |
63 | ]); | |
e27cb316 | 64 | |
6831c7cd CS |
65 | var debounce; |
66 | textAreas.on('keyup', function(e){ | |
67 | clearTimeout(debounce); | |
68 | ||
69 | //If no more changes within 2 seconds, look up the matching tool URL | |
70 | debounce = setTimeout(function(){ | |
d8d04121 | 71 | updateToolMatches(); |
6831c7cd CS |
72 | }, 2000); |
73 | }); | |
e27cb316 | 74 | |
d8d04121 | 75 | updateToolMatches(); |
6831c7cd CS |
76 | }, |
77 | ||
16e8f130 CS |
78 | clearToolCache: function(){ |
79 | this.urlCache = {}; | |
80 | }, | |
81 | ||
d8d04121 | 82 | updateAutomaticToolMatch: function(field){ |
16e8f130 | 83 | var self = this; |
e27cb316 | 84 | |
d8d04121 | 85 | var toolurl = field; |
606ab1a1 | 86 | var typeSelector = Y.one('#id_typeid'); |
e27cb316 | 87 | |
d8d04121 CS |
88 | var id = field.get('id') + '_lti_automatch_tool'; |
89 | var automatchToolDisplay = Y.one('#' + id); | |
6831c7cd CS |
90 | |
91 | if(!automatchToolDisplay){ | |
92 | automatchToolDisplay = Y.Node.create('<span />') | |
d8d04121 | 93 | .set('id', id) |
6831c7cd | 94 | .setStyle('padding-left', '1em'); |
e27cb316 | 95 | |
6831c7cd CS |
96 | toolurl.insert(automatchToolDisplay, 'after'); |
97 | } | |
98 | ||
99 | var url = toolurl.get('value'); | |
996b0fd9 | 100 | |
16e8f130 CS |
101 | //Hide the display if the url box is empty |
102 | if(!url){ | |
6831c7cd | 103 | automatchToolDisplay.setStyle('display', 'none'); |
16e8f130 CS |
104 | } else { |
105 | automatchToolDisplay.set('innerHTML', ''); | |
106 | automatchToolDisplay.setStyle('display', ''); | |
107 | } | |
108 | ||
109 | var selectedToolType = typeSelector.get('value'); | |
110 | var selectedOption = typeSelector.one('option[value=' + selectedToolType + ']'); | |
111 | ||
112 | //A specific tool type is selected (not "auto")" | |
113 | if(selectedToolType > 0){ | |
114 | //If the entered domain matches the domain of the tool configuration... | |
115 | var domainRegex = /(?:https?:\/\/)?(?:www\.)?([^\/]+)(?:\/|$)/i; | |
116 | var match = domainRegex.exec(url); | |
117 | if(match && match[1] && match[1].toLowerCase() === selectedOption.getAttribute('domain').toLowerCase()){ | |
118 | 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')); | |
119 | } else { | |
120 | //The entered URL does not match the domain of the tool configuration | |
121 | automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.warning_icon_url + '" />' + M.str.lti.domain_mismatch); | |
122 | } | |
e27cb316 | 123 | |
6831c7cd CS |
124 | return; |
125 | } | |
126 | ||
127 | var key = Y.one('#id_resourcekey'); | |
128 | var secret = Y.one('#id_password'); | |
129 | ||
130 | //We don't care what tool type this tool is associated with if it's manually configured' | |
131 | if(key.get('value') !== '' && secret.get('value') !== ''){ | |
16e8f130 | 132 | automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.custom_config); |
6831c7cd CS |
133 | } else { |
134 | var continuation = function(toolInfo){ | |
6831c7cd | 135 | if(toolInfo.toolname){ |
16e8f130 | 136 | automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.green_check_icon_url + '" />' + M.str.lti.using_tool_configuration + toolInfo.toolname); |
6831c7cd CS |
137 | } else { |
138 | //Inform them custom configuration is in use | |
139 | if(key.get('value') === '' || secret.get('value') === ''){ | |
16e8f130 | 140 | automatchToolDisplay.set('innerHTML', '<img style="vertical-align:text-bottom" src="' + self.settings.warning_icon_url + '" />' + M.str.lti.tool_config_not_found); |
996b0fd9 CS |
141 | } |
142 | } | |
6831c7cd | 143 | }; |
e27cb316 | 144 | |
6831c7cd CS |
145 | //Cache urls which have already been checked to increaes performance |
146 | if(self.urlCache[url]){ | |
147 | continuation(self.urlCache[url]); | |
148 | } else { | |
149 | self.findToolByUrl(url, function(toolInfo){ | |
150 | self.urlCache[url] = toolInfo; | |
151 | ||
152 | continuation(toolInfo); | |
153 | }); | |
154 | } | |
155 | } | |
156 | }, | |
157 | ||
158 | getSelectedToolTypeOption: function(){ | |
159 | var typeSelector = Y.one('#id_typeid'); | |
160 | ||
161 | return typeSelector.one('option[value=' + typeSelector.get('value') + ']'); | |
162 | }, | |
163 | ||
164 | /** | |
165 | * Separate tool listing into option groups. Server-side select control | |
166 | * doesn't seem to support this. | |
167 | */ | |
168 | addOptGroups: function(){ | |
169 | var typeSelector = Y.one('#id_typeid'); | |
170 | ||
171 | if(typeSelector.one('option[courseTool=1]')){ | |
172 | //One ore more course tools exist | |
173 | ||
174 | var globalGroup = Y.Node.create('<optgroup />') | |
175 | .set('id', 'global_tool_group') | |
176 | .set('label', M.str.lti.global_tool_types); | |
177 | ||
178 | var courseGroup = Y.Node.create('<optgroup />') | |
179 | .set('id', 'course_tool_group') | |
180 | .set('label', M.str.lti.course_tool_types); | |
181 | ||
606ab1a1 | 182 | var globalOptions = typeSelector.all('option[globalTool=1]').remove().each(function(node){ |
6831c7cd CS |
183 | globalGroup.append(node); |
184 | }); | |
185 | ||
606ab1a1 | 186 | var courseOptions = typeSelector.all('option[courseTool=1]').remove().each(function(node){ |
6831c7cd | 187 | courseGroup.append(node); |
996b0fd9 | 188 | }); |
6831c7cd | 189 | |
606ab1a1 CS |
190 | if(globalOptions.size() > 0){ |
191 | typeSelector.append(globalGroup); | |
192 | } | |
e27cb316 | 193 | |
606ab1a1 CS |
194 | if(courseOptions.size() > 0){ |
195 | typeSelector.append(courseGroup); | |
196 | } | |
996b0fd9 | 197 | } |
6831c7cd CS |
198 | }, |
199 | ||
200 | /** | |
201 | * Adds buttons for creating, editing, and deleting tool types. | |
202 | * Javascript is a requirement to edit course level tools at this point. | |
203 | */ | |
204 | createTypeEditorButtons: function(){ | |
16e8f130 | 205 | var self = this; |
e27cb316 | 206 | |
6831c7cd CS |
207 | var typeSelector = Y.one('#id_typeid'); |
208 | ||
209 | var createIcon = function(id, tooltip, iconUrl){ | |
e27cb316 | 210 | return Y.Node.create('<a />') |
6831c7cd CS |
211 | .set('id', id) |
212 | .set('title', tooltip) | |
213 | .setStyle('margin-left', '.5em') | |
214 | .set('href', 'javascript:void(0);') | |
215 | .append(Y.Node.create('<img src="' + iconUrl + '" />')); | |
216 | } | |
217 | ||
218 | var addIcon = createIcon('lti_add_tool_type', M.str.lti.addtype, this.settings.add_icon_url); | |
219 | var editIcon = createIcon('lti_edit_tool_type', M.str.lti.edittype, this.settings.edit_icon_url); | |
220 | var deleteIcon = createIcon('lti_delete_tool_type', M.str.lti.deletetype, this.settings.delete_icon_url); | |
221 | ||
222 | editIcon.on('click', function(e){ | |
223 | var toolTypeId = typeSelector.get('value'); | |
224 | ||
225 | if(self.getSelectedToolTypeOption().getAttribute('editable')){ | |
226 | window.open(self.settings.instructor_tool_type_edit_url + '&action=edit&typeid=' + toolTypeId, 'edit_tool'); | |
227 | } else { | |
228 | alert(M.str.lti.cannot_edit); | |
229 | } | |
230 | }); | |
231 | ||
232 | addIcon.on('click', function(e){ | |
233 | window.open(self.settings.instructor_tool_type_edit_url + '&action=add', 'add_tool'); | |
234 | }); | |
235 | ||
236 | deleteIcon.on('click', function(e){ | |
237 | var toolTypeId = typeSelector.get('value'); | |
238 | ||
239 | if(self.getSelectedToolTypeOption().getAttribute('editable')){ | |
240 | if(confirm(M.str.lti.delete_confirmation)){ | |
16e8f130 | 241 | self.deleteTool(toolTypeId); |
6831c7cd CS |
242 | } |
243 | } else { | |
244 | alert(M.str.lti.cannot_delete); | |
245 | } | |
246 | }); | |
247 | ||
248 | typeSelector.insert(addIcon, 'after'); | |
249 | addIcon.insert(editIcon, 'after'); | |
250 | editIcon.insert(deleteIcon, 'after'); | |
251 | }, | |
252 | ||
253 | toggleEditButtons: function(){ | |
254 | var lti_edit_tool_type = Y.one('#lti_edit_tool_type'); | |
255 | var lti_delete_tool_type = Y.one('#lti_delete_tool_type'); | |
256 | ||
257 | //Make the edit / delete icons look enabled / disabled. | |
258 | //Does not work in older browsers, but alerts will catch those cases. | |
259 | if(this.getSelectedToolTypeOption().getAttribute('editable')){ | |
260 | lti_edit_tool_type.setStyle('opacity', '1'); | |
261 | lti_delete_tool_type.setStyle('opacity', '1'); | |
262 | } else { | |
263 | lti_edit_tool_type.setStyle('opacity', '.2'); | |
264 | lti_delete_tool_type.setStyle('opacity', '.2'); | |
265 | } | |
266 | }, | |
267 | ||
16e8f130 | 268 | addToolType: function(toolType){ |
6831c7cd CS |
269 | var typeSelector = Y.one('#id_typeid'); |
270 | var course_tool_group = Y.one('#course_tool_group'); | |
271 | ||
272 | var option = Y.Node.create('<option />') | |
16e8f130 CS |
273 | .set('text', toolType.name) |
274 | .set('value', toolType.id) | |
6831c7cd CS |
275 | .set('selected', 'selected') |
276 | .setAttribute('editable', '1') | |
16e8f130 CS |
277 | .setAttribute('courseTool', '1') |
278 | .setAttribute('domain', toolType.tooldomain); | |
6831c7cd CS |
279 | |
280 | if(course_tool_group){ | |
281 | course_tool_group.append(option); | |
282 | } else { | |
283 | typeSelector.append(option); | |
284 | } | |
e27cb316 | 285 | |
16e8f130 CS |
286 | //Adding the new tool may affect which tool gets matched automatically |
287 | this.clearToolCache(); | |
288 | this.updateAutomaticToolMatch(); | |
6831c7cd CS |
289 | }, |
290 | ||
16e8f130 | 291 | updateToolType: function(toolType){ |
6831c7cd CS |
292 | var typeSelector = Y.one('#id_typeid'); |
293 | ||
16e8f130 CS |
294 | var option = typeSelector.one('option[value=' + toolType.id + ']'); |
295 | option.set('text', toolType.name) | |
296 | .set('domain', toolType.tooldomain); | |
e27cb316 | 297 | |
16e8f130 CS |
298 | //Editing the tool may affect which tool gets matched automatically |
299 | this.clearToolCache(); | |
300 | this.updateAutomaticToolMatch(); | |
301 | }, | |
302 | ||
303 | deleteTool: function(toolTypeId){ | |
304 | var self = this; | |
e27cb316 | 305 | |
16e8f130 CS |
306 | Y.io(self.settings.instructor_tool_type_edit_url + '&action=delete&typeid=' + toolTypeId, { |
307 | on: { | |
308 | success: function(){ | |
309 | self.getSelectedToolTypeOption().remove(); | |
e27cb316 | 310 | |
16e8f130 CS |
311 | //Editing the tool may affect which tool gets matched automatically |
312 | self.clearToolCache(); | |
313 | self.updateAutomaticToolMatch(); | |
314 | }, | |
315 | failure: function(){ | |
316 | ||
317 | } | |
318 | } | |
319 | }); | |
6831c7cd CS |
320 | }, |
321 | ||
322 | findToolByUrl: function(url, callback){ | |
16e8f130 | 323 | var self = this; |
e27cb316 CS |
324 | |
325 | Y.io(self.settings.ajax_url, { | |
16e8f130 | 326 | data: {action: 'find_tool_config', |
6831c7cd CS |
327 | course: self.settings.courseId, |
328 | toolurl: url | |
329 | }, | |
996b0fd9 | 330 | |
6831c7cd CS |
331 | on: { |
332 | success: function(transactionid, xhr){ | |
333 | var response = xhr.response; | |
e27cb316 | 334 | |
6831c7cd | 335 | var toolInfo = Y.JSON.parse(response); |
e27cb316 | 336 | |
6831c7cd CS |
337 | callback(toolInfo); |
338 | }, | |
339 | failure: function(){ | |
996b0fd9 | 340 | |
6831c7cd CS |
341 | } |
342 | } | |
343 | }); | |
344 | } | |
996b0fd9 | 345 | |
6831c7cd | 346 | }; |
61eb12d4 | 347 | })(); |