Merge branch 'MDL-72703-402' of https://github.com/paulholden/moodle into MOODLE_402_...
[moodle.git] / completion / classes / api.php
blobd4c83a238ce58fbaf65c517ffa21eac47c1f4ba4
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.
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/>.
17 /**
18 * Contains class containing completion API.
20 * @package core_completion
21 * @copyright 2017 Mark Nelson <markn@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 namespace core_completion;
27 defined('MOODLE_INTERNAL') || die();
29 /**
30 * Class containing completion API.
32 * @package core_completion
33 * @copyright 2017 Mark Nelson <markn@moodle.com>
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 class api {
38 /**
39 * @var string The completion expected on event.
41 const COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED = 'expectcompletionon';
43 /**
44 * Creates, updates or deletes an event for the expected completion date.
46 * @param int $cmid The course module id
47 * @param string $modulename The name of the module (eg. assign, quiz)
48 * @param \stdClass|int $instanceorid The instance object or ID.
49 * @param int|null $completionexpectedtime The time completion is expected, null if not set
50 * @return bool
52 public static function update_completion_date_event($cmid, $modulename, $instanceorid, $completionexpectedtime) {
53 global $CFG, $DB;
55 // Required for calendar constant CALENDAR_EVENT_TYPE_ACTION.
56 require_once($CFG->dirroot . '/calendar/lib.php');
58 $instance = null;
59 if (is_object($instanceorid)) {
60 $instance = $instanceorid;
61 } else {
62 $instance = $DB->get_record($modulename, array('id' => $instanceorid), '*', IGNORE_MISSING);
64 if (!$instance) {
65 return false;
67 $course = get_course($instance->course);
69 $completion = new \completion_info($course);
71 // Can not create/update an event if completion is disabled.
72 if (!$completion->is_enabled() && $completionexpectedtime !== null) {
73 return true;
76 // Create the \stdClass we will be using for our language strings.
77 $lang = new \stdClass();
78 $lang->modulename = get_string('pluginname', $modulename);
79 $lang->instancename = $instance->name;
81 // Create the calendar event.
82 $event = new \stdClass();
83 $event->type = CALENDAR_EVENT_TYPE_ACTION;
84 $event->eventtype = self::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED;
85 if ($event->id = $DB->get_field('event', 'id', array('modulename' => $modulename,
86 'instance' => $instance->id, 'eventtype' => $event->eventtype))) {
87 if ($completionexpectedtime !== null) {
88 // Calendar event exists so update it.
89 $event->name = get_string('completionexpectedfor', 'completion', $lang);
90 $event->description = format_module_intro($modulename, $instance, $cmid, false);
91 $event->format = FORMAT_HTML;
92 $event->timestart = $completionexpectedtime;
93 $event->timesort = $completionexpectedtime;
94 $event->visible = instance_is_visible($modulename, $instance);
95 $event->timeduration = 0;
97 $calendarevent = \calendar_event::load($event->id);
98 $calendarevent->update($event, false);
99 } else {
100 // Calendar event is no longer needed.
101 $calendarevent = \calendar_event::load($event->id);
102 $calendarevent->delete();
104 } else {
105 // Event doesn't exist so create one.
106 if ($completionexpectedtime !== null) {
107 $event->name = get_string('completionexpectedfor', 'completion', $lang);
108 $event->description = format_module_intro($modulename, $instance, $cmid, false);
109 $event->format = FORMAT_HTML;
110 $event->courseid = $instance->course;
111 $event->groupid = 0;
112 $event->userid = 0;
113 $event->modulename = $modulename;
114 $event->instance = $instance->id;
115 $event->timestart = $completionexpectedtime;
116 $event->timesort = $completionexpectedtime;
117 $event->visible = instance_is_visible($modulename, $instance);
118 $event->timeduration = 0;
120 \calendar_event::create($event, false);
124 return true;
128 * Mark users who completed course based on activity criteria.
129 * @param array $userdata If set only marks specified user in given course else checks all courses/users.
130 * @return int Completion record id if $userdata is set, 0 else.
131 * @since Moodle 4.0
133 public static function mark_course_completions_activity_criteria($userdata = null): int {
134 global $DB;
136 // Get all users who meet this criteria
137 $sql = "SELECT DISTINCT c.id AS course,
138 cr.id AS criteriaid,
139 ra.userid AS userid,
140 mc.timemodified AS timecompleted
141 FROM {course_completion_criteria} cr
142 INNER JOIN {course} c ON cr.course = c.id
143 INNER JOIN {context} con ON con.instanceid = c.id
144 INNER JOIN {role_assignments} ra ON ra.contextid = con.id
145 INNER JOIN {course_modules} cm ON cm.id = cr.moduleinstance
146 INNER JOIN {course_modules_completion} mc ON mc.coursemoduleid = cr.moduleinstance AND mc.userid = ra.userid
147 LEFT JOIN {course_completion_crit_compl} cc ON cc.criteriaid = cr.id AND cc.userid = ra.userid
148 WHERE cr.criteriatype = :criteriatype
149 AND con.contextlevel = :contextlevel
150 AND c.enablecompletion = 1
151 AND cc.id IS NULL
152 AND (
153 mc.completionstate = :completionstate
154 OR (cm.completionpassgrade = 1 AND mc.completionstate = :completionstatepass1)
155 OR (cm.completionpassgrade = 0 AND (mc.completionstate = :completionstatepass2
156 OR mc.completionstate = :completionstatefail))
159 $params = [
160 'criteriatype' => COMPLETION_CRITERIA_TYPE_ACTIVITY,
161 'contextlevel' => CONTEXT_COURSE,
162 'completionstate' => COMPLETION_COMPLETE,
163 'completionstatepass1' => COMPLETION_COMPLETE_PASS,
164 'completionstatepass2' => COMPLETION_COMPLETE_PASS,
165 'completionstatefail' => COMPLETION_COMPLETE_FAIL
168 if ($userdata) {
169 $params['courseid'] = $userdata['courseid'];
170 $params['userid'] = $userdata['userid'];
171 $sql .= " AND c.id = :courseid AND ra.userid = :userid";
172 // Mark as complete.
173 $record = $DB->get_record_sql($sql, $params);
174 if ($record) {
175 $completion = new \completion_criteria_completion((array) $record, DATA_OBJECT_FETCH_BY_KEY);
176 $result = $completion->mark_complete($record->timecompleted);
177 return $result;
179 } else {
180 // Loop through completions, and mark as complete.
181 $rs = $DB->get_recordset_sql($sql, $params);
182 foreach ($rs as $record) {
183 $completion = new \completion_criteria_completion((array) $record, DATA_OBJECT_FETCH_BY_KEY);
184 $completion->mark_complete($record->timecompleted);
186 $rs->close();
188 return 0;