MDL-63387 enrol_cohort: show both roles names in instance config form.
[moodle.git] / enrol / cohort / 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 * Cohort enrolment plugin.
19 *
e7193380 20 * @package enrol_cohort
465f5088
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 26
7efd0c0a
AG
27/**
28 * COHORT_CREATEGROUP constant for automatically creating a group for a cohort.
29 */
30define('COHORT_CREATE_GROUP', -1);
31
df997f84
PS
32/**
33 * Cohort enrolment plugin implementation.
34 * @author Petr Skoda
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 */
37class enrol_cohort_plugin extends enrol_plugin {
ee9e079d
DN
38
39 /**
40 * Is it possible to delete enrol instance via standard UI?
41 *
42 * @param stdClass $instance
43 * @return bool
44 */
45 public function can_delete_instance($instance) {
46 $context = context_course::instance($instance->courseid);
47 return has_capability('enrol/cohort:config', $context);
48 }
49
df997f84 50 /**
e7193380 51 * Returns localised name of enrol instance.
df997f84 52 *
e7193380 53 * @param stdClass $instance (null is accepted too)
df997f84
PS
54 * @return string
55 */
56 public function get_instance_name($instance) {
57 global $DB;
58
59 if (empty($instance)) {
60 $enrol = $this->get_name();
61 return get_string('pluginname', 'enrol_'.$enrol);
7cfe6404 62
df997f84
PS
63 } else if (empty($instance->name)) {
64 $enrol = $this->get_name();
7881024e 65 $cohort = $DB->get_record('cohort', array('id'=>$instance->customint1));
48391606
PS
66 if (!$cohort) {
67 return get_string('pluginname', 'enrol_'.$enrol);
68 }
7881024e 69 $cohortname = format_string($cohort->name, true, array('context'=>context::instance_by_id($cohort->contextid)));
df997f84 70 if ($role = $DB->get_record('role', array('id'=>$instance->roleid))) {
f6a2d57b 71 $role = role_get_name($role, context_course::instance($instance->courseid, IGNORE_MISSING), ROLENAME_BOTH);
7881024e 72 return get_string('pluginname', 'enrol_'.$enrol) . ' (' . $cohortname . ' - ' . $role .')';
df997f84 73 } else {
7881024e 74 return get_string('pluginname', 'enrol_'.$enrol) . ' (' . $cohortname . ')';
df997f84
PS
75 }
76
df997f84 77 } else {
7881024e 78 return format_string($instance->name, true, array('context'=>context_course::instance($instance->courseid)));
df997f84
PS
79 }
80 }
81
b69ca6be 82 /**
e7193380 83 * Given a courseid this function returns true if the user is able to enrol or configure cohorts.
b69ca6be
SH
84 * AND there are cohorts that the user can view.
85 *
86 * @param int $courseid
87 * @return bool
88 */
60010fd6 89 public function can_add_instance($courseid) {
80f98467
MG
90 global $CFG;
91 require_once($CFG->dirroot . '/cohort/lib.php');
55bcef29 92 $coursecontext = context_course::instance($courseid);
465f5088 93 if (!has_capability('moodle/course:enrolconfig', $coursecontext) or !has_capability('enrol/cohort:config', $coursecontext)) {
b69ca6be 94 return false;
df997f84 95 }
3e70f579 96 return cohort_get_available_cohorts($coursecontext, 0, 0, 1) ? true : false;
df997f84
PS
97 }
98
60010fd6
DW
99 /**
100 * Add new instance of enrol plugin.
101 * @param object $course
102 * @param array $fields instance fields
103 * @return int id of new instance, null if can not be created
104 */
105 public function add_instance($course, array $fields = null) {
e3f24ac0 106 global $CFG;
60010fd6
DW
107
108 if (!empty($fields['customint2']) && $fields['customint2'] == COHORT_CREATE_GROUP) {
109 // Create a new group for the cohort if requested.
110 $context = context_course::instance($course->id);
111 require_capability('moodle/course:managegroups', $context);
112 $groupid = enrol_cohort_create_new_group($course->id, $fields['customint1']);
113 $fields['customint2'] = $groupid;
114 }
115
e3f24ac0
DW
116 $result = parent::add_instance($course, $fields);
117
118 require_once("$CFG->dirroot/enrol/cohort/locallib.php");
119 $trace = new null_progress_trace();
120 enrol_cohort_sync($trace, $course->id);
121 $trace->finished();
122
123 return $result;
60010fd6
DW
124 }
125
126 /**
127 * Update instance of enrol plugin.
128 * @param stdClass $instance
129 * @param stdClass $data modified instance fields
130 * @return boolean
131 */
132 public function update_instance($instance, $data) {
72fcaae7
DW
133 global $CFG;
134
60010fd6
DW
135 // NOTE: no cohort changes here!!!
136 $context = context_course::instance($instance->courseid);
137 if ($data->roleid != $instance->roleid) {
138 // The sync script can only add roles, for perf reasons it does not modify them.
139 $params = array(
140 'contextid' => $context->id,
141 'roleid' => $instance->roleid,
142 'component' => 'enrol_cohort',
143 'itemid' => $instance->id
144 );
145 role_unassign_all($params);
146 }
147 // Create a new group for the cohort if requested.
148 if ($data->customint2 == COHORT_CREATE_GROUP) {
149 require_capability('moodle/course:managegroups', $context);
150 $groupid = enrol_cohort_create_new_group($instance->courseid, $data->customint1);
151 $data->customint2 = $groupid;
152 }
153
e3f24ac0
DW
154 $result = parent::update_instance($instance, $data);
155
156 require_once("$CFG->dirroot/enrol/cohort/locallib.php");
157 $trace = new null_progress_trace();
ebdbc82b 158 enrol_cohort_sync($trace, $instance->courseid);
e3f24ac0
DW
159 $trace->finished();
160
161 return $result;
60010fd6
DW
162 }
163
df997f84
PS
164 /**
165 * Called after updating/inserting course.
166 *
167 * @param bool $inserted true if course just inserted
e7193380
PS
168 * @param stdClass $course
169 * @param stdClass $data form data
df997f84
PS
170 * @return void
171 */
172 public function course_updated($inserted, $course, $data) {
d94ef563 173 // It turns out there is no need for cohorts to deal with this hook, see MDL-34870.
df997f84 174 }
b69ca6be 175
7cfe6404
PS
176 /**
177 * Update instance status
178 *
179 * @param stdClass $instance
180 * @param int $newstatus ENROL_INSTANCE_ENABLED, ENROL_INSTANCE_DISABLED
181 * @return void
182 */
183 public function update_status($instance, $newstatus) {
184 global $CFG;
185
186 parent::update_status($instance, $newstatus);
187
188 require_once("$CFG->dirroot/enrol/cohort/locallib.php");
c6116231
PS
189 $trace = new null_progress_trace();
190 enrol_cohort_sync($trace, $instance->courseid);
191 $trace->finished();
7cfe6404
PS
192 }
193
194 /**
195 * Does this plugin allow manual unenrolment of a specific user?
196 * Yes, but only if user suspended...
197 *
198 * @param stdClass $instance course enrol instance
199 * @param stdClass $ue record from user_enrolments table
200 *
201 * @return bool - true means user with 'enrol/xxx:unenrol' may unenrol this user, false means nobody may touch this user enrolment
202 */
203 public function allow_unenrol_user(stdClass $instance, stdClass $ue) {
204 if ($ue->status == ENROL_USER_SUSPENDED) {
205 return true;
206 }
207
208 return false;
209 }
210
94335e5a
PS
211 /**
212 * Restore instance and map settings.
213 *
214 * @param restore_enrolments_structure_step $step
215 * @param stdClass $data
216 * @param stdClass $course
217 * @param int $oldid
218 */
219 public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
220 global $DB, $CFG;
221
222 if (!$step->get_task()->is_samesite()) {
223 // No cohort restore from other sites.
224 $step->set_mapping('enrol', $oldid, 0);
225 return;
226 }
227
7881024e
PS
228 if (!empty($data->customint2)) {
229 $data->customint2 = $step->get_mappingid('group', $data->customint2);
230 }
231
94335e5a
PS
232 if ($data->roleid and $DB->record_exists('cohort', array('id'=>$data->customint1))) {
233 $instance = $DB->get_record('enrol', array('roleid'=>$data->roleid, 'customint1'=>$data->customint1, 'courseid'=>$course->id, 'enrol'=>$this->get_name()));
234 if ($instance) {
9549090d 235 $instanceid = $instance->id;
94335e5a
PS
236 } else {
237 $instanceid = $this->add_instance($course, (array)$data);
238 }
239 $step->set_mapping('enrol', $oldid, $instanceid);
240
241 require_once("$CFG->dirroot/enrol/cohort/locallib.php");
c6116231
PS
242 $trace = new null_progress_trace();
243 enrol_cohort_sync($trace, $course->id);
244 $trace->finished();
94335e5a
PS
245
246 } else if ($this->get_config('unenrolaction') == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
247 $data->customint1 = 0;
248 $instance = $DB->get_record('enrol', array('roleid'=>$data->roleid, 'customint1'=>$data->customint1, 'courseid'=>$course->id, 'enrol'=>$this->get_name()));
249
250 if ($instance) {
9549090d 251 $instanceid = $instance->id;
94335e5a
PS
252 } else {
253 $data->status = ENROL_INSTANCE_DISABLED;
254 $instanceid = $this->add_instance($course, (array)$data);
255 }
256 $step->set_mapping('enrol', $oldid, $instanceid);
df997f84 257
94335e5a 258 require_once("$CFG->dirroot/enrol/cohort/locallib.php");
c6116231
PS
259 $trace = new null_progress_trace();
260 enrol_cohort_sync($trace, $course->id);
261 $trace->finished();
94335e5a
PS
262
263 } else {
264 $step->set_mapping('enrol', $oldid, 0);
265 }
266 }
267
268 /**
269 * Restore user enrolment.
270 *
271 * @param restore_enrolments_structure_step $step
272 * @param stdClass $data
273 * @param stdClass $instance
274 * @param int $oldinstancestatus
275 * @param int $userid
276 */
277 public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
278 global $DB;
279
280 if ($this->get_config('unenrolaction') != ENROL_EXT_REMOVED_SUSPENDNOROLES) {
281 // Enrolments were already synchronised in restore_instance(), we do not want any suspended leftovers.
282 return;
283 }
284
285 // ENROL_EXT_REMOVED_SUSPENDNOROLES means all previous enrolments are restored
286 // but without roles and suspended.
287
288 if (!$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
289 $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, ENROL_USER_SUSPENDED);
290 }
291 }
7881024e
PS
292
293 /**
294 * Restore user group membership.
295 * @param stdClass $instance
296 * @param int $groupid
297 * @param int $userid
298 */
299 public function restore_group_member($instance, $groupid, $userid) {
300 // Nothing to do here, the group members are added in $this->restore_group_restored()
301 return;
302 }
b5a289c4
DNA
303
304 /**
305 * Is it possible to hide/show enrol instance via standard UI?
306 *
307 * @param stdClass $instance
308 * @return bool
309 */
310 public function can_hide_show_instance($instance) {
311 $context = context_course::instance($instance->courseid);
312 return has_capability('enrol/cohort:config', $context);
313 }
60010fd6
DW
314
315 /**
316 * Return an array of valid options for the status.
317 *
318 * @return array
319 */
320 protected function get_status_options() {
321 $options = array(ENROL_INSTANCE_ENABLED => get_string('yes'),
322 ENROL_INSTANCE_DISABLED => get_string('no'));
323 return $options;
324 }
325
326 /**
327 * Return an array of valid options for the cohorts.
328 *
329 * @param stdClass $instance
330 * @param context $context
331 * @return array
332 */
333 protected function get_cohort_options($instance, $context) {
334 global $DB, $CFG;
335
336 require_once($CFG->dirroot . '/cohort/lib.php');
337
338 $cohorts = array();
339
340 if ($instance->id) {
341 if ($cohort = $DB->get_record('cohort', array('id' => $instance->customint1))) {
342 $name = format_string($cohort->name, true, array('context' => context::instance_by_id($cohort->contextid)));
343 $cohorts = array($instance->customint1 => $name);
344 } else {
345 $cohorts = array($instance->customint1 => get_string('error'));
346 }
347 } else {
348 $cohorts = array('' => get_string('choosedots'));
349 $allcohorts = cohort_get_available_cohorts($context, 0, 0, 0);
350 foreach ($allcohorts as $c) {
351 $cohorts[$c->id] = format_string($c->name);
352 }
353 }
354 return $cohorts;
355 }
356
357 /**
358 * Return an array of valid options for the roles.
359 *
360 * @param stdClass $instance
361 * @param context $coursecontext
362 * @return array
363 */
364 protected function get_role_options($instance, $coursecontext) {
365 global $DB;
366
f6a2d57b 367 $roles = get_assignable_roles($coursecontext, ROLENAME_BOTH);
60010fd6
DW
368 $roles[0] = get_string('none');
369 $roles = array_reverse($roles, true); // Descending default sortorder.
f6a2d57b
PH
370
371 // If the instance is already configured, but the configured role is no longer assignable in the course then add it back.
60010fd6
DW
372 if ($instance->id and !isset($roles[$instance->roleid])) {
373 if ($role = $DB->get_record('role', array('id' => $instance->roleid))) {
f6a2d57b 374 $roles[$instance->roleid] = role_get_name($role, $coursecontext, ROLENAME_BOTH);
60010fd6
DW
375 } else {
376 $roles[$instance->roleid] = get_string('error');
377 }
378 }
379
380 return $roles;
381 }
382
383 /**
384 * Return an array of valid options for the groups.
385 *
386 * @param context $coursecontext
387 * @return array
388 */
389 protected function get_group_options($coursecontext) {
390 $groups = array(0 => get_string('none'));
391 if (has_capability('moodle/course:managegroups', $coursecontext)) {
392 $groups[COHORT_CREATE_GROUP] = get_string('creategroup', 'enrol_cohort');
393 }
394
395 foreach (groups_get_all_groups($coursecontext->instanceid) as $group) {
396 $groups[$group->id] = format_string($group->name, true, array('context' => $coursecontext));
397 }
398
399 return $groups;
400 }
401
402 /**
403 * We are a good plugin and don't invent our own UI/validation code path.
404 *
405 * @return boolean
406 */
407 public function use_standard_editing_ui() {
408 return true;
409 }
410
411 /**
412 * Add elements to the edit instance form.
413 *
414 * @param stdClass $instance
415 * @param MoodleQuickForm $mform
416 * @param context $coursecontext
417 * @return bool
418 */
419 public function edit_instance_form($instance, MoodleQuickForm $mform, $coursecontext) {
420 global $DB;
421
422 $mform->addElement('text', 'name', get_string('custominstancename', 'enrol'));
423 $mform->setType('name', PARAM_TEXT);
424
425 $options = $this->get_status_options();
426 $mform->addElement('select', 'status', get_string('status', 'enrol_cohort'), $options);
427
1a11bf37
DM
428 $options = ['contextid' => $coursecontext->id, 'multiple' => false];
429 $mform->addElement('cohort', 'customint1', get_string('cohort', 'cohort'), $options);
430
60010fd6
DW
431 if ($instance->id) {
432 $mform->setConstant('customint1', $instance->customint1);
433 $mform->hardFreeze('customint1', $instance->customint1);
434 } else {
435 $mform->addRule('customint1', get_string('required'), 'required', null, 'client');
436 }
437
438 $roles = $this->get_role_options($instance, $coursecontext);
439 $mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_cohort'), $roles);
440 $mform->setDefault('roleid', $this->get_config('roleid'));
441 $groups = $this->get_group_options($coursecontext);
442 $mform->addElement('select', 'customint2', get_string('addgroup', 'enrol_cohort'), $groups);
443 }
444
445 /**
446 * Perform custom validation of the data used to edit the instance.
447 *
448 * @param array $data array of ("fieldname" => value) of submitted data
449 * @param array $files array of uploaded files "element_name" => tmp_file_path
450 * @param object $instance The instance loaded from the DB
451 * @param context $context The context of the instance we are editing
452 * @return array of "element_name" => "error_description" if there are errors,
453 * or an empty array if everything is OK.
454 * @return void
455 */
456 public function edit_instance_validation($data, $files, $instance, $context) {
457 global $DB;
458 $errors = array();
459
460 $params = array(
461 'roleid' => $data['roleid'],
462 'customint1' => $data['customint1'],
463 'courseid' => $data['courseid'],
464 'id' => $data['id']
465 );
466 $sql = "roleid = :roleid AND customint1 = :customint1 AND courseid = :courseid AND enrol = 'cohort' AND id <> :id";
467 if ($DB->record_exists_select('enrol', $sql, $params)) {
468 $errors['roleid'] = get_string('instanceexists', 'enrol_cohort');
469 }
470 $validstatus = array_keys($this->get_status_options());
471 $validcohorts = array_keys($this->get_cohort_options($instance, $context));
472 $validroles = array_keys($this->get_role_options($instance, $context));
473 $validgroups = array_keys($this->get_group_options($context));
474 $tovalidate = array(
475 'name' => PARAM_TEXT,
476 'status' => $validstatus,
477 'customint1' => $validcohorts,
478 'roleid' => $validroles,
479 'customint2' => $validgroups
480 );
481 $typeerrors = $this->validate_param_types($data, $tovalidate);
482 $errors = array_merge($errors, $typeerrors);
483
484 return $errors;
485 }
7881024e
PS
486}
487
488/**
489 * Prevent removal of enrol roles.
490 * @param int $itemid
491 * @param int $groupid
492 * @param int $userid
493 * @return bool
494 */
495function enrol_cohort_allow_group_member_remove($itemid, $groupid, $userid) {
496 return false;
94335e5a 497}
73985b14 498
df9dbc9f
AG
499/**
500 * Create a new group with the cohorts name.
501 *
502 * @param int $courseid
503 * @param int $cohortid
504 * @return int $groupid Group ID for this cohort.
505 */
73985b14 506function enrol_cohort_create_new_group($courseid, $cohortid) {
60010fd6
DW
507 global $DB, $CFG;
508
509 require_once($CFG->dirroot . '/group/lib.php');
7efd0c0a
AG
510
511 $groupname = $DB->get_field('cohort', 'name', array('id' => $cohortid), MUST_EXIST);
512 $a = new stdClass();
513 $a->name = $groupname;
514 $a->increment = '';
5894432c 515 $groupname = trim(get_string('defaultgroupnametext', 'enrol_cohort', $a));
42c92eef 516 $inc = 1;
7efd0c0a 517 // Check to see if the cohort group name already exists. Add an incremented number if it does.
42c92eef
MG
518 while ($DB->record_exists('groups', array('name' => $groupname, 'courseid' => $courseid))) {
519 $a->increment = '(' . (++$inc) . ')';
5894432c 520 $newshortname = trim(get_string('defaultgroupnametext', 'enrol_cohort', $a));
7efd0c0a 521 $groupname = $newshortname;
73985b14 522 }
7efd0c0a
AG
523 // Create a new group for the cohort.
524 $groupdata = new stdClass();
525 $groupdata->courseid = $courseid;
526 $groupdata->name = $groupname;
527 $groupid = groups_create_group($groupdata);
528
73985b14 529 return $groupid;
32b97a03 530}