source: moodle/trunk/fuentes/enrol/manual/lib.php @ 1331

Last change on this file since 1331 was 1331, checked in by jrpelegrina, 3 years ago

Updated to moodle 3.0.3

File size: 23.9 KB
Line 
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
25defined('MOODLE_INTERNAL') || die();
26
27class 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     * Returns enrolment instance manage link.
82     *
83     * By defaults looks for manage.php file and tests for manage capability.
84     *
85     * @param navigation_node $instancesnode
86     * @param stdClass $instance
87     * @return moodle_url;
88     */
89    public function add_course_navigation($instancesnode, stdClass $instance) {
90        if ($instance->enrol !== 'manual') {
91             throw new coding_exception('Invalid enrol instance type!');
92        }
93
94        $context = context_course::instance($instance->courseid);
95        if (has_capability('enrol/manual:config', $context)) {
96            $managelink = new moodle_url('/enrol/manual/edit.php', array('courseid'=>$instance->courseid));
97            $instancesnode->add($this->get_instance_name($instance), $managelink, navigation_node::TYPE_SETTING);
98        }
99    }
100
101    /**
102     * Returns edit icons for the page with list of instances.
103     * @param stdClass $instance
104     * @return array
105     */
106    public function get_action_icons(stdClass $instance) {
107        global $OUTPUT;
108
109        if ($instance->enrol !== 'manual') {
110            throw new coding_exception('invalid enrol instance!');
111        }
112        $context = context_course::instance($instance->courseid);
113
114        $icons = array();
115
116        if (has_capability('enrol/manual:enrol', $context) or has_capability('enrol/manual:unenrol', $context)) {
117            $managelink = new moodle_url("/enrol/manual/manage.php", array('enrolid'=>$instance->id));
118            $icons[] = $OUTPUT->action_icon($managelink, new pix_icon('t/enrolusers', get_string('enrolusers', 'enrol_manual'), 'core', array('class'=>'iconsmall')));
119        }
120        if (has_capability('enrol/manual:config', $context)) {
121            $editlink = new moodle_url("/enrol/manual/edit.php", array('courseid'=>$instance->courseid));
122            $icons[] = $OUTPUT->action_icon($editlink, new pix_icon('t/edit', get_string('edit'), 'core',
123                    array('class' => 'iconsmall')));
124        }
125
126        return $icons;
127    }
128
129    /**
130     * Returns link to page which may be used to add new instance of enrolment plugin in course.
131     * @param int $courseid
132     * @return moodle_url page url
133     */
134    public function get_newinstance_link($courseid) {
135        global $DB;
136
137        $context = context_course::instance($courseid, MUST_EXIST);
138
139        if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/manual:config', $context)) {
140            return NULL;
141        }
142
143        if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'manual'))) {
144            return NULL;
145        }
146
147        return new moodle_url('/enrol/manual/edit.php', array('courseid'=>$courseid));
148    }
149
150    /**
151     * Add new instance of enrol plugin with default settings.
152     * @param stdClass $course
153     * @return int id of new instance, null if can not be created
154     */
155    public function add_default_instance($course) {
156        $expirynotify = $this->get_config('expirynotify', 0);
157        if ($expirynotify == 2) {
158            $expirynotify = 1;
159            $notifyall = 1;
160        } else {
161            $notifyall = 0;
162        }
163        $fields = array(
164            'status'          => $this->get_config('status'),
165            'roleid'          => $this->get_config('roleid', 0),
166            'enrolperiod'     => $this->get_config('enrolperiod', 0),
167            'expirynotify'    => $expirynotify,
168            'notifyall'       => $notifyall,
169            'expirythreshold' => $this->get_config('expirythreshold', 86400),
170        );
171        return $this->add_instance($course, $fields);
172    }
173
174    /**
175     * Add new instance of enrol plugin.
176     * @param stdClass $course
177     * @param array instance fields
178     * @return int id of new instance, null if can not be created
179     */
180    public function add_instance($course, array $fields = NULL) {
181        global $DB;
182
183        if ($DB->record_exists('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'))) {
184            // only one instance allowed, sorry
185            return NULL;
186        }
187
188        return parent::add_instance($course, $fields);
189    }
190
191    /**
192     * Returns a button to manually enrol users through the manual enrolment plugin.
193     *
194     * By default the first manual enrolment plugin instance available in the course is used.
195     * If no manual enrolment instances exist within the course then false is returned.
196     *
197     * This function also adds a quickenrolment JS ui to the page so that users can be enrolled
198     * via AJAX.
199     *
200     * @param course_enrolment_manager $manager
201     * @return enrol_user_button
202     */
203    public function get_manual_enrol_button(course_enrolment_manager $manager) {
204        global $CFG;
205        require_once($CFG->dirroot.'/cohort/lib.php');
206
207        $instance = null;
208        $instances = array();
209        foreach ($manager->get_enrolment_instances() as $tempinstance) {
210            if ($tempinstance->enrol == 'manual') {
211                if ($instance === null) {
212                    $instance = $tempinstance;
213                }
214                $instances[] = array('id' => $tempinstance->id, 'name' => $this->get_instance_name($tempinstance));
215            }
216        }
217        if (empty($instance)) {
218            return false;
219        }
220
221        if (!$manuallink = $this->get_manual_enrol_link($instance)) {
222            return false;
223        }
224
225        $button = new enrol_user_button($manuallink, get_string('enrolusers', 'enrol_manual'), 'get');
226        $button->class .= ' enrol_manual_plugin';
227
228        $startdate = $manager->get_course()->startdate;
229        if (!$defaultstart = get_config('enrol_manual', 'enrolstart')) {
230            // Default to now if there is no system setting.
231            $defaultstart = 4;
232        }
233        $startdateoptions = array();
234        $dateformat = get_string('strftimedatefullshort');
235        if ($startdate > 0) {
236            $startdateoptions[2] = get_string('coursestart') . ' (' . userdate($startdate, $dateformat) . ')';
237        }
238        $now = time();
239        $today = make_timestamp(date('Y', $now), date('m', $now), date('d', $now), 0, 0, 0);
240        $startdateoptions[3] = get_string('today') . ' (' . userdate($today, $dateformat) . ')';
241        $startdateoptions[4] = get_string('now', 'enrol_manual') . ' (' . userdate($now, get_string('strftimedatetimeshort')) . ')';
242        $defaultduration = $instance->enrolperiod > 0 ? $instance->enrolperiod / 86400 : '';
243
244        $modules = array('moodle-enrol_manual-quickenrolment', 'moodle-enrol_manual-quickenrolment-skin');
245        $arguments = array(
246            'instances'           => $instances,
247            'courseid'            => $instance->courseid,
248            'ajaxurl'             => '/enrol/manual/ajax.php',
249            'url'                 => $manager->get_moodlepage()->url->out(false),
250            'optionsStartDate'    => $startdateoptions,
251            'defaultRole'         => $instance->roleid,
252            'defaultDuration'     => $defaultduration,
253            'defaultStartDate'    => (int)$defaultstart,
254            'disableGradeHistory' => $CFG->disablegradehistory,
255            'recoverGradesDefault'=> '',
256            'cohortsAvailable'    => cohort_get_available_cohorts($manager->get_context(), COHORT_WITH_NOTENROLLED_MEMBERS_ONLY, 0, 1) ? true : false
257        );
258
259        if ($CFG->recovergradesdefault) {
260            $arguments['recoverGradesDefault'] = ' checked="checked"';
261        }
262
263        $function = 'M.enrol_manual.quickenrolment.init';
264        $button->require_yui_module($modules, $function, array($arguments));
265        $button->strings_for_js(array(
266            'ajaxoneuserfound',
267            'ajaxxusersfound',
268            'ajaxnext25',
269            'enrol',
270            'enrolmentoptions',
271            'enrolusers',
272            'enrolxusers',
273            'errajaxfailedenrol',
274            'errajaxsearch',
275            'foundxcohorts',
276            'none',
277            'usersearch',
278            'unlimitedduration',
279            'startdatetoday',
280            'durationdays',
281            'enrolperiod',
282            'finishenrollingusers',
283            'recovergrades'), 'enrol');
284        $button->strings_for_js(array('browseusers', 'browsecohorts'), 'enrol_manual');
285        $button->strings_for_js('assignroles', 'role');
286        $button->strings_for_js('startingfrom', 'moodle');
287
288        return $button;
289    }
290
291    /**
292     * Enrol cron support.
293     * @return void
294     */
295    public function cron() {
296        $trace = new text_progress_trace();
297        $this->sync($trace, null);
298        $this->send_expiry_notifications($trace);
299    }
300
301    /**
302     * Sync all meta course links.
303     *
304     * @param progress_trace $trace
305     * @param int $courseid one course, empty mean all
306     * @return int 0 means ok, 1 means error, 2 means plugin disabled
307     */
308    public function sync(progress_trace $trace, $courseid = null) {
309        global $DB;
310
311        if (!enrol_is_enabled('manual')) {
312            $trace->finished();
313            return 2;
314        }
315
316        // Unfortunately this may take a long time, execution can be interrupted safely here.
317        core_php_time_limit::raise();
318        raise_memory_limit(MEMORY_HUGE);
319
320        $trace->output('Verifying manual enrolment expiration...');
321
322        $params = array('now'=>time(), 'useractive'=>ENROL_USER_ACTIVE, 'courselevel'=>CONTEXT_COURSE);
323        $coursesql = "";
324        if ($courseid) {
325            $coursesql = "AND e.courseid = :courseid";
326            $params['courseid'] = $courseid;
327        }
328
329        // Deal with expired accounts.
330        $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP);
331
332        if ($action == ENROL_EXT_REMOVED_UNENROL) {
333            $instances = array();
334            $sql = "SELECT ue.*, e.courseid, c.id AS contextid
335                      FROM {user_enrolments} ue
336                      JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')
337                      JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)
338                     WHERE ue.timeend > 0 AND ue.timeend < :now
339                           $coursesql";
340            $rs = $DB->get_recordset_sql($sql, $params);
341            foreach ($rs as $ue) {
342                if (empty($instances[$ue->enrolid])) {
343                    $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
344                }
345                $instance = $instances[$ue->enrolid];
346                // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here.
347                role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true);
348                $this->unenrol_user($instance, $ue->userid);
349                $trace->output("unenrolling expired user $ue->userid from course $instance->courseid", 1);
350            }
351            $rs->close();
352            unset($instances);
353
354        } else if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES or $action == ENROL_EXT_REMOVED_SUSPEND) {
355            $instances = array();
356            $sql = "SELECT ue.*, e.courseid, c.id AS contextid
357                      FROM {user_enrolments} ue
358                      JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual')
359                      JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel)
360                     WHERE ue.timeend > 0 AND ue.timeend < :now
361                           AND ue.status = :useractive
362                           $coursesql";
363            $rs = $DB->get_recordset_sql($sql, $params);
364            foreach ($rs as $ue) {
365                if (empty($instances[$ue->enrolid])) {
366                    $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
367                }
368                $instance = $instances[$ue->enrolid];
369                if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
370                    // Remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here.
371                    role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true);
372                    $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
373                    $trace->output("suspending expired user $ue->userid in course $instance->courseid, roles unassigned", 1);
374                } else {
375                    $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
376                    $trace->output("suspending expired user $ue->userid in course $instance->courseid, roles kept", 1);
377                }
378            }
379            $rs->close();
380            unset($instances);
381
382        } else {
383            // ENROL_EXT_REMOVED_KEEP means no changes.
384        }
385
386        $trace->output('...manual enrolment updates finished.');
387        $trace->finished();
388
389        return 0;
390    }
391
392    /**
393     * Returns the user who is responsible for manual enrolments in given instance.
394     *
395     * Usually it is the first editing teacher - the person with "highest authority"
396     * as defined by sort_by_roleassignment_authority() having 'enrol/manual:manage'
397     * capability.
398     *
399     * @param int $instanceid enrolment instance id
400     * @return stdClass user record
401     */
402    protected function get_enroller($instanceid) {
403        global $DB;
404
405        if ($this->lasternollerinstanceid == $instanceid and $this->lasternoller) {
406            return $this->lasternoller;
407        }
408
409        $instance = $DB->get_record('enrol', array('id'=>$instanceid, 'enrol'=>$this->get_name()), '*', MUST_EXIST);
410        $context = context_course::instance($instance->courseid);
411
412        if ($users = get_enrolled_users($context, 'enrol/manual:manage')) {
413            $users = sort_by_roleassignment_authority($users, $context);
414            $this->lasternoller = reset($users);
415            unset($users);
416        } else {
417            $this->lasternoller = parent::get_enroller($instanceid);
418        }
419
420        $this->lasternollerinstanceid = $instanceid;
421
422        return $this->lasternoller;
423    }
424
425    /**
426     * Gets an array of the user enrolment actions.
427     *
428     * @param course_enrolment_manager $manager
429     * @param stdClass $ue A user enrolment object
430     * @return array An array of user_enrolment_actions
431     */
432    public function get_user_enrolment_actions(course_enrolment_manager $manager, $ue) {
433        $actions = array();
434        $context = $manager->get_context();
435        $instance = $ue->enrolmentinstance;
436        $params = $manager->get_moodlepage()->url->params();
437        $params['ue'] = $ue->id;
438        if ($this->allow_unenrol_user($instance, $ue) && has_capability("enrol/manual:unenrol", $context)) {
439            $url = new moodle_url('/enrol/unenroluser.php', $params);
440            $actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
441        }
442        if ($this->allow_manage($instance) && has_capability("enrol/manual:manage", $context)) {
443            $url = new moodle_url('/enrol/editenrolment.php', $params);
444            $actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, array('class'=>'editenrollink', 'rel'=>$ue->id));
445        }
446        return $actions;
447    }
448
449    /**
450     * The manual plugin has several bulk operations that can be performed.
451     * @param course_enrolment_manager $manager
452     * @return array
453     */
454    public function get_bulk_operations(course_enrolment_manager $manager) {
455        global $CFG;
456        require_once($CFG->dirroot.'/enrol/manual/locallib.php');
457        $context = $manager->get_context();
458        $bulkoperations = array();
459        if (has_capability("enrol/manual:manage", $context)) {
460            $bulkoperations['editselectedusers'] = new enrol_manual_editselectedusers_operation($manager, $this);
461        }
462        if (has_capability("enrol/manual:unenrol", $context)) {
463            $bulkoperations['deleteselectedusers'] = new enrol_manual_deleteselectedusers_operation($manager, $this);
464        }
465        return $bulkoperations;
466    }
467
468    /**
469     * Restore instance and map settings.
470     *
471     * @param restore_enrolments_structure_step $step
472     * @param stdClass $data
473     * @param stdClass $course
474     * @param int $oldid
475     */
476    public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
477        global $DB;
478        // There is only I manual enrol instance allowed per course.
479        if ($instances = $DB->get_records('enrol', array('courseid'=>$data->courseid, 'enrol'=>'manual'), 'id')) {
480            $instance = reset($instances);
481            $instanceid = $instance->id;
482        } else {
483            $instanceid = $this->add_instance($course, (array)$data);
484        }
485        $step->set_mapping('enrol', $oldid, $instanceid);
486    }
487
488    /**
489     * Restore user enrolment.
490     *
491     * @param restore_enrolments_structure_step $step
492     * @param stdClass $data
493     * @param stdClass $instance
494     * @param int $oldinstancestatus
495     * @param int $userid
496     */
497    public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
498        global $DB;
499
500        // Note: this is a bit tricky because other types may be converted to manual enrolments,
501        //       and manual is restricted to one enrolment per user.
502
503        $ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid));
504        $enrol = false;
505        if ($ue and $ue->status == ENROL_USER_ACTIVE) {
506            // We do not want to restrict current active enrolments, let's kind of merge the times only.
507            // This prevents some teacher lockouts too.
508            if ($data->status == ENROL_USER_ACTIVE) {
509                if ($data->timestart > $ue->timestart) {
510                    $data->timestart = $ue->timestart;
511                    $enrol = true;
512                }
513
514                if ($data->timeend == 0) {
515                    if ($ue->timeend != 0) {
516                        $enrol = true;
517                    }
518                } else if ($ue->timeend == 0) {
519                    $data->timeend = 0;
520                } else if ($data->timeend < $ue->timeend) {
521                    $data->timeend = $ue->timeend;
522                    $enrol = true;
523                }
524            }
525        } else {
526            if ($instance->status == ENROL_INSTANCE_ENABLED and $oldinstancestatus != ENROL_INSTANCE_ENABLED) {
527                // Make sure that user enrolments are not activated accidentally,
528                // we do it only here because it is not expected that enrolments are migrated to other plugins.
529                $data->status = ENROL_USER_SUSPENDED;
530            }
531            $enrol = true;
532        }
533
534        if ($enrol) {
535            $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);
536        }
537    }
538
539    /**
540     * Restore role assignment.
541     *
542     * @param stdClass $instance
543     * @param int $roleid
544     * @param int $userid
545     * @param int $contextid
546     */
547    public function restore_role_assignment($instance, $roleid, $userid, $contextid) {
548        // This is necessary only because we may migrate other types to this instance,
549        // we do not use component in manual or self enrol.
550        role_assign($roleid, $userid, $contextid, '', 0);
551    }
552
553    /**
554     * Restore user group membership.
555     * @param stdClass $instance
556     * @param int $groupid
557     * @param int $userid
558     */
559    public function restore_group_member($instance, $groupid, $userid) {
560        global $CFG;
561        require_once("$CFG->dirroot/group/lib.php");
562
563        // This might be called when forcing restore as manual enrolments.
564
565        groups_add_member($groupid, $userid);
566    }
567
568    /**
569     * Is it possible to delete enrol instance via standard UI?
570     *
571     * @param object $instance
572     * @return bool
573     */
574    public function can_delete_instance($instance) {
575        $context = context_course::instance($instance->courseid);
576        return has_capability('enrol/manual:config', $context);
577    }
578
579    /**
580     * Is it possible to hide/show enrol instance via standard UI?
581     *
582     * @param stdClass $instance
583     * @return bool
584     */
585    public function can_hide_show_instance($instance) {
586        $context = context_course::instance($instance->courseid);
587        return has_capability('enrol/manual:config', $context);
588    }
589
590    /**
591     * Enrol all not enrolled cohort members into course via enrol instance.
592     *
593     * @param stdClass $instance
594     * @param int $cohortid
595     * @param int $roleid optional role id
596     * @param int $timestart 0 means unknown
597     * @param int $timeend 0 means forever
598     * @param int $status default to ENROL_USER_ACTIVE for new enrolments, no change by default in updates
599     * @param bool $recovergrades restore grade history
600     */
601    public function enrol_cohort(stdClass $instance, $cohortid, $roleid = null, $timestart = 0, $timeend = 0, $status = null, $recovergrades = null) {
602        global $DB;
603        $context = context_course::instance($instance->courseid);
604        list($esql, $params) = get_enrolled_sql($context);
605        $sql = "SELECT cm.userid FROM {cohort_members} cm LEFT JOIN ($esql) u ON u.id = cm.userid ".
606            "WHERE cm.cohortid = :cohortid AND u.id IS NULL";
607        $params['cohortid'] = $cohortid;
608        $members = $DB->get_fieldset_sql($sql, $params);
609        foreach ($members as $userid) {
610            $this->enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status, $recovergrades);
611        }
612    }
613}
Note: See TracBrowser for help on using the repository browser.