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