Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Manual enrolment plugin main library file. 19 * 20 * @package enrol_manual 21 * @copyright 2010 Petr Skoda {@link http://skodak.org} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 class enrol_manual_plugin extends enrol_plugin { 28 29 protected $lasternoller = null; 30 protected $lasternollerinstanceid = 0; 31 32 public function roles_protected() { 33 // Users may tweak the roles later. 34 return false; 35 } 36 37 public function allow_enrol(stdClass $instance) { 38 // Users with enrol cap may unenrol other users manually manually. 39 return true; 40 } 41 42 public function allow_unenrol(stdClass $instance) { 43 // Users with unenrol cap may unenrol other users manually manually. 44 return true; 45 } 46 47 public function allow_manage(stdClass $instance) { 48 // Users with manage cap may tweak period and status. 49 return true; 50 } 51 52 /** 53 * Returns link to manual enrol UI if exists. 54 * Does the access control tests automatically. 55 * 56 * @param stdClass $instance 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 65 if (!enrol_is_enabled($name)) { 66 return NULL; 67 } 68 69 $context = context_course::instance($instance->courseid, MUST_EXIST); 70 71 if (!has_capability('enrol/manual:enrol', $context)) { 72 // Note: manage capability not used here because it is used for editing 73 // of existing enrolments which is not possible here. 74 return NULL; 75 } 76 77 return new moodle_url('/enrol/manual/manage.php', array('enrolid'=>$instance->id, 'id'=>$instance->courseid)); 78 } 79 80 /** 81 * Return true if we can add a new instance to this course. 82 * 83 * @param int $courseid 84 * @return boolean 85 */ 86 public function can_add_instance($courseid) { 87 global $DB; 88 89 $context = context_course::instance($courseid, MUST_EXIST); 90 if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/manual:config', $context)) { 91 return false; 92 } 93 94 if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'manual'))) { 95 // Multiple instances not supported. 96 return false; 97 } 98 99 return true; 100 } 101 102 /** 103 * Returns edit icons for the page with list of instances. 104 * @param stdClass $instance 105 * @return array 106 */ 107 public function get_action_icons(stdClass $instance) { 108 global $OUTPUT; 109 110 $context = context_course::instance($instance->courseid); 111 112 $icons = array(); 113 if (has_capability('enrol/manual:enrol', $context) or has_capability('enrol/manual:unenrol', $context)) { 114 $managelink = new moodle_url("/enrol/manual/manage.php", array('enrolid'=>$instance->id)); 115 $icons[] = $OUTPUT->action_icon($managelink, new pix_icon('t/enrolusers', get_string('enrolusers', 'enrol_manual'), 'core', array('class'=>'iconsmall'))); 116 } 117 $parenticons = parent::get_action_icons($instance); 118 $icons = array_merge($icons, $parenticons); 119 120 return $icons; 121 } 122 123 /** 124 * Add new instance of enrol plugin with default settings. 125 * @param stdClass $course 126 * @return int id of new instance, null if can not be created 127 */ 128 public function add_default_instance($course) { 129 $expirynotify = $this->get_config('expirynotify', 0); 130 if ($expirynotify == 2) { 131 $expirynotify = 1; 132 $notifyall = 1; 133 } else { 134 $notifyall = 0; 135 } 136 $fields = array( 137 'status' => $this->get_config('status'), 138 'roleid' => $this->get_config('roleid', 0), 139 'enrolperiod' => $this->get_config('enrolperiod', 0), 140 'expirynotify' => $expirynotify, 141 'notifyall' => $notifyall, 142 'expirythreshold' => $this->get_config('expirythreshold', 86400), 143 ); 144 return $this->add_instance($course, $fields); 145 } 146 147 /** 148 * Add new instance of enrol plugin. 149 * @param stdClass $course 150 * @param array instance fields 151 * @return int id of new instance, null if can not be created 152 */ 153 public function add_instance($course, array $fields = NULL) { 154 global $DB; 155 156 if ($DB->record_exists('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'))) { 157 // only one instance allowed, sorry 158 return NULL; 159 } 160 161 return parent::add_instance($course, $fields); 162 } 163 164 /** 165 * Update instance of enrol plugin. 166 * @param stdClass $instance 167 * @param stdClass $data modified instance fields 168 * @return boolean 169 */ 170 public function update_instance($instance, $data) { 171 global $DB; 172 173 // Delete all other instances, leaving only one. 174 if ($instances = $DB->get_records('enrol', array('courseid' => $instance->courseid, 'enrol' => 'manual'), 'id ASC')) { 175 foreach ($instances as $anotherinstance) { 176 if ($anotherinstance->id != $instance->id) { 177 $this->delete_instance($anotherinstance); 178 } 179 } 180 } 181 return parent::update_instance($instance, $data); 182 } 183 184 /** 185 * Returns a button to manually enrol users through the manual enrolment plugin. 186 * 187 * By default the first manual enrolment plugin instance available in the course is used. 188 * If no manual enrolment instances exist within the course then false is returned. 189 * 190 * This function also adds a quickenrolment JS ui to the page so that users can be enrolled 191 * via AJAX. 192 * 193 * @param course_enrolment_manager $manager 194 * @return enrol_user_button 195 */ 196 public function get_manual_enrol_button(course_enrolment_manager $manager) { 197 global $CFG, $PAGE; 198 require_once($CFG->dirroot.'/cohort/lib.php'); 199 200 static $called = false; 201 202 $instance = null; 203 foreach ($manager->get_enrolment_instances() as $tempinstance) { 204 if ($tempinstance->enrol == 'manual') { 205 if ($instance === null) { 206 $instance = $tempinstance; 207 } 208 } 209 } 210 if (empty($instance)) { 211 return false; 212 } 213 214 $link = $this->get_manual_enrol_link($instance); 215 if (!$link) { 216 return false; 217 } 218 219 $button = new enrol_user_button($link, get_string('enrolusers', 'enrol_manual'), 'get'); 220 $button->class .= ' enrol_manual_plugin'; 221 $button->primary = true; 222 223 $context = context_course::instance($instance->courseid); 224 $arguments = array('contextid' => $context->id); 225 226 if (!$called) { 227 $called = true; 228 // Calling the following more than once will cause unexpected results. 229 $PAGE->requires->js_call_amd('enrol_manual/quickenrolment', 'init', array($arguments)); 230 } 231 232 return $button; 233 } 234 235 /** 236 * Sync all meta course links. 237 * 238 * @param progress_trace $trace 239 * @param int $courseid one course, empty mean all 240 * @return int 0 means ok, 1 means error, 2 means plugin disabled 241 */ 242 public function sync(progress_trace $trace, $courseid = null) { 243 global $DB; 244 245 if (!enrol_is_enabled('manual')) { 246 $trace->finished(); 247 return 2; 248 } 249 250 // Unfortunately this may take a long time, execution can be interrupted safely here. 251 core_php_time_limit::raise(); 252 raise_memory_limit(MEMORY_HUGE); 253 254 $trace->output('Verifying manual enrolment expiration...'); 255 256 $params = array('now'=>time(), 'useractive'=>ENROL_USER_ACTIVE, 'courselevel'=>CONTEXT_COURSE); 257 $coursesql = ""; 258 if ($courseid) { 259 $coursesql = "AND e.courseid = :courseid"; 260 $params['courseid'] = $courseid; 261 } 262 263 // Deal with expired accounts. 264 $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP); 265 266 if ($action == ENROL_EXT_REMOVED_UNENROL) { 267 $instances = array(); 268 $sql = "SELECT ue.*, e.courseid, c.id AS contextid 269 FROM {user_enrolments} ue 270 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual') 271 JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel) 272 WHERE ue.timeend > 0 AND ue.timeend < :now 273 $coursesql"; 274 $rs = $DB->get_recordset_sql($sql, $params); 275 foreach ($rs as $ue) { 276 if (empty($instances[$ue->enrolid])) { 277 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid)); 278 } 279 $instance = $instances[$ue->enrolid]; 280 // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here. 281 role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true); 282 $this->unenrol_user($instance, $ue->userid); 283 $trace->output("unenrolling expired user $ue->userid from course $instance->courseid", 1); 284 } 285 $rs->close(); 286 unset($instances); 287 288 } else if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES or $action == ENROL_EXT_REMOVED_SUSPEND) { 289 $instances = array(); 290 $sql = "SELECT ue.*, e.courseid, c.id AS contextid 291 FROM {user_enrolments} ue 292 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual') 293 JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel) 294 WHERE ue.timeend > 0 AND ue.timeend < :now 295 AND ue.status = :useractive 296 $coursesql"; 297 $rs = $DB->get_recordset_sql($sql, $params); 298 foreach ($rs as $ue) { 299 if (empty($instances[$ue->enrolid])) { 300 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid)); 301 } 302 $instance = $instances[$ue->enrolid]; 303 if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) { 304 // Remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here. 305 role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true); 306 $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED); 307 $trace->output("suspending expired user $ue->userid in course $instance->courseid, roles unassigned", 1); 308 } else { 309 $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED); 310 $trace->output("suspending expired user $ue->userid in course $instance->courseid, roles kept", 1); 311 } 312 } 313 $rs->close(); 314 unset($instances); 315 316 } else { 317 // ENROL_EXT_REMOVED_KEEP means no changes. 318 } 319 320 $trace->output('...manual enrolment updates finished.'); 321 $trace->finished(); 322 323 return 0; 324 } 325 326 /** 327 * Returns the user who is responsible for manual enrolments in given instance. 328 * 329 * Usually it is the first editing teacher - the person with "highest authority" 330 * as defined by sort_by_roleassignment_authority() having 'enrol/manual:manage' 331 * capability. 332 * 333 * @param int $instanceid enrolment instance id 334 * @return stdClass user record 335 */ 336 protected function get_enroller($instanceid) { 337 global $DB; 338 339 if ($this->lasternollerinstanceid == $instanceid and $this->lasternoller) { 340 return $this->lasternoller; 341 } 342 343 $instance = $DB->get_record('enrol', array('id'=>$instanceid, 'enrol'=>$this->get_name()), '*', MUST_EXIST); 344 $context = context_course::instance($instance->courseid); 345 346 if ($users = get_enrolled_users($context, 'enrol/manual:manage')) { 347 $users = sort_by_roleassignment_authority($users, $context); 348 $this->lasternoller = reset($users); 349 unset($users); 350 } else { 351 $this->lasternoller = parent::get_enroller($instanceid); 352 } 353 354 $this->lasternollerinstanceid = $instanceid; 355 356 return $this->lasternoller; 357 } 358 359 /** 360 * The manual plugin has several bulk operations that can be performed. 361 * @param course_enrolment_manager $manager 362 * @return array 363 */ 364 public function get_bulk_operations(course_enrolment_manager $manager) { 365 global $CFG; 366 require_once($CFG->dirroot.'/enrol/manual/locallib.php'); 367 $context = $manager->get_context(); 368 $bulkoperations = array(); 369 if (has_capability("enrol/manual:manage", $context)) { 370 $bulkoperations['editselectedusers'] = new enrol_manual_editselectedusers_operation($manager, $this); 371 } 372 if (has_capability("enrol/manual:unenrol", $context)) { 373 $bulkoperations['deleteselectedusers'] = new enrol_manual_deleteselectedusers_operation($manager, $this); 374 } 375 return $bulkoperations; 376 } 377 378 /** 379 * Restore instance and map settings. 380 * 381 * @param restore_enrolments_structure_step $step 382 * @param stdClass $data 383 * @param stdClass $course 384 * @param int $oldid 385 */ 386 public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) { 387 global $DB; 388 // There is only I manual enrol instance allowed per course. 389 if ($instances = $DB->get_records('enrol', array('courseid'=>$data->courseid, 'enrol'=>'manual'), 'id')) { 390 $instance = reset($instances); 391 $instanceid = $instance->id; 392 } else { 393 $instanceid = $this->add_instance($course, (array)$data); 394 } 395 $step->set_mapping('enrol', $oldid, $instanceid); 396 } 397 398 /** 399 * Restore user enrolment. 400 * 401 * @param restore_enrolments_structure_step $step 402 * @param stdClass $data 403 * @param stdClass $instance 404 * @param int $oldinstancestatus 405 * @param int $userid 406 */ 407 public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) { 408 global $DB; 409 410 // Note: this is a bit tricky because other types may be converted to manual enrolments, 411 // and manual is restricted to one enrolment per user. 412 413 $ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid)); 414 $enrol = false; 415 if ($ue and $ue->status == ENROL_USER_ACTIVE) { 416 // We do not want to restrict current active enrolments, let's kind of merge the times only. 417 // This prevents some teacher lockouts too. 418 if ($data->status == ENROL_USER_ACTIVE) { 419 if ($data->timestart > $ue->timestart) { 420 $data->timestart = $ue->timestart; 421 $enrol = true; 422 } 423 424 if ($data->timeend == 0) { 425 if ($ue->timeend != 0) { 426 $enrol = true; 427 } 428 } else if ($ue->timeend == 0) { 429 $data->timeend = 0; 430 } else if ($data->timeend < $ue->timeend) { 431 $data->timeend = $ue->timeend; 432 $enrol = true; 433 } 434 } 435 } else { 436 if ($instance->status == ENROL_INSTANCE_ENABLED and $oldinstancestatus != ENROL_INSTANCE_ENABLED) { 437 // Make sure that user enrolments are not activated accidentally, 438 // we do it only here because it is not expected that enrolments are migrated to other plugins. 439 $data->status = ENROL_USER_SUSPENDED; 440 } 441 $enrol = true; 442 } 443 444 if ($enrol) { 445 $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status); 446 } 447 } 448 449 /** 450 * Restore role assignment. 451 * 452 * @param stdClass $instance 453 * @param int $roleid 454 * @param int $userid 455 * @param int $contextid 456 */ 457 public function restore_role_assignment($instance, $roleid, $userid, $contextid) { 458 // This is necessary only because we may migrate other types to this instance, 459 // we do not use component in manual or self enrol. 460 role_assign($roleid, $userid, $contextid, '', 0); 461 } 462 463 /** 464 * Restore user group membership. 465 * @param stdClass $instance 466 * @param int $groupid 467 * @param int $userid 468 */ 469 public function restore_group_member($instance, $groupid, $userid) { 470 global $CFG; 471 require_once("$CFG->dirroot/group/lib.php"); 472 473 // This might be called when forcing restore as manual enrolments. 474 475 groups_add_member($groupid, $userid); 476 } 477 478 /** 479 * Is it possible to delete enrol instance via standard UI? 480 * 481 * @param object $instance 482 * @return bool 483 */ 484 public function can_delete_instance($instance) { 485 $context = context_course::instance($instance->courseid); 486 return has_capability('enrol/manual:config', $context); 487 } 488 489 /** 490 * Is it possible to hide/show enrol instance via standard UI? 491 * 492 * @param stdClass $instance 493 * @return bool 494 */ 495 public function can_hide_show_instance($instance) { 496 $context = context_course::instance($instance->courseid); 497 return has_capability('enrol/manual:config', $context); 498 } 499 500 /** 501 * Enrol all not enrolled cohort members into course via enrol instance. 502 * 503 * @param stdClass $instance 504 * @param int $cohortid 505 * @param int $roleid optional role id 506 * @param int $timestart 0 means unknown 507 * @param int $timeend 0 means forever 508 * @param int $status default to ENROL_USER_ACTIVE for new enrolments, no change by default in updates 509 * @param bool $recovergrades restore grade history 510 * @return int The number of enrolled cohort users 511 */ 512 public function enrol_cohort(stdClass $instance, $cohortid, $roleid = null, $timestart = 0, $timeend = 0, $status = null, $recovergrades = null) { 513 global $DB; 514 $context = context_course::instance($instance->courseid); 515 list($esql, $params) = get_enrolled_sql($context); 516 $sql = "SELECT cm.userid FROM {cohort_members} cm LEFT JOIN ($esql) u ON u.id = cm.userid ". 517 "WHERE cm.cohortid = :cohortid AND u.id IS NULL"; 518 $params['cohortid'] = $cohortid; 519 $members = $DB->get_fieldset_sql($sql, $params); 520 foreach ($members as $userid) { 521 $this->enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status, $recovergrades); 522 } 523 return count($members); 524 } 525 526 /** 527 * We are a good plugin and don't invent our own UI/validation code path. 528 * 529 * @return boolean 530 */ 531 public function use_standard_editing_ui() { 532 return true; 533 } 534 535 /** 536 * Return an array of valid options for the status. 537 * 538 * @return array 539 */ 540 protected function get_status_options() { 541 $options = array(ENROL_INSTANCE_ENABLED => get_string('yes'), 542 ENROL_INSTANCE_DISABLED => get_string('no')); 543 return $options; 544 } 545 546 /** 547 * Return an array of valid options for the roleid. 548 * 549 * @param stdClass $instance 550 * @param context $context 551 * @return array 552 */ 553 protected function get_roleid_options($instance, $context) { 554 if ($instance->id) { 555 $roles = get_default_enrol_roles($context, $instance->roleid); 556 } else { 557 $roles = get_default_enrol_roles($context, $this->get_config('roleid')); 558 } 559 return $roles; 560 } 561 562 /** 563 * Return an array of valid options for the expirynotify. 564 * 565 * @return array 566 */ 567 protected function get_expirynotify_options() { 568 $options = array( 569 0 => get_string('no'), 570 1 => get_string('expirynotifyenroller', 'core_enrol'), 571 2 => get_string('expirynotifyall', 'core_enrol') 572 ); 573 return $options; 574 } 575 576 /** 577 * Add elements to the edit instance form. 578 * 579 * @param stdClass $instance 580 * @param MoodleQuickForm $mform 581 * @param context $context 582 * @return bool 583 */ 584 public function edit_instance_form($instance, MoodleQuickForm $mform, $context) { 585 586 $options = $this->get_status_options(); 587 $mform->addElement('select', 'status', get_string('status', 'enrol_manual'), $options); 588 $mform->addHelpButton('status', 'status', 'enrol_manual'); 589 $mform->setDefault('status', $this->get_config('status')); 590 591 $roles = $this->get_roleid_options($instance, $context); 592 $mform->addElement('select', 'roleid', get_string('defaultrole', 'role'), $roles); 593 $mform->setDefault('roleid', $this->get_config('roleid')); 594 595 $options = array('optional' => true, 'defaultunit' => 86400); 596 $mform->addElement('duration', 'enrolperiod', get_string('defaultperiod', 'enrol_manual'), $options); 597 $mform->setDefault('enrolperiod', $this->get_config('enrolperiod')); 598 $mform->addHelpButton('enrolperiod', 'defaultperiod', 'enrol_manual'); 599 600 $options = $this->get_expirynotify_options(); 601 $mform->addElement('select', 'expirynotify', get_string('expirynotify', 'core_enrol'), $options); 602 $mform->addHelpButton('expirynotify', 'expirynotify', 'core_enrol'); 603 604 $options = array('optional' => false, 'defaultunit' => 86400); 605 $mform->addElement('duration', 'expirythreshold', get_string('expirythreshold', 'core_enrol'), $options); 606 $mform->addHelpButton('expirythreshold', 'expirythreshold', 'core_enrol'); 607 $mform->disabledIf('expirythreshold', 'expirynotify', 'eq', 0); 608 609 if (enrol_accessing_via_instance($instance)) { 610 $warntext = get_string('instanceeditselfwarningtext', 'core_enrol'); 611 $mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warntext); 612 } 613 } 614 615 /** 616 * Perform custom validation of the data used to edit the instance. 617 * 618 * @param array $data array of ("fieldname"=>value) of submitted data 619 * @param array $files array of uploaded files "element_name"=>tmp_file_path 620 * @param object $instance The instance loaded from the DB 621 * @param context $context The context of the instance we are editing 622 * @return array of "element_name"=>"error_description" if there are errors, 623 * or an empty array if everything is OK. 624 * @return void 625 */ 626 public function edit_instance_validation($data, $files, $instance, $context) { 627 $errors = array(); 628 629 if ($data['expirynotify'] > 0 and $data['expirythreshold'] < 86400) { 630 $errors['expirythreshold'] = get_string('errorthresholdlow', 'core_enrol'); 631 } 632 633 $validstatus = array_keys($this->get_status_options()); 634 $validroles = array_keys($this->get_roleid_options($instance, $context)); 635 $validexpirynotify = array_keys($this->get_expirynotify_options()); 636 637 $tovalidate = array( 638 'status' => $validstatus, 639 'roleid' => $validroles, 640 'enrolperiod' => PARAM_INT, 641 'expirynotify' => $validexpirynotify, 642 'expirythreshold' => PARAM_INT 643 ); 644 645 $typeerrors = $this->validate_param_types($data, $tovalidate); 646 $errors = array_merge($errors, $typeerrors); 647 648 return $errors; 649 } 650 651 } 652 653 /** 654 * Serve the manual enrol users form as a fragment. 655 * 656 * @param array $args List of named arguments for the fragment loader. 657 * @return string 658 */ 659 function enrol_manual_output_fragment_enrol_users_form($args) { 660 $args = (object) $args; 661 $context = $args->context; 662 $o = ''; 663 664 require_capability('enrol/manual:enrol', $context); 665 $mform = new enrol_manual_enrol_users_form(null, $args); 666 667 ob_start(); 668 $mform->display(); 669 $o .= ob_get_contents(); 670 ob_end_clean(); 671 672 return $o; 673 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body