MDL-36622 usability: Adaptations following new icons
[moodle.git] / enrol / manual / lib.php
CommitLineData
df997f84 1<?php
df997f84
PS
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 * Manual enrolment plugin main library file.
19 *
6f6c9e5c 20 * @package enrol_manual
eafb7a72
PS
21 * @copyright 2010 Petr Skoda {@link http://skodak.org}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
df997f84
PS
23 */
24
97795859 25defined('MOODLE_INTERNAL') || die();
df997f84
PS
26
27class enrol_manual_plugin extends enrol_plugin {
28
129e6d36 29 protected $lasternoller = null;
8c04252c 30 protected $lasternollerinstanceid = 0;
129e6d36 31
df997f84 32 public function roles_protected() {
6f6c9e5c 33 // Users may tweak the roles later.
df997f84
PS
34 return false;
35 }
36
91b99e80 37 public function allow_enrol(stdClass $instance) {
6f6c9e5c 38 // Users with enrol cap may unenrol other users manually manually.
91b99e80
PS
39 return true;
40 }
41
df997f84 42 public function allow_unenrol(stdClass $instance) {
6f6c9e5c 43 // Users with unenrol cap may unenrol other users manually manually.
df997f84
PS
44 return true;
45 }
46
47 public function allow_manage(stdClass $instance) {
6f6c9e5c 48 // Users with manage cap may tweak period and status.
df997f84
PS
49 return true;
50 }
51
52 /**
53 * Returns link to manual enrol UI if exists.
54 * Does the access control tests automatically.
55 *
6f6c9e5c 56 * @param stdClass $instance
df997f84
PS
57 * @return moodle_url
58 */
59 public function get_manual_enrol_link($instance) {
60 $name = $this->get_name();
61 if ($instance->enrol !== $name) {
62 throw new coding_exception('invalid enrol instance!');
63 }
64
df997f84
PS
65 if (!enrol_is_enabled($name)) {
66 return NULL;
67 }
68
55bcef29 69 $context = context_course::instance($instance->courseid, MUST_EXIST);
df997f84 70
91b99e80 71 if (!has_capability('enrol/manual:manage', $context) or !has_capability('enrol/manual:enrol', $context) or !has_capability('enrol/manual:unenrol', $context)) {
df997f84
PS
72 return NULL;
73 }
74
75 return new moodle_url('/enrol/manual/manage.php', array('enrolid'=>$instance->id, 'id'=>$instance->courseid));
76 }
77
2d4b1f3e
PS
78 /**
79 * Returns enrolment instance manage link.
80 *
81 * By defaults looks for manage.php file and tests for manage capability.
82 *
282b5cc7
PS
83 * @param navigation_node $instancesnode
84 * @param stdClass $instance
2d4b1f3e
PS
85 * @return moodle_url;
86 */
87 public function add_course_navigation($instancesnode, stdClass $instance) {
eafb7a72 88 if ($instance->enrol !== 'manual') {
2d4b1f3e
PS
89 throw new coding_exception('Invalid enrol instance type!');
90 }
91
55bcef29 92 $context = context_course::instance($instance->courseid);
eafb7a72
PS
93 if (has_capability('enrol/manual:config', $context)) {
94 $managelink = new moodle_url('/enrol/manual/edit.php', array('courseid'=>$instance->courseid));
95 $instancesnode->add($this->get_instance_name($instance), $managelink, navigation_node::TYPE_SETTING);
2d4b1f3e 96 }
2d4b1f3e
PS
97 }
98
99 /**
6f6c9e5c 100 * Returns edit icons for the page with list of instances.
2d4b1f3e
PS
101 * @param stdClass $instance
102 * @return array
103 */
104 public function get_action_icons(stdClass $instance) {
105 global $OUTPUT;
106
eafb7a72 107 if ($instance->enrol !== 'manual') {
2d4b1f3e
PS
108 throw new coding_exception('invalid enrol instance!');
109 }
55bcef29 110 $context = context_course::instance($instance->courseid);
eafb7a72
PS
111
112 $icons = array();
113
114 if (has_capability('enrol/manual:manage', $context)) {
115 $managelink = new moodle_url("/enrol/manual/manage.php", array('enrolid'=>$instance->id));
c42651d6 116 $icons[] = $OUTPUT->action_icon($managelink, new pix_icon('t/enrolusers', get_string('enrolusers', 'enrol_manual'), 'core', array('class'=>'iconsmall')));
eafb7a72
PS
117 }
118 if (has_capability('enrol/manual:config', $context)) {
119 $editlink = new moodle_url("/enrol/manual/edit.php", array('courseid'=>$instance->courseid));
120 $icons[] = $OUTPUT->action_icon($editlink, new pix_icon('i/edit', get_string('edit'), 'core', array('class'=>'icon')));
2d4b1f3e
PS
121 }
122
eafb7a72 123 return $icons;
2d4b1f3e
PS
124 }
125
df997f84
PS
126 /**
127 * Returns link to page which may be used to add new instance of enrolment plugin in course.
128 * @param int $courseid
129 * @return moodle_url page url
130 */
e25f2466 131 public function get_newinstance_link($courseid) {
df997f84
PS
132 global $DB;
133
55bcef29 134 $context = context_course::instance($courseid, MUST_EXIST);
eafb7a72
PS
135
136 if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/manual:config', $context)) {
df997f84
PS
137 return NULL;
138 }
139
140 if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'manual'))) {
141 return NULL;
142 }
143
eafb7a72 144 return new moodle_url('/enrol/manual/edit.php', array('courseid'=>$courseid));
df997f84
PS
145 }
146
147 /**
148 * Add new instance of enrol plugin with default settings.
6f6c9e5c 149 * @param stdClass $course
0848a196 150 * @return int id of new instance, null if can not be created
df997f84
PS
151 */
152 public function add_default_instance($course) {
9f4a3c73
PS
153 $expirynotify = $this->get_config('expirynotify', 0);
154 if ($expirynotify == 2) {
155 $expirynotify = 1;
156 $notifyall = 1;
157 } else {
158 $notifyall = 0;
159 }
129e6d36
PS
160 $fields = array(
161 'status' => $this->get_config('status'),
162 'roleid' => $this->get_config('roleid', 0),
163 'enrolperiod' => $this->get_config('enrolperiod', 0),
9f4a3c73
PS
164 'expirynotify' => $expirynotify,
165 'notifyall' => $notifyall,
129e6d36
PS
166 'expirythreshold' => $this->get_config('expirythreshold', 86400),
167 );
df997f84
PS
168 return $this->add_instance($course, $fields);
169 }
170
0848a196
PS
171 /**
172 * Add new instance of enrol plugin.
6f6c9e5c 173 * @param stdClass $course
0848a196
PS
174 * @param array instance fields
175 * @return int id of new instance, null if can not be created
176 */
177 public function add_instance($course, array $fields = NULL) {
178 global $DB;
179
180 if ($DB->record_exists('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'))) {
181 // only one instance allowed, sorry
182 return NULL;
183 }
184
185 return parent::add_instance($course, $fields);
186 }
b69ca6be
SH
187
188 /**
189 * Returns a button to manually enrol users through the manual enrolment plugin.
190 *
191 * By default the first manual enrolment plugin instance available in the course is used.
192 * If no manual enrolment instances exist within the course then false is returned.
193 *
194 * This function also adds a quickenrolment JS ui to the page so that users can be enrolled
195 * via AJAX.
196 *
b69ca6be
SH
197 * @param course_enrolment_manager $manager
198 * @return enrol_user_button
199 */
200 public function get_manual_enrol_button(course_enrolment_manager $manager) {
d4c98cff
SH
201 global $CFG;
202
b69ca6be
SH
203 $instance = null;
204 $instances = array();
205 foreach ($manager->get_enrolment_instances() as $tempinstance) {
206 if ($tempinstance->enrol == 'manual') {
207 if ($instance === null) {
208 $instance = $tempinstance;
209 }
210 $instances[] = array('id' => $tempinstance->id, 'name' => $this->get_instance_name($tempinstance));
211 }
212 }
213 if (empty($instance)) {
214 return false;
215 }
216
1e3c41c0
PS
217 if (!$manuallink = $this->get_manual_enrol_link($instance)) {
218 return false;
219 }
220
221 $button = new enrol_user_button($manuallink, get_string('enrolusers', 'enrol_manual'), 'get');
b69ca6be
SH
222 $button->class .= ' enrol_manual_plugin';
223
224 $startdate = $manager->get_course()->startdate;
225 $startdateoptions = array();
226 $timeformat = get_string('strftimedatefullshort');
227 if ($startdate > 0) {
b69ca6be
SH
228 $startdateoptions[2] = get_string('coursestart') . ' (' . userdate($startdate, $timeformat) . ')';
229 }
4f500c25
PS
230 $today = time();
231 $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
b69ca6be 232 $startdateoptions[3] = get_string('today') . ' (' . userdate($today, $timeformat) . ')' ;
2e7e9403 233 $defaultduration = $instance->enrolperiod > 0 ? $instance->enrolperiod / 86400 : '';
b69ca6be
SH
234
235 $modules = array('moodle-enrol_manual-quickenrolment', 'moodle-enrol_manual-quickenrolment-skin');
236 $arguments = array(
d4c98cff
SH
237 'instances' => $instances,
238 'courseid' => $instance->courseid,
239 'ajaxurl' => '/enrol/manual/ajax.php',
240 'url' => $manager->get_moodlepage()->url->out(false),
241 'optionsStartDate' => $startdateoptions,
242 'defaultRole' => $instance->roleid,
2e7e9403 243 'defaultDuration' => $defaultduration,
dbe93bc7
CF
244 'disableGradeHistory' => $CFG->disablegradehistory,
245 'recoverGradesDefault'=> ''
b69ca6be 246 );
aad1a1f7 247
dbe93bc7
CF
248 if ($CFG->recovergradesdefault) {
249 $arguments['recoverGradesDefault'] = ' checked="checked"';
250 }
aad1a1f7 251
b69ca6be
SH
252 $function = 'M.enrol_manual.quickenrolment.init';
253 $button->require_yui_module($modules, $function, array($arguments));
254 $button->strings_for_js(array(
255 'ajaxoneuserfound',
256 'ajaxxusersfound',
257 'ajaxnext25',
258 'enrol',
259 'enrolmentoptions',
260 'enrolusers',
261 'errajaxfailedenrol',
262 'errajaxsearch',
263 'none',
264 'usersearch',
265 'unlimitedduration',
266 'startdatetoday',
267 'durationdays',
d4c98cff
SH
268 'enrolperiod',
269 'finishenrollingusers',
270 'recovergrades'), 'enrol');
b69ca6be
SH
271 $button->strings_for_js('assignroles', 'role');
272 $button->strings_for_js('startingfrom', 'moodle');
273
274 return $button;
275 }
24a0c1ea 276
5ddd9921
PS
277 /**
278 * Enrol cron support.
279 * @return void
280 */
281 public function cron() {
282 $this->sync(null, true);
8e941204 283 $this->send_expiry_notifications(true);
5ddd9921
PS
284 }
285
286 /**
287 * Sync all meta course links.
288 *
289 * @param int $courseid one course, empty mean all
290 * @param bool $verbose verbose CLI output
291 * @return int 0 means ok, 1 means error, 2 means plugin disabled
292 */
293 public function sync($courseid = null, $verbose = false) {
294 global $DB;
295
296 if (!enrol_is_enabled('manual')) {
297 return 2;
298 }
299
300 // Unfortunately this may take a long time, execution can be interrupted safely here.
301 @set_time_limit(0);
302 raise_memory_limit(MEMORY_HUGE);
303
304 if ($verbose) {
305 mtrace('Verifying manual enrolment expiration...');
306 }
307
308 $params = array('now'=>time(), 'useractive'=>ENROL_USER_ACTIVE, 'courselevel'=>CONTEXT_COURSE);
309 $coursesql = "";
310 if ($courseid) {
311 $coursesql = "AND e.courseid = :courseid";
312 $params['courseid'] = $courseid;
313 }
314
315 // Deal with expired accounts.
316 $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP);
317
318 if ($action == ENROL_EXT_REMOVED_UNENROL) {
319 $instances = array();
320 $sql = "SELECT ue.*, e.courseid, c.id AS contextid
321 FROM {user_enrolments} ue
322 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')
323 JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)
324 WHERE ue.timeend > 0 AND ue.timeend < :now
325 $coursesql";
326 $rs = $DB->get_recordset_sql($sql, $params);
327 foreach ($rs as $ue) {
328 if (empty($instances[$ue->enrolid])) {
329 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
330 }
331 $instance = $instances[$ue->enrolid];
332 // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here.
333 role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true);
334 $this->unenrol_user($instance, $ue->userid);
335 if ($verbose) {
336 mtrace(" unenrolling expired user $ue->userid from course $instance->courseid");
337 }
338 }
339 $rs->close();
340 unset($instances);
341
342 } else if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
343 $instances = array();
344 $sql = "SELECT ue.*, e.courseid, c.id AS contextid
345 FROM {user_enrolments} ue
346 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')
347 JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)
348 WHERE ue.timeend > 0 AND ue.timeend < :now
349 AND ue.status = :useractive
350 $coursesql";
351 $rs = $DB->get_recordset_sql($sql, $params);
352 foreach ($rs as $ue) {
353 if (empty($instances[$ue->enrolid])) {
354 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
355 }
356 $instance = $instances[$ue->enrolid];
357 // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here.
358 role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true);
359 $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
360 if ($verbose) {
361 mtrace(" suspending expired user $ue->userid in course $instance->courseid");
362 }
363 }
364 $rs->close();
365 unset($instances);
366
367 } else {
368 // ENROL_EXT_REMOVED_KEEP means no changes.
369 }
370
371 if ($verbose) {
372 mtrace('...manual enrolment updates finished.');
373 }
374
375 return 0;
376 }
377
129e6d36 378 /**
8c04252c 379 * Returns the user who is responsible for manual enrolments in given instance.
70cae084
PS
380 *
381 * Usually it is the first editing teacher - the person with "highest authority"
382 * as defined by sort_by_roleassignment_authority() having 'enrol/manual:manage'
383 * capability.
384 *
8c04252c 385 * @param int $instanceid enrolment instance id
129e6d36
PS
386 * @return stdClass user record
387 */
8c04252c
PS
388 protected function get_enroller($instanceid) {
389 global $DB;
390
391 if ($this->lasternollerinstanceid == $instanceid and $this->lasternoller) {
129e6d36
PS
392 return $this->lasternoller;
393 }
394
8c04252c
PS
395 $instance = $DB->get_record('enrol', array('id'=>$instanceid, 'enrol'=>$this->get_name()), '*', MUST_EXIST);
396 $context = context_course::instance($instance->courseid);
397
129e6d36
PS
398 if ($users = get_enrolled_users($context, 'enrol/manual:manage')) {
399 $users = sort_by_roleassignment_authority($users, $context);
400 $this->lasternoller = reset($users);
401 unset($users);
402 } else {
8c04252c 403 $this->lasternoller = parent::get_enroller($instanceid);
129e6d36
PS
404 }
405
8c04252c 406 $this->lasternollerinstanceid = $instanceid;
129e6d36
PS
407
408 return $this->lasternoller;
409 }
410
24a0c1ea 411 /**
6f6c9e5c 412 * Gets an array of the user enrolment actions.
24a0c1ea
SH
413 *
414 * @param course_enrolment_manager $manager
415 * @param stdClass $ue A user enrolment object
416 * @return array An array of user_enrolment_actions
417 */
418 public function get_user_enrolment_actions(course_enrolment_manager $manager, $ue) {
419 $actions = array();
420 $context = $manager->get_context();
421 $instance = $ue->enrolmentinstance;
422 $params = $manager->get_moodlepage()->url->params();
423 $params['ue'] = $ue->id;
282b5cc7
PS
424 if ($this->allow_unenrol_user($instance, $ue) && has_capability("enrol/manual:unenrol", $context)) {
425 $url = new moodle_url('/enrol/unenroluser.php', $params);
24a0c1ea
SH
426 $actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
427 }
428 if ($this->allow_manage($instance) && has_capability("enrol/manual:manage", $context)) {
429 $url = new moodle_url('/enrol/manual/editenrolment.php', $params);
430 $actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, array('class'=>'editenrollink', 'rel'=>$ue->id));
431 }
432 return $actions;
433 }
75ee207b
SH
434
435 /**
6f6c9e5c 436 * The manual plugin has several bulk operations that can be performed.
282b5cc7 437 * @param course_enrolment_manager $manager
75ee207b
SH
438 * @return array
439 */
440 public function get_bulk_operations(course_enrolment_manager $manager) {
441 global $CFG;
442 require_once($CFG->dirroot.'/enrol/manual/locallib.php');
443 $bulkoperations = array(
444 'editselectedusers' => new enrol_manual_editselectedusers_operation($manager, $this),
445 'deleteselectedusers' => new enrol_manual_deleteselectedusers_operation($manager, $this)
446 );
447 return $bulkoperations;
448 }
df997f84 449
7a7b8a1f
PS
450 /**
451 * Restore instance and map settings.
452 *
453 * @param restore_enrolments_structure_step $step
454 * @param stdClass $data
455 * @param stdClass $course
456 * @param int $oldid
457 */
458 public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
459 global $DB;
460 // There is only I manual enrol instance allowed per course.
461 if ($instances = $DB->get_records('enrol', array('courseid'=>$data->courseid, 'enrol'=>'manual'), 'id')) {
462 $instance = reset($instances);
463 $instanceid = $instance->id;
464 } else {
465 $instanceid = $this->add_instance($course, (array)$data);
466 }
467 $step->set_mapping('enrol', $oldid, $instanceid);
468 }
f2a9be5f 469
7a7b8a1f
PS
470 /**
471 * Restore user enrolment.
472 *
473 * @param restore_enrolments_structure_step $step
474 * @param stdClass $data
475 * @param stdClass $instance
476 * @param int $oldinstancestatus
477 * @param int $userid
478 */
479 public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
480 global $DB;
481
482 // Note: this is a bit tricky because other types may be converted to manual enrolments,
483 // and manual is restricted to one enrolment per user.
484
485 $ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid));
486 $enrol = false;
487 if ($ue and $ue->status == ENROL_USER_ACTIVE) {
488 // We do not want to restrict current active enrolments, let's kind of merge the times only.
489 // This prevents some teacher lockouts too.
490 if ($data->status == ENROL_USER_ACTIVE) {
491 if ($data->timestart > $ue->timestart) {
492 $data->timestart = $ue->timestart;
493 $enrol = true;
494 }
495
496 if ($data->timeend == 0) {
497 if ($ue->timeend != 0) {
498 $enrol = true;
499 }
500 } else if ($ue->timeend == 0) {
501 $data->timeend = 0;
502 } else if ($data->timeend < $ue->timeend) {
503 $data->timeend = $ue->timeend;
504 $enrol = true;
505 }
506 }
507 } else {
508 if ($instance->status == ENROL_INSTANCE_ENABLED and $oldinstancestatus != ENROL_INSTANCE_ENABLED) {
509 // Make sure that user enrolments are not activated accidentally,
510 // we do it only here because it is not expected that enrolments are migrated to other plugins.
511 $data->status = ENROL_USER_SUSPENDED;
512 }
513 $enrol = true;
514 }
515
516 if ($enrol) {
517 $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);
518 }
519 }
520
521 /**
522 * Restore role assignment.
523 *
524 * @param stdClass $instance
525 * @param int $roleid
526 * @param int $userid
527 * @param int $contextid
528 */
529 public function restore_role_assignment($instance, $roleid, $userid, $contextid) {
530 // This is necessary only because we may migrate other types to this instance,
531 // we do not use component in manual or self enrol.
532 role_assign($roleid, $userid, $contextid, '', 0);
f2a9be5f 533 }
7881024e
PS
534
535 /**
536 * Restore user group membership.
537 * @param stdClass $instance
538 * @param int $groupid
539 * @param int $userid
540 */
541 public function restore_group_member($instance, $groupid, $userid) {
542 global $CFG;
543 require_once("$CFG->dirroot/group/lib.php");
544
545 // This might be called when forcing restore as manual enrolments.
546
547 groups_add_member($groupid, $userid);
548 }
f2a9be5f 549}