Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** Manual enrolment plugin main library file.** @package enrol_manual* @copyright 2010 Petr Skoda {@link http://skodak.org}* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/defined('MOODLE_INTERNAL') || die();class enrol_manual_plugin extends enrol_plugin {protected $lasternoller = null;protected $lasternollerinstanceid = 0;public function roles_protected() {// Users may tweak the roles later.return false;}public function allow_enrol(stdClass $instance) {// Users with enrol cap may unenrol other users manually manually.return true;}public function allow_unenrol(stdClass $instance) {// Users with unenrol cap may unenrol other users manually manually.return true;}public function allow_manage(stdClass $instance) {// Users with manage cap may tweak period and status.return true;}/*** Returns link to manual enrol UI if exists.* Does the access control tests automatically.** @param stdClass $instance* @return moodle_url*/public function get_manual_enrol_link($instance) {$name = $this->get_name();if ($instance->enrol !== $name) {throw new coding_exception('invalid enrol instance!');}if (!enrol_is_enabled($name)) {return NULL;}$context = context_course::instance($instance->courseid, MUST_EXIST);if (!has_capability('enrol/manual:enrol', $context)) {// Note: manage capability not used here because it is used for editing// of existing enrolments which is not possible here.return NULL;}return new moodle_url('/enrol/manual/manage.php', array('enrolid'=>$instance->id, 'id'=>$instance->courseid));}/*** Return true if we can add a new instance to this course.** @param int $courseid* @return boolean*/public function can_add_instance($courseid) {global $DB;$context = context_course::instance($courseid, MUST_EXIST);if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/manual:config', $context)) {return false;}if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'manual'))) {// Multiple instances not supported.return false;}return true;}/*** Returns edit icons for the page with list of instances.* @param stdClass $instance* @return array*/public function get_action_icons(stdClass $instance) {global $OUTPUT;$context = context_course::instance($instance->courseid);$icons = array();if (has_capability('enrol/manual:enrol', $context) or has_capability('enrol/manual:unenrol', $context)) {$managelink = new moodle_url("/enrol/manual/manage.php", array('enrolid'=>$instance->id));$icons[] = $OUTPUT->action_icon($managelink, new pix_icon('t/enrolusers', get_string('enrolusers', 'enrol_manual'), 'core', array('class'=>'iconsmall')));}$parenticons = parent::get_action_icons($instance);$icons = array_merge($icons, $parenticons);return $icons;}/*** Add new instance of enrol plugin with default settings.* @param stdClass $course* @return int id of new instance, null if can not be created*/public function add_default_instance($course) {$expirynotify = $this->get_config('expirynotify', 0);$fields = array('status' => $this->get_config('status'),'roleid' => $this->get_config('roleid', 0),'enrolperiod' => $this->get_config('enrolperiod', 0),'expirynotify' => $expirynotify,'notifyall' => $expirynotify == 2 ? 1 : 0,'expirythreshold' => $this->get_config('expirythreshold', 86400),'customint1' => $this->get_config('sendcoursewelcomemessage'),);return $this->add_instance($course, $fields);}/*** Add new instance of enrol plugin.* @param stdClass $course* @param array instance fields* @return int id of new instance, null if can not be created*/public function add_instance($course, array $fields = NULL) {global $DB;if ($DB->record_exists('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'))) {// only one instance allowed, sorryreturn NULL;}return parent::add_instance($course, $fields);}/*** Update instance of enrol plugin.* @param stdClass $instance* @param stdClass $data modified instance fields* @return boolean*/public function update_instance($instance, $data) {global $DB;// Delete all other instances, leaving only one.if ($instances = $DB->get_records('enrol', array('courseid' => $instance->courseid, 'enrol' => 'manual'), 'id ASC')) {foreach ($instances as $anotherinstance) {if ($anotherinstance->id != $instance->id) {$this->delete_instance($anotherinstance);}}}$data->notifyall = $data->expirynotify == 2 ? 1 : 0;return parent::update_instance($instance, $data);}/*** Returns a button to manually enrol users through the manual enrolment plugin.** By default the first manual enrolment plugin instance available in the course is used.* If no manual enrolment instances exist within the course then false is returned.** This function also adds a quickenrolment JS ui to the page so that users can be enrolled* via AJAX.** @param course_enrolment_manager $manager* @return enrol_user_button*/public function get_manual_enrol_button(course_enrolment_manager $manager) {global $CFG, $PAGE;require_once($CFG->dirroot.'/cohort/lib.php');static $called = false;$instance = null;foreach ($manager->get_enrolment_instances() as $tempinstance) {if ($tempinstance->enrol == 'manual') {if ($instance === null) {$instance = $tempinstance;}}}if (empty($instance)) {return false;}$link = $this->get_manual_enrol_link($instance);if (!$link) {return false;}$button = new enrol_user_button($link, get_string('enrolusers', 'enrol_manual'), 'get');$button->class .= ' enrol_manual_plugin';$button->type = single_button::BUTTON_PRIMARY;$context = context_course::instance($instance->courseid);$arguments = array('contextid' => $context->id);if (!$called) {$called = true;// Calling the following more than once will cause unexpected results.$PAGE->requires->js_call_amd('enrol_manual/quickenrolment', 'init', array($arguments));}return $button;}/*** Sync all meta course links.** @param progress_trace $trace* @param int $courseid one course, empty mean all* @return int 0 means ok, 1 means error, 2 means plugin disabled*/public function sync(progress_trace $trace, $courseid = null) {global $DB;if (!enrol_is_enabled('manual')) {$trace->finished();return 2;}// Unfortunately this may take a long time, execution can be interrupted safely here.core_php_time_limit::raise();raise_memory_limit(MEMORY_HUGE);$trace->output('Verifying manual enrolment expiration...');$params = array('now'=>time(), 'useractive'=>ENROL_USER_ACTIVE, 'courselevel'=>CONTEXT_COURSE);$coursesql = "";if ($courseid) {$coursesql = "AND e.courseid = :courseid";$params['courseid'] = $courseid;}// Deal with expired accounts.$action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP);if ($action == ENROL_EXT_REMOVED_UNENROL) {$instances = array();$sql = "SELECT ue.*, e.courseid, c.id AS contextidFROM {user_enrolments} ueJOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)WHERE ue.timeend > 0 AND ue.timeend < :now$coursesql";$rs = $DB->get_recordset_sql($sql, $params);foreach ($rs as $ue) {if (empty($instances[$ue->enrolid])) {$instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));}$instance = $instances[$ue->enrolid];// Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here.role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true);$this->unenrol_user($instance, $ue->userid);$trace->output("unenrolling expired user $ue->userid from course $instance->courseid", 1);}$rs->close();unset($instances);} else if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES or $action == ENROL_EXT_REMOVED_SUSPEND) {$instances = array();$sql = "SELECT ue.*, e.courseid, c.id AS contextidFROM {user_enrolments} ueJOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)WHERE ue.timeend > 0 AND ue.timeend < :nowAND ue.status = :useractive$coursesql";$rs = $DB->get_recordset_sql($sql, $params);foreach ($rs as $ue) {if (empty($instances[$ue->enrolid])) {$instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));}$instance = $instances[$ue->enrolid];if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) {// Remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here.role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true);$this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);$trace->output("suspending expired user $ue->userid in course $instance->courseid, roles unassigned", 1);} else {$this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);$trace->output("suspending expired user $ue->userid in course $instance->courseid, roles kept", 1);}}$rs->close();unset($instances);} else {// ENROL_EXT_REMOVED_KEEP means no changes.}$trace->output('...manual enrolment updates finished.');$trace->finished();return 0;}/*** Returns the user who is responsible for manual enrolments in given instance.** Usually it is the first editing teacher - the person with "highest authority"* as defined by sort_by_roleassignment_authority() having 'enrol/manual:manage'* capability.** @param int $instanceid enrolment instance id* @return stdClass user record*/protected function get_enroller($instanceid) {global $DB;if ($this->lasternollerinstanceid == $instanceid and $this->lasternoller) {return $this->lasternoller;}$instance = $DB->get_record('enrol', array('id'=>$instanceid, 'enrol'=>$this->get_name()), '*', MUST_EXIST);$context = context_course::instance($instance->courseid);if ($users = get_enrolled_users($context, 'enrol/manual:manage')) {$users = sort_by_roleassignment_authority($users, $context);$this->lasternoller = reset($users);unset($users);} else {$this->lasternoller = parent::get_enroller($instanceid);}$this->lasternollerinstanceid = $instanceid;return $this->lasternoller;}/*** The manual plugin has several bulk operations that can be performed.* @param course_enrolment_manager $manager* @return array*/public function get_bulk_operations(course_enrolment_manager $manager) {global $CFG;require_once($CFG->dirroot.'/enrol/manual/locallib.php');$context = $manager->get_context();$bulkoperations = array();if (has_capability("enrol/manual:manage", $context)) {$bulkoperations['editselectedusers'] = new enrol_manual_editselectedusers_operation($manager, $this);}if (has_capability("enrol/manual:unenrol", $context)) {$bulkoperations['deleteselectedusers'] = new enrol_manual_deleteselectedusers_operation($manager, $this);}return $bulkoperations;}/*** Restore instance and map settings.** @param restore_enrolments_structure_step $step* @param stdClass $data* @param stdClass $course* @param int $oldid*/public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {global $DB;// There is only I manual enrol instance allowed per course.if ($instances = $DB->get_records('enrol', array('courseid'=>$data->courseid, 'enrol'=>'manual'), 'id')) {$instance = reset($instances);$instanceid = $instance->id;} else {$instanceid = $this->add_instance($course, (array)$data);}$step->set_mapping('enrol', $oldid, $instanceid);}/*** Restore user enrolment.** @param restore_enrolments_structure_step $step* @param stdClass $data* @param stdClass $instance* @param int $oldinstancestatus* @param int $userid*/public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {global $DB;// Note: this is a bit tricky because other types may be converted to manual enrolments,// and manual is restricted to one enrolment per user.$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid));$enrol = false;if ($ue and $ue->status == ENROL_USER_ACTIVE) {// We do not want to restrict current active enrolments, let's kind of merge the times only.// This prevents some teacher lockouts too.if ($data->status == ENROL_USER_ACTIVE) {if ($data->timestart > $ue->timestart) {$data->timestart = $ue->timestart;$enrol = true;}if ($data->timeend == 0) {if ($ue->timeend != 0) {$enrol = true;}} else if ($ue->timeend == 0) {$data->timeend = 0;} else if ($data->timeend < $ue->timeend) {$data->timeend = $ue->timeend;$enrol = true;}}} else {if ($instance->status == ENROL_INSTANCE_ENABLED and $oldinstancestatus != ENROL_INSTANCE_ENABLED) {// Make sure that user enrolments are not activated accidentally,// we do it only here because it is not expected that enrolments are migrated to other plugins.$data->status = ENROL_USER_SUSPENDED;}$enrol = true;}if ($enrol) {$this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);}}/*** Restore role assignment.** @param stdClass $instance* @param int $roleid* @param int $userid* @param int $contextid*/public function restore_role_assignment($instance, $roleid, $userid, $contextid) {// This is necessary only because we may migrate other types to this instance,// we do not use component in manual or self enrol.role_assign($roleid, $userid, $contextid, '', 0);}/*** Restore user group membership.* @param stdClass $instance* @param int $groupid* @param int $userid*/public function restore_group_member($instance, $groupid, $userid) {global $CFG;require_once("$CFG->dirroot/group/lib.php");// This might be called when forcing restore as manual enrolments.groups_add_member($groupid, $userid);}/*** Is it possible to delete enrol instance via standard UI?** @param object $instance* @return bool*/public function can_delete_instance($instance) {$context = context_course::instance($instance->courseid);return has_capability('enrol/manual:config', $context);}/*** Is it possible to hide/show enrol instance via standard UI?** @param stdClass $instance* @return bool*/public function can_hide_show_instance($instance) {$context = context_course::instance($instance->courseid);return has_capability('enrol/manual:config', $context);}/*** Enrol all not enrolled cohort members into course via enrol instance.** @param stdClass $instance* @param int $cohortid* @param int $roleid optional role id* @param int $timestart 0 means unknown* @param int $timeend 0 means forever* @param int $status default to ENROL_USER_ACTIVE for new enrolments, no change by default in updates* @param bool $recovergrades restore grade history* @return int The number of enrolled cohort users*/public function enrol_cohort(stdClass $instance, $cohortid, $roleid = null, $timestart = 0, $timeend = 0, $status = null, $recovergrades = null) {global $DB;$context = context_course::instance($instance->courseid);list($esql, $params) = get_enrolled_sql($context);$sql = "SELECT cm.userid FROM {cohort_members} cm LEFT JOIN ($esql) u ON u.id = cm.userid "."WHERE cm.cohortid = :cohortid AND u.id IS NULL";$params['cohortid'] = $cohortid;$members = $DB->get_fieldset_sql($sql, $params);foreach ($members as $userid) {$this->enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status, $recovergrades);}return count($members);}/*** We are a good plugin and don't invent our own UI/validation code path.** @return boolean*/public function use_standard_editing_ui() {return true;}/*** Return an array of valid options for the status.** @return array*/protected function get_status_options() {$options = array(ENROL_INSTANCE_ENABLED => get_string('yes'),ENROL_INSTANCE_DISABLED => get_string('no'));return $options;}/*** Return an array of valid options for the roleid.** @param stdClass $instance* @param context $context* @return array*/protected function get_roleid_options($instance, $context) {if ($instance->id) {$roles = get_default_enrol_roles($context, $instance->roleid);} else {$roles = get_default_enrol_roles($context, $this->get_config('roleid'));}return $roles;}/*** Return an array of valid options for the expirynotify.** @return array*/protected function get_expirynotify_options() {$options = array(0 => get_string('no'),1 => get_string('expirynotifyenroller', 'core_enrol'),2 => get_string('expirynotifyall', 'core_enrol'));return $options;}/*** Add elements to the edit instance form.** @param stdClass $instance* @param MoodleQuickForm $mform* @param context $context* @return bool*/public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {$options = $this->get_status_options();$mform->addElement('select', 'status', get_string('status', 'enrol_manual'), $options);$mform->addHelpButton('status', 'status', 'enrol_manual');$mform->setDefault('status', $this->get_config('status'));$roles = $this->get_roleid_options($instance, $context);$mform->addElement('select', 'roleid', get_string('defaultrole', 'role'), $roles);$mform->setDefault('roleid', $this->get_config('roleid'));$options = array('optional' => true, 'defaultunit' => 86400);$mform->addElement('duration', 'enrolperiod', get_string('defaultperiod', 'enrol_manual'), $options);$mform->setDefault('enrolperiod', $this->get_config('enrolperiod'));$mform->addHelpButton('enrolperiod', 'defaultperiod', 'enrol_manual');$options = $this->get_expirynotify_options();$mform->addElement('select', 'expirynotify', get_string('expirynotify', 'core_enrol'), $options);$mform->addHelpButton('expirynotify', 'expirynotify', 'core_enrol');$options = array('optional' => false, 'defaultunit' => 86400);$mform->addElement('duration', 'expirythreshold', get_string('expirythreshold', 'core_enrol'), $options);$mform->addHelpButton('expirythreshold', 'expirythreshold', 'core_enrol');$mform->disabledIf('expirythreshold', 'expirynotify', 'eq', 0);// Course welcome message.$mform->addElement('select','customint1',get_string(identifier: 'sendcoursewelcomemessage',component: 'core_enrol',),enrol_send_welcome_email_options(),);$mform->addHelpButton(elementname: 'customint1',identifier: 'sendcoursewelcomemessage',component: 'core_enrol',);$options = ['cols' => '60','rows' => '8',];$mform->addElement('textarea','customtext1',get_string(identifier: 'customwelcomemessage',component: 'core_enrol',),$options,);$mform->setDefault('customtext1', get_string('customwelcomemessageplaceholder', 'core_enrol'));$mform->hideIf(elementname: 'customtext1',dependenton: 'customint1',condition: 'eq',value: ENROL_DO_NOT_SEND_EMAIL,);// Static form elements cannot be hidden by hideIf() so we need to add a dummy group.// See: https://tracker.moodle.org/browse/MDL-66251.$group[] = $mform->createElement('static','customwelcomemessage_extra_help',null,get_string(identifier: 'customwelcomemessage_help',component: 'core_enrol',),);$mform->addGroup($group, 'group_customwelcomemessage_extra_help', '', ' ', false);$mform->hideIf(elementname: 'group_customwelcomemessage_extra_help',dependenton: 'customint1',condition: 'eq',value: ENROL_DO_NOT_SEND_EMAIL,);if (enrol_accessing_via_instance($instance)) {$warntext = get_string('instanceeditselfwarningtext', 'core_enrol');$mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warntext);}}/*** Perform custom validation of the data used to edit the instance.** @param array $data array of ("fieldname"=>value) of submitted data* @param array $files array of uploaded files "element_name"=>tmp_file_path* @param object $instance The instance loaded from the DB* @param context $context The context of the instance we are editing* @return array of "element_name"=>"error_description" if there are errors,* or an empty array if everything is OK.* @return void*/public function edit_instance_validation($data, $files, $instance, $context) {$errors = array();if ($data['expirynotify'] > 0 and $data['expirythreshold'] < 86400) {$errors['expirythreshold'] = get_string('errorthresholdlow', 'core_enrol');}$validstatus = array_keys($this->get_status_options());$validroles = array_keys($this->get_roleid_options($instance, $context));$validexpirynotify = array_keys($this->get_expirynotify_options());$tovalidate = array('status' => $validstatus,'roleid' => $validroles,'enrolperiod' => PARAM_INT,'expirynotify' => $validexpirynotify,'expirythreshold' => PARAM_INT);$typeerrors = $this->validate_param_types($data, $tovalidate);$errors = array_merge($errors, $typeerrors);return $errors;}/*** Check if enrolment plugin is supported in csv course upload.** @return bool*/public function is_csv_upload_supported(): bool {return true;}/*** Finds matching instances for a given course.** @param array $enrolmentdata enrolment data.* @param int $courseid Course ID.* @return stdClass|null Matching instance*/public function find_instance(array $enrolmentdata, int $courseid): ?stdClass {$instances = enrol_get_instances($courseid, false);$instance = null;foreach ($instances as $i) {if ($i->enrol == 'manual') {// There can be only one manual enrol instance so find first available.$instance = $i;break;}}return $instance;}/*** Fill custom fields data for a given enrolment plugin.** @param array $enrolmentdata enrolment data.* @param int $courseid Course ID.* @return array Updated enrolment data with custom fields info.*/public function fill_enrol_custom_fields(array $enrolmentdata, int $courseid): array {return $enrolmentdata + ['expirynotify' => 0,'expirythreshold' => 0,];}/*** Returns defaults for new instances.** @return array*/public function get_instance_defaults(): array {$fields['customint1'] = $this->get_config('sendcoursewelcomemessage');return $fields;}}/*** Serve the manual enrol users form as a fragment.** @param array $args List of named arguments for the fragment loader.* @return string*/function enrol_manual_output_fragment_enrol_users_form($args) {$args = (object) $args;$context = $args->context;$o = '';require_capability('enrol/manual:enrol', $context);$mform = new enrol_manual_enrol_users_form(null, $args);ob_start();$mform->display();$o .= ob_get_contents();ob_end_clean();return $o;}