Proyectos de Subversion Moodle

Rev

Autoría | 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/>.

/**
 * Enrol coursecompleted plugin
 *
 * @package   enrol_coursecompleted
 * @copyright 2017 eWallah (www.eWallah.net)
 * @author    Renaat Debleu <info@eWallah.net>
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

/**
 * Enrol coursecompleted plugin
 *
 * @package   enrol_coursecompleted
 * @copyright 2017 eWallah (www.eWallah.net)
 * @author    Renaat Debleu <info@eWallah.net>
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class enrol_coursecompleted_plugin extends enrol_plugin {
    /** @var bool singleinstance. */
    private $singleinstance = false;

    /**
     * Returns localised name of enrol instance
     *
     * @param object $instance (null is accepted too)
     * @return string
     */
    public function get_instance_name($instance) {
        global $DB;
        if ($short = $DB->get_field('course', 'shortname', ['id' => $instance->customint1])) {
            $coursename = format_string($short, true, ['context' => \context_course::instance($instance->customint1)]);
            return get_string('aftercourse', 'enrol_coursecompleted', $coursename);
        }
        return get_string('coursedeleted', '', $instance->customint1);
    }

    /**
     * Returns optional enrolment information icons.
     *
     * @param array $instances all enrol instances of this type in one course
     * @return array of pix_icon
     */
    public function get_info_icons(array $instances) {
        global $DB;
        $arr = [];
        foreach ($instances as $instance) {
            if ($fullname = $DB->get_field('course', 'fullname', ['id' => $instance->customint1])) {
                $context = \context_course::instance($instance->customint1);
                $name = format_string($fullname, true, ['context' => $context]);
                $arr[] = new pix_icon('icon', get_string('aftercourse', 'enrol_coursecompleted', $name), 'enrol_coursecompleted');
            }
        }
        return $arr;
    }

    /**
     * Returns optional enrolment instance description text.
     *
     * @param object $instance
     * @return string short html text
     */
    public function get_description_text($instance) {
        return 'Enrolment by completion of course with id ' . $instance->customint1;
    }

    /**
     * Add information for people who want to enrol.
     *
     * @param stdClass $instance
     * @return string html text, usually a form in a text box
     */
    public function enrol_page_hook(stdClass $instance) {
        global $OUTPUT;
        $data = [];
        if ($this->get_config('svglearnpath')) {
            $items = $this->build_course_path($instance);
            $i = 1;
            foreach ($items as $item) {
                $course = get_course($item);
                $context = \context_course::instance($item);
                $data[] =
                    [
                        'first' => ($i === 1),
                        'course' => $item == $instance->courseid,
                        'title' => format_string($course->fullname, true, ['context' => $context]),
                        'href' => new moodle_url('/course/view.php', ['id' => $item]),
                        'seqnumber' => $i,
                    ];
                $i++;
            }
        }
        $course = get_course($instance->customint1);
        $context = \context_course::instance($instance->customint1);
        $rdata =
            [
                'coursetitle' => format_string($course->fullname, true, ['context' => $context]),
                'courseurl' => new moodle_url('/course/view.php', ['id' => $instance->customint1]),
                'hasdata' => count($data) > 1,
                'items' => $data,
            ];
        $str = $OUTPUT->render_from_template('enrol_coursecompleted/learnpath', $rdata);
        return $OUTPUT->box($str);
    }

    /**
     * Gets an array of the user enrolment actions
     *
     * @param \course_enrolment_manager $manager
     * @param stdClass $ue A user enrolment object
     * @return array An array of user_enrolment_actions
     */
    public function get_user_enrolment_actions(\course_enrolment_manager $manager, $ue) {
        global $DB;
        $actions = parent::get_user_enrolment_actions($manager, $ue);
        $id = $ue->enrolmentinstance->customint1;
        if ($DB->record_exists('course', ['id' => $id])) {
            $context = \context_course::instance($id);
            if (has_capability('report/completion:view', $context)) {
                $actions[] = new user_enrolment_action(
                    new pix_icon('a/search', ''),
                    get_string('pluginname', 'report_completion'),
                    new moodle_url('/report/completion/index.php', ['course' => $id]),
                    ['class' => 'originlink', 'rel' => $ue->id]
                );
            }
        }
        return $actions;
    }

    /**
     * Returns edit icons for the page with list of instances
     * @param stdClass $instance
     * @return array
     */
    public function get_action_icons(stdClass $instance) {
        global $OUTPUT;
        if ($instance->enrol !== 'coursecompleted') {
            throw new coding_exception('invalid enrol instance!');
        }
        $context = \context_course::instance($instance->courseid);
        $icons = [];
        if (has_capability('enrol/coursecompleted:enrolpast', $context)) {
            $managelink = new moodle_url("/enrol/coursecompleted/manage.php", ['enrolid' => $instance->id]);
            $icon = new pix_icon('t/enrolusers', get_string('enrolusers', 'enrol_manual'), 'core', ['class' => 'iconsmall']);
            $icons[] = $OUTPUT->action_icon($managelink, $icon);
        }
        return array_merge(parent::get_action_icons($instance), $icons);
    }

    /**
     * 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;
        if ($step->get_task()->get_target() == backup::TARGET_NEW_COURSE) {
            $merge = false;
        } else {
            $merge = [
                'courseid' => $course->id,
                'enrol' => 'coursecompleted',
                'roleid' => $data->roleid,
                'customint1' => $data->customint1,
                'customint2' => $data->customint2,
                'customint3' => $data->customint3,
            ];
        }
        if ($merge && $instances = $DB->get_records('enrol', $merge, 'id')) {
            $instance = reset($instances);
            $instanceid = $instance->id;
        } else {
            $instanceid = $this->add_instance($course, (array)$data);
        }
        $step->set_mapping('enrol', $oldid, $instanceid);
    }

    /**
     * Enrol user into course via enrol instance.
     *
     * @param stdClass $instance
     * @param int $userid
     * @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 void
     */
    public function enrol_user(
        stdClass $instance,
        $userid,
        $roleid = null,
        $timestart = 0,
        $timeend = 0,
        $status = null,
        $recovergrades = null
    ) {
        global $CFG, $DB;
        // We need to keep the role of the user.
        if (isset($instance->roleid)) {
            $roleid = $instance->roleid;
        }
        if (isset($instance->enrolstartdate)) {
            $timestart = $instance->enrolstartdate;
        }
        if (isset($instance->enrolenddate)) {
            $timeend = $instance->enrolenddate;
        }
        if (isset($instance->enrolperiod) && $instance->enrolperiod > 0) {
            $timeend = max(time(), $timestart) + $instance->enrolperiod;
        }

        if ($DB->record_exists('role', ['id' => $roleid])) {
            $context = \context_course::instance($instance->courseid, MUST_EXIST);
            parent::enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status, $recovergrades);
            role_assign($roleid, $userid, $context->id, 'enrol_coursecompleted', $instance->id);

            // Send welcome message if needed.
            if ($instance->customint2 > 0) {
                // There is a course welcome message to be sent.
                $adhock = new \enrol_coursecompleted\task\send_welcome();
                $adhock->set_custom_data(
                    [
                        'userid' => $userid,
                        'enrolid' => $instance->id,
                        'courseid' => $instance->courseid,
                        'completedid' => $instance->customint1,
                    ]
                );
                $adhock->set_component('enrol_coursecompleted');
                \core\task\manager::queue_adhoc_task($adhock);
            }

            // Keep the user in a group when needed.
            if ($instance->customint3 > 0) {
                require_once($CFG->dirroot . '/group/lib.php');
                $groups = array_values(groups_get_user_groups($instance->customint1, $userid));
                foreach ($groups as $group) {
                    $subs = array_values($group);
                    foreach ($subs as $sub) {
                        $groupnamea = groups_get_group_name($sub);
                        $groupnameb = groups_get_group_by_name($instance->courseid, $groupnamea);
                        if ($groupnameb) {
                            groups_add_member($groupnameb, $userid);
                        }
                    }
                }
            }
            mark_user_dirty($userid);
        } else {
            debugging('Role does not exist', DEBUG_DEVELOPER);
        }
    }

    /**
     * Restore user enrolment.
     *
     * @param \restore_enrolments_structure_step $step
     * @param stdClass $data
     * @param stdClass $instance
     * @param int $userid
     * @param int $oldstatus
     */
    public function restore_user_enrolment(\restore_enrolments_structure_step $step, $data, $instance, $userid, $oldstatus) {
        if ($step->get_task()->get_target() == backup::TARGET_NEW_COURSE) {
            $this->enrol_user($instance, $userid);
        }
    }

    /**
     * Is it possible to add enrol instance via standard UI?
     *
     * @param int $courseid id of the course to add the instance to
     * @return boolean
     */
    public function can_add_instance($courseid) {
        return has_capability('enrol/coursecompleted:manage', context_course::instance($courseid));
    }

    /**
     * Is it possible to delete enrol instance via standard UI?
     *
     * @param object $instance
     * @return bool
     */
    public function can_delete_instance($instance) {
        return has_capability('enrol/coursecompleted:manage', context_course::instance($instance->courseid));
    }

    /**
     * Is it possible to hide/show enrol instance via standard UI?
     *
     * @param stdClass $instance
     * @return bool
     */
    public function can_hide_show_instance($instance): bool {
        return has_capability('enrol/coursecompleted:manage', context_course::instance($instance->courseid));
    }

    /**
     * Does this plugin allow manual unenrolment of all users?
     *
     * @param stdClass $instance course enrol instance
     * @return bool - true means user with 'enrol/xxx:unenrol' may unenrol others freely
     */
    public function allow_unenrol(stdClass $instance): bool {
        return has_capability('enrol/coursecompleted:manage', context_course::instance($instance->courseid));
    }

    /**
     * Does this plugin allow manual changes in user_enrolments table?
     *
     * @param stdClass $instance course enrol instance
     * @return bool - true means it is possible to change enrol period and status in user_enrolments table
     */
    public function allow_manage(stdClass $instance): bool {
        return has_capability('enrol/coursecompleted:manage', context_course::instance($instance->courseid));
    }

    /**
     * Does this plugin shows enrol me?
     *
     * @param stdClass $instance course enrol instance
     * @return bool - true means it is possible to enrol.
     */
    public function show_enrolme_link(stdClass $instance): bool {
        return ($instance->status == ENROL_INSTANCE_ENABLED);
    }

    /**
     * Execute synchronisation.
     * @param progress_trace $trace
     * @return int exit code, 0 means ok
     */
    public function sync(progress_trace $trace): int {
        $this->process_expirations($trace);
        return 0;
    }

    /**
     * We are a good plugin and don't invent our own UI/validation code path.
     *
     * @return bool
     */
    public function use_standard_editing_ui(): bool {
        return true;
    }

    /**
     * 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 = [ENROL_INSTANCE_ENABLED => get_string('yes'), ENROL_INSTANCE_DISABLED => get_string('no')];
        $mform->addElement('select', 'status', get_string('enabled', 'admin'), $options);
        $mform->setDefault('status', $this->get_config('status'));

        $role = ($instance && isset($instance->roleid)) ? $instance->roleid : $this->get_config('roleid');
        $roles = get_default_enrol_roles($context, $role);
        $mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_fee'), $roles);
        $mform->setDefault('roleid', $this->get_config('roleid'));

        $arr = ['optional' => true, 'defaultunit' => 86400];
        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_coursecompleted'), $arr);
        $mform->setDefault('enrolperiod', $this->get_config('enrolperiod'));
        $mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_coursecompleted');

        $start = ($instance && isset($instance->customint1)) ? get_course($instance->customint1)->startdate : time();
        $arr = ['optional' => true, 'defaulttime' => $start];
        $mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_coursecompleted'), $arr);
        $mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_coursecompleted');

        $duration = intval(get_config('moodlecourse', 'courseduration')) ?? YEARSECS;
        $arr['defaulttime'] = $start + $duration;
        $mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_coursecompleted'), $arr);
        $mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_coursecompleted');

        $conditions = ['onlywithcompletion' => true, 'multiple' => false, 'includefrontpage' => false];
        $mform->addElement('course', 'customint1', get_string('course'), $conditions);
        $mform->addRule('customint1', get_string('required'), 'required', null, 'client');
        $mform->addHelpButton('customint1', 'compcourse', 'enrol_coursecompleted');

        $mform->addElement('advcheckbox', 'customint3', get_string('groups'), get_string('group', 'enrol_coursecompleted'));
        $mform->addHelpButton('customint3', 'group', 'enrol_coursecompleted');
        $mform->setDefault('customint3', $this->get_config('keepgroup'));

        $mform->addElement(
            'advcheckbox',
            'customint2',
            get_string('categoryemail', 'admin'),
            get_string('welcome', 'enrol_coursecompleted')
        );
        $mform->addHelpButton('customint2', 'welcome', 'enrol_coursecompleted');
        $mform->setDefault('customint2', $this->get_config('welcome'));

        $arr = ['cols' => '60', 'rows' => '8'];
        $mform->addElement('textarea', 'customtext1', get_string('customwelcome', 'enrol_coursecompleted'), $arr);
        $mform->addHelpButton('customtext1', 'customwelcome', 'enrol_coursecompleted');
        $mform->disabledIf('customtext1', 'customint2', 'notchecked');
    }

    /**
     * Add new instance of enrol plugin.
     * @param object $course
     * @param array $fields
     * @return int id of new instance, null if can not be created
     */
    public function add_instance($course, array $fields = null): int {
        if ($fields) {
            if (!isset($fields['customint2'])) {
                $fields['customint2'] = $this->get_config('welcome', 1);
            }
            if (!isset($fields['customint3'])) {
                $fields['customint3'] = $this->get_config('keepgroup', 1);
            }
        }
        return parent::add_instance($course, $fields);
    }

    /**
     * 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.
     */
    public function edit_instance_validation($data, $files, $instance, $context): array {
        global $DB;
        $errors = [];
        if ($data['status'] == ENROL_INSTANCE_ENABLED) {
            if (!empty($data['enrolenddate']) && $data['enrolenddate'] < $data['enrolstartdate']) {
                $errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_fee');
            }
            if (
                empty($data['customint1']) ||
                $data['customint1'] === 1 ||
                !$DB->record_exists('course', ['id' => $data['customint1']])
            ) {
                $errors['customint'] = get_string('error_nonexistingcourse', 'tool_generator');
            }
        }
        return $errors;
    }

    /**
     * Build (possible) coursepath
     *
     * @param stdClass $instance
     * @return array $items
     */
    public function build_course_path(stdClass $instance): array {
        $parents = $this->search_parents($instance->customint1);
        $children = $this->search_children($instance->courseid);
        return array_unique(array_merge($parents, $children));
    }

    /**
     * Search parents
     *
     * @param int $id
     * @param int $level
     * @return array items
     */
    private function search_parents($id, $level = 1): array {
        global $DB;
        $arr = [$id];
        if ($level < 5) {
            $level++;
            $params = ['enrol' => 'coursecompleted', 'courseid' => $id];
            if ($parent = $DB->get_field('enrol', 'customint1', $params, IGNORE_MULTIPLE)) {
                $arr = array_merge($this->search_parents($parent, $level), $arr);
            }
        }
        return $arr;
    }

    /**
     * Search children
     *
     * @param int $id
     * @param int $level
     * @return array items
     */
    private function search_children($id, $level = 1): array {
        global $DB;
        $arr = [$id];
        if ($level < 5) {
            $level++;
            $params = ['enrol' => 'coursecompleted', 'customint1' => $id];
            if ($child = $DB->get_field('enrol', 'courseid', $params, IGNORE_MULTIPLE)) {
                $arr = array_merge($arr, $this->search_children($child, $level));
            }
        }
        return $arr;
    }

    /**
     * Returns true if the plugin has one or more bulk operations that can be performed on
     * user enrolments.
     *
     * @param \course_enrolment_manager $manager
     * @return bool
     */
    public function has_bulk_operations(\course_enrolment_manager $manager): bool {
        if ($this->singleinstance == false) {
            $instances = array_values($manager->get_enrolment_instances(false));
            $i = 0;
            foreach ($instances as $instance) {
                if ($instance->enrol == 'coursecompleted') {
                    $i++;
                }
            }
            $this->singleinstance = (bool)($i === 1);
        }
        return $this->singleinstance;
    }

    /**
     * The enrol plugin has bulk operations that can be performed.
     * @param \course_enrolment_manager $manager
     * @return array
     */
    public function get_bulk_operations(\course_enrolment_manager $manager): array {
        $context = $manager->get_context();
        $bulkoperations = [];
        if ($this->has_bulk_operations($manager)) {
            if (has_capability("enrol/coursecompleted:manage", $context)) {
                $bulkoperations['editselectedusers'] = new \enrol_coursecompleted\bulkedit($manager, $this);
            }
            if (has_capability("enrol/coursecompleted:unenrol", $context)) {
                $bulkoperations['deleteselectedusers'] = new \enrol_coursecompleted\bulkdelete($manager, $this);
            }
        }
        return $bulkoperations;
    }

    /**
     * Enrol all users who completed in the past.
     * @param int $courseid
     */
    public static function enrol_past(int $courseid) {
        global $DB;
        $params = [
            'enrol' => 'coursecompleted',
            'status' => ENROL_INSTANCE_ENABLED,
            'customint1' => $courseid,
        ];

        if ($enrols = $DB->get_records('enrol', $params)) {
            $plugin = \enrol_get_plugin('coursecompleted');
            foreach ($enrols as $enrol) {
                $candidates = self::get_candidates($enrol->customint1);
                foreach ($candidates as $canid) {
                    $plugin->enrol_user($enrol, $canid);
                }
            }
        }
    }

    /**
     * Get all candidates for an enrolment.
     * @param int $courseid
     * @return array
     */
    public static function get_candidates(int $courseid): array {
        global $DB;
        $condition = 'course = ? AND timecompleted > 0';
        $candidates = [];
        if ($return = $DB->get_fieldset_select('course_completions', 'userid', $condition, [$courseid])) {
            $candidates = $return;
        }
        return $candidates;
    }
}