Commit | Line | Data |
---|---|---|
a084ac0d | 1 | <?php |
bbd0e548 DW |
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 | /** | |
e5403f8c | 18 | * This file contains the moodle hooks for the assign module. |
1561a37c | 19 | * |
e5403f8c | 20 | * It delegates most functions to the assignment class. |
bbd0e548 DW |
21 | * |
22 | * @package mod_assign | |
23 | * @copyright 2012 NetSpot {@link http://www.netspot.com.au} | |
24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
25 | */ | |
26 | defined('MOODLE_INTERNAL') || die(); | |
27 | ||
28 | /** | |
29 | * Adds an assignment instance | |
30 | * | |
31 | * This is done by calling the add_instance() method of the assignment type class | |
32 | * @param stdClass $data | |
33 | * @param mod_assign_mod_form $form | |
34 | * @return int The instance id of the new assignment | |
35 | */ | |
2cffef9f | 36 | function assign_add_instance(stdClass $data, mod_assign_mod_form $form = null) { |
bbd0e548 DW |
37 | global $CFG; |
38 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
39 | ||
40 | $assignment = new assign(context_module::instance($data->coursemodule), null, null); | |
41 | return $assignment->add_instance($data, true); | |
42 | } | |
43 | ||
44 | /** | |
45 | * delete an assignment instance | |
46 | * @param int $id | |
47 | * @return bool | |
48 | */ | |
49 | function assign_delete_instance($id) { | |
50 | global $CFG; | |
51 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
52 | $cm = get_coursemodule_from_instance('assign', $id, 0, false, MUST_EXIST); | |
53 | $context = context_module::instance($cm->id); | |
54 | ||
55 | $assignment = new assign($context, null, null); | |
56 | return $assignment->delete_instance(); | |
57 | } | |
58 | ||
d38dc52f RW |
59 | /** |
60 | * This function is used by the reset_course_userdata function in moodlelib. | |
61 | * This function will remove all assignment submissions and feedbacks in the database | |
62 | * and clean up any related data. | |
1561a37c DW |
63 | * |
64 | * @param stdClass $data the data submitted from the reset course. | |
65 | * @return array | |
d38dc52f RW |
66 | */ |
67 | function assign_reset_userdata($data) { | |
68 | global $CFG, $DB; | |
69 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
70 | ||
71 | $status = array(); | |
72 | $params = array('courseid'=>$data->courseid); | |
73 | $sql = "SELECT a.id FROM {assign} a WHERE a.course=:courseid"; | |
74df2951 | 74 | $course = $DB->get_record('course', array('id'=>$data->courseid), '*', MUST_EXIST); |
e5403f8c | 75 | if ($assigns = $DB->get_records_sql($sql, $params)) { |
d38dc52f | 76 | foreach ($assigns as $assign) { |
e5403f8c DW |
77 | $cm = get_coursemodule_from_instance('assign', |
78 | $assign->id, | |
79 | $data->courseid, | |
80 | false, | |
81 | MUST_EXIST); | |
d38dc52f RW |
82 | $context = context_module::instance($cm->id); |
83 | $assignment = new assign($context, $cm, $course); | |
84 | $status = array_merge($status, $assignment->reset_userdata($data)); | |
85 | } | |
86 | } | |
87 | return $status; | |
88 | } | |
89 | ||
418a0d04 JP |
90 | /** |
91 | * This standard function will check all instances of this module | |
92 | * and make sure there are up-to-date events created for each of them. | |
93 | * If courseid = 0, then every assignment event in the site is checked, else | |
94 | * only assignment events belonging to the course specified are checked. | |
95 | * | |
96 | * @param int $courseid | |
9ffbdafc AG |
97 | * @param int|stdClass $instance Assign module instance or ID. |
98 | * @param int|stdClass $cm Course module object or ID (not used in this module). | |
418a0d04 JP |
99 | * @return bool |
100 | */ | |
9ffbdafc | 101 | function assign_refresh_events($courseid = 0, $instance = null, $cm = null) { |
418a0d04 JP |
102 | global $CFG, $DB; |
103 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
104 | ||
9ffbdafc AG |
105 | // If we have instance information then we can just update the one event instead of updating all events. |
106 | if (isset($instance)) { | |
107 | if (!is_object($instance)) { | |
108 | $instance = $DB->get_record('assign', array('id' => $instance), '*', MUST_EXIST); | |
109 | } | |
110 | if (isset($cm)) { | |
111 | if (!is_object($cm)) { | |
112 | assign_prepare_update_events($instance); | |
113 | return true; | |
114 | } else { | |
115 | $course = get_course($instance->course); | |
116 | assign_prepare_update_events($instance, $course, $cm); | |
117 | return true; | |
118 | } | |
119 | } | |
120 | } | |
121 | ||
418a0d04 JP |
122 | if ($courseid) { |
123 | // Make sure that the course id is numeric. | |
124 | if (!is_numeric($courseid)) { | |
125 | return false; | |
126 | } | |
127 | if (!$assigns = $DB->get_records('assign', array('course' => $courseid))) { | |
128 | return false; | |
129 | } | |
130 | // Get course from courseid parameter. | |
131 | if (!$course = $DB->get_record('course', array('id' => $courseid), '*')) { | |
132 | return false; | |
133 | } | |
134 | } else { | |
135 | if (!$assigns = $DB->get_records('assign')) { | |
136 | return false; | |
137 | } | |
138 | } | |
139 | foreach ($assigns as $assign) { | |
9ffbdafc AG |
140 | assign_prepare_update_events($assign); |
141 | } | |
142 | ||
143 | return true; | |
144 | } | |
145 | ||
146 | /** | |
147 | * This actually updates the normal and completion calendar events. | |
148 | * | |
149 | * @param stdClass $assign Assignment object (from DB). | |
150 | * @param stdClass $course Course object. | |
151 | * @param stdClass $cm Course module object. | |
152 | */ | |
153 | function assign_prepare_update_events($assign, $course = null, $cm = null) { | |
154 | global $DB; | |
155 | if (!isset($course)) { | |
3651c22b JP |
156 | // Get course and course module for the assignment. |
157 | list($course, $cm) = get_course_and_cm_from_instance($assign->id, 'assign', $assign->course); | |
9ffbdafc AG |
158 | } |
159 | // Refresh the assignment's calendar events. | |
160 | $context = context_module::instance($cm->id); | |
161 | $assignment = new assign($context, $cm, $course); | |
162 | $assignment->update_calendar($cm->id); | |
163 | // Refresh the calendar events also for the assignment overrides. | |
164 | $overrides = $DB->get_records('assign_overrides', ['assignid' => $assign->id], '', | |
165 | 'id, groupid, userid, duedate, sortorder'); | |
166 | foreach ($overrides as $override) { | |
167 | if (empty($override->userid)) { | |
168 | unset($override->userid); | |
169 | } | |
170 | if (empty($override->groupid)) { | |
171 | unset($override->groupid); | |
3651c22b | 172 | } |
9ffbdafc | 173 | assign_update_events($assignment, $override); |
418a0d04 | 174 | } |
418a0d04 JP |
175 | } |
176 | ||
d38dc52f RW |
177 | /** |
178 | * Removes all grades from gradebook | |
179 | * | |
180 | * @param int $courseid The ID of the course to reset | |
181 | * @param string $type Optional type of assignment to limit the reset to a particular assignment type | |
182 | */ | |
183 | function assign_reset_gradebook($courseid, $type='') { | |
184 | global $CFG, $DB; | |
185 | ||
e5403f8c | 186 | $params = array('moduletype'=>'assign', 'courseid'=>$courseid); |
d38dc52f RW |
187 | $sql = 'SELECT a.*, cm.idnumber as cmidnumber, a.course as courseid |
188 | FROM {assign} a, {course_modules} cm, {modules} m | |
189 | WHERE m.name=:moduletype AND m.id=cm.module AND cm.instance=a.id AND a.course=:courseid'; | |
190 | ||
e5403f8c | 191 | if ($assignments = $DB->get_records_sql($sql, $params)) { |
d38dc52f RW |
192 | foreach ($assignments as $assignment) { |
193 | assign_grade_item_update($assignment, 'reset'); | |
194 | } | |
195 | } | |
196 | } | |
197 | ||
198 | /** | |
199 | * Implementation of the function for printing the form elements that control | |
200 | * whether the course reset functionality affects the assignment. | |
1561a37c | 201 | * @param moodleform $mform form passed by reference |
d38dc52f RW |
202 | */ |
203 | function assign_reset_course_form_definition(&$mform) { | |
204 | $mform->addElement('header', 'assignheader', get_string('modulenameplural', 'assign')); | |
e5403f8c DW |
205 | $name = get_string('deleteallsubmissions', 'assign'); |
206 | $mform->addElement('advcheckbox', 'reset_assign_submissions', $name); | |
d685b959 IT |
207 | $mform->addElement('advcheckbox', 'reset_assign_user_overrides', |
208 | get_string('removealluseroverrides', 'assign')); | |
209 | $mform->addElement('advcheckbox', 'reset_assign_group_overrides', | |
210 | get_string('removeallgroupoverrides', 'assign')); | |
d38dc52f RW |
211 | } |
212 | ||
213 | /** | |
214 | * Course reset form defaults. | |
215 | * @param object $course | |
216 | * @return array | |
217 | */ | |
218 | function assign_reset_course_form_defaults($course) { | |
d685b959 IT |
219 | return array('reset_assign_submissions' => 1, |
220 | 'reset_assign_group_overrides' => 1, | |
221 | 'reset_assign_user_overrides' => 1); | |
d38dc52f RW |
222 | } |
223 | ||
bbd0e548 DW |
224 | /** |
225 | * Update an assignment instance | |
226 | * | |
227 | * This is done by calling the update_instance() method of the assignment type class | |
228 | * @param stdClass $data | |
1561a37c | 229 | * @param stdClass $form - unused |
bbd0e548 DW |
230 | * @return object |
231 | */ | |
dd5d933f | 232 | function assign_update_instance(stdClass $data, $form) { |
bbd0e548 DW |
233 | global $CFG; |
234 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
235 | $context = context_module::instance($data->coursemodule); | |
236 | $assignment = new assign($context, null, null); | |
237 | return $assignment->update_instance($data); | |
238 | } | |
239 | ||
d685b959 IT |
240 | /** |
241 | * This function updates the events associated to the assign. | |
242 | * If $override is non-zero, then it updates only the events | |
243 | * associated with the specified override. | |
244 | * | |
f3ec5411 | 245 | * @param assign $assign the assign object. |
d685b959 IT |
246 | * @param object $override (optional) limit to a specific override |
247 | */ | |
248 | function assign_update_events($assign, $override = null) { | |
249 | global $CFG, $DB; | |
250 | ||
251 | require_once($CFG->dirroot . '/calendar/lib.php'); | |
252 | ||
f3ec5411 JP |
253 | $assigninstance = $assign->get_instance(); |
254 | ||
d685b959 | 255 | // Load the old events relating to this assign. |
f3ec5411 | 256 | $conds = array('modulename' => 'assign', 'instance' => $assigninstance->id); |
d685b959 IT |
257 | if (!empty($override)) { |
258 | // Only load events for this override. | |
259 | if (isset($override->userid)) { | |
260 | $conds['userid'] = $override->userid; | |
6bcc6f87 | 261 | } else if (isset($override->groupid)) { |
d685b959 | 262 | $conds['groupid'] = $override->groupid; |
8ea4df50 ZT |
263 | } else { |
264 | // This is not a valid override, it may have been left from a bad import or restore. | |
265 | $conds['groupid'] = $conds['userid'] = 0; | |
d685b959 IT |
266 | } |
267 | } | |
56d1ddd7 | 268 | $oldevents = $DB->get_records('event', $conds, 'id ASC'); |
d685b959 | 269 | |
98239b2d | 270 | // Now make a to-do list of all that needs to be updated. |
d685b959 | 271 | if (empty($override)) { |
56d1ddd7 CB |
272 | // We are updating the primary settings for the assignment, so we need to add all the overrides. |
273 | $overrides = $DB->get_records('assign_overrides', array('assignid' => $assigninstance->id), 'id ASC'); | |
274 | // It is necessary to add an empty stdClass to the beginning of the array as the $oldevents | |
275 | // list contains the original (non-override) event for the module. If this is not included | |
276 | // the logic below will end up updating the wrong row when we try to reconcile this $overrides | |
277 | // list against the $oldevents list. | |
278 | array_unshift($overrides, new stdClass()); | |
d685b959 IT |
279 | } else { |
280 | // Just do the one override. | |
281 | $overrides = array($override); | |
282 | } | |
283 | ||
f3ec5411 JP |
284 | if (!empty($assign->get_course_module())) { |
285 | $cmid = $assign->get_course_module()->id; | |
286 | } else { | |
287 | $cmid = get_coursemodule_from_instance('assign', $assigninstance->id, $assigninstance->course)->id; | |
288 | } | |
289 | ||
d685b959 IT |
290 | foreach ($overrides as $current) { |
291 | $groupid = isset($current->groupid) ? $current->groupid : 0; | |
292 | $userid = isset($current->userid) ? $current->userid : 0; | |
f3ec5411 | 293 | $duedate = isset($current->duedate) ? $current->duedate : $assigninstance->duedate; |
d685b959 | 294 | |
f7a9bca8 | 295 | // Only add 'due' events for an override if they differ from the assign default. |
d685b959 IT |
296 | $addclose = empty($current->id) || !empty($current->duedate); |
297 | ||
d685b959 | 298 | $event = new stdClass(); |
294dce67 | 299 | $event->type = CALENDAR_EVENT_TYPE_ACTION; |
f3ec5411 | 300 | $event->description = format_module_intro('assign', $assigninstance, $cmid); |
d685b959 | 301 | // Events module won't show user events when the courseid is nonzero. |
f3ec5411 | 302 | $event->courseid = ($userid) ? 0 : $assigninstance->course; |
d685b959 IT |
303 | $event->groupid = $groupid; |
304 | $event->userid = $userid; | |
305 | $event->modulename = 'assign'; | |
f3ec5411 | 306 | $event->instance = $assigninstance->id; |
f7a9bca8 JP |
307 | $event->timestart = $duedate; |
308 | $event->timeduration = 0; | |
294dce67 | 309 | $event->timesort = $event->timestart + $event->timeduration; |
f3ec5411 | 310 | $event->visible = instance_is_visible('assign', $assigninstance); |
c7d75292 | 311 | $event->eventtype = ASSIGN_EVENT_TYPE_DUE; |
56d1ddd7 | 312 | $event->priority = null; |
d685b959 | 313 | |
98239b2d | 314 | // Determine the event name and priority. |
d685b959 | 315 | if ($groupid) { |
98239b2d | 316 | // Group override event. |
d685b959 | 317 | $params = new stdClass(); |
f3ec5411 | 318 | $params->assign = $assigninstance->name; |
d685b959 IT |
319 | $params->group = groups_get_group_name($groupid); |
320 | if ($params->group === false) { | |
321 | // Group doesn't exist, just skip it. | |
322 | continue; | |
323 | } | |
324 | $eventname = get_string('overridegroupeventname', 'assign', $params); | |
98239b2d JP |
325 | // Set group override priority. |
326 | if (isset($current->sortorder)) { | |
327 | $event->priority = $current->sortorder; | |
328 | } | |
d685b959 | 329 | } else if ($userid) { |
98239b2d | 330 | // User override event. |
d685b959 | 331 | $params = new stdClass(); |
f3ec5411 | 332 | $params->assign = $assigninstance->name; |
d685b959 | 333 | $eventname = get_string('overrideusereventname', 'assign', $params); |
98239b2d JP |
334 | // Set user override priority. |
335 | $event->priority = CALENDAR_EVENT_USER_OVERRIDE_PRIORITY; | |
d685b959 | 336 | } else { |
98239b2d | 337 | // The parent event. |
f3ec5411 | 338 | $eventname = $assigninstance->name; |
d685b959 | 339 | } |
98239b2d | 340 | |
f7a9bca8 JP |
341 | if ($duedate && $addclose) { |
342 | if ($oldevent = array_shift($oldevents)) { | |
343 | $event->id = $oldevent->id; | |
344 | } else { | |
345 | unset($event->id); | |
d685b959 | 346 | } |
f7a9bca8 | 347 | $event->name = $eventname.' ('.get_string('duedate', 'assign').')'; |
bbf1fde4 | 348 | calendar_event::create($event, false); |
d685b959 IT |
349 | } |
350 | } | |
351 | ||
352 | // Delete any leftover events. | |
353 | foreach ($oldevents as $badevent) { | |
354 | $badevent = calendar_event::load($badevent); | |
355 | $badevent->delete(); | |
356 | } | |
357 | } | |
358 | ||
bbd0e548 DW |
359 | /** |
360 | * Return the list if Moodle features this module supports | |
361 | * | |
362 | * @param string $feature FEATURE_xx constant for requested feature | |
363 | * @return mixed True if module supports feature, null if doesn't know | |
364 | */ | |
365 | function assign_supports($feature) { | |
366 | switch($feature) { | |
df211804 DW |
367 | case FEATURE_GROUPS: |
368 | return true; | |
369 | case FEATURE_GROUPINGS: | |
370 | return true; | |
df211804 DW |
371 | case FEATURE_MOD_INTRO: |
372 | return true; | |
373 | case FEATURE_COMPLETION_TRACKS_VIEWS: | |
374 | return true; | |
375 | case FEATURE_COMPLETION_HAS_RULES: | |
376 | return true; | |
377 | case FEATURE_GRADE_HAS_GRADE: | |
378 | return true; | |
379 | case FEATURE_GRADE_OUTCOMES: | |
380 | return true; | |
381 | case FEATURE_BACKUP_MOODLE2: | |
382 | return true; | |
383 | case FEATURE_SHOW_DESCRIPTION: | |
384 | return true; | |
385 | case FEATURE_ADVANCED_GRADING: | |
386 | return true; | |
387 | case FEATURE_PLAGIARISM: | |
388 | return true; | |
6492401d JL |
389 | case FEATURE_COMMENT: |
390 | return true; | |
df211804 DW |
391 | |
392 | default: | |
393 | return null; | |
bbd0e548 DW |
394 | } |
395 | } | |
396 | ||
bbd0e548 DW |
397 | /** |
398 | * extend an assigment navigation settings | |
399 | * | |
400 | * @param settings_navigation $settings | |
401 | * @param navigation_node $navref | |
402 | * @return void | |
403 | */ | |
404 | function assign_extend_settings_navigation(settings_navigation $settings, navigation_node $navref) { | |
b473171a | 405 | global $PAGE, $DB; |
bbd0e548 | 406 | |
d685b959 IT |
407 | // We want to add these new nodes after the Edit settings node, and before the |
408 | // Locally assigned roles node. Of course, both of those are controlled by capabilities. | |
409 | $keys = $navref->get_children_key_list(); | |
410 | $beforekey = null; | |
411 | $i = array_search('modedit', $keys); | |
412 | if ($i === false and array_key_exists(0, $keys)) { | |
413 | $beforekey = $keys[0]; | |
414 | } else if (array_key_exists($i + 1, $keys)) { | |
415 | $beforekey = $keys[$i + 1]; | |
416 | } | |
417 | ||
bbd0e548 DW |
418 | $cm = $PAGE->cm; |
419 | if (!$cm) { | |
420 | return; | |
421 | } | |
422 | ||
423 | $context = $cm->context; | |
424 | $course = $PAGE->course; | |
425 | ||
bbd0e548 DW |
426 | if (!$course) { |
427 | return; | |
428 | } | |
429 | ||
d685b959 IT |
430 | if (has_capability('mod/assign:manageoverrides', $PAGE->cm->context)) { |
431 | $url = new moodle_url('/mod/assign/overrides.php', array('cmid' => $PAGE->cm->id)); | |
432 | $node = navigation_node::create(get_string('groupoverrides', 'assign'), | |
433 | new moodle_url($url, array('mode' => 'group')), | |
434 | navigation_node::TYPE_SETTING, null, 'mod_assign_groupoverrides'); | |
435 | $navref->add_node($node, $beforekey); | |
436 | ||
437 | $node = navigation_node::create(get_string('useroverrides', 'assign'), | |
438 | new moodle_url($url, array('mode' => 'user')), | |
439 | navigation_node::TYPE_SETTING, null, 'mod_assign_useroverrides'); | |
440 | $navref->add_node($node, $beforekey); | |
441 | } | |
442 | ||
e5403f8c DW |
443 | // Link to gradebook. |
444 | if (has_capability('gradereport/grader:view', $cm->context) && | |
445 | has_capability('moodle/grade:viewall', $cm->context)) { | |
446 | $link = new moodle_url('/grade/report/grader/index.php', array('id' => $course->id)); | |
447 | $linkname = get_string('viewgradebook', 'assign'); | |
448 | $node = $navref->add($linkname, $link, navigation_node::TYPE_SETTING); | |
449 | } | |
bbd0e548 | 450 | |
e5403f8c | 451 | // Link to download all submissions. |
4a47008c | 452 | if (has_any_capability(array('mod/assign:grade', 'mod/assign:viewgrades'), $context)) { |
e5403f8c DW |
453 | $link = new moodle_url('/mod/assign/view.php', array('id' => $cm->id, 'action'=>'grading')); |
454 | $node = $navref->add(get_string('viewgrading', 'assign'), $link, navigation_node::TYPE_SETTING); | |
bbd0e548 | 455 | |
e5403f8c DW |
456 | $link = new moodle_url('/mod/assign/view.php', array('id' => $cm->id, 'action'=>'downloadall')); |
457 | $node = $navref->add(get_string('downloadall', 'assign'), $link, navigation_node::TYPE_SETTING); | |
458 | } | |
bbd0e548 | 459 | |
e5403f8c DW |
460 | if (has_capability('mod/assign:revealidentities', $context)) { |
461 | $dbparams = array('id'=>$cm->instance); | |
462 | $assignment = $DB->get_record('assign', $dbparams, 'blindmarking, revealidentities'); | |
b473171a | 463 | |
e5403f8c DW |
464 | if ($assignment && $assignment->blindmarking && !$assignment->revealidentities) { |
465 | $urlparams = array('id' => $cm->id, 'action'=>'revealidentities'); | |
466 | $url = new moodle_url('/mod/assign/view.php', $urlparams); | |
467 | $linkname = get_string('revealidentities', 'assign'); | |
468 | $node = $navref->add($linkname, $url, navigation_node::TYPE_SETTING); | |
469 | } | |
470 | } | |
bbd0e548 DW |
471 | } |
472 | ||
bbd0e548 DW |
473 | /** |
474 | * Add a get_coursemodule_info function in case any assignment type wants to add 'extra' information | |
475 | * for the course (see resource). | |
476 | * | |
477 | * Given a course_module object, this function returns any "extra" information that may be needed | |
478 | * when printing this activity in a course listing. See get_array_of_activities() in course/lib.php. | |
479 | * | |
480 | * @param stdClass $coursemodule The coursemodule object (record). | |
e5403f8c DW |
481 | * @return cached_cm_info An object on information that the courses |
482 | * will know about (most noticeably, an icon). | |
bbd0e548 DW |
483 | */ |
484 | function assign_get_coursemodule_info($coursemodule) { | |
485 | global $CFG, $DB; | |
486 | ||
e5403f8c | 487 | $dbparams = array('id'=>$coursemodule->instance); |
b54bcddd | 488 | $fields = 'id, name, alwaysshowdescription, allowsubmissionsfromdate, intro, introformat, completionsubmit'; |
e5403f8c | 489 | if (! $assignment = $DB->get_record('assign', $dbparams, $fields)) { |
bbd0e548 DW |
490 | return false; |
491 | } | |
492 | ||
493 | $result = new cached_cm_info(); | |
494 | $result->name = $assignment->name; | |
495 | if ($coursemodule->showdescription) { | |
496 | if ($assignment->alwaysshowdescription || time() > $assignment->allowsubmissionsfromdate) { | |
497 | // Convert intro to html. Do not filter cached version, filters run at display time. | |
498 | $result->content = format_module_intro('assign', $assignment, $coursemodule->id, false); | |
499 | } | |
500 | } | |
b54bcddd JD |
501 | |
502 | // Populate the custom completion rules as key => value pairs, but only if the completion mode is 'automatic'. | |
503 | if ($coursemodule->completion == COMPLETION_TRACKING_AUTOMATIC) { | |
504 | $result->customdata['customcompletionrules']['completionsubmit'] = $assignment->completionsubmit; | |
505 | } | |
506 | ||
bbd0e548 DW |
507 | return $result; |
508 | } | |
509 | ||
b54bcddd JD |
510 | /** |
511 | * Callback which returns human-readable strings describing the active completion custom rules for the module instance. | |
512 | * | |
7f53e8aa | 513 | * @param cm_info|stdClass $cm object with fields ->completion and ->customdata['customcompletionrules'] |
b54bcddd JD |
514 | * @return array $descriptions the array of descriptions for the custom rules. |
515 | */ | |
516 | function mod_assign_get_completion_active_rule_descriptions($cm) { | |
517 | // Values will be present in cm_info, and we assume these are up to date. | |
7f53e8aa | 518 | if (empty($cm->customdata['customcompletionrules']) |
b54bcddd JD |
519 | || $cm->completion != COMPLETION_TRACKING_AUTOMATIC) { |
520 | return []; | |
521 | } | |
522 | ||
523 | $descriptions = []; | |
524 | foreach ($cm->customdata['customcompletionrules'] as $key => $val) { | |
525 | switch ($key) { | |
526 | case 'completionsubmit': | |
bd5fdcfc EL |
527 | if (!empty($val)) { |
528 | $descriptions[] = get_string('completionsubmit', 'assign'); | |
b54bcddd | 529 | } |
b54bcddd JD |
530 | break; |
531 | default: | |
532 | break; | |
533 | } | |
534 | } | |
535 | return $descriptions; | |
536 | } | |
537 | ||
bbd0e548 DW |
538 | /** |
539 | * Return a list of page types | |
540 | * @param string $pagetype current page type | |
541 | * @param stdClass $parentcontext Block's parent context | |
542 | * @param stdClass $currentcontext Current context of block | |
543 | */ | |
544 | function assign_page_type_list($pagetype, $parentcontext, $currentcontext) { | |
539af602 | 545 | $modulepagetype = array( |
bbd0e548 DW |
546 | 'mod-assign-*' => get_string('page-mod-assign-x', 'assign'), |
547 | 'mod-assign-view' => get_string('page-mod-assign-view', 'assign'), | |
548 | ); | |
539af602 | 549 | return $modulepagetype; |
bbd0e548 DW |
550 | } |
551 | ||
552 | /** | |
375e2ece | 553 | * @deprecated since Moodle 3.3, when the block_course_overview block was removed. |
bbd0e548 | 554 | */ |
375e2ece SA |
555 | function assign_print_overview() { |
556 | throw new coding_exception('assign_print_overview() can not be used any more and is obsolete.'); | |
a7366bc8 AA |
557 | } |
558 | ||
559 | /** | |
375e2ece | 560 | * @deprecated since Moodle 3.3, when the block_course_overview block was removed. |
a7366bc8 | 561 | */ |
375e2ece SA |
562 | function assign_get_mysubmission_details_for_print_overview() { |
563 | throw new coding_exception('assign_get_mysubmission_details_for_print_overview() can not be used any more and is obsolete.'); | |
a7366bc8 AA |
564 | } |
565 | ||
566 | /** | |
375e2ece | 567 | * @deprecated since Moodle 3.3, when the block_course_overview block was removed. |
a7366bc8 | 568 | */ |
375e2ece SA |
569 | function assign_get_grade_details_for_print_overview() { |
570 | throw new coding_exception('assign_get_grade_details_for_print_overview() can not be used any more and is obsolete.'); | |
bbd0e548 DW |
571 | } |
572 | ||
573 | /** | |
574 | * Print recent activity from all assignments in a given course | |
575 | * | |
576 | * This is used by the recent activity block | |
577 | * @param mixed $course the course to print activity for | |
578 | * @param bool $viewfullnames boolean to determine whether to show full names or not | |
9682626e | 579 | * @param int $timestart the time the rendering started |
5c7ec9f6 | 580 | * @return bool true if activity was printed, false otherwise. |
bbd0e548 DW |
581 | */ |
582 | function assign_print_recent_activity($course, $viewfullnames, $timestart) { | |
583 | global $CFG, $USER, $DB, $OUTPUT; | |
9e035dae | 584 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); |
bbd0e548 | 585 | |
e5403f8c | 586 | // Do not use log table if possible, it may be huge. |
bbd0e548 | 587 | |
9e035dae | 588 | $dbparams = array($timestart, $course->id, 'assign', ASSIGN_SUBMISSION_STATUS_SUBMITTED); |
5c7ec9f6 | 589 | $namefields = user_picture::fields('u', null, 'userid'); |
5e171c69 | 590 | if (!$submissions = $DB->get_records_sql("SELECT asb.id, asb.timemodified, cm.id AS cmid, um.id as recordid, |
5c7ec9f6 | 591 | $namefields |
bbd0e548 DW |
592 | FROM {assign_submission} asb |
593 | JOIN {assign} a ON a.id = asb.assignment | |
594 | JOIN {course_modules} cm ON cm.instance = a.id | |
595 | JOIN {modules} md ON md.id = cm.module | |
596 | JOIN {user} u ON u.id = asb.userid | |
5e171c69 | 597 | LEFT JOIN {assign_user_mapping} um ON um.userid = u.id AND um.assignment = a.id |
bbd0e548 | 598 | WHERE asb.timemodified > ? AND |
ad030ab4 | 599 | asb.latest = 1 AND |
bbd0e548 | 600 | a.course = ? AND |
9e035dae DW |
601 | md.name = ? AND |
602 | asb.status = ? | |
5c7ec9f6 | 603 | ORDER BY asb.timemodified ASC", $dbparams)) { |
bbd0e548 DW |
604 | return false; |
605 | } | |
606 | ||
e5403f8c | 607 | $modinfo = get_fast_modinfo($course); |
bbd0e548 DW |
608 | $show = array(); |
609 | $grader = array(); | |
610 | ||
d6f418cb | 611 | $showrecentsubmissions = get_config('assign', 'showrecentsubmissions'); |
cfc81f03 | 612 | |
e5403f8c | 613 | foreach ($submissions as $submission) { |
bbd0e548 DW |
614 | if (!array_key_exists($submission->cmid, $modinfo->get_cms())) { |
615 | continue; | |
616 | } | |
617 | $cm = $modinfo->get_cm($submission->cmid); | |
618 | if (!$cm->uservisible) { | |
619 | continue; | |
620 | } | |
621 | if ($submission->userid == $USER->id) { | |
622 | $show[] = $submission; | |
623 | continue; | |
624 | } | |
625 | ||
626 | $context = context_module::instance($submission->cmid); | |
e5403f8c DW |
627 | // The act of submitting of assignment may be considered private - |
628 | // only graders will see it if specified. | |
cfc81f03 | 629 | if (empty($showrecentsubmissions)) { |
bbd0e548 | 630 | if (!array_key_exists($cm->id, $grader)) { |
e5403f8c | 631 | $grader[$cm->id] = has_capability('moodle/grade:viewall', $context); |
bbd0e548 DW |
632 | } |
633 | if (!$grader[$cm->id]) { | |
634 | continue; | |
635 | } | |
636 | } | |
637 | ||
638 | $groupmode = groups_get_activity_groupmode($cm, $course); | |
639 | ||
e5403f8c DW |
640 | if ($groupmode == SEPARATEGROUPS && |
641 | !has_capability('moodle/site:accessallgroups', $context)) { | |
bbd0e548 | 642 | if (isguestuser()) { |
e5403f8c | 643 | // Shortcut - guest user does not belong into any group. |
bbd0e548 DW |
644 | continue; |
645 | } | |
646 | ||
e5403f8c | 647 | // This will be slow - show only users that share group with me in this cm. |
e19c086c | 648 | if (!$modinfo->get_groups($cm->groupingid)) { |
bbd0e548 DW |
649 | continue; |
650 | } | |
651 | $usersgroups = groups_get_all_groups($course->id, $submission->userid, $cm->groupingid); | |
652 | if (is_array($usersgroups)) { | |
653 | $usersgroups = array_keys($usersgroups); | |
e19c086c | 654 | $intersect = array_intersect($usersgroups, $modinfo->get_groups($cm->groupingid)); |
bbd0e548 DW |
655 | if (empty($intersect)) { |
656 | continue; | |
657 | } | |
658 | } | |
659 | } | |
660 | $show[] = $submission; | |
661 | } | |
662 | ||
663 | if (empty($show)) { | |
664 | return false; | |
665 | } | |
666 | ||
667 | echo $OUTPUT->heading(get_string('newsubmissions', 'assign').':', 3); | |
668 | ||
669 | foreach ($show as $submission) { | |
670 | $cm = $modinfo->get_cm($submission->cmid); | |
d89dc6da TL |
671 | $context = context_module::instance($submission->cmid); |
672 | $assign = new assign($context, $cm, $cm->course); | |
bbd0e548 | 673 | $link = $CFG->wwwroot.'/mod/assign/view.php?id='.$cm->id; |
d89dc6da TL |
674 | // Obscure first and last name if blind marking enabled. |
675 | if ($assign->is_blind_marking()) { | |
676 | $submission->firstname = get_string('participant', 'mod_assign'); | |
5e171c69 AN |
677 | if (empty($submission->recordid)) { |
678 | $submission->recordid = $assign->get_uniqueid_for_user($submission->userid); | |
679 | } | |
680 | $submission->lastname = $submission->recordid; | |
d89dc6da | 681 | } |
e5403f8c DW |
682 | print_recent_activity_note($submission->timemodified, |
683 | $submission, | |
684 | $cm->name, | |
685 | $link, | |
686 | false, | |
687 | $viewfullnames); | |
bbd0e548 DW |
688 | } |
689 | ||
690 | return true; | |
691 | } | |
692 | ||
693 | /** | |
e5403f8c | 694 | * Returns all assignments since a given time. |
c06b2127 | 695 | * |
bbd0e548 DW |
696 | * @param array $activities The activity information is returned in this array |
697 | * @param int $index The current index in the activities array | |
698 | * @param int $timestart The earliest activity to show | |
699 | * @param int $courseid Limit the search to this course | |
700 | * @param int $cmid The course module id | |
701 | * @param int $userid Optional user id | |
702 | * @param int $groupid Optional group id | |
703 | * @return void | |
704 | */ | |
e5403f8c DW |
705 | function assign_get_recent_mod_activity(&$activities, |
706 | &$index, | |
707 | $timestart, | |
708 | $courseid, | |
709 | $cmid, | |
710 | $userid=0, | |
711 | $groupid=0) { | |
bbd0e548 DW |
712 | global $CFG, $COURSE, $USER, $DB; |
713 | ||
9e035dae DW |
714 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); |
715 | ||
bbd0e548 DW |
716 | if ($COURSE->id == $courseid) { |
717 | $course = $COURSE; | |
718 | } else { | |
719 | $course = $DB->get_record('course', array('id'=>$courseid)); | |
720 | } | |
721 | ||
e5403f8c | 722 | $modinfo = get_fast_modinfo($course); |
bbd0e548 DW |
723 | |
724 | $cm = $modinfo->get_cm($cmid); | |
725 | $params = array(); | |
726 | if ($userid) { | |
e5403f8c | 727 | $userselect = 'AND u.id = :userid'; |
bbd0e548 DW |
728 | $params['userid'] = $userid; |
729 | } else { | |
e5403f8c | 730 | $userselect = ''; |
bbd0e548 DW |
731 | } |
732 | ||
733 | if ($groupid) { | |
e5403f8c DW |
734 | $groupselect = 'AND gm.groupid = :groupid'; |
735 | $groupjoin = 'JOIN {groups_members} gm ON gm.userid=u.id'; | |
bbd0e548 DW |
736 | $params['groupid'] = $groupid; |
737 | } else { | |
e5403f8c DW |
738 | $groupselect = ''; |
739 | $groupjoin = ''; | |
bbd0e548 DW |
740 | } |
741 | ||
742 | $params['cminstance'] = $cm->instance; | |
743 | $params['timestart'] = $timestart; | |
9e035dae | 744 | $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED; |
bbd0e548 DW |
745 | |
746 | $userfields = user_picture::fields('u', null, 'userid'); | |
747 | ||
f93fdcef DW |
748 | if (!$submissions = $DB->get_records_sql('SELECT asb.id, asb.timemodified, ' . |
749 | $userfields . | |
750 | ' FROM {assign_submission} asb | |
751 | JOIN {assign} a ON a.id = asb.assignment | |
752 | JOIN {user} u ON u.id = asb.userid ' . | |
753 | $groupjoin . | |
754 | ' WHERE asb.timemodified > :timestart AND | |
9e035dae | 755 | asb.status = :submitted AND |
e5403f8c DW |
756 | a.id = :cminstance |
757 | ' . $userselect . ' ' . $groupselect . | |
758 | ' ORDER BY asb.timemodified ASC', $params)) { | |
bbd0e548 DW |
759 | return; |
760 | } | |
761 | ||
762 | $groupmode = groups_get_activity_groupmode($cm, $course); | |
539af602 DW |
763 | $cmcontext = context_module::instance($cm->id); |
764 | $grader = has_capability('moodle/grade:viewall', $cmcontext); | |
765 | $accessallgroups = has_capability('moodle/site:accessallgroups', $cmcontext); | |
766 | $viewfullnames = has_capability('moodle/site:viewfullnames', $cmcontext); | |
bbd0e548 | 767 | |
bbd0e548 | 768 | |
d6f418cb | 769 | $showrecentsubmissions = get_config('assign', 'showrecentsubmissions'); |
bbd0e548 | 770 | $show = array(); |
e5403f8c | 771 | foreach ($submissions as $submission) { |
bbd0e548 DW |
772 | if ($submission->userid == $USER->id) { |
773 | $show[] = $submission; | |
774 | continue; | |
775 | } | |
e5403f8c DW |
776 | // The act of submitting of assignment may be considered private - |
777 | // only graders will see it if specified. | |
cfc81f03 | 778 | if (empty($showrecentsubmissions)) { |
bbd0e548 DW |
779 | if (!$grader) { |
780 | continue; | |
781 | } | |
782 | } | |
783 | ||
784 | if ($groupmode == SEPARATEGROUPS and !$accessallgroups) { | |
785 | if (isguestuser()) { | |
e5403f8c | 786 | // Shortcut - guest user does not belong into any group. |
bbd0e548 DW |
787 | continue; |
788 | } | |
789 | ||
e5403f8c | 790 | // This will be slow - show only users that share group with me in this cm. |
e19c086c | 791 | if (!$modinfo->get_groups($cm->groupingid)) { |
bbd0e548 DW |
792 | continue; |
793 | } | |
e19c086c | 794 | $usersgroups = groups_get_all_groups($course->id, $submission->userid, $cm->groupingid); |
bbd0e548 | 795 | if (is_array($usersgroups)) { |
e19c086c MG |
796 | $usersgroups = array_keys($usersgroups); |
797 | $intersect = array_intersect($usersgroups, $modinfo->get_groups($cm->groupingid)); | |
bbd0e548 DW |
798 | if (empty($intersect)) { |
799 | continue; | |
800 | } | |
801 | } | |
802 | } | |
803 | $show[] = $submission; | |
804 | } | |
805 | ||
806 | if (empty($show)) { | |
807 | return; | |
808 | } | |
809 | ||
810 | if ($grader) { | |
811 | require_once($CFG->libdir.'/gradelib.php'); | |
812 | $userids = array(); | |
e5403f8c | 813 | foreach ($show as $id => $submission) { |
bbd0e548 | 814 | $userids[] = $submission->userid; |
bbd0e548 DW |
815 | } |
816 | $grades = grade_get_grades($courseid, 'mod', 'assign', $cm->instance, $userids); | |
817 | } | |
818 | ||
e5403f8c | 819 | $aname = format_string($cm->name, true); |
bbd0e548 DW |
820 | foreach ($show as $submission) { |
821 | $activity = new stdClass(); | |
822 | ||
823 | $activity->type = 'assign'; | |
824 | $activity->cmid = $cm->id; | |
825 | $activity->name = $aname; | |
826 | $activity->sectionnum = $cm->sectionnum; | |
827 | $activity->timestamp = $submission->timemodified; | |
828 | $activity->user = new stdClass(); | |
829 | if ($grader) { | |
830 | $activity->grade = $grades->items[0]->grades[$submission->userid]->str_long_grade; | |
831 | } | |
832 | ||
833 | $userfields = explode(',', user_picture::fields()); | |
834 | foreach ($userfields as $userfield) { | |
835 | if ($userfield == 'id') { | |
e5403f8c DW |
836 | // Aliased in SQL above. |
837 | $activity->user->{$userfield} = $submission->userid; | |
bbd0e548 DW |
838 | } else { |
839 | $activity->user->{$userfield} = $submission->{$userfield}; | |
840 | } | |
841 | } | |
842 | $activity->user->fullname = fullname($submission, $viewfullnames); | |
843 | ||
844 | $activities[$index++] = $activity; | |
845 | } | |
846 | ||
847 | return; | |
848 | } | |
849 | ||
850 | /** | |
851 | * Print recent activity from all assignments in a given course | |
852 | * | |
853 | * This is used by course/recent.php | |
854 | * @param stdClass $activity | |
855 | * @param int $courseid | |
856 | * @param bool $detail | |
857 | * @param array $modnames | |
858 | */ | |
e5403f8c | 859 | function assign_print_recent_mod_activity($activity, $courseid, $detail, $modnames) { |
bbd0e548 DW |
860 | global $CFG, $OUTPUT; |
861 | ||
862 | echo '<table border="0" cellpadding="3" cellspacing="0" class="assignment-recent">'; | |
863 | ||
e5403f8c | 864 | echo '<tr><td class="userpicture" valign="top">'; |
bbd0e548 | 865 | echo $OUTPUT->user_picture($activity->user); |
e5403f8c | 866 | echo '</td><td>'; |
bbd0e548 DW |
867 | |
868 | if ($detail) { | |
869 | $modname = $modnames[$activity->type]; | |
870 | echo '<div class="title">'; | |
3e6adcd6 | 871 | echo $OUTPUT->image_icon('icon', $modname, 'assign'); |
e5403f8c DW |
872 | echo '<a href="' . $CFG->wwwroot . '/mod/assign/view.php?id=' . $activity->cmid . '">'; |
873 | echo $activity->name; | |
874 | echo '</a>'; | |
bbd0e548 DW |
875 | echo '</div>'; |
876 | } | |
877 | ||
878 | if (isset($activity->grade)) { | |
879 | echo '<div class="grade">'; | |
880 | echo get_string('grade').': '; | |
881 | echo $activity->grade; | |
882 | echo '</div>'; | |
883 | } | |
884 | ||
885 | echo '<div class="user">'; | |
e5403f8c DW |
886 | echo "<a href=\"$CFG->wwwroot/user/view.php?id={$activity->user->id}&course=$courseid\">"; |
887 | echo "{$activity->user->fullname}</a> - " . userdate($activity->timestamp); | |
bbd0e548 DW |
888 | echo '</div>'; |
889 | ||
e5403f8c | 890 | echo '</td></tr></table>'; |
bbd0e548 DW |
891 | } |
892 | ||
893 | /** | |
c0375ba7 | 894 | * @deprecated since Moodle 3.8 |
bbd0e548 | 895 | */ |
c0375ba7 AA |
896 | function assign_scale_used() { |
897 | throw new coding_exception('assign_scale_used() can not be used anymore. Plugins can implement ' . | |
898 | '<modname>_scale_used_anywhere, all implementations of <modname>_scale_used are now ignored'); | |
bbd0e548 DW |
899 | } |
900 | ||
901 | /** | |
902 | * Checks if scale is being used by any instance of assignment | |
903 | * | |
904 | * This is used to find out if scale used anywhere | |
9682626e | 905 | * @param int $scaleid |
bbd0e548 DW |
906 | * @return boolean True if the scale is used by any assignment |
907 | */ | |
908 | function assign_scale_used_anywhere($scaleid) { | |
909 | global $DB; | |
910 | ||
911 | if ($scaleid and $DB->record_exists('assign', array('grade'=>-$scaleid))) { | |
912 | return true; | |
913 | } else { | |
914 | return false; | |
915 | } | |
916 | } | |
917 | ||
918 | /** | |
e5403f8c DW |
919 | * List the actions that correspond to a view of this module. |
920 | * This is used by the participation report. | |
b2b4ec30 RT |
921 | * |
922 | * Note: This is not used by new logging system. Event with | |
923 | * crud = 'r' and edulevel = LEVEL_PARTICIPATING will | |
924 | * be considered as view action. | |
925 | * | |
bbd0e548 DW |
926 | * @return array |
927 | */ | |
928 | function assign_get_view_actions() { | |
929 | return array('view submission', 'view feedback'); | |
930 | } | |
931 | ||
932 | /** | |
e5403f8c DW |
933 | * List the actions that correspond to a post of this module. |
934 | * This is used by the participation report. | |
b2b4ec30 RT |
935 | * |
936 | * Note: This is not used by new logging system. Event with | |
937 | * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING | |
938 | * will be considered as post action. | |
939 | * | |
bbd0e548 DW |
940 | * @return array |
941 | */ | |
942 | function assign_get_post_actions() { | |
943 | return array('upload', 'submit', 'submit for grading'); | |
944 | } | |
945 | ||
bbd0e548 DW |
946 | /** |
947 | * Returns all other capabilities used by this module. | |
948 | * @return array Array of capability strings | |
949 | */ | |
950 | function assign_get_extra_capabilities() { | |
939218c2 | 951 | return ['gradereport/grader:view', 'moodle/grade:viewall']; |
bbd0e548 DW |
952 | } |
953 | ||
954 | /** | |
e5403f8c | 955 | * Create grade item for given assignment. |
bbd0e548 DW |
956 | * |
957 | * @param stdClass $assign record with extra cmidnumber | |
958 | * @param array $grades optional array/object of grade(s); 'reset' means reset grades in gradebook | |
959 | * @return int 0 if ok, error code otherwise | |
960 | */ | |
e5403f8c | 961 | function assign_grade_item_update($assign, $grades=null) { |
bbd0e548 DW |
962 | global $CFG; |
963 | require_once($CFG->libdir.'/gradelib.php'); | |
964 | ||
965 | if (!isset($assign->courseid)) { | |
966 | $assign->courseid = $assign->course; | |
967 | } | |
968 | ||
969 | $params = array('itemname'=>$assign->name, 'idnumber'=>$assign->cmidnumber); | |
970 | ||
456d7bc7 RT |
971 | // Check if feedback plugin for gradebook is enabled, if yes then |
972 | // gradetype = GRADE_TYPE_TEXT else GRADE_TYPE_NONE. | |
973 | $gradefeedbackenabled = false; | |
974 | ||
975 | if (isset($assign->gradefeedbackenabled)) { | |
976 | $gradefeedbackenabled = $assign->gradefeedbackenabled; | |
977 | } else if ($assign->grade == 0) { // Grade feedback is needed only when grade == 0. | |
2507b7e0 | 978 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); |
456d7bc7 RT |
979 | $mod = get_coursemodule_from_instance('assign', $assign->id, $assign->courseid); |
980 | $cm = context_module::instance($mod->id); | |
981 | $assignment = new assign($cm, null, null); | |
982 | $gradefeedbackenabled = $assignment->is_gradebook_feedback_enabled(); | |
983 | } | |
984 | ||
bbd0e548 DW |
985 | if ($assign->grade > 0) { |
986 | $params['gradetype'] = GRADE_TYPE_VALUE; | |
987 | $params['grademax'] = $assign->grade; | |
988 | $params['grademin'] = 0; | |
989 | ||
990 | } else if ($assign->grade < 0) { | |
991 | $params['gradetype'] = GRADE_TYPE_SCALE; | |
992 | $params['scaleid'] = -$assign->grade; | |
993 | ||
456d7bc7 RT |
994 | } else if ($gradefeedbackenabled) { |
995 | // $assign->grade == 0 and feedback enabled. | |
e5403f8c | 996 | $params['gradetype'] = GRADE_TYPE_TEXT; |
456d7bc7 RT |
997 | } else { |
998 | // $assign->grade == 0 and no feedback enabled. | |
999 | $params['gradetype'] = GRADE_TYPE_NONE; | |
bbd0e548 DW |
1000 | } |
1001 | ||
1002 | if ($grades === 'reset') { | |
1003 | $params['reset'] = true; | |
e5403f8c | 1004 | $grades = null; |
bbd0e548 DW |
1005 | } |
1006 | ||
e5403f8c DW |
1007 | return grade_update('mod/assign', |
1008 | $assign->courseid, | |
1009 | 'mod', | |
1010 | 'assign', | |
1011 | $assign->id, | |
1012 | 0, | |
1013 | $grades, | |
1014 | $params); | |
bbd0e548 DW |
1015 | } |
1016 | ||
1017 | /** | |
1018 | * Return grade for given user or all users. | |
1019 | * | |
1020 | * @param stdClass $assign record of assign with an additional cmidnumber | |
1021 | * @param int $userid optional user id, 0 means all users | |
1022 | * @return array array of grades, false if none | |
1023 | */ | |
1024 | function assign_get_user_grades($assign, $userid=0) { | |
1025 | global $CFG; | |
e5403f8c | 1026 | |
bbd0e548 DW |
1027 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); |
1028 | ||
79e7dfcc RW |
1029 | $cm = get_coursemodule_from_instance('assign', $assign->id, 0, false, MUST_EXIST); |
1030 | $context = context_module::instance($cm->id); | |
1031 | $assignment = new assign($context, null, null); | |
b11808c7 | 1032 | $assignment->set_instance($assign); |
bbd0e548 DW |
1033 | return $assignment->get_user_grades_for_gradebook($userid); |
1034 | } | |
1035 | ||
1036 | /** | |
e5403f8c | 1037 | * Update activity grades. |
bbd0e548 DW |
1038 | * |
1039 | * @param stdClass $assign database record | |
1040 | * @param int $userid specific user only, 0 means all | |
1041 | * @param bool $nullifnone - not used | |
1042 | */ | |
1043 | function assign_update_grades($assign, $userid=0, $nullifnone=true) { | |
1044 | global $CFG; | |
1045 | require_once($CFG->libdir.'/gradelib.php'); | |
1046 | ||
1047 | if ($assign->grade == 0) { | |
1048 | assign_grade_item_update($assign); | |
1049 | ||
1050 | } else if ($grades = assign_get_user_grades($assign, $userid)) { | |
e5403f8c | 1051 | foreach ($grades as $k => $v) { |
bbd0e548 DW |
1052 | if ($v->rawgrade == -1) { |
1053 | $grades[$k]->rawgrade = null; | |
1054 | } | |
1055 | } | |
1056 | assign_grade_item_update($assign, $grades); | |
1057 | ||
1058 | } else { | |
1059 | assign_grade_item_update($assign); | |
1060 | } | |
1061 | } | |
1062 | ||
1063 | /** | |
e5403f8c | 1064 | * List the file areas that can be browsed. |
bbd0e548 DW |
1065 | * |
1066 | * @param stdClass $course | |
1067 | * @param stdClass $cm | |
1068 | * @param stdClass $context | |
1069 | * @return array | |
1070 | */ | |
6b8b0b2e | 1071 | function assign_get_file_areas($course, $cm, $context) { |
bbd0e548 DW |
1072 | global $CFG; |
1073 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
7faf78cb HB |
1074 | |
1075 | $areas = array(ASSIGN_INTROATTACHMENT_FILEAREA => get_string('introattachments', 'mod_assign')); | |
bbd0e548 DW |
1076 | |
1077 | $assignment = new assign($context, $cm, $course); | |
1078 | foreach ($assignment->get_submission_plugins() as $plugin) { | |
1079 | if ($plugin->is_visible()) { | |
1080 | $pluginareas = $plugin->get_file_areas(); | |
1081 | ||
1082 | if ($pluginareas) { | |
1083 | $areas = array_merge($areas, $pluginareas); | |
1084 | } | |
1085 | } | |
1086 | } | |
1087 | foreach ($assignment->get_feedback_plugins() as $plugin) { | |
1088 | if ($plugin->is_visible()) { | |
1089 | $pluginareas = $plugin->get_file_areas(); | |
1090 | ||
1091 | if ($pluginareas) { | |
1092 | $areas = array_merge($areas, $pluginareas); | |
1093 | } | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | return $areas; | |
1098 | } | |
1099 | ||
1100 | /** | |
1101 | * File browsing support for assign module. | |
1102 | * | |
1103 | * @param file_browser $browser | |
1104 | * @param object $areas | |
1105 | * @param object $course | |
1106 | * @param object $cm | |
1107 | * @param object $context | |
1108 | * @param string $filearea | |
1109 | * @param int $itemid | |
1110 | * @param string $filepath | |
1111 | * @param string $filename | |
1112 | * @return object file_info instance or null if not found | |
1113 | */ | |
e5403f8c DW |
1114 | function assign_get_file_info($browser, |
1115 | $areas, | |
1116 | $course, | |
1117 | $cm, | |
1118 | $context, | |
1119 | $filearea, | |
1120 | $itemid, | |
1121 | $filepath, | |
1122 | $filename) { | |
bbd0e548 DW |
1123 | global $CFG; |
1124 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
1125 | ||
1126 | if ($context->contextlevel != CONTEXT_MODULE) { | |
1127 | return null; | |
1128 | } | |
1129 | ||
7faf78cb | 1130 | $urlbase = $CFG->wwwroot.'/pluginfile.php'; |
bbd0e548 DW |
1131 | $fs = get_file_storage(); |
1132 | $filepath = is_null($filepath) ? '/' : $filepath; | |
1133 | $filename = is_null($filename) ? '.' : $filename; | |
1134 | ||
7faf78cb | 1135 | // Need to find where this belongs to. |
bbd0e548 | 1136 | $assignment = new assign($context, $cm, $course); |
7faf78cb HB |
1137 | if ($filearea === ASSIGN_INTROATTACHMENT_FILEAREA) { |
1138 | if (!has_capability('moodle/course:managefiles', $context)) { | |
1139 | // Students can not peak here! | |
1140 | return null; | |
1141 | } | |
1142 | if (!($storedfile = $fs->get_file($assignment->get_context()->id, | |
1143 | 'mod_assign', $filearea, 0, $filepath, $filename))) { | |
1144 | return null; | |
1145 | } | |
1146 | return new file_info_stored($browser, | |
1147 | $assignment->get_context(), | |
1148 | $storedfile, | |
1149 | $urlbase, | |
1150 | $filearea, | |
1151 | $itemid, | |
1152 | true, | |
1153 | true, | |
1154 | false); | |
1155 | } | |
1156 | ||
bbd0e548 DW |
1157 | $pluginowner = null; |
1158 | foreach ($assignment->get_submission_plugins() as $plugin) { | |
1159 | if ($plugin->is_visible()) { | |
1160 | $pluginareas = $plugin->get_file_areas(); | |
1161 | ||
1162 | if (array_key_exists($filearea, $pluginareas)) { | |
1163 | $pluginowner = $plugin; | |
1164 | break; | |
1165 | } | |
1166 | } | |
1167 | } | |
1168 | if (!$pluginowner) { | |
1169 | foreach ($assignment->get_feedback_plugins() as $plugin) { | |
1170 | if ($plugin->is_visible()) { | |
1171 | $pluginareas = $plugin->get_file_areas(); | |
1172 | ||
1173 | if (array_key_exists($filearea, $pluginareas)) { | |
1174 | $pluginowner = $plugin; | |
1175 | break; | |
1176 | } | |
1177 | } | |
1178 | } | |
1179 | } | |
1180 | ||
1181 | if (!$pluginowner) { | |
1182 | return null; | |
1183 | } | |
1184 | ||
1185 | $result = $pluginowner->get_file_info($browser, $filearea, $itemid, $filepath, $filename); | |
1186 | return $result; | |
1187 | } | |
1188 | ||
1189 | /** | |
e5403f8c | 1190 | * Prints the complete info about a user's interaction with an assignment. |
bbd0e548 DW |
1191 | * |
1192 | * @param stdClass $course | |
1193 | * @param stdClass $user | |
1194 | * @param stdClass $coursemodule | |
1195 | * @param stdClass $assign the database assign record | |
1196 | * | |
e5403f8c | 1197 | * This prints the submission summary and feedback summary for this student. |
bbd0e548 DW |
1198 | */ |
1199 | function assign_user_complete($course, $user, $coursemodule, $assign) { | |
1200 | global $CFG; | |
1201 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
e5403f8c | 1202 | |
bbd0e548 DW |
1203 | $context = context_module::instance($coursemodule->id); |
1204 | ||
1205 | $assignment = new assign($context, $coursemodule, $course); | |
1206 | ||
1207 | echo $assignment->view_student_summary($user, false); | |
1208 | } | |
1209 | ||
d629c601 DW |
1210 | /** |
1211 | * Rescale all grades for this activity and push the new grades to the gradebook. | |
1212 | * | |
1213 | * @param stdClass $course Course db record | |
1214 | * @param stdClass $cm Course module db record | |
1215 | * @param float $oldmin | |
1216 | * @param float $oldmax | |
1217 | * @param float $newmin | |
1218 | * @param float $newmax | |
1219 | */ | |
1220 | function assign_rescale_activity_grades($course, $cm, $oldmin, $oldmax, $newmin, $newmax) { | |
1221 | global $DB; | |
1222 | ||
1223 | if ($oldmax <= $oldmin) { | |
1224 | // Grades cannot be scaled. | |
1225 | return false; | |
1226 | } | |
1227 | $scale = ($newmax - $newmin) / ($oldmax - $oldmin); | |
1228 | if (($newmax - $newmin) <= 1) { | |
1229 | // We would lose too much precision, lets bail. | |
1230 | return false; | |
1231 | } | |
1232 | ||
1233 | $params = array( | |
1234 | 'p1' => $oldmin, | |
1235 | 'p2' => $scale, | |
1236 | 'p3' => $newmin, | |
1237 | 'a' => $cm->instance | |
1238 | ); | |
1239 | ||
e04be435 JO |
1240 | // Only rescale grades that are greater than or equal to 0. Anything else is a special value. |
1241 | $sql = 'UPDATE {assign_grades} set grade = (((grade - :p1) * :p2) + :p3) where assignment = :a and grade >= 0'; | |
d629c601 DW |
1242 | $dbupdate = $DB->execute($sql, $params); |
1243 | if (!$dbupdate) { | |
1244 | return false; | |
1245 | } | |
1246 | ||
1247 | // Now re-push all grades to the gradebook. | |
1248 | $dbparams = array('id' => $cm->instance); | |
1249 | $assign = $DB->get_record('assign', $dbparams); | |
1250 | $assign->cmidnumber = $cm->idnumber; | |
1251 | ||
1252 | assign_update_grades($assign); | |
1253 | ||
1254 | return true; | |
1255 | } | |
1256 | ||
bbd0e548 | 1257 | /** |
e5403f8c | 1258 | * Print the grade information for the assignment for this user. |
9682626e | 1259 | * |
bbd0e548 DW |
1260 | * @param stdClass $course |
1261 | * @param stdClass $user | |
1262 | * @param stdClass $coursemodule | |
1263 | * @param stdClass $assignment | |
1264 | */ | |
1265 | function assign_user_outline($course, $user, $coursemodule, $assignment) { | |
1266 | global $CFG; | |
1267 | require_once($CFG->libdir.'/gradelib.php'); | |
1268 | require_once($CFG->dirroot.'/grade/grading/lib.php'); | |
1269 | ||
1270 | $gradinginfo = grade_get_grades($course->id, | |
1271 | 'mod', | |
1272 | 'assign', | |
1273 | $assignment->id, | |
1274 | $user->id); | |
1275 | ||
1276 | $gradingitem = $gradinginfo->items[0]; | |
1277 | $gradebookgrade = $gradingitem->grades[$user->id]; | |
1278 | ||
f6aa750e | 1279 | if (empty($gradebookgrade->str_long_grade)) { |
bbd0e548 DW |
1280 | return null; |
1281 | } | |
1282 | $result = new stdClass(); | |
f5d1dbb3 MF |
1283 | if (!$gradingitem->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) { |
1284 | $result->info = get_string('outlinegrade', 'assign', $gradebookgrade->str_long_grade); | |
1285 | } else { | |
1286 | $result->info = get_string('grade') . ': ' . get_string('hidden', 'grades'); | |
1287 | } | |
bbd0e548 DW |
1288 | $result->time = $gradebookgrade->dategraded; |
1289 | ||
1290 | return $result; | |
1291 | } | |
79ed4d84 DW |
1292 | |
1293 | /** | |
1294 | * Obtains the automatic completion state for this module based on any conditions | |
1295 | * in assign settings. | |
1296 | * | |
1297 | * @param object $course Course | |
1298 | * @param object $cm Course-module | |
1299 | * @param int $userid User ID | |
1300 | * @param bool $type Type of comparison (or/and; can be used as return value if no conditions) | |
1301 | * @return bool True if completed, false if not, $type if conditions not set. | |
1302 | */ | |
3a66d425 | 1303 | function assign_get_completion_state($course, $cm, $userid, $type) { |
e5403f8c | 1304 | global $CFG, $DB; |
79ed4d84 DW |
1305 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); |
1306 | ||
1307 | $assign = new assign(null, $cm, $course); | |
1308 | ||
3a66d425 DP |
1309 | // If completion option is enabled, evaluate it and return true/false. |
1310 | if ($assign->get_instance()->completionsubmit) { | |
9569b01c SB |
1311 | if ($assign->get_instance()->teamsubmission) { |
1312 | $submission = $assign->get_group_submission($userid, 0, false); | |
1313 | } else { | |
1314 | $submission = $assign->get_user_submission($userid, false); | |
1315 | } | |
79ed4d84 DW |
1316 | return $submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED; |
1317 | } else { | |
3a66d425 | 1318 | // Completion option is not enabled so just return $type. |
79ed4d84 DW |
1319 | return $type; |
1320 | } | |
1321 | } | |
7faf78cb HB |
1322 | |
1323 | /** | |
1324 | * Serves intro attachment files. | |
1325 | * | |
1326 | * @param mixed $course course or id of the course | |
1327 | * @param mixed $cm course module or id of the course module | |
1328 | * @param context $context | |
1329 | * @param string $filearea | |
1330 | * @param array $args | |
1331 | * @param bool $forcedownload | |
1332 | * @param array $options additional options affecting the file serving | |
1333 | * @return bool false if file not found, does not return if found - just send the file | |
1334 | */ | |
1335 | function assign_pluginfile($course, | |
1336 | $cm, | |
1337 | context $context, | |
1338 | $filearea, | |
1339 | $args, | |
1340 | $forcedownload, | |
1341 | array $options=array()) { | |
1342 | global $CFG; | |
1343 | ||
1344 | if ($context->contextlevel != CONTEXT_MODULE) { | |
1345 | return false; | |
1346 | } | |
1347 | ||
1348 | require_login($course, false, $cm); | |
1349 | if (!has_capability('mod/assign:view', $context)) { | |
1350 | return false; | |
1351 | } | |
1352 | ||
1353 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
1354 | $assign = new assign($context, $cm, $course); | |
1355 | ||
1356 | if ($filearea !== ASSIGN_INTROATTACHMENT_FILEAREA) { | |
1357 | return false; | |
1358 | } | |
1359 | if (!$assign->show_intro()) { | |
1360 | return false; | |
1361 | } | |
1362 | ||
1363 | $itemid = (int)array_shift($args); | |
1364 | if ($itemid != 0) { | |
1365 | return false; | |
1366 | } | |
1367 | ||
1368 | $relativepath = implode('/', $args); | |
1369 | ||
1370 | $fullpath = "/{$context->id}/mod_assign/$filearea/$itemid/$relativepath"; | |
1371 | ||
1372 | $fs = get_file_storage(); | |
1373 | if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { | |
1374 | return false; | |
1375 | } | |
1376 | send_stored_file($file, 0, 0, $forcedownload, $options); | |
1377 | } | |
bb690849 DW |
1378 | |
1379 | /** | |
1380 | * Serve the grading panel as a fragment. | |
1381 | * | |
1382 | * @param array $args List of named arguments for the fragment loader. | |
1383 | * @return string | |
1384 | */ | |
1385 | function mod_assign_output_fragment_gradingpanel($args) { | |
1386 | global $CFG; | |
1387 | ||
1388 | $context = $args['context']; | |
1389 | ||
1390 | if ($context->contextlevel != CONTEXT_MODULE) { | |
1391 | return null; | |
1392 | } | |
1393 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
1394 | $assign = new assign($context, null, null); | |
1395 | ||
1396 | $userid = clean_param($args['userid'], PARAM_INT); | |
1397 | $attemptnumber = clean_param($args['attemptnumber'], PARAM_INT); | |
1398 | $formdata = array(); | |
1399 | if (!empty($args['jsonformdata'])) { | |
1400 | $serialiseddata = json_decode($args['jsonformdata']); | |
1401 | parse_str($serialiseddata, $formdata); | |
1402 | } | |
1403 | $viewargs = array( | |
1404 | 'userid' => $userid, | |
1405 | 'attemptnumber' => $attemptnumber, | |
1406 | 'formdata' => $formdata | |
1407 | ); | |
1408 | ||
1409 | return $assign->view('gradingpanel', $viewargs); | |
1410 | } | |
02a73d76 JL |
1411 | |
1412 | /** | |
1413 | * Check if the module has any update that affects the current user since a given time. | |
1414 | * | |
1415 | * @param cm_info $cm course module data | |
1416 | * @param int $from the time to check updates from | |
1417 | * @param array $filter if we need to check only specific updates | |
1418 | * @return stdClass an object with the different type of areas indicating if they were updated or not | |
1419 | * @since Moodle 3.2 | |
1420 | */ | |
1421 | function assign_check_updates_since(cm_info $cm, $from, $filter = array()) { | |
1422 | global $DB, $USER, $CFG; | |
1423 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
1424 | ||
1425 | $updates = new stdClass(); | |
1426 | $updates = course_check_module_updates_since($cm, $from, array(ASSIGN_INTROATTACHMENT_FILEAREA), $filter); | |
1427 | ||
1428 | // Check if there is a new submission by the user or new grades. | |
1429 | $select = 'assignment = :id AND userid = :userid AND (timecreated > :since1 OR timemodified > :since2)'; | |
1430 | $params = array('id' => $cm->instance, 'userid' => $USER->id, 'since1' => $from, 'since2' => $from); | |
65b2669d JL |
1431 | $updates->submissions = (object) array('updated' => false); |
1432 | $submissions = $DB->get_records_select('assign_submission', $select, $params, '', 'id'); | |
1433 | if (!empty($submissions)) { | |
1434 | $updates->submissions->updated = true; | |
1435 | $updates->submissions->itemids = array_keys($submissions); | |
1436 | } | |
1437 | ||
1438 | $updates->grades = (object) array('updated' => false); | |
1439 | $grades = $DB->get_records_select('assign_grades', $select, $params, '', 'id'); | |
1440 | if (!empty($grades)) { | |
1441 | $updates->grades->updated = true; | |
1442 | $updates->grades->itemids = array_keys($grades); | |
1443 | } | |
02a73d76 | 1444 | |
0a348387 JL |
1445 | // Now, teachers should see other students updates. |
1446 | if (has_capability('mod/assign:viewgrades', $cm->context)) { | |
1447 | $params = array('id' => $cm->instance, 'since1' => $from, 'since2' => $from); | |
1448 | $select = 'assignment = :id AND (timecreated > :since1 OR timemodified > :since2)'; | |
1449 | ||
1450 | if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) { | |
1451 | $groupusers = array_keys(groups_get_activity_shared_group_members($cm)); | |
1452 | if (empty($groupusers)) { | |
1453 | return $updates; | |
1454 | } | |
1455 | list($insql, $inparams) = $DB->get_in_or_equal($groupusers, SQL_PARAMS_NAMED); | |
1456 | $select .= ' AND userid ' . $insql; | |
1457 | $params = array_merge($params, $inparams); | |
1458 | } | |
1459 | ||
1460 | $updates->usersubmissions = (object) array('updated' => false); | |
1461 | $submissions = $DB->get_records_select('assign_submission', $select, $params, '', 'id'); | |
1462 | if (!empty($submissions)) { | |
1463 | $updates->usersubmissions->updated = true; | |
1464 | $updates->usersubmissions->itemids = array_keys($submissions); | |
1465 | } | |
1466 | ||
1467 | $updates->usergrades = (object) array('updated' => false); | |
1468 | $grades = $DB->get_records_select('assign_grades', $select, $params, '', 'id'); | |
1469 | if (!empty($grades)) { | |
1470 | $updates->usergrades->updated = true; | |
1471 | $updates->usergrades->itemids = array_keys($grades); | |
1472 | } | |
1473 | } | |
1474 | ||
02a73d76 JL |
1475 | return $updates; |
1476 | } | |
5ca71c2d | 1477 | |
294dce67 MN |
1478 | /** |
1479 | * Is the event visible? | |
1480 | * | |
1054c6f8 | 1481 | * This is used to determine global visibility of an event in all places throughout Moodle. For example, |
4c114500 | 1482 | * the ASSIGN_EVENT_TYPE_GRADINGDUE event will not be shown to students on their calendar. |
1054c6f8 | 1483 | * |
e1cd93ce | 1484 | * @param calendar_event $event |
fda4374a | 1485 | * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default). |
294dce67 MN |
1486 | * @return bool Returns true if the event is visible to the current user, false otherwise. |
1487 | */ | |
fda4374a | 1488 | function mod_assign_core_calendar_is_event_visible(calendar_event $event, $userid = 0) { |
1054c6f8 MN |
1489 | global $CFG, $USER; |
1490 | ||
1491 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
294dce67 | 1492 | |
fda4374a SR |
1493 | if (empty($userid)) { |
1494 | $userid = $USER->id; | |
1495 | } | |
1496 | ||
1497 | $cm = get_fast_modinfo($event->courseid, $userid)->instances['assign'][$event->instance]; | |
294dce67 MN |
1498 | $context = context_module::instance($cm->id); |
1499 | ||
1500 | $assign = new assign($context, $cm, null); | |
1501 | ||
1502 | if ($event->eventtype == ASSIGN_EVENT_TYPE_GRADINGDUE) { | |
fda4374a | 1503 | return $assign->can_grade($userid); |
294dce67 | 1504 | } else { |
4c114500 | 1505 | return true; |
294dce67 MN |
1506 | } |
1507 | } | |
1508 | ||
1509 | /** | |
59391e80 MN |
1510 | * This function receives a calendar event and returns the action associated with it, or null if there is none. |
1511 | * | |
1512 | * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event | |
1513 | * is not displayed on the block. | |
294dce67 | 1514 | * |
e1cd93ce | 1515 | * @param calendar_event $event |
294dce67 | 1516 | * @param \core_calendar\action_factory $factory |
fda4374a | 1517 | * @param int $userid User id to use for all capability checks, etc. Set to 0 for current user (default). |
01f96180 | 1518 | * @return \core_calendar\local\event\entities\action_interface|null |
294dce67 | 1519 | */ |
e1cd93ce | 1520 | function mod_assign_core_calendar_provide_event_action(calendar_event $event, |
fda4374a SR |
1521 | \core_calendar\action_factory $factory, |
1522 | $userid = 0) { | |
294dce67 MN |
1523 | |
1524 | global $CFG, $USER; | |
1525 | ||
1526 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
1527 | ||
fda4374a SR |
1528 | if (empty($userid)) { |
1529 | $userid = $USER->id; | |
1530 | } | |
1531 | ||
1532 | $cm = get_fast_modinfo($event->courseid, $userid)->instances['assign'][$event->instance]; | |
294dce67 MN |
1533 | $context = context_module::instance($cm->id); |
1534 | ||
96e44466 JY |
1535 | $completion = new \completion_info($cm->get_course()); |
1536 | ||
1537 | $completiondata = $completion->get_data($cm, false, $userid); | |
1538 | ||
1539 | if ($completiondata->completionstate != COMPLETION_INCOMPLETE) { | |
1540 | return null; | |
1541 | } | |
1542 | ||
294dce67 MN |
1543 | $assign = new assign($context, $cm, null); |
1544 | ||
a82becaa | 1545 | // Apply overrides. |
fda4374a | 1546 | $assign->update_effective_access($userid); |
a82becaa | 1547 | |
294dce67 MN |
1548 | if ($event->eventtype == ASSIGN_EVENT_TYPE_GRADINGDUE) { |
1549 | $name = get_string('grade'); | |
1550 | $url = new \moodle_url('/mod/assign/view.php', [ | |
1551 | 'id' => $cm->id, | |
1552 | 'action' => 'grader' | |
1553 | ]); | |
1554 | $itemcount = $assign->count_submissions_need_grading(); | |
fda4374a | 1555 | $actionable = $assign->can_grade($userid) && (time() >= $assign->get_instance()->allowsubmissionsfromdate); |
294dce67 | 1556 | } else { |
fda4374a | 1557 | $usersubmission = $assign->get_user_submission($userid, false); |
d846cb24 AN |
1558 | if ($usersubmission && $usersubmission->status === ASSIGN_SUBMISSION_STATUS_SUBMITTED) { |
1559 | // The user has already submitted. | |
1560 | // We do not want to change the text to edit the submission, we want to remove the event from the Dashboard entirely. | |
1561 | return null; | |
1562 | } | |
1563 | ||
fda4374a | 1564 | $participant = $assign->get_participant($userid); |
7ec6d873 RW |
1565 | |
1566 | if (!$participant) { | |
1567 | // If the user is not a participant in the assignment then they have | |
1568 | // no action to take. This will filter out the events for teachers. | |
1569 | return null; | |
1570 | } | |
1571 | ||
d846cb24 | 1572 | // The user has not yet submitted anything. Show the addsubmission link. |
294dce67 MN |
1573 | $name = get_string('addsubmission', 'assign'); |
1574 | $url = new \moodle_url('/mod/assign/view.php', [ | |
1575 | 'id' => $cm->id, | |
1576 | 'action' => 'editsubmission' | |
1577 | ]); | |
1578 | $itemcount = 1; | |
fda4374a | 1579 | $actionable = $assign->is_any_submission_plugin_enabled() && $assign->can_edit_submission($userid, $userid); |
294dce67 MN |
1580 | } |
1581 | ||
5ca71c2d | 1582 | return $factory->create_instance( |
294dce67 MN |
1583 | $name, |
1584 | $url, | |
1585 | $itemcount, | |
1586 | $actionable | |
5ca71c2d CB |
1587 | ); |
1588 | } | |
fb36d8dd JP |
1589 | |
1590 | /** | |
1591 | * Callback function that determines whether an action event should be showing its item count | |
1592 | * based on the event type and the item count. | |
1593 | * | |
e1cd93ce | 1594 | * @param calendar_event $event The calendar event. |
fb36d8dd JP |
1595 | * @param int $itemcount The item count associated with the action event. |
1596 | * @return bool | |
1597 | */ | |
e1cd93ce | 1598 | function mod_assign_core_calendar_event_action_shows_item_count(calendar_event $event, $itemcount = 0) { |
fb36d8dd JP |
1599 | // List of event types where the action event's item count should be shown. |
1600 | $eventtypesshowingitemcount = [ | |
1601 | ASSIGN_EVENT_TYPE_GRADINGDUE | |
1602 | ]; | |
1603 | // For mod_assign, item count should be shown if the event type is 'gradingdue' and there is one or more item count. | |
1604 | return in_array($event->eventtype, $eventtypesshowingitemcount) && $itemcount > 0; | |
1605 | } | |
f4c21561 RW |
1606 | |
1607 | /** | |
1608 | * This function calculates the minimum and maximum cutoff values for the timestart of | |
1609 | * the given event. | |
1610 | * | |
1611 | * It will return an array with two values, the first being the minimum cutoff value and | |
1612 | * the second being the maximum cutoff value. Either or both values can be null, which | |
1613 | * indicates there is no minimum or maximum, respectively. | |
1614 | * | |
1615 | * If a cutoff is required then the function must return an array containing the cutoff | |
1616 | * timestamp and error string to display to the user if the cutoff value is violated. | |
1617 | * | |
1618 | * A minimum and maximum cutoff return value will look like: | |
1619 | * [ | |
1620 | * [1505704373, 'The due date must be after the sbumission start date'], | |
1621 | * [1506741172, 'The due date must be before the cutoff date'] | |
1622 | * ] | |
1623 | * | |
6688ae2b RW |
1624 | * If the event does not have a valid timestart range then [false, false] will |
1625 | * be returned. | |
1626 | * | |
f4c21561 | 1627 | * @param calendar_event $event The calendar event to get the time range for |
478b1d19 | 1628 | * @param stdClass $instance The module instance to get the range from |
6688ae2b | 1629 | * @return array |
f4c21561 | 1630 | */ |
478b1d19 RW |
1631 | function mod_assign_core_calendar_get_valid_event_timestart_range(\calendar_event $event, \stdClass $instance) { |
1632 | global $CFG; | |
dbe537fb RW |
1633 | |
1634 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
f4c21561 | 1635 | |
478b1d19 RW |
1636 | $courseid = $event->courseid; |
1637 | $modulename = $event->modulename; | |
1638 | $instanceid = $event->instance; | |
1639 | $coursemodule = get_fast_modinfo($courseid)->instances[$modulename][$instanceid]; | |
f4c21561 RW |
1640 | $context = context_module::instance($coursemodule->id); |
1641 | $assign = new assign($context, null, null); | |
1642 | $assign->set_instance($instance); | |
1643 | ||
1644 | return $assign->get_valid_calendar_event_timestart_range($event); | |
1645 | } | |
1646 | ||
f4c21561 RW |
1647 | /** |
1648 | * This function will update the assign module according to the | |
1649 | * event that has been modified. | |
1650 | * | |
1651 | * @throws \moodle_exception | |
1652 | * @param \calendar_event $event | |
478b1d19 | 1653 | * @param stdClass $instance The module instance to get the range from |
f4c21561 | 1654 | */ |
478b1d19 | 1655 | function mod_assign_core_calendar_event_timestart_updated(\calendar_event $event, \stdClass $instance) { |
dbe537fb RW |
1656 | global $CFG, $DB; |
1657 | ||
1658 | require_once($CFG->dirroot . '/mod/assign/locallib.php'); | |
f4c21561 RW |
1659 | |
1660 | if (empty($event->instance) || $event->modulename != 'assign') { | |
1661 | return; | |
1662 | } | |
1663 | ||
478b1d19 RW |
1664 | if ($instance->id != $event->instance) { |
1665 | return; | |
1666 | } | |
f4c21561 | 1667 | |
478b1d19 | 1668 | if (!in_array($event->eventtype, [ASSIGN_EVENT_TYPE_DUE, ASSIGN_EVENT_TYPE_GRADINGDUE])) { |
f4c21561 RW |
1669 | return; |
1670 | } | |
1671 | ||
478b1d19 RW |
1672 | $courseid = $event->courseid; |
1673 | $modulename = $event->modulename; | |
1674 | $instanceid = $event->instance; | |
1675 | $modified = false; | |
1676 | $coursemodule = get_fast_modinfo($courseid)->instances[$modulename][$instanceid]; | |
f4c21561 RW |
1677 | $context = context_module::instance($coursemodule->id); |
1678 | ||
1679 | // The user does not have the capability to modify this activity. | |
1680 | if (!has_capability('moodle/course:manageactivities', $context)) { | |
1681 | return; | |
1682 | } | |
1683 | ||
1684 | $assign = new assign($context, $coursemodule, null); | |
478b1d19 | 1685 | $assign->set_instance($instance); |
f4c21561 RW |
1686 | |
1687 | if ($event->eventtype == ASSIGN_EVENT_TYPE_DUE) { | |
1688 | // This check is in here because due date events are currently | |
1689 | // the only events that can be overridden, so we can save a DB | |
1690 | // query if we don't bother checking other events. | |
1691 | if ($assign->is_override_calendar_event($event)) { | |
1692 | // This is an override event so we should ignore it. | |
1693 | return; | |
1694 | } | |
1695 | ||
f4c21561 RW |
1696 | $newduedate = $event->timestart; |
1697 | ||
1698 | if ($newduedate != $instance->duedate) { | |
1699 | $instance->duedate = $newduedate; | |
f4c21561 RW |
1700 | $modified = true; |
1701 | } | |
028fa144 | 1702 | } else if ($event->eventtype == ASSIGN_EVENT_TYPE_GRADINGDUE) { |
028fa144 AN |
1703 | $newduedate = $event->timestart; |
1704 | ||
1705 | if ($newduedate != $instance->gradingduedate) { | |
1706 | $instance->gradingduedate = $newduedate; | |
028fa144 AN |
1707 | $modified = true; |
1708 | } | |
f4c21561 RW |
1709 | } |
1710 | ||
1711 | if ($modified) { | |
478b1d19 | 1712 | $instance->timemodified = time(); |
f4c21561 RW |
1713 | // Persist the assign instance changes. |
1714 | $DB->update_record('assign', $instance); | |
1715 | $assign->update_calendar($coursemodule->id); | |
1716 | $event = \core\event\course_module_updated::create_from_cm($coursemodule, $context); | |
1717 | $event->trigger(); | |
1718 | } | |
1719 | } | |
064f1503 DW |
1720 | |
1721 | /** | |
1722 | * Return a list of all the user preferences used by mod_assign. | |
1723 | * | |
1724 | * @return array | |
1725 | */ | |
1726 | function mod_assign_user_preferences() { | |
1727 | $preferences = array(); | |
1728 | $preferences['assign_filter'] = array( | |
1729 | 'type' => PARAM_ALPHA, | |
1730 | 'null' => NULL_NOT_ALLOWED, | |
1731 | 'default' => '' | |
1732 | ); | |
1733 | $preferences['assign_workflowfilter'] = array( | |
1734 | 'type' => PARAM_ALPHA, | |
1735 | 'null' => NULL_NOT_ALLOWED, | |
1736 | 'default' => '' | |
1737 | ); | |
1738 | $preferences['assign_markerfilter'] = array( | |
1739 | 'type' => PARAM_ALPHANUMEXT, | |
1740 | 'null' => NULL_NOT_ALLOWED, | |
1741 | 'default' => '' | |
1742 | ); | |
1743 | ||
1744 | return $preferences; | |
1745 | } |