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