Your IP : 216.73.216.95


Current Path : /var/www/ljmtc/cbt/mod/grouptool/
Upload File :
Current File : /var/www/ljmtc/cbt/mod/grouptool/lib.php

<?php
// This file is part of mod_grouptool for Moodle - http://moodle.org/
//
// It 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.
//
// It 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/>.

/**
 * This file contains the moodle hooks for the grouptool module.
 *
 * @package   mod_grouptool
 * @author    Philipp Hager
 * @copyright 2014 Academic Moodle Cooperation {@link http://www.academic-moodle-cooperation.org}
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

defined('MOODLE_INTERNAL') || die();

require_once(dirname(__FILE__).'/definitions.php');

/*******************************************************************************
 * Moodle core API                                                             *
 *******************************************************************************/

/**
 * Returns the information on whether the module supports a feature
 *
 * @see plugin_supports() in lib/moodlelib.php
 * @param string $feature FEATURE_xx constant for requested feature
 * @return mixed true if the feature is supported, null if unknown
 */
function grouptool_supports($feature) {
    switch ($feature) {
        case FEATURE_MOD_INTRO:
            return true;
        case FEATURE_BACKUP_MOODLE2:
            return true;
        case FEATURE_SHOW_DESCRIPTION:
            return true;
        case FEATURE_MOD_ARCHETYPE:
            return MOD_ARCHETYPE_OTHER;

        case FEATURE_GROUPS:
        case FEATURE_GROUPINGS:
        case FEATURE_IDNUMBER:
        default:
            return false;
    }
}

/**
 * Saves a new instance of the grouptool into the database
 *
 * Given an object containing all the necessary data,
 * (defined by the form in mod_form.php) this function
 * will create a new instance and return the id number
 * of the new instance.
 *
 * @param stdClass $grouptool An object from the form in mod_form.php
 * @return bool|int The id of the newly inserted grouptool record
 * @throws coding_exception
 * @throws dml_exception
 */
function grouptool_add_instance(stdClass $grouptool) {
    global $DB;

    $grouptool->timecreated = time();

    if (!isset($grouptool->use_size)) {
        $grouptool->use_size = 0;
    }
    if (!isset($grouptool->use_queue)) {
        $grouptool->use_queue = 0;
    }
    if (!isset($grouptool->users_queues_limit) || empty($grouptool->limit_users_queues)) {
        $grouptool->users_queues_limit = 0;
    }
    if (!isset($grouptool->groups_queues_limit) || empty($grouptool->limit_groups_queues)) {
        $grouptool->groups_queues_limit = 0;
    }
    if (!isset($grouptool->allow_multiple)) {
        $grouptool->allow_multiple = 0;
        $grouptool->choose_min = 0;
        $grouptool->choose_max = 1;
    } else {
        $grouptool->choose_min = clean_param($grouptool->choose_min, PARAM_INT);
        $grouptool->choose_max = clean_param($grouptool->choose_max, PARAM_INT);
    }

    $grouptool->grpsize = clean_param($grouptool->grpsize, PARAM_INT);

    $return = $DB->insert_record('grouptool', $grouptool);

    grouptool_refresh_events($grouptool->course, $return);

    $coursegroups = $DB->get_fieldset_select('groups', 'id', 'courseid = ?', [$grouptool->course]);
    foreach ($coursegroups as $groupid) {
        if (!$DB->record_exists('grouptool_agrps', [
                'grouptoolid' => $return,
                'groupid'     => $groupid
        ])) {
            $record = new stdClass();
            $record->grouptoolid = $return;
            $record->groupid = $groupid;
            $record->sort_order = 9999999;
            $record->grpsize = $grouptool->grpsize;
            $record->active = 0;
            $DB->insert_record('grouptool_agrps', $record);
        }
    }

    return $return;
}

/**
 * Updates an instance of the grouptool in the database
 *
 * Given an object containing all the necessary data,
 * (defined by the form in mod_form.php) this function
 * will update an existing instance with new data.
 *
 * @param stdClass $grouptool An object from the form in mod_form.php
 * @return bool Success/Fail
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 */
function grouptool_update_instance(stdClass $grouptool) {
    global $DB, $CFG;

    $grouptool->timemodified = time();
    $grouptool->id = $grouptool->instance;

    if (!isset($grouptool->use_size)) {
        $grouptool->use_size = 0;
    }
    if (!isset($grouptool->use_queue) || ($grouptool->use_size == 0)) {
        $queues = $DB->count_records_sql("SELECT COUNT(DISTINCT queues.id) AS count
                                            FROM {grouptool_agrps} agrps
                                       LEFT JOIN {grouptool_queued} queues ON queues.agrpid = agrps.id
                                           WHERE agrps.grouptoolid = ? AND agrps.active = 1", [$grouptool->instance]);
        if (!empty($queues)) {
            $grouptool->use_queue = 1;
        } else {
            $grouptool->use_queue = 0;
            $grouptool->users_queues_limit = 0;
            $grouptool->groups_queues_limit = 0;
        }
    }
    if (!isset($grouptool->users_queues_limit) || empty($grouptool->limit_users_queues)) {
        $grouptool->users_queues_limit = 0;
    }
    if (!isset($grouptool->groups_queues_limit) || empty($grouptool->limit_groups_queues)) {
        $grouptool->groups_queues_limit = 0;
    }
    if (!isset($grouptool->allow_multiple)) {
        $grouptool->allow_multiple = 0;
    }

    $grouptool->grpsize = clean_param($grouptool->grpsize, PARAM_INT);
    $grouptool->choose_min = clean_param($grouptool->choose_min, PARAM_INT);
    $grouptool->choose_max = clean_param($grouptool->choose_max, PARAM_INT);

    // Register students if immediate registration has been turned on!
    if ($grouptool->immediate_reg) {
        require_once($CFG->dirroot.'/mod/grouptool/locallib.php');
        $instance = new mod_grouptool($grouptool->coursemodule, $grouptool);
        $instance->push_registrations();
    }

    grouptool_refresh_events($grouptool->course, $grouptool);

    $coursegroups = $DB->get_fieldset_select('groups', 'id', 'courseid = ?', [$grouptool->course]);
    foreach ($coursegroups as $groupid) {
        if (!$DB->record_exists('grouptool_agrps', [
                'grouptoolid' => $grouptool->instance,
                'groupid'     => $groupid
        ])) {
            $record = new stdClass();
            $record->grouptoolid = $grouptool->instance;
            $record->groupid = $groupid;
            $record->sort_order = 9999999;
            $record->grpsize = $grouptool->grpsize;
            $record->active = 0;
            $DB->insert_record('grouptool_agrps', $record);
        }
    }

    // We have to override the functions fetching of data, because it's not updated yet!
    grouptool_update_queues($grouptool);

    return $DB->update_record('grouptool', $grouptool);
}

/**
 * Make sure up-to-date events are created for all grouptool instances
 *
 * This standard function will check all instances of this module
 * and make sure there are up-to-date vents created for each of them.
 * If courseid = 0, then every grouptool event in the site is checked, else
 * only grouptool events belonging to the course specified are checked.
 * This function is used, in it's new format, by restore_refresh_events()
 *
 * TODO this callback changed in 3.3.2 and we quick fixed it but should take a look at at for 3.4!
 *
 * @param int $courseid
 * @param int|stdClass $instance Assign module instance or ID.
 * @param int|stdClass $cm Course module object or ID (not used in this module).
 * @return bool
 * @throws coding_exception
 * @throws dml_exception
 */
function grouptool_refresh_events($courseid = 0, $instance = null, $cm = null) {
    global $DB, $CFG;
    require_once($CFG->dirroot.'/calendar/lib.php');

    // If we have instance information then we can just update the one event instead of updating all events.
    if (isset($instance)) {
        if (!is_object($instance)) {
            $instance = $DB->get_record('grouptool', ['id' => $instance], '*', MUST_EXIST);
        }
        $grouptools = [$instance->id => $instance];
    } else {
        $cond = [];

        if ($courseid) {
            // Make sure that the course id is numeric.
            if (!is_numeric($courseid)) {
                return false;
            }
            $cond['course'] = $courseid;
        }

        if (!$grouptools = $DB->get_records('grouptool', $cond)) {
            return true;
        }
    }

    if ($grouptools) {
        foreach ($grouptools as $grouptool) {
            if (count($grouptools) > 1 || !isset($cm) || !is_object($cm)) {
                $cm = get_coursemodule_from_instance('grouptool', $grouptool->id);
            }

            // Start with creating the event.
            $event = new stdClass();
            $event->modulename  = 'grouptool';
            $event->courseid = $grouptool->course;
            $event->groupid = 0;
            $event->userid  = 0;
            $event->instance  = $grouptool->id;
            $event->name = $grouptool->name;
            $event->type = CALENDAR_EVENT_TYPE_ACTION;

            if (!empty($grouptool->intro)) {
                if (!$cm) {
                    // Convert the links to pluginfile. It is a bit hacky but at this stage the files
                    // might not have been saved in the module area yet.
                    $intro = $grouptool->intro;
                    if ($draftid = file_get_submitted_draft_itemid('introeditor')) {
                        $intro = file_rewrite_urls_to_pluginfile($intro, $draftid);
                    }

                    // We need to remove the links to files as the calendar is not ready
                    // to support module events with file areas.
                    $intro = strip_pluginfile_content($intro);
                    $event->description = [
                            'text' => $intro,
                            'format' => $grouptool->introformat
                    ];
                } else {
                    $event->description = format_module_intro('grouptool', $grouptool, $cm->id);
                }
            }

            if ($grouptool->timedue) {
                $event->eventtype = GROUPTOOL_EVENT_TYPE_DUE;
                $event->name = $grouptool->name;

                $event->timestart = $grouptool->timedue;
                $event->timesort = $grouptool->timedue;
                $select = "modulename = :modulename
                           AND instance = :instance
                           AND eventtype = :eventtype
                           AND groupid = 0
                           AND courseid <> 0";
                $params = ['modulename' => 'grouptool', 'instance' => $grouptool->id, 'eventtype' => $event->eventtype];
                $event->id = $DB->get_field_select('event', 'id', $select, $params);

                // Now process the event.
                if ($event->id) {
                    $calendarevent = calendar_event::load($event->id);
                    $calendarevent->update($event, false);
                } else {
                    calendar_event::create($event, false);
                }
            } else {
                $DB->delete_records('event', [
                        'modulename' => 'grouptool', 'instance' => $grouptool->id,
                        'eventtype' => GROUPTOOL_EVENT_TYPE_DUE
                ]);
            }
        }
    }
    return true;
}

/**
 * function looks through all the queues and moves users from queue to reg if there's place
 *
 * @param stdClass|int $grouptool grouptool object or grouptoolid
 * @throws coding_exception
 * @throws dml_exception
 */
function grouptool_update_queues($grouptool = 0) {
    global $DB;

    // Update queues and move users from queue to reg if there's place!
    if (!is_object($grouptool)) {
        $grouptool = $DB->get_record('grouptool', ['id' => $grouptool], MUST_EXIST);
        $grouptool->instance = $grouptool->id;
    } else {
        $grouptool->instance = $grouptool->id;
    }

    if ($agrps = $DB->get_records('grouptool_agrps', ['grouptoolid' => $grouptool->instance])) {
        list($agrpsql, $params) = $DB->get_in_or_equal(array_keys($agrps));
        $groupregs = $DB->get_records_sql_menu('SELECT agrpid, COUNT(id)
                                                  FROM {grouptool_registered}
                                                 WHERE agrpid '.$agrpsql.' AND modified_by >= 0
                                              GROUP BY agrpid', $params);
        foreach ($agrps as $agrpid => $agrp) {
            $size = empty($agrp->grpsize) ? $grouptool->grpsize : $agrp->grpsize;
            $min = empty($grouptool->allow_multiple) ? 0 : $grouptool->choose_min;
            $max = empty($grouptool->allow_multiple) ? 1 : $grouptool->choose_max;
            // We use MAX to trick Postgres into thinking this is an full GROUP BY statement.
            $sql = "SELECT queued.id AS id, MAX(queued.agrpid) AS agrpid, MAX(queued.timestamp) AS timestamp,
                           MAX(queued.userid) AS userid, (regs < ?) AS priority, MAX(reg.regs) AS regs
                      FROM {grouptool_queued} queued
                 LEFT JOIN (SELECT userid, COUNT(DISTINCT id) AS regs
                              FROM {grouptool_registered}
                             WHERE agrpid ".$agrpsql." AND modified_by >= 0
                          GROUP BY userid) reg ON queued.userid = reg.userid
                     WHERE queued.agrpid = ?
                  GROUP BY queued.id, priority
                  ORDER BY priority DESC, queued.timestamp ASC";

            if ($records = $DB->get_records_sql($sql, array_merge([$min],
                                                                 $params, [$agrpid]))) {
                foreach ($records as $record) {
                    if (!empty($grouptool->use_size) && ($groupregs[$agrpid] >= $size)) {
                        // Group is full!
                        break;
                    }
                    if ($record->regs >= $max) {
                        // User got too many regs!
                        continue;
                    }
                    unset($record->id);
                    if (!$DB->record_exists('grouptool_registered', [
                            'agrpid' => $agrpid,
                            'userid' => $record->userid
                    ])) {
                        unset($record->priority);
                        unset($record->regs);
                        $record->modified_by = 0;
                        $DB->insert_record('grouptool_registered', $record);
                        if (!empty($grouptool->immediate_reg)) {
                            groups_add_member($agrp->groupid, $record->userid);
                        }
                    } else if ($mark = $DB->get_record('grouptool_registered', [
                            'agrpid' => $agrpid,
                            'userid' => $record->userid,
                            'modified_by' => -1
                    ])) {
                        $mark->modified_by = 0;
                        $DB->update_record('grouptool_registered', $mark);
                        if (!empty($grouptool->immediate_reg)) {
                            groups_add_member($agrp->groupid, $record->userid);
                        }
                    }
                    $DB->delete_records('grouptool_queued', [
                            'agrpid' => $agrpid,
                            'userid' => $record->userid
                    ]);
                    $groupregs[$agrpid]++;
                }
            }
        }
    }
}

/**
 * Removes an instance of the grouptool from the database
 *
 * Given an ID of an instance of this module,
 * this function will permanently delete the instance
 * and any data that depends on it.
 *
 * @param int $id Id of the module instance
 * @return boolean Success/Failure
 * @throws coding_exception
 * @throws dml_exception
 */
function grouptool_delete_instance($id) {
    global $DB;

    if (! $grouptool = $DB->get_record('grouptool', ['id' => $id])) {
        return false;
    }

    // Get all agrp-ids for this grouptool-instance!
    if ($DB->record_exists('grouptool_agrps', ['grouptoolid' => $id])) {
        $ids = $DB->get_fieldset_select('grouptool_agrps', 'id', "grouptoolid = ?", [$id]);

        /*
         * delete all entries in grouptool_agrps, grouptool_queued, grouptool_registered
         * with correct grouptoolid or agrps_id
         */
        if (is_array($ids)) {
            list($sql, $params) = $DB->get_in_or_equal($ids);
            $DB->delete_records_select('grouptool_queued', "agrpid ".$sql, $params);
            $DB->delete_records_select('grouptool_registered', "agrpid ".$sql, $params);
            $DB->delete_records_select('grouptool_agrps', "id ".$sql, $params);
        }
    }

    $DB->delete_records('event', ['modulename' => 'grouptool', 'instance' => $grouptool->id]);

    $DB->delete_records('grouptool', ['id' => $id]);

    return true;
}

/**
 * Add a get_coursemodule_info function in case any grouptool type wants to add 'extra' information
 * for the course (see resource).
 *
 * Given a course_module object, this function returns any "extra" information that may be needed
 * when printing this activity in a course listing.  See get_array_of_activities() in course/lib.php.
 *
 * @param stdClass $coursemodule The coursemodule object (record).
 * @return cached_cm_info|bool An object on information that the courses
 *                             will know about (most noticeably, an icon).
 * @throws dml_exception
 */
function grouptool_get_coursemodule_info($coursemodule) {
    global $DB;

    $dbparams = ['id' => $coursemodule->instance];
    $fields = 'id, name, alwaysshowdescription, timeavailable, intro, introformat';
    if (! $grouptool = $DB->get_record('grouptool', $dbparams, $fields)) {
        return false;
    }

    $result = new cached_cm_info();
    $result->name = $grouptool->name;
    if ($coursemodule->showdescription) {
        if ($grouptool->alwaysshowdescription || (time() > $grouptool->timeavailable)) {
            // Convert intro to html. Do not filter cached version, filters run at display time.
            $result->content = format_module_intro('grouptool', $grouptool, $coursemodule->id, false);
        } else {
            unset($result->content);
        }
    }
    return $result;
}

/**
 * Returns all other caps used in the module
 *
 * @return array
 */
function grouptool_get_extra_capabilities() {
    return ['moodle/course:managegroups'];
}

/*******************************************************************************
 * Navigation API                                                              *
 *******************************************************************************/
/**
 * Extends the global navigation tree by adding grouptool nodes if there is a relevant content
 *
 * This can be called by an AJAX request so do not rely on $PAGE as it might not be set up properly.
 *
 * @param navigation_node $navref Object representing the nav tree node of the grouptool mod instance
 * @param stdClass $course course object
 * @param stdClass $module module object
 * @param cm_info $cm course module info object
 * @throws coding_exception
 * @throws moodle_exception
 */
function grouptool_extend_navigation(navigation_node $navref, stdClass $course, stdClass $module, cm_info $cm) {
    if ($course->id != $module->course) {
        // Just so PHPMD won't complain about $course being here ;) These have to be equal all the time!
        return;
    }

    $context = context_module::instance($cm->id);
    $creategrps = has_capability('mod/grouptool:create_groups', $context);
    $creategrpgs = has_capability('mod/grouptool:create_groupings', $context);
    $admingrps = has_capability('mod/grouptool:administrate_groups', $context);

    if ($creategrps || $creategrpgs || $admingrps) {
        if ($creategrps && ($admingrps || $creategrpgs)) {
            $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'administration']);
            $admin = $navref->add(get_string('administration', 'grouptool'), $url);
            $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'group_admin']);
            $admin->add(get_string('group_administration', 'grouptool'), $url);
            $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'group_creation']);
            $admin->add(get_string('group_creation', 'grouptool'), $url);
        } else if ($creategrps) {
            $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'group_creation']);
            $navref->add(get_string('group_creation', 'grouptool'), $url);
        } else if ($creategrpgs || $admingrps) {
            $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'group_admin']);
            $navref->add(get_string('group_administration', 'grouptool'), $url);
        }
    }
    if (has_capability('mod/grouptool:grade', $context)
            || has_capability('mod/grouptool:grade_own_group', $context)) {
        $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'grading']);
        $navref->add(get_string('grading', 'grouptool'), $url);
    }

    $gt = $module;
    $regopen = ($gt->allow_reg && (($gt->timedue == 0) || (time() < $gt->timedue))
            && ($gt->timeavailable < time()));

    if (has_capability('mod/grouptool:register_students', $context)
            || ($regopen && has_capability('mod/grouptool:register', $context))) {
        $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'selfregistration']);
        $navref->add(get_string('selfregistration', 'grouptool'), $url);
    }

    if (has_capability('mod/grouptool:register_students', $context)) {
        $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'import']);
        $navref->add(get_string('import', 'grouptool'), $url);
    }
    if (has_capability('mod/grouptool:view_regs_course_view', $context)
            && has_capability('mod/grouptool:view_regs_group_view', $context)) {
        $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'overview']);
        $userstab = $navref->add(get_string('users_tab', 'grouptool'), $url);
        $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'overview']);
        $userstab->add(get_string('overview_tab', 'grouptool'), $url);
        $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'userlist']);
        $userstab->add(get_string('userlist_tab', 'grouptool'), $url);
    } else if (has_capability('mod/grouptool:view_regs_group_view', $context)) {
        $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'overview']);
        $navref->add(get_string('users_tab', 'grouptool'), $url);
    } else if (has_capability('mod/grouptool:view_regs_course_view', $context)) {
        $url = new moodle_url('/mod/grouptool/view.php', ['id' => $cm->id, 'tab' => 'userlist']);
        $navref->add(get_string('users_tab', 'grouptool'), $url);
    }

    $navref->nodetype = navigation_node::NODETYPE_BRANCH;
}

/**
 * displays if submission was early enough or late...
 *
 * @param int $timesubmitted
 * @param int $timedue
 * @return array string color class, string html-fragment
 * @throws coding_exception
 */
function grouptool_display_lateness($timesubmitted = null, $timedue = null) {
    if ($timesubmitted == null) {
        $timesubmitted = time();
    }
    $time = $timedue - $timesubmitted;
    if (empty($timedue)) {
        $colorclass = 'early';
        $timeremaining = ' ('.html_writer::tag('span', format_time($time),
                                               ['class' => 'early']).')';
    } else if ($time >= 7 * 24 * 60 * 60) { // More than 7 days?
        $colorclass = 'early';
        $timeremaining = ' ('.html_writer::tag('span', get_string('early', 'grouptool',
                                                                  format_time($time)),
                                               ['class' => 'early']).')';
    } else if ($time >= 24 * 60 * 60) { // More than 1 day (less than 7 days)?
        $colorclass = 'soon';
        $timeremaining = ' ('.html_writer::tag('span', get_string('early', 'grouptool',
                                                                  format_time($time)),
                                               ['class' => 'soon']).')';
    } else if ($time >= 0) { // In future but less than 1 day?
        $colorclass = 'today';
        $timeremaining = ' ('.html_writer::tag('span', get_string('early', 'grouptool',
                                                                  format_time($time)),
                                               ['class' => 'today']).')';
    } else {
        $colorclass = 'late';
        $timeremaining = ' ('.html_writer::tag('span', get_string('late', 'grouptool',
                                               format_time($time)), ['class' => 'late']).')';
    }
    return [$colorclass, $timeremaining];
}

/**
 * prepare text for mymoodle-Page to be displayed
 *
 * @deprecated since 3.3
 * @todo The final deprecation of this function will take place in Moodle 3.7 - see MDL-57487.
 * @param stdClass[] $courses
 * @param string[][] $htmlarray
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 */
function grouptool_print_overview($courses, &$htmlarray) {
    global $CFG;

    debugging('The function grouptool_print_overview() is now deprecated.', DEBUG_DEVELOPER);

    require_once($CFG->dirroot.'/mod/grouptool/locallib.php');

    if (empty($courses) || !is_array($courses) || count($courses) == 0) {
        return;
    }

    if (!$grouptools = get_all_instances_in_courses('grouptool', $courses)) {
        return;
    }

    foreach ($grouptools as $grouptool) {

        $context = context_module::instance($grouptool->coursemodule, MUST_EXIST);

        $strgrouptool = get_string('grouptool', 'grouptool');
        $strduedate = get_string('duedate', 'grouptool');
        $strduedateno = get_string('duedateno', 'grouptool');

        $str = "";
        if (has_capability('mod/grouptool:register', $context)
            || has_capability('mod/grouptool:view_regs_group_view', $context)
            || has_capability('mod/grouptool:view_regs_course_view', $context)) {
            $attrib = [
                    'title' => $strgrouptool, 'href' => $CFG->wwwroot.
                                                                '/mod/grouptool/view.php?id='.
                                                                $grouptool->coursemodule
            ];
            if (!$grouptool->visible
                || (($grouptool->timedue != 0) && ($grouptool->timedue <= time()))) {
                $attrib['class'] = 'dimmed';
            }
            list($cc, ) = grouptool_display_lateness(time(), $grouptool->timedue);
            $str .= html_writer::tag('div', $strgrouptool.': '.
                    html_writer::tag('a', $grouptool->name, $attrib),
                    ['class' => 'name']);
            $attr = ['class' => 'info'];
            if ($grouptool->timeavailable > time()) {
                $ta = $grouptool->timeavailable;
                $str .= html_writer::tag('div', get_string('availabledate', 'grouptool').': '.
                                                html_writer::tag('span', userdate($ta)), $attr);
            }
            if ($grouptool->timedue) {
                $tagargs = ['class' => (($cc == 'late') ? ' late' : '')];
                $datesnippet = html_writer::tag('span', userdate($grouptool->timedue), $tagargs);
                $str .= html_writer::tag('div', $strduedate.': '. $datesnippet, $attr);
            } else {
                $str .= html_writer::tag('div', $strduedateno, $attr);
            }
        }
        $details = grouptool_get_user_reg_details($grouptool, $context);

        if (has_capability('mod/grouptool:view_regs_group_view', $context)
            || has_capability('mod/grouptool:view_regs_course_view', $context)
            || has_capability('mod/grouptool:register', $context)) {
            $str = html_writer::tag('div', $str.$details, ['class' => 'grouptool overview']);
            if (empty($htmlarray[$grouptool->course]['grouptool'])) {
                $htmlarray[$grouptool->course]['grouptool'] = $str;
            } else {
                $htmlarray[$grouptool->course]['grouptool'] .= $str;
            }
        }
    }
}

/**
 * Get a nice overview over user's registration details!
 *
 * @param stdClass $grouptool Grouptool DB record with additional coursemodule property set!
 * @param context $context Context instance
 * @return string HTML snippet with user's registration details
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 */
function grouptool_get_user_reg_details($grouptool, $context) {
    global $USER;

    $details = '';
    if (has_capability('mod/grouptool:register', $context)
        || has_capability('mod/grouptool:view_regs_course_view', $context)
        || has_capability('mod/grouptool:view_regs_group_view', $context)) {
        // It's similar to the student mymoodle output!
        $instance = new mod_grouptool($grouptool->coursemodule, $grouptool);
        $userstats = $instance->get_registration_stats($USER->id);
    } else {
        return '';
    }

    list($colorclass, ) = grouptool_display_lateness(time(), $grouptool->timedue);

    if (has_capability('mod/grouptool:register', $context)) {
        if ($grouptool->allow_reg) {
            if (count($userstats->registered)) {
                $tempstr = "";
                foreach ($userstats->registered as $registration) {
                    if ($tempstr != "") {
                        $tempstr .= '; ';
                    }
                    $tempstr .= html_writer::tag('span', $registration->grpname);
                }
                if (($grouptool->allow_multiple &&
                        (count($userstats->registered) < $grouptool->choose_min))
                        || (!$grouptool->allow_multiple && !count($userstats->registered))) {
                    if ($grouptool->allow_multiple) {
                        $missing = ($grouptool->choose_min - count($userstats->registered));
                        $stringlabel = ($missing > 1) ? 'registrations_missing' : 'registration_missing';
                    } else {
                        $missing = 1;
                        $stringlabel = 'registration_missing';
                    }
                    $details .= html_writer::tag('div',
                            html_writer::tag('div',
                                    get_string($stringlabel, 'grouptool', $missing),
                                    ['class' => $colorclass]).' '.
                            get_string('registrations', 'grouptool').': '.$tempstr,
                            ['class' => 'registered']);
                } else {
                    $details .= html_writer::tag('div',
                            get_string('registrations', 'grouptool').': '.$tempstr,
                            ['class' => 'registered']);
                }
            } else {
                if ($grouptool->allow_multiple) {
                    $missing = ($grouptool->choose_min - count($userstats->registered));
                    $stringlabel = ($missing > 1) ? 'registrations_missing' : 'registration_missing';
                } else {
                    $missing = 1;
                    $stringlabel = 'registration_missing';
                }
                $details .= html_writer::tag('div',
                        html_writer::tag('div',
                                get_string($stringlabel, 'grouptool', $missing),
                                ['class' => $colorclass]).
                        get_string('registrations', 'grouptool').': '.
                        get_string('not_registered', 'grouptool'),
                        ['class' => 'registered']);
            }
            if (count($userstats->queued)) {
                $tempstr = "";
                foreach ($userstats->queued as $queue) {
                    list($colorclass, ) = grouptool_display_lateness($queue->timestamp,
                                                                          $grouptool->timedue);
                    if ($tempstr != "") {
                        $tempstr .= ", ";
                    }
                    $tempstr .= html_writer::tag('span', $queue->grpname.' ('.$queue->rank.')',
                                                  ['class' => $colorclass]);
                }
                $details .= html_writer::tag('div', get_string('queues', 'grouptool').': '.
                        $tempstr, ['class' => 'queued']);
            }
        }
    }

    if ((has_capability('mod/grouptool:view_regs_group_view', $context) || has_capability('mod/grouptool:view_regs_course_view',
                                                                                          $context))
            && $grouptool->allow_reg) {
        $details .= html_writer::tag('div', get_string('global_userstats', 'grouptool', $userstats), ['class' => 'userstats']);

    }

    return html_writer::tag('div', $details, ['class' => 'details']);
}

/**
 * This function is used by the reset_course_userdata function in moodlelib.
 * This function will remove all posts from the specified grouptool(s)
 * and clean up any related data.
 *
 * @param stdClass $data the data submitted from the reset course.
 * @return array status array
 * @throws coding_exception
 * @throws dml_exception
 */
function grouptool_reset_userdata($data) {
    global $CFG, $DB;

    if (!$DB->count_records('grouptool', ['course' => $data->courseid])) {
        return []; // No grouptools present!
    }

    $componentstr = get_string('modulenameplural', 'grouptool');
    $status = [];

    $grouptoolids = $DB->get_fieldset_select('grouptool', 'id', 'course = ?',
                                              [$data->courseid]);

    $agrps = $DB->get_records_list('grouptool_agrps', 'grouptoolid', $grouptoolids);

    if (!empty($data->reset_grouptool_transparent_unreg)) {
        require_once($CFG->dirroot.'/group/lib.php');
        $regdata = $DB->get_records_list('grouptool_registered', 'agrpid', array_keys($agrps));
        foreach ($regdata as $registration) {
            groups_remove_member($agrps[$registration->agrpid]->groupid, $registration->userid);
        }
        $status[] = [
                'component'    => $componentstr,
                'item'         => get_string('reset_transparent_unreg', 'grouptool'),
                'error'        => false
        ];
    }

    if (!empty($data->reset_grouptool_queues) || !empty($data->reset_grouptool_agrps)) {
        $DB->delete_records_list('grouptool_queued', 'agrpid', array_keys($agrps));
        $status[] = [
                'component'    => $componentstr,
                'item'         => get_string('reset_queues', 'grouptool'),
                'error'        => false
        ];
    }

    if (!empty($data->reset_grouptool_registrations) || !empty($data->reset_grouptool_agrps)) {
        $DB->delete_records_list('grouptool_registered', 'agrpid', array_keys($agrps));
        $status[] = [
                'component' => $componentstr,
                'item'      => get_string('reset_registrations', 'grouptool'),
                'error'     => false
        ];
    }

    if (!empty($data->reset_grouptool_agrps)) {
        $DB->delete_records_list('grouptool_agrps', 'grouptoolid', $grouptoolids);
        $status[] = [
                'component'    => $componentstr,
                'item'         => get_string('reset_agrps', 'grouptool'),
                'error'        => false
        ];
    }

    return $status;
}

/**
 * Implementation of the function for printing the form elements that control
 * whether the course reset functionality affects the grouptool.
 * @param MoodleQuickForm $mform form passed by reference
 * @throws coding_exception
 */
function grouptool_reset_course_form_definition(MoodleQuickForm &$mform) {
    $mform->addElement('header', 'grouptoolheader', get_string('modulenameplural', 'grouptool'));
    $mform->addElement('advcheckbox', 'reset_grouptool_agrps',
                       get_string('reset_agrps', 'grouptool'));
    $mform->addHelpButton('reset_grouptool_agrps', 'reset_agrps', 'grouptool');
    $mform->addElement('advcheckbox', 'reset_grouptool_registrations',
                       get_string('reset_registrations', 'grouptool'));
    $mform->addHelpButton('reset_grouptool_registrations', 'reset_registrations', 'grouptool');
    $mform->disabledIf('reset_grouptool_registrations', 'reset_grouptool_agrps', 'checked');
    $mform->addElement('advcheckbox', 'reset_grouptool_queues',
                       get_string('reset_queues', 'grouptool'));
    $mform->addHelpButton('reset_grouptool_queues', 'reset_queues', 'grouptool');
    $mform->disabledIf('reset_grouptool_queues', 'reset_grouptool_agrps', 'checked');
    $mform->addElement('advcheckbox', 'reset_grouptool_transparent_unreg',
                       get_string('reset_transparent_unreg', 'grouptool'));
    $mform->addHelpButton('reset_grouptool_transparent_unreg', 'reset_transparent_unreg',
                          'grouptool');
}

/**
 * Course reset form defaults.
 */
function grouptool_reset_course_form_defaults() {
    return [
            'reset_grouptool_registrations'     => 1,
            'reset_grouptool_queues'            => 1,
            'reset_grouptool_agrps'             => 0,
            'reset_grouptool_transparent_unreg' => 0
    ];
}

/**
 * Copy Assign Grades from one user to another user (in assign_grade table)
 *
 * @param int $id Assignment ID
 * @param int $fromid User ID from whom will be copied
 * @param int $toid User ID to whom will be copied
 * @throws coding_exception
 * @throws dml_exception
 */
function grouptool_copy_assign_grades($id, $fromid, $toid) {
    global $DB, $CFG;

    $source = $DB->get_records('assign_grades', ['assignment' => $id, 'userid' => $fromid], 'id DESC', '*', 0, 1);
    if (!is_array($toid)) {
        $toid = [$toid];
    }
    $source = reset($source);
    $user = $DB->get_record('user', ['id' => $source->userid]);
    $grader = $DB->get_record('user', ['id' => $source->grader]);
    // Get corresponding feedback!
    $feedbackcomment = $DB->get_record('assignfeedback_comments', [
            'assignment' => $id,
            'grade'      => $source->id
    ]);
    $feedbackfile = $DB->get_record('assignfeedback_file', [
            'assignment' => $id,
            'grade'      => $source->id
    ]);
    foreach ($toid as $curid) {
        $record = clone $source;
        $record->userid = $curid;
        unset($record->id);
        if ($record->id = $DB->get_field('assign_grades', 'id', [
                'assignment'    => $id,
                'userid'        => $curid,
                'attemptnumber' => $source->attemptnumber
        ])) {
            $DB->update_record('assign_grades', $record);
            if ($feedbackcomment) {
                $newfeedbackcomment = clone $feedbackcomment;
                unset($newfeedbackcomment->id);
                $newfeedbackcomment->grade = $record->id;
                $newfeedbackcomment->assignment = $id;
                $details = [
                        'student'  => fullname($user),
                        'teacher'  => fullname($grader),
                        'date'     => userdate($source->timemodified,
                                                        get_string('strftimedatetimeshort')),
                        'feedback' => $newfeedbackcomment->commenttext
                ];
                $newfeedbackcomment->commenttext = format_text(get_string('copied_grade_feedback',
                                                                          'grouptool',
                                                                          $details),
                                                               $newfeedbackcomment->commentformat);
                if ($newfeedbackcomment->id = $DB->get_field('assignfeedback_comments', 'id', [
                        'assignment' => $id,
                        'grade'      => $record->id
                ])) {
                    $DB->update_record('assignfeedback_comments', $newfeedbackcomment);
                } else {
                    $DB->insert_record('assignfeedback_comments', $newfeedbackcomment);
                }
            }
            if ($feedbackfile) {
                $newfeedbackfile = clone $feedbackfile;
                unset($newfeedbackfile->id);
                $newfeedbackfile->grade = $record->id;
                $newfeedbackfile->assignment = $id;
                if ($newfeedbackfile->id = $DB->get_field('assignfeedback_file', 'id', [
                        'assignment' => $id,
                        'grade'      => $record->id
                ])) {
                    $DB->update_record('assignfeedback_file', $newfeedbackfile);
                } else {
                    $DB->insert_record('assignfeedback_file', $newfeedbackfile);
                }
            }
        } else {
            $gradeid = $DB->insert_record('assign_grades', $record);
            if ($feedbackcomment) {
                $newfeedbackcomment = clone $feedbackcomment;
                unset($newfeedbackcomment->id);
                $newfeedbackcomment->grade = $gradeid;
                $newfeedbackcomment->assignment = $id;
                $details = [
                        'student'  => fullname($user),
                        'teacher'  => fullname($grader),
                        'date'     => userdate($source->timemodified,
                                                        get_string('strftimedatetimeshort')),
                        'feedback' => $newfeedbackcomment->commenttext
                ];
                $newfeedbackcomment->commenttext = format_text(get_string('copied_grade_feedback',
                                                                          'grouptool',
                                                                          $details),
                                                               $newfeedbackcomment->commentformat);
                if ($newfeedbackcomment->id = $DB->get_field('assignfeedback_comments', 'id', [
                        'assignment' => $id,
                        'grade'      => $gradeid
                ])) {
                    $DB->update_record('assignfeedback_comments', $newfeedbackcomment);
                } else {
                    $DB->insert_record('assignfeedback_comments', $newfeedbackcomment);
                }
            }
            if ($feedbackfile) {
                $newfeedbackfile = clone $feedbackfile;
                unset($newfeedbackfile->id);
                $newfeedbackfile->grade = $gradeid;
                $newfeedbackfile->assignment = $id;
                if ($newfeedbackfile->id = $DB->get_field('assignfeedback_file', 'id', [
                        'assignment' => $id,
                        'grade'      => $gradeid
                ])) {
                    $DB->update_record('assignfeedback_file', $newfeedbackfile);
                } else {
                    $DB->insert_record('assignfeedback_file', $newfeedbackfile);
                }
            }
        }

        // User must have an assign_submission record, or the grade wont be displayed properly!
        if (!$DB->record_exists('assign_submission', ['assignment' => $id, 'userid' => $curid])) {
            require_once($CFG->dirroot.'/mod/assign/locallib.php');
            $rec = new stdClass();
            $rec->assignment = $id;
            $rec->userid = $curid;
            $rec->timecreated = time();
            $rec->timemodified = $rec->timecreated;
            $rec->groupid = 0;
            $rec->attemptnumber = 0;
            $rec->latest = 1;
            $rec->status = ASSIGN_SUBMISSION_STATUS_NEW;
            $DB->insert_record('assign_submission', $rec);
        }
    }
}

/*
 ******************** CALENDAR API AND SIMILAR FUNCTIONS FOR GROUPTOOLS ***********************
 */

/**
 * Is the event visible?
 *
 * This is used to determine global visibility of an event in all places throughout Moodle. For example,
 * the ASSIGN_EVENT_TYPE_GRADINGDUE event will not be shown to students on their calendar, and
 * ASSIGN_EVENT_TYPE_DUE events will not be shown to teachers.
 *
 * @param calendar_event $event
 * @return bool Returns true if the event is visible to the current user, false otherwise.
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 */
function mod_grouptool_core_calendar_is_event_visible(calendar_event $event) {
    global $CFG;

    require_once($CFG->dirroot . '/mod/grouptool/locallib.php');

    $cm = get_fast_modinfo($event->courseid)->instances['grouptool'][$event->instance];
    $context = context_module::instance($cm->id);

    $grouptool = new mod_grouptool($cm->id, null, $cm, null);

    $managesregs = has_capability('mod/grouptool:register_students', $context) || has_capability('mod/grouptool:move_students',
                                                                                                 $context);

    if ($event->eventtype == GROUPTOOL_EVENT_TYPE_DUE) {
        return ((has_capability('mod/grouptool:register', $context) && $grouptool->is_registration_open())
                || ($managesregs && ($grouptool->get_missing_registrations() >= 1 || $grouptool->is_registration_open())));
    }

    return false;
}

/**
 * This function receives a calendar event and returns the action associated with it, or null if there is none.
 *
 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
 * is not displayed on the block.
 *
 * @param calendar_event $event
 * @param \core_calendar\action_factory $factory
 * @return \core_calendar\local\event\entities\action_interface|\core_calendar\local\event\value_objects\action
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 */
function mod_grouptool_core_calendar_provide_event_action(calendar_event $event, \core_calendar\action_factory $factory) {
    global $CFG, $USER;

    require_once($CFG->dirroot . '/mod/grouptool/locallib.php');

    $cm = get_fast_modinfo($event->courseid)->instances['grouptool'][$event->instance];
    $context = context_module::instance($cm->id);

    $grouptool = new mod_grouptool($cm->id, null, $cm, null);

    $managesregs = has_capability('mod/grouptool:register_students', $context) || has_capability('mod/grouptool:move_students',
                                                                                                 $context);
    $isopen = $grouptool->is_registration_open();

    $url = new \moodle_url('/mod/grouptool/view.php', [
        'id' => $cm->id
    ]);

    $actionable = false;
    // Item count can't be 0 for the event to be displayed, but now we use it to count the real items!
    $itemcount = -1;
    $label = '';

    if (!$managesregs && has_capability('mod/grouptool:register', $context)) {
        $userstats = $grouptool->get_registration_stats($USER->id);
        list($allowmultiple, $choosemin, ) = $grouptool->get_reg_settings();
        if ($allowmultiple) {
            $itemcount = ($choosemin - count($userstats->registered));
            $label = get_string(($itemcount > 1) ? 'register' : 'register', 'grouptool');
        } else {
            $itemcount = !empty($userstats->registered) ? 0 : 1;
            $label = get_string('register', 'grouptool');
        }
        if ($itemcount <= 0) {
            $label = get_string('view_registrations', 'grouptool');
            $itemcount = -1;
        }
        // Clickable if registration is open and registrations are missing or enough registrations are made!
        $actionable = ($isopen && ($itemcount > 0)) || ($itemcount <= 0);
    } else if ($managesregs) {
        $missing = $grouptool->get_missing_registrations();
        $itemcount = ($missing > 0) ? $missing : 0;
        if ($missing > 1) {
            $label = get_string('myoverview_registrations_missing', 'grouptool');
        } else if ($missing == 1) {
            $label = get_string('myoverview_registrations_missing', 'grouptool');
        } else {
            $label = get_string('view');
            $itemcount = -1;
        }
        $url = new moodle_url($url, ['tab' => 'overview']);
        $actionable = true;
    }

    return $factory->create_instance($label, $url, $itemcount, $actionable);
}

/**
 * Callback function that determines whether an action event should be showing its item count
 * based on the event type and the item count.
 *
 * @param calendar_event $event The calendar event.
 * @param int $itemcount The item count associated with the action event.
 * @return bool
 */
function mod_grouptool_core_calendar_event_action_shows_item_count(calendar_event $event, $itemcount = 0) {
    // List of event types where the action event's item count should be shown.
    $showitemcountfor = [
        GROUPTOOL_EVENT_TYPE_DUE
    ];
    // For mod_grouptool, item count should be shown if the event type is 'due' and there is one or more items.
    return in_array($event->eventtype, $showitemcountfor) && $itemcount > 0;
}

/**
 * Map icons for font-awesome themes.
 *
 * @return string[] Mapping array with font awesome classes indexed by image names
 */
function mod_grouptool_get_fontawesome_icon_map() {
    return [
        'mod_grouptool:active' => 'fa-circle text-success',
        'mod_grouptool:inactive' => 'fa-circle'
    ];
}