Commit | Line | Data |
---|---|---|
01bb32a2 MN |
1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ | |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * LTI enrolment plugin main library file. | |
19 | * | |
20 | * @package enrol_lti | |
21 | * @copyright 2016 Mark Nelson <markn@moodle.com> | |
22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
23 | */ | |
24 | ||
6092bbc0 JP |
25 | use enrol_lti\data_connector; |
26 | use IMSGlobal\LTI\ToolProvider\ToolConsumer; | |
27 | ||
01bb32a2 MN |
28 | defined('MOODLE_INTERNAL') || die(); |
29 | ||
30 | /** | |
31 | * LTI enrolment plugin class. | |
32 | * | |
33 | * @package enrol_lti | |
34 | * @copyright 2016 Mark Nelson <markn@moodle.com> | |
35 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
36 | */ | |
37 | class enrol_lti_plugin extends enrol_plugin { | |
38 | ||
39 | /** | |
40 | * Return true if we can add a new instance to this course. | |
41 | * | |
42 | * @param int $courseid | |
43 | * @return boolean | |
44 | */ | |
45 | public function can_add_instance($courseid) { | |
46 | $context = context_course::instance($courseid, MUST_EXIST); | |
47 | return has_capability('moodle/course:enrolconfig', $context) && has_capability('enrol/lti:config', $context); | |
48 | } | |
49 | ||
50 | /** | |
51 | * Is it possible to delete enrol instance via standard UI? | |
52 | * | |
53 | * @param object $instance | |
54 | * @return bool | |
55 | */ | |
56 | public function can_delete_instance($instance) { | |
57 | $context = context_course::instance($instance->courseid); | |
58 | return has_capability('enrol/lti:config', $context); | |
59 | } | |
60 | ||
61 | /** | |
62 | * Is it possible to hide/show enrol instance via standard UI? | |
63 | * | |
64 | * @param stdClass $instance | |
65 | * @return bool | |
66 | */ | |
67 | public function can_hide_show_instance($instance) { | |
68 | $context = context_course::instance($instance->courseid); | |
69 | return has_capability('enrol/lti:config', $context); | |
70 | } | |
71 | ||
72 | /** | |
73 | * Returns true if it's possible to unenrol users. | |
74 | * | |
75 | * @param stdClass $instance course enrol instance | |
76 | * @return bool | |
77 | */ | |
78 | public function allow_unenrol(stdClass $instance) { | |
79 | return true; | |
80 | } | |
81 | ||
82 | /** | |
83 | * We are a good plugin and don't invent our own UI/validation code path. | |
84 | * | |
85 | * @return boolean | |
86 | */ | |
87 | public function use_standard_editing_ui() { | |
88 | return true; | |
89 | } | |
90 | ||
91 | /** | |
92 | * Add new instance of enrol plugin. | |
93 | * | |
94 | * @param object $course | |
95 | * @param array $fields instance fields | |
96 | * @return int id of new instance, null if can not be created | |
97 | */ | |
98 | public function add_instance($course, array $fields = null) { | |
99 | global $DB; | |
100 | ||
101 | $instanceid = parent::add_instance($course, $fields); | |
102 | ||
103 | // Add additional data to our table. | |
104 | $data = new stdClass(); | |
105 | $data->enrolid = $instanceid; | |
106 | $data->timecreated = time(); | |
107 | $data->timemodified = $data->timecreated; | |
108 | foreach ($fields as $field => $value) { | |
109 | $data->$field = $value; | |
110 | } | |
111 | ||
112 | $DB->insert_record('enrol_lti_tools', $data); | |
113 | ||
114 | return $instanceid; | |
115 | } | |
116 | ||
117 | /** | |
118 | * Update instance of enrol plugin. | |
119 | * | |
120 | * @param stdClass $instance | |
121 | * @param stdClass $data modified instance fields | |
122 | * @return boolean | |
123 | */ | |
124 | public function update_instance($instance, $data) { | |
125 | global $DB; | |
126 | ||
127 | parent::update_instance($instance, $data); | |
128 | ||
129 | // Remove the fields we don't want to override. | |
130 | unset($data->id); | |
131 | unset($data->timecreated); | |
132 | unset($data->timemodified); | |
133 | ||
134 | // Convert to an array we can loop over. | |
135 | $fields = (array) $data; | |
136 | ||
137 | // Update the data in our table. | |
138 | $tool = new stdClass(); | |
139 | $tool->id = $data->toolid; | |
140 | $tool->timemodified = time(); | |
141 | foreach ($fields as $field => $value) { | |
142 | $tool->$field = $value; | |
143 | } | |
144 | ||
145 | return $DB->update_record('enrol_lti_tools', $tool); | |
146 | } | |
147 | ||
148 | /** | |
149 | * Delete plugin specific information. | |
150 | * | |
151 | * @param stdClass $instance | |
152 | * @return void | |
153 | */ | |
154 | public function delete_instance($instance) { | |
155 | global $DB; | |
156 | ||
157 | // Get the tool associated with this instance. | |
158 | $tool = $DB->get_record('enrol_lti_tools', array('enrolid' => $instance->id), 'id', MUST_EXIST); | |
159 | ||
160 | // Delete any users associated with this tool. | |
161 | $DB->delete_records('enrol_lti_users', array('toolid' => $tool->id)); | |
6092bbc0 JP |
162 | |
163 | // Get tool and consumer mappings. | |
164 | $rsmapping = $DB->get_recordset('enrol_lti_tool_consumer_map', array('toolid' => $tool->id)); | |
165 | ||
166 | // Delete consumers that are linked to this tool and their related data. | |
167 | $dataconnector = new data_connector(); | |
168 | foreach ($rsmapping as $mapping) { | |
169 | $consumer = new ToolConsumer(null, $dataconnector); | |
b75d0f0d | 170 | $consumer->setRecordId($mapping->consumerid); |
6092bbc0 JP |
171 | $dataconnector->deleteToolConsumer($consumer); |
172 | } | |
173 | $rsmapping->close(); | |
174 | ||
175 | // Delete mapping records. | |
176 | $DB->delete_records('enrol_lti_tool_consumer_map', array('toolid' => $tool->id)); | |
01bb32a2 MN |
177 | |
178 | // Delete the lti tool record. | |
179 | $DB->delete_records('enrol_lti_tools', array('id' => $tool->id)); | |
180 | ||
181 | // Time for the parent to do it's thang, yeow. | |
182 | parent::delete_instance($instance); | |
183 | } | |
184 | ||
185 | /** | |
186 | * Handles un-enrolling a user. | |
187 | * | |
188 | * @param stdClass $instance | |
189 | * @param int $userid | |
190 | * @return void | |
191 | */ | |
192 | public function unenrol_user(stdClass $instance, $userid) { | |
193 | global $DB; | |
194 | ||
a8abd53a MN |
195 | // Get the tool associated with this instance. Note - it may not exist if we have deleted |
196 | // the tool. This is fine because we have already cleaned the 'enrol_lti_users' table. | |
197 | if ($tool = $DB->get_record('enrol_lti_tools', array('enrolid' => $instance->id), 'id')) { | |
198 | // Need to remove the user from the users table. | |
199 | $DB->delete_records('enrol_lti_users', array('userid' => $userid, 'toolid' => $tool->id)); | |
200 | } | |
01bb32a2 MN |
201 | |
202 | parent::unenrol_user($instance, $userid); | |
203 | } | |
204 | ||
205 | /** | |
206 | * Add elements to the edit instance form. | |
207 | * | |
208 | * @param stdClass $instance | |
209 | * @param MoodleQuickForm $mform | |
210 | * @param context $context | |
211 | * @return bool | |
212 | */ | |
213 | public function edit_instance_form($instance, MoodleQuickForm $mform, $context) { | |
214 | global $DB; | |
215 | ||
216 | $nameattribs = array('size' => '20', 'maxlength' => '255'); | |
217 | $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'), $nameattribs); | |
218 | $mform->setType('name', PARAM_TEXT); | |
219 | $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'server'); | |
220 | ||
221 | $tools = array(); | |
222 | $tools[$context->id] = get_string('course'); | |
223 | $modinfo = get_fast_modinfo($instance->courseid); | |
224 | $mods = $modinfo->get_cms(); | |
225 | foreach ($mods as $mod) { | |
226 | $tools[$mod->context->id] = format_string($mod->name); | |
227 | } | |
228 | ||
229 | $mform->addElement('select', 'contextid', get_string('tooltobeprovided', 'enrol_lti'), $tools); | |
230 | $mform->setDefault('contextid', $context->id); | |
231 | ||
232 | $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_lti'), | |
233 | array('optional' => true, 'defaultunit' => DAYSECS)); | |
234 | $mform->setDefault('enrolperiod', 0); | |
235 | $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_lti'); | |
236 | ||
237 | $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_lti'), | |
238 | array('optional' => true)); | |
239 | $mform->setDefault('enrolstartdate', 0); | |
240 | $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_lti'); | |
241 | ||
242 | $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_lti'), | |
243 | array('optional' => true)); | |
244 | $mform->setDefault('enrolenddate', 0); | |
245 | $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_lti'); | |
246 | ||
247 | $mform->addElement('text', 'maxenrolled', get_string('maxenrolled', 'enrol_lti')); | |
248 | $mform->setDefault('maxenrolled', 0); | |
249 | $mform->addHelpButton('maxenrolled', 'maxenrolled', 'enrol_lti'); | |
250 | $mform->setType('maxenrolled', PARAM_INT); | |
251 | ||
252 | $assignableroles = get_assignable_roles($context); | |
253 | ||
254 | $mform->addElement('select', 'roleinstructor', get_string('roleinstructor', 'enrol_lti'), $assignableroles); | |
255 | $mform->setDefault('roleinstructor', '3'); | |
256 | $mform->addHelpButton('roleinstructor', 'roleinstructor', 'enrol_lti'); | |
257 | ||
258 | $mform->addElement('select', 'rolelearner', get_string('rolelearner', 'enrol_lti'), $assignableroles); | |
259 | $mform->setDefault('rolelearner', '5'); | |
260 | $mform->addHelpButton('rolelearner', 'rolelearner', 'enrol_lti'); | |
261 | ||
262 | $mform->addElement('header', 'remotesystem', get_string('remotesystem', 'enrol_lti')); | |
263 | ||
264 | $mform->addElement('text', 'secret', get_string('secret', 'enrol_lti'), 'maxlength="64" size="25"'); | |
265 | $mform->setType('secret', PARAM_ALPHANUM); | |
266 | $mform->setDefault('secret', random_string(32)); | |
267 | $mform->addHelpButton('secret', 'secret', 'enrol_lti'); | |
268 | $mform->addRule('secret', get_string('required'), 'required'); | |
269 | ||
270 | $mform->addElement('selectyesno', 'gradesync', get_string('gradesync', 'enrol_lti')); | |
271 | $mform->setDefault('gradesync', 1); | |
272 | $mform->addHelpButton('gradesync', 'gradesync', 'enrol_lti'); | |
273 | ||
274 | $mform->addElement('selectyesno', 'gradesynccompletion', get_string('requirecompletion', 'enrol_lti')); | |
275 | $mform->setDefault('gradesynccompletion', 0); | |
276 | $mform->disabledIf('gradesynccompletion', 'gradesync', 0); | |
277 | ||
278 | $mform->addElement('selectyesno', 'membersync', get_string('membersync', 'enrol_lti')); | |
279 | $mform->setDefault('membersync', 1); | |
280 | $mform->addHelpButton('membersync', 'membersync', 'enrol_lti'); | |
281 | ||
282 | $options = array(); | |
283 | $options[\enrol_lti\helper::MEMBER_SYNC_ENROL_AND_UNENROL] = get_string('membersyncmodeenrolandunenrol', 'enrol_lti'); | |
284 | $options[\enrol_lti\helper::MEMBER_SYNC_ENROL_NEW] = get_string('membersyncmodeenrolnew', 'enrol_lti'); | |
285 | $options[\enrol_lti\helper::MEMBER_SYNC_UNENROL_MISSING] = get_string('membersyncmodeunenrolmissing', 'enrol_lti'); | |
286 | $mform->addElement('select', 'membersyncmode', get_string('membersyncmode', 'enrol_lti'), $options); | |
287 | $mform->setDefault('membersyncmode', \enrol_lti\helper::MEMBER_SYNC_ENROL_AND_UNENROL); | |
288 | $mform->addHelpButton('membersyncmode', 'membersyncmode', 'enrol_lti'); | |
289 | $mform->disabledIf('membersyncmode', 'membersync', 0); | |
290 | ||
291 | $mform->addElement('header', 'defaultheader', get_string('userdefaultvalues', 'enrol_lti')); | |
292 | ||
293 | $emaildisplay = get_config('enrol_lti', 'emaildisplay'); | |
294 | $choices = array( | |
295 | 0 => get_string('emaildisplayno'), | |
296 | 1 => get_string('emaildisplayyes'), | |
297 | 2 => get_string('emaildisplaycourse') | |
298 | ); | |
299 | $mform->addElement('select', 'maildisplay', get_string('emaildisplay'), $choices); | |
300 | $mform->setDefault('maildisplay', $emaildisplay); | |
301 | ||
302 | $city = get_config('enrol_lti', 'city'); | |
303 | $mform->addElement('text', 'city', get_string('city'), 'maxlength="100" size="25"'); | |
304 | $mform->setType('city', PARAM_TEXT); | |
305 | $mform->setDefault('city', $city); | |
306 | ||
307 | $country = get_config('enrol_lti', 'country'); | |
308 | $countries = array('' => get_string('selectacountry') . '...') + get_string_manager()->get_list_of_countries(); | |
309 | $mform->addElement('select', 'country', get_string('selectacountry'), $countries); | |
310 | $mform->setDefault('country', $country); | |
311 | $mform->setAdvanced('country'); | |
312 | ||
313 | $timezone = get_config('enrol_lti', 'timezone'); | |
314 | $choices = core_date::get_list_of_timezones(null, true); | |
315 | $mform->addElement('select', 'timezone', get_string('timezone'), $choices); | |
316 | $mform->setDefault('timezone', $timezone); | |
317 | $mform->setAdvanced('timezone'); | |
318 | ||
319 | $lang = get_config('enrol_lti', 'lang'); | |
320 | $mform->addElement('select', 'lang', get_string('preferredlanguage'), get_string_manager()->get_list_of_translations()); | |
321 | $mform->setDefault('lang', $lang); | |
322 | $mform->setAdvanced('lang'); | |
323 | ||
324 | $institution = get_config('enrol_lti', 'institution'); | |
325 | $mform->addElement('text', 'institution', get_string('institution'), 'maxlength="40" size="25"'); | |
326 | $mform->setType('institution', core_user::get_property_type('institution')); | |
327 | $mform->setDefault('institution', $institution); | |
328 | $mform->setAdvanced('institution'); | |
329 | ||
330 | // Check if we are editing an instance. | |
331 | if (!empty($instance->id)) { | |
332 | // Get the details from the enrol_lti_tools table. | |
333 | $ltitool = $DB->get_record('enrol_lti_tools', array('enrolid' => $instance->id), '*', MUST_EXIST); | |
334 | ||
335 | $mform->addElement('hidden', 'toolid'); | |
336 | $mform->setType('toolid', PARAM_INT); | |
337 | $mform->setConstant('toolid', $ltitool->id); | |
338 | ||
339 | $mform->setDefaults((array) $ltitool); | |
340 | } | |
341 | } | |
342 | ||
343 | /** | |
344 | * Perform custom validation of the data used to edit the instance. | |
345 | * | |
346 | * @param array $data array of ("fieldname"=>value) of submitted data | |
347 | * @param array $files array of uploaded files "element_name"=>tmp_file_path | |
348 | * @param object $instance The instance loaded from the DB | |
349 | * @param context $context The context of the instance we are editing | |
350 | * @return array of "element_name"=>"error_description" if there are errors, | |
351 | * or an empty array if everything is OK. | |
352 | * @return void | |
353 | */ | |
354 | public function edit_instance_validation($data, $files, $instance, $context) { | |
355 | global $COURSE, $DB; | |
356 | ||
357 | $errors = array(); | |
358 | ||
359 | if (!empty($data['enrolenddate']) && $data['enrolenddate'] < $data['enrolstartdate']) { | |
360 | $errors['enrolenddate'] = get_string('enrolenddateerror', 'enrol_lti'); | |
361 | } | |
362 | ||
363 | if (!empty($data['requirecompletion'])) { | |
364 | $completion = new completion_info($COURSE); | |
365 | $moodlecontext = $DB->get_record('context', array('id' => $data['contextid'])); | |
366 | if ($moodlecontext->contextlevel == CONTEXT_MODULE) { | |
367 | $cm = get_coursemodule_from_id(false, $moodlecontext->instanceid, 0, false, MUST_EXIST); | |
368 | } else { | |
369 | $cm = null; | |
370 | } | |
371 | ||
372 | if (!$completion->is_enabled($cm)) { | |
373 | $errors['requirecompletion'] = get_string('errorcompletionenabled', 'enrol_lti'); | |
374 | } | |
375 | } | |
376 | ||
377 | return $errors; | |
378 | } | |
379 | ||
380 | /** | |
381 | * Gets an array of the user enrolment actions. | |
382 | * | |
383 | * @param course_enrolment_manager $manager | |
384 | * @param stdClass $ue A user enrolment object | |
385 | * @return array An array of user_enrolment_actions | |
386 | */ | |
387 | public function get_user_enrolment_actions(course_enrolment_manager $manager, $ue) { | |
388 | $actions = array(); | |
389 | $context = $manager->get_context(); | |
390 | $instance = $ue->enrolmentinstance; | |
391 | $params = $manager->get_moodlepage()->url->params(); | |
392 | $params['ue'] = $ue->id; | |
393 | if ($this->allow_unenrol_user($instance, $ue) && has_capability("enrol/lti:unenrol", $context)) { | |
394 | $url = new moodle_url('/enrol/unenroluser.php', $params); | |
fd0a43be | 395 | $actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL); |
01bb32a2 | 396 | $actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, |
fd0a43be | 397 | $actionparams); |
01bb32a2 MN |
398 | } |
399 | if ($this->allow_manage($instance) && has_capability("enrol/lti:manage", $context)) { | |
400 | $url = new moodle_url('/enrol/editenrolment.php', $params); | |
fd0a43be JP |
401 | $actionparams = array('class' => 'editenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_EDIT); |
402 | $actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, $actionparams); | |
01bb32a2 MN |
403 | } |
404 | return $actions; | |
405 | } | |
da39029a MN |
406 | |
407 | /** | |
408 | * Restore instance and map settings. | |
409 | * | |
410 | * @param restore_enrolments_structure_step $step | |
411 | * @param stdClass $data | |
412 | * @param stdClass $course | |
413 | * @param int $oldid | |
414 | */ | |
415 | public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) { | |
416 | // We want to call the parent because we do not want to add an enrol_lti_tools row | |
417 | // as that is done as part of the restore process. | |
418 | $instanceid = parent::add_instance($course, (array)$data); | |
419 | $step->set_mapping('enrol', $oldid, $instanceid); | |
420 | } | |
01bb32a2 MN |
421 | } |
422 | ||
423 | /** | |
424 | * Display the LTI link in the course administration menu. | |
425 | * | |
426 | * @param settings_navigation $navigation The settings navigation object | |
427 | * @param stdClass $course The course | |
428 | * @param stdclass $context Course context | |
429 | */ | |
430 | function enrol_lti_extend_navigation_course($navigation, $course, $context) { | |
431 | // Check that the LTI plugin is enabled. | |
432 | if (enrol_is_enabled('lti')) { | |
433 | // Check that they can add an instance. | |
434 | $ltiplugin = enrol_get_plugin('lti'); | |
435 | if ($ltiplugin->can_add_instance($course->id)) { | |
436 | $url = new moodle_url('/enrol/lti/index.php', array('courseid' => $course->id)); | |
437 | $settingsnode = navigation_node::create(get_string('sharedexternaltools', 'enrol_lti'), $url, | |
438 | navigation_node::TYPE_SETTING, null, null, new pix_icon('i/settings', '')); | |
439 | ||
440 | $navigation->add_node($settingsnode); | |
441 | } | |
442 | } | |
443 | } |