Commit | Line | Data |
---|---|---|
b8e13a57 | 1 | <?php |
e8e0d845 AB |
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/>. | |
b8e13a57 | 16 | |
e8e0d845 AB |
17 | /** |
18 | * Flatfile enrolment plugin. | |
19 | * | |
20 | * This plugin lets the user specify a "flatfile" (CSV) containing enrolment information. | |
21 | * On a regular cron cycle, the specified file is parsed and then deleted. | |
4460eab5 | 22 | * |
917dddaa | 23 | * @package enrol_flatfile |
4460eab5 PS |
24 | * @copyright 2010 Eugene Venter |
25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
e8e0d845 | 26 | */ |
0f093efa | 27 | |
97795859 | 28 | defined('MOODLE_INTERNAL') || die(); |
4460eab5 | 29 | |
917dddaa | 30 | |
e8e0d845 AB |
31 | /** |
32 | * Flatfile enrolment plugin implementation. | |
917dddaa PS |
33 | * |
34 | * Comma separated file assumed to have four or six fields per line: | |
35 | * operation, role, idnumber(user), idnumber(course) [, starttime [, endtime]] | |
36 | * where: | |
37 | * operation = add | del | |
38 | * role = student | teacher | teacheredit | |
39 | * idnumber(user) = idnumber in the user table NB not id | |
40 | * idnumber(course) = idnumber in the course table NB not id | |
41 | * starttime = start time (in seconds since epoch) - optional | |
42 | * endtime = end time (in seconds since epoch) - optional | |
43 | * | |
e8e0d845 AB |
44 | * @author Eugene Venter - based on code by Petr Skoda, Martin Dougiamas, Martin Langhoff and others |
45 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
46 | */ | |
e8e0d845 | 47 | class enrol_flatfile_plugin extends enrol_plugin { |
917dddaa PS |
48 | protected $lasternoller = null; |
49 | protected $lasternollercourseid = 0; | |
50 | ||
51 | /** | |
52 | * Does this plugin assign protected roles are can they be manually removed? | |
53 | * @return bool - false means anybody may tweak roles, it does not use itemid and component when assigning roles | |
54 | */ | |
55 | public function roles_protected() { | |
56 | return false; | |
57 | } | |
58 | ||
59 | /** | |
60 | * Does this plugin allow manual unenrolment of all users? | |
61 | * All plugins allowing this must implement 'enrol/xxx:unenrol' capability | |
62 | * | |
63 | * @param stdClass $instance course enrol instance | |
64 | * @return bool - true means user with 'enrol/xxx:unenrol' may unenrol others freely, false means nobody may touch user_enrolments | |
65 | */ | |
66 | public function allow_unenrol(stdClass $instance) { | |
67 | return true; | |
68 | } | |
f9667a5a | 69 | |
4460eab5 | 70 | /** |
917dddaa PS |
71 | * Does this plugin allow manual unenrolment of a specific user? |
72 | * All plugins allowing this must implement 'enrol/xxx:unenrol' capability | |
73 | * | |
74 | * This is useful especially for synchronisation plugins that | |
75 | * do suspend instead of full unenrolment. | |
4460eab5 | 76 | * |
917dddaa PS |
77 | * @param stdClass $instance course enrol instance |
78 | * @param stdClass $ue record from user_enrolments table, specifies user | |
79 | * | |
80 | * @return bool - true means user with 'enrol/xxx:unenrol' may unenrol this user, false means nobody may touch this user enrolment | |
4460eab5 | 81 | */ |
917dddaa PS |
82 | public function allow_unenrol_user(stdClass $instance, stdClass $ue) { |
83 | return true; | |
84 | } | |
85 | ||
86 | /** | |
87 | * Does this plugin allow manual changes in user_enrolments table? | |
88 | * | |
89 | * All plugins allowing this must implement 'enrol/xxx:manage' capability | |
90 | * | |
91 | * @param stdClass $instance course enrol instance | |
92 | * @return bool - true means it is possible to change enrol period and status in user_enrolments table | |
93 | */ | |
94 | public function allow_manage(stdClass $instance) { | |
95 | return true; | |
96 | } | |
97 | ||
98 | /** | |
99 | * Is it possible to delete enrol instance via standard UI? | |
100 | * | |
101 | * @param object $instance | |
102 | * @return bool | |
103 | */ | |
ee9e079d DN |
104 | public function can_delete_instance($instance) { |
105 | $context = context_course::instance($instance->courseid); | |
106 | return has_capability('enrol/flatfile:manage', $context); | |
917dddaa PS |
107 | } |
108 | ||
b5a289c4 DNA |
109 | /** |
110 | * Is it possible to hide/show enrol instance via standard UI? | |
111 | * | |
112 | * @param stdClass $instance | |
113 | * @return bool | |
114 | */ | |
115 | public function can_hide_show_instance($instance) { | |
116 | $context = context_course::instance($instance->courseid); | |
117 | return has_capability('enrol/flatfile:manage', $context); | |
118 | } | |
119 | ||
917dddaa PS |
120 | /** |
121 | * Gets an array of the user enrolment actions. | |
122 | * | |
123 | * @param course_enrolment_manager $manager | |
124 | * @param stdClass $ue A user enrolment object | |
125 | * @return array An array of user_enrolment_actions | |
126 | */ | |
127 | public function get_user_enrolment_actions(course_enrolment_manager $manager, $ue) { | |
128 | $actions = array(); | |
129 | $context = $manager->get_context(); | |
130 | $instance = $ue->enrolmentinstance; | |
131 | $params = $manager->get_moodlepage()->url->params(); | |
132 | $params['ue'] = $ue->id; | |
133 | if ($this->allow_unenrol_user($instance, $ue) && has_capability("enrol/flatfile:unenrol", $context)) { | |
134 | $url = new moodle_url('/enrol/unenroluser.php', $params); | |
fd0a43be JP |
135 | $actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL); |
136 | $actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, | |
137 | $actionparams); | |
917dddaa PS |
138 | } |
139 | if ($this->allow_manage($instance) && has_capability("enrol/flatfile:manage", $context)) { | |
140 | $url = new moodle_url('/enrol/editenrolment.php', $params); | |
fd0a43be JP |
141 | $actionparams = array('class' => 'editenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_EDIT); |
142 | $actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, $actionparams); | |
917dddaa PS |
143 | } |
144 | return $actions; | |
145 | } | |
146 | ||
147 | /** | |
148 | * Enrol user into course via enrol instance. | |
149 | * | |
150 | * @param stdClass $instance | |
151 | * @param int $userid | |
152 | * @param int $roleid optional role id | |
153 | * @param int $timestart 0 means unknown | |
154 | * @param int $timeend 0 means forever | |
155 | * @param int $status default to ENROL_USER_ACTIVE for new enrolments, no change by default in updates | |
ef8a733a | 156 | * @param bool $recovergrades restore grade history |
917dddaa PS |
157 | * @return void |
158 | */ | |
ef8a733a CF |
159 | public function enrol_user(stdClass $instance, $userid, $roleid = null, $timestart = 0, $timeend = 0, $status = null, $recovergrades = null) { |
160 | parent::enrol_user($instance, $userid, null, $timestart, $timeend, $status, $recovergrades); | |
917dddaa PS |
161 | if ($roleid) { |
162 | $context = context_course::instance($instance->courseid, MUST_EXIST); | |
163 | role_assign($roleid, $userid, $context->id, 'enrol_'.$this->get_name(), $instance->id); | |
164 | } | |
165 | } | |
b6845dba | 166 | |
917dddaa PS |
167 | /** |
168 | * Execute synchronisation. | |
169 | * @param progress_trace | |
170 | * @return int exit code, 0 means ok, 2 means plugin disabled | |
171 | */ | |
172 | public function sync(progress_trace $trace) { | |
173 | if (!enrol_is_enabled('flatfile')) { | |
174 | return 2; | |
175 | } | |
176 | ||
177 | $mailadmins = $this->get_config('mailadmins', 0); | |
178 | ||
179 | if ($mailadmins) { | |
180 | $buffer = new progress_trace_buffer(new text_progress_trace(), false); | |
181 | $trace = new combined_progress_trace(array($trace, $buffer)); | |
182 | } | |
183 | ||
184 | $processed = false; | |
185 | ||
186 | $processed = $this->process_file($trace) || $processed; | |
187 | $processed = $this->process_buffer($trace) || $processed; | |
188 | $processed = $this->process_expirations($trace) || $processed; | |
189 | ||
190 | if ($processed and $mailadmins) { | |
191 | if ($log = $buffer->get_buffer()) { | |
cc350fd9 | 192 | $eventdata = new \core\message\message(); |
880fc15b | 193 | $eventdata->courseid = SITEID; |
917dddaa PS |
194 | $eventdata->modulename = 'moodle'; |
195 | $eventdata->component = 'enrol_flatfile'; | |
196 | $eventdata->name = 'flatfile_enrolment'; | |
197 | $eventdata->userfrom = get_admin(); | |
198 | $eventdata->userto = get_admin(); | |
199 | $eventdata->subject = 'Flatfile Enrolment Log'; | |
200 | $eventdata->fullmessage = $log; | |
201 | $eventdata->fullmessageformat = FORMAT_PLAIN; | |
202 | $eventdata->fullmessagehtml = ''; | |
203 | $eventdata->smallmessage = ''; | |
204 | message_send($eventdata); | |
205 | } | |
206 | $buffer->reset_buffer(); | |
207 | } | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
212 | /** | |
213 | * Sorry, we do not want to show paths in cron output. | |
214 | * | |
215 | * @param string $filepath | |
216 | * @return string | |
217 | */ | |
218 | protected function obfuscate_filepath($filepath) { | |
219 | global $CFG; | |
b6845dba | 220 | |
917dddaa PS |
221 | if (strpos($filepath, $CFG->dataroot.'/') === 0 or strpos($filepath, $CFG->dataroot.'\\') === 0) { |
222 | $disclosefile = '$CFG->dataroot'.substr($filepath, strlen($CFG->dataroot)); | |
b6845dba | 223 | |
917dddaa PS |
224 | } else if (strpos($filepath, $CFG->dirroot.'/') === 0 or strpos($filepath, $CFG->dirroot.'\\') === 0) { |
225 | $disclosefile = '$CFG->dirroot'.substr($filepath, strlen($CFG->dirroot)); | |
b6845dba | 226 | |
917dddaa PS |
227 | } else { |
228 | $disclosefile = basename($filepath); | |
229 | } | |
230 | ||
231 | return $disclosefile; | |
232 | } | |
233 | ||
234 | /** | |
235 | * Process flatfile. | |
236 | * @param progress_trace $trace | |
237 | * @return bool true if any data processed, false if not | |
238 | */ | |
239 | protected function process_file(progress_trace $trace) { | |
50c5bef4 | 240 | global $CFG, $DB; |
b8e13a57 | 241 | |
917dddaa | 242 | // We may need more memory here. |
3ef7279f | 243 | core_php_time_limit::raise(); |
917dddaa PS |
244 | raise_memory_limit(MEMORY_HUGE); |
245 | ||
e8e0d845 | 246 | $filelocation = $this->get_config('location'); |
e8e0d845 | 247 | if (empty($filelocation)) { |
917dddaa PS |
248 | // Default legacy location. |
249 | $filelocation = "$CFG->dataroot/1/enrolments.txt"; | |
b8e13a57 | 250 | } |
917dddaa | 251 | $disclosefile = $this->obfuscate_filepath($filelocation); |
b8e13a57 | 252 | |
917dddaa PS |
253 | if (!file_exists($filelocation)) { |
254 | $trace->output("Flatfile enrolments file not found: $disclosefile"); | |
255 | $trace->finished(); | |
256 | return false; | |
257 | } | |
258 | $trace->output("Processing flat file enrolments from: $disclosefile ..."); | |
b8e13a57 | 259 | |
917dddaa | 260 | $content = file_get_contents($filelocation); |
a3081bfd | 261 | |
917dddaa | 262 | if ($content !== false) { |
4317f92f | 263 | |
917dddaa | 264 | $rolemap = $this->get_role_map($trace); |
4317f92f | 265 | |
2f1e464a | 266 | $content = core_text::convert($content, $this->get_config('encoding', 'utf-8'), 'utf-8'); |
917dddaa PS |
267 | $content = str_replace("\r", '', $content); |
268 | $content = explode("\n", $content); | |
b8e13a57 | 269 | |
917dddaa PS |
270 | $line = 0; |
271 | foreach($content as $fields) { | |
272 | $line++; | |
4317f92f | 273 | |
917dddaa PS |
274 | if (trim($fields) === '') { |
275 | // Empty lines are ignored. | |
276 | continue; | |
277 | } | |
4317f92f | 278 | |
917dddaa PS |
279 | // Deal with different separators. |
280 | if (strpos($fields, ',') !== false) { | |
281 | $fields = explode(',', $fields); | |
282 | } else { | |
283 | $fields = explode(';', $fields); | |
284 | } | |
285 | ||
286 | // If a line is incorrectly formatted ie does not have 4 comma separated fields then ignore it. | |
287 | if (count($fields) < 4 or count($fields) > 6) { | |
288 | $trace->output("Line incorrectly formatted - ignoring $line", 1); | |
289 | continue; | |
290 | } | |
4317f92f | 291 | |
2f1e464a PS |
292 | $fields[0] = trim(core_text::strtolower($fields[0])); |
293 | $fields[1] = trim(core_text::strtolower($fields[1])); | |
917dddaa PS |
294 | $fields[2] = trim($fields[2]); |
295 | $fields[3] = trim($fields[3]); | |
296 | $fields[4] = isset($fields[4]) ? (int)trim($fields[4]) : 0; | |
297 | $fields[5] = isset($fields[5]) ? (int)trim($fields[5]) : 0; | |
298 | ||
299 | // Deal with quoted values - all or nothing, we need to support "' in idnumbers, sorry. | |
300 | if (strpos($fields[0], "'") === 0) { | |
301 | foreach ($fields as $k=>$v) { | |
302 | $fields[$k] = trim($v, "'"); | |
4317f92f | 303 | } |
917dddaa PS |
304 | } else if (strpos($fields[0], '"') === 0) { |
305 | foreach ($fields as $k=>$v) { | |
306 | $fields[$k] = trim($v, '"'); | |
307 | } | |
308 | } | |
b8e13a57 | 309 | |
917dddaa | 310 | $trace->output("$line: $fields[0], $fields[1], $fields[2], $fields[3], $fields[4], $fields[5]", 1); |
b8e13a57 | 311 | |
917dddaa PS |
312 | // Check correct formatting of operation field. |
313 | if ($fields[0] !== "add" and $fields[0] !== "del") { | |
314 | $trace->output("Unknown operation in field 1 - ignoring line $line", 1); | |
315 | continue; | |
316 | } | |
b8e13a57 | 317 | |
917dddaa PS |
318 | // Check correct formatting of role field. |
319 | if (!isset($rolemap[$fields[1]])) { | |
320 | $trace->output("Unknown role in field2 - ignoring line $line", 1); | |
321 | continue; | |
322 | } | |
323 | $roleid = $rolemap[$fields[1]]; | |
b8e13a57 | 324 | |
a4aaeeaa DM |
325 | if (empty($fields[2]) or !$user = $DB->get_record("user", array("idnumber"=>$fields[2], 'deleted'=>0))) { |
326 | $trace->output("Unknown user idnumber or deleted user in field 3 - ignoring line $line", 1); | |
917dddaa PS |
327 | continue; |
328 | } | |
b8e13a57 | 329 | |
917dddaa PS |
330 | if (!$course = $DB->get_record("course", array("idnumber"=>$fields[3]))) { |
331 | $trace->output("Unknown course idnumber in field 4 - ignoring line $line", 1); | |
332 | continue; | |
333 | } | |
b8e13a57 | 334 | |
917dddaa PS |
335 | if ($fields[4] > $fields[5] and $fields[5] != 0) { |
336 | $trace->output("Start time was later than end time - ignoring line $line", 1); | |
337 | continue; | |
338 | } | |
a3081bfd | 339 | |
917dddaa PS |
340 | $this->process_records($trace, $fields[0], $roleid, $user, $course, $fields[4], $fields[5]); |
341 | } | |
342 | ||
343 | unset($content); | |
344 | } | |
345 | ||
346 | if (!unlink($filelocation)) { | |
cc350fd9 | 347 | $eventdata = new \core\message\message(); |
880fc15b | 348 | $eventdata->courseid = SITEID; |
917dddaa PS |
349 | $eventdata->modulename = 'moodle'; |
350 | $eventdata->component = 'enrol_flatfile'; | |
351 | $eventdata->name = 'flatfile_enrolment'; | |
352 | $eventdata->userfrom = get_admin(); | |
353 | $eventdata->userto = get_admin(); | |
354 | $eventdata->subject = get_string('filelockedmailsubject', 'enrol_flatfile'); | |
355 | $eventdata->fullmessage = get_string('filelockedmail', 'enrol_flatfile', $filelocation); | |
356 | $eventdata->fullmessageformat = FORMAT_PLAIN; | |
357 | $eventdata->fullmessagehtml = ''; | |
358 | $eventdata->smallmessage = ''; | |
359 | message_send($eventdata); | |
360 | $trace->output("Error deleting enrolment file: $disclosefile", 1); | |
361 | } else { | |
362 | $trace->output("Deleted enrolment file", 1); | |
363 | } | |
364 | ||
365 | $trace->output("...finished enrolment file processing."); | |
366 | $trace->finished(); | |
367 | ||
368 | return true; | |
369 | } | |
370 | ||
371 | /** | |
372 | * Process any future enrollments stored in the buffer. | |
373 | * @param progress_trace $trace | |
374 | * @return bool true if any data processed, false if not | |
375 | */ | |
376 | protected function process_buffer(progress_trace $trace) { | |
377 | global $DB; | |
b8e13a57 | 378 | |
917dddaa PS |
379 | if (!$future_enrols = $DB->get_records_select('enrol_flatfile', "timestart < ?", array(time()))) { |
380 | $trace->output("No enrolments to be processed in flatfile buffer"); | |
381 | $trace->finished(); | |
382 | return false; | |
383 | } | |
b8e13a57 | 384 | |
917dddaa PS |
385 | $trace->output("Starting processing of flatfile buffer"); |
386 | foreach($future_enrols as $en) { | |
387 | $user = $DB->get_record('user', array('id'=>$en->userid)); | |
388 | $course = $DB->get_record('course', array('id'=>$en->courseid)); | |
389 | if ($user and $course) { | |
390 | $trace->output("buffer: $en->action $en->roleid $user->id $course->id $en->timestart $en->timeend", 1); | |
391 | $this->process_records($trace, $en->action, $en->roleid, $user, $course, $en->timestart, $en->timeend, false); | |
392 | } | |
393 | $DB->delete_records('enrol_flatfile', array('id'=>$en->id)); | |
394 | } | |
395 | $trace->output("Finished processing of flatfile buffer"); | |
396 | $trace->finished(); | |
b8e13a57 | 397 | |
917dddaa PS |
398 | return true; |
399 | } | |
400 | ||
401 | /** | |
402 | * Process user enrolment line. | |
403 | * | |
404 | * @param progress_trace $trace | |
405 | * @param string $action | |
406 | * @param int $roleid | |
407 | * @param stdClass $user | |
408 | * @param stdClass $course | |
409 | * @param int $timestart | |
410 | * @param int $timeend | |
411 | * @param bool $buffer_if_future | |
412 | */ | |
413 | protected function process_records(progress_trace $trace, $action, $roleid, $user, $course, $timestart, $timeend, $buffer_if_future = true) { | |
c484af5a | 414 | global $CFG, $DB; |
917dddaa PS |
415 | |
416 | // Check if timestart is for future processing. | |
417 | if ($timestart > time() and $buffer_if_future) { | |
418 | // Populate into enrol_flatfile table as a future role to be assigned by cron. | |
419 | // Note: since 2.0 future enrolments do not cause problems if you disable guest access. | |
420 | $future_en = new stdClass(); | |
421 | $future_en->action = $action; | |
422 | $future_en->roleid = $roleid; | |
423 | $future_en->userid = $user->id; | |
424 | $future_en->courseid = $course->id; | |
425 | $future_en->timestart = $timestart; | |
426 | $future_en->timeend = $timeend; | |
427 | $future_en->timemodified = time(); | |
428 | $DB->insert_record('enrol_flatfile', $future_en); | |
429 | $trace->output("User $user->id will be enrolled later into course $course->id using role $roleid ($timestart, $timeend)", 1); | |
430 | return; | |
431 | } | |
432 | ||
433 | $context = context_course::instance($course->id); | |
434 | ||
435 | if ($action === 'add') { | |
436 | // Clear the buffer just in case there were some future enrolments. | |
437 | $DB->delete_records('enrol_flatfile', array('userid'=>$user->id, 'courseid'=>$course->id, 'roleid'=>$roleid)); | |
438 | ||
439 | $instance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'flatfile')); | |
440 | if (empty($instance)) { | |
441 | // Only add an enrol instance to the course if non-existent. | |
442 | $enrolid = $this->add_instance($course); | |
443 | $instance = $DB->get_record('enrol', array('id' => $enrolid)); | |
444 | } | |
445 | ||
446 | $notify = false; | |
447 | if ($ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$user->id))) { | |
448 | // Update only. | |
7fca67ee | 449 | $this->update_user_enrol($instance, $user->id, ENROL_USER_ACTIVE, $timestart, $timeend); |
917dddaa PS |
450 | if (!$DB->record_exists('role_assignments', array('contextid'=>$context->id, 'roleid'=>$roleid, 'userid'=>$user->id, 'component'=>'enrol_flatfile', 'itemid'=>$instance->id))) { |
451 | role_assign($roleid, $user->id, $context->id, 'enrol_flatfile', $instance->id); | |
452 | } | |
453 | $trace->output("User $user->id enrolment updated in course $course->id using role $roleid ($timestart, $timeend)", 1); | |
454 | ||
455 | } else { | |
456 | // Enrol the user with this plugin instance. | |
457 | $this->enrol_user($instance, $user->id, $roleid, $timestart, $timeend); | |
458 | $trace->output("User $user->id enrolled in course $course->id using role $roleid ($timestart, $timeend)", 1); | |
459 | $notify = true; | |
460 | } | |
461 | ||
462 | if ($notify and $this->get_config('mailstudents')) { | |
c484af5a | 463 | $oldforcelang = force_current_language($user->lang); |
917dddaa PS |
464 | |
465 | // Send welcome notification to enrolled users. | |
466 | $a = new stdClass(); | |
467 | $a->coursename = format_string($course->fullname, true, array('context' => $context)); | |
468 | $a->profileurl = "$CFG->wwwroot/user/view.php?id=$user->id&course=$course->id"; | |
469 | $subject = get_string('enrolmentnew', 'enrol', format_string($course->shortname, true, array('context' => $context))); | |
b8e13a57 | 470 | |
cc350fd9 | 471 | $eventdata = new \core\message\message(); |
880fc15b | 472 | $eventdata->courseid = $course->id; |
3b120e46 | 473 | $eventdata->modulename = 'moodle'; |
ba4c451f | 474 | $eventdata->component = 'enrol_flatfile'; |
e8e0d845 | 475 | $eventdata->name = 'flatfile_enrolment'; |
917dddaa PS |
476 | $eventdata->userfrom = $this->get_enroller($course->id); |
477 | $eventdata->userto = $user; | |
478 | $eventdata->subject = $subject; | |
479 | $eventdata->fullmessage = get_string('welcometocoursetext', '', $a); | |
3b120e46 | 480 | $eventdata->fullmessageformat = FORMAT_PLAIN; |
481 | $eventdata->fullmessagehtml = ''; | |
482 | $eventdata->smallmessage = ''; | |
917dddaa PS |
483 | if (message_send($eventdata)) { |
484 | $trace->output("Notified enrolled user", 1); | |
485 | } else { | |
486 | $trace->output("Failed to notify enrolled user", 1); | |
487 | } | |
488 | ||
c484af5a | 489 | force_current_language($oldforcelang); |
b8e13a57 | 490 | } |
491 | ||
917dddaa PS |
492 | if ($notify and $this->get_config('mailteachers', 0)) { |
493 | // Notify person responsible for enrolments. | |
494 | $enroller = $this->get_enroller($course->id); | |
495 | ||
c484af5a | 496 | $oldforcelang = force_current_language($enroller->lang); |
917dddaa PS |
497 | |
498 | $a = new stdClass(); | |
499 | $a->course = format_string($course->fullname, true, array('context' => $context)); | |
500 | $a->user = fullname($user); | |
501 | $subject = get_string('enrolmentnew', 'enrol', format_string($course->shortname, true, array('context' => $context))); | |
e8e0d845 | 502 | |
cc350fd9 | 503 | $eventdata = new \core\message\message(); |
880fc15b | 504 | $eventdata->courseid = $course->id; |
3b120e46 | 505 | $eventdata->modulename = 'moodle'; |
ba4c451f | 506 | $eventdata->component = 'enrol_flatfile'; |
e8e0d845 | 507 | $eventdata->name = 'flatfile_enrolment'; |
3b120e46 | 508 | $eventdata->userfrom = get_admin(); |
917dddaa PS |
509 | $eventdata->userto = $enroller; |
510 | $eventdata->subject = $subject; | |
511 | $eventdata->fullmessage = get_string('enrolmentnewuser', 'enrol', $a); | |
3b120e46 | 512 | $eventdata->fullmessageformat = FORMAT_PLAIN; |
513 | $eventdata->fullmessagehtml = ''; | |
514 | $eventdata->smallmessage = ''; | |
917dddaa PS |
515 | if (message_send($eventdata)) { |
516 | $trace->output("Notified enroller {$eventdata->userto->id}", 1); | |
517 | } else { | |
518 | $trace->output("Failed to notify enroller {$eventdata->userto->id}", 1); | |
519 | } | |
520 | ||
c484af5a | 521 | force_current_language($oldforcelang); |
b8e13a57 | 522 | } |
917dddaa | 523 | return; |
b8e13a57 | 524 | |
917dddaa PS |
525 | } else if ($action === 'del') { |
526 | // Clear the buffer just in case there were some future enrolments. | |
527 | $DB->delete_records('enrol_flatfile', array('userid'=>$user->id, 'courseid'=>$course->id, 'roleid'=>$roleid)); | |
b8e13a57 | 528 | |
917dddaa PS |
529 | $action = $this->get_config('unenrolaction'); |
530 | if ($action == ENROL_EXT_REMOVED_KEEP) { | |
531 | $trace->output("del action is ignored", 1); | |
532 | return; | |
533 | } | |
b8e13a57 | 534 | |
917dddaa PS |
535 | // Loops through all enrolment methods, try to unenrol if roleid somehow matches. |
536 | $instances = $DB->get_records('enrol', array('courseid' => $course->id)); | |
537 | $unenrolled = false; | |
538 | foreach ($instances as $instance) { | |
539 | if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$user->id))) { | |
540 | continue; | |
541 | } | |
542 | if ($instance->enrol === 'flatfile') { | |
543 | $plugin = $this; | |
544 | } else { | |
545 | if (!enrol_is_enabled($instance->enrol)) { | |
546 | continue; | |
b6845dba | 547 | } |
917dddaa PS |
548 | if (!$plugin = enrol_get_plugin($instance->enrol)) { |
549 | continue; | |
550 | } | |
551 | if (!$plugin->allow_unenrol_user($instance, $ue)) { | |
552 | continue; | |
553 | } | |
554 | } | |
b6845dba | 555 | |
917dddaa PS |
556 | // For some reason the del action includes a role name, this complicates everything. |
557 | $componentroles = array(); | |
558 | $manualroles = array(); | |
559 | $ras = $DB->get_records('role_assignments', array('userid'=>$user->id, 'contextid'=>$context->id)); | |
560 | foreach ($ras as $ra) { | |
561 | if ($ra->component === '') { | |
562 | $manualroles[$ra->roleid] = $ra->roleid; | |
563 | } else if ($ra->component === 'enrol_'.$instance->enrol and $ra->itemid == $instance->id) { | |
564 | $componentroles[$ra->roleid] = $ra->roleid; | |
565 | } | |
566 | } | |
b6845dba | 567 | |
917dddaa PS |
568 | if ($componentroles and !isset($componentroles[$roleid])) { |
569 | // Do not unenrol using this method, user has some other protected role! | |
570 | continue; | |
b6845dba | 571 | |
917dddaa PS |
572 | } else if (empty($ras)) { |
573 | // If user does not have any roles then let's just suspend as many methods as possible. | |
b6845dba | 574 | |
917dddaa PS |
575 | } else if (!$plugin->roles_protected()) { |
576 | if (!$componentroles and $manualroles and !isset($manualroles[$roleid])) { | |
577 | // Most likely we want to keep users enrolled because they have some other course roles. | |
578 | continue; | |
579 | } | |
580 | } | |
b6845dba | 581 | |
917dddaa PS |
582 | if ($action == ENROL_EXT_REMOVED_UNENROL) { |
583 | $unenrolled = true; | |
584 | if (!$plugin->roles_protected()) { | |
585 | role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'roleid'=>$roleid, 'component'=>'', 'itemid'=>0), true); | |
586 | } | |
587 | $plugin->unenrol_user($instance, $user->id); | |
588 | $trace->output("User $user->id was unenrolled from course $course->id (enrol_$instance->enrol)", 1); | |
589 | ||
590 | } else if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) { | |
591 | if ($plugin->allow_manage($instance)) { | |
592 | if ($ue->status == ENROL_USER_ACTIVE) { | |
593 | $unenrolled = true; | |
594 | $plugin->update_user_enrol($instance, $user->id, ENROL_USER_SUSPENDED); | |
595 | if (!$plugin->roles_protected()) { | |
596 | role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_'.$instance->enrol, 'itemid'=>$instance->id), true); | |
597 | role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'roleid'=>$roleid, 'component'=>'', 'itemid'=>0), true); | |
598 | } | |
599 | $trace->output("User $user->id enrolment was suspended in course $course->id (enrol_$instance->enrol)", 1); | |
600 | } | |
601 | } | |
602 | } | |
b6845dba | 603 | } |
917dddaa PS |
604 | |
605 | if (!$unenrolled) { | |
606 | if (0 == $DB->count_records('role_assignments', array('userid'=>$user->id, 'contextid'=>$context->id))) { | |
607 | role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'', 'itemid'=>0), true); | |
608 | } | |
609 | $trace->output("User $user->id (with role $roleid) not unenrolled from course $course->id", 1); | |
b6845dba | 610 | } |
917dddaa PS |
611 | |
612 | return; | |
b6845dba | 613 | } |
917dddaa | 614 | } |
b6845dba | 615 | |
917dddaa PS |
616 | /** |
617 | * Returns the user who is responsible for flatfile enrolments in given curse. | |
618 | * | |
619 | * Usually it is the first editing teacher - the person with "highest authority" | |
620 | * as defined by sort_by_roleassignment_authority() having 'enrol/flatfile:manage' | |
621 | * or 'moodle/role:assign' capability. | |
622 | * | |
623 | * @param int $courseid enrolment instance id | |
624 | * @return stdClass user record | |
625 | */ | |
626 | protected function get_enroller($courseid) { | |
627 | if ($this->lasternollercourseid == $courseid and $this->lasternoller) { | |
628 | return $this->lasternoller; | |
b6845dba AB |
629 | } |
630 | ||
917dddaa | 631 | $context = context_course::instance($courseid); |
b6845dba | 632 | |
917dddaa PS |
633 | $users = get_enrolled_users($context, 'enrol/flatfile:manage'); |
634 | if (!$users) { | |
635 | $users = get_enrolled_users($context, 'moodle/role:assign'); | |
b6845dba | 636 | } |
b6845dba | 637 | |
917dddaa PS |
638 | if ($users) { |
639 | $users = sort_by_roleassignment_authority($users, $context); | |
640 | $this->lasternoller = reset($users); | |
641 | unset($users); | |
642 | } else { | |
643 | $this->lasternoller = get_admin(); | |
644 | } | |
645 | ||
646 | $this->lasternollercourseid == $courseid; | |
647 | ||
648 | return $this->lasternoller; | |
b6845dba AB |
649 | } |
650 | ||
a3081bfd | 651 | /** |
917dddaa | 652 | * Returns a mapping of ims roles to role ids. |
a3081bfd | 653 | * |
917dddaa PS |
654 | * @param progress_trace $trace |
655 | * @return array imsrolename=>roleid | |
a3081bfd | 656 | */ |
917dddaa | 657 | protected function get_role_map(progress_trace $trace) { |
50c5bef4 | 658 | global $DB; |
659 | ||
917dddaa PS |
660 | // Get all roles. |
661 | $rolemap = array(); | |
e8e0d845 | 662 | $roles = $DB->get_records('role', null, '', 'id, name, shortname'); |
917dddaa PS |
663 | foreach ($roles as $id=>$role) { |
664 | $alias = $this->get_config('map_'.$id, $role->shortname, ''); | |
2f1e464a | 665 | $alias = trim(core_text::strtolower($alias)); |
917dddaa PS |
666 | if ($alias === '') { |
667 | // Either not configured yet or somebody wants to skip these intentionally. | |
668 | continue; | |
669 | } | |
670 | if (isset($rolemap[$alias])) { | |
671 | $trace->output("Duplicate role alias $alias detected!"); | |
e8e0d845 | 672 | } else { |
917dddaa | 673 | $rolemap[$alias] = $id; |
e8e0d845 AB |
674 | } |
675 | } | |
a3081bfd | 676 | |
917dddaa PS |
677 | return $rolemap; |
678 | } | |
679 | ||
680 | /** | |
681 | * Restore instance and map settings. | |
682 | * | |
683 | * @param restore_enrolments_structure_step $step | |
684 | * @param stdClass $data | |
685 | * @param stdClass $course | |
686 | * @param int $oldid | |
687 | */ | |
688 | public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) { | |
689 | global $DB; | |
690 | ||
691 | if ($instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>$this->get_name()))) { | |
692 | $instanceid = $instance->id; | |
693 | } else { | |
694 | $instanceid = $this->add_instance($course); | |
a3081bfd | 695 | } |
917dddaa PS |
696 | $step->set_mapping('enrol', $oldid, $instanceid); |
697 | } | |
a3081bfd | 698 | |
917dddaa PS |
699 | /** |
700 | * Restore user enrolment. | |
701 | * | |
702 | * @param restore_enrolments_structure_step $step | |
703 | * @param stdClass $data | |
704 | * @param stdClass $instance | |
705 | * @param int $oldinstancestatus | |
706 | * @param int $userid | |
707 | */ | |
708 | public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) { | |
709 | $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status); | |
a3081bfd | 710 | } |
711 | ||
917dddaa PS |
712 | /** |
713 | * Restore role assignment. | |
714 | * | |
715 | * @param stdClass $instance | |
716 | * @param int $roleid | |
717 | * @param int $userid | |
718 | * @param int $contextid | |
719 | */ | |
720 | public function restore_role_assignment($instance, $roleid, $userid, $contextid) { | |
721 | role_assign($roleid, $userid, $contextid, 'enrol_'.$instance->enrol, $instance->id); | |
722 | } | |
723 | } |