MDL-55611 calendar: remove duplicated 'scheduled task stuff'
[moodle.git] / mod / assign / lib.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
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/>.
17 /**
18  * This file contains the moodle hooks for the assign module.
19  *
20  * It delegates most functions to the assignment class.
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();
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  */
36 function assign_add_instance(stdClass $data, mod_assign_mod_form $form = null) {
37     global $CFG;
38     require_once($CFG->dirroot . '/mod/assign/locallib.php');
40     $assignment = new assign(context_module::instance($data->coursemodule), null, null);
41     return $assignment->add_instance($data, true);
42 }
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);
55     $assignment = new assign($context, null, null);
56     return $assignment->delete_instance();
57 }
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.
63  *
64  * @param stdClass $data the data submitted from the reset course.
65  * @return array
66  */
67 function assign_reset_userdata($data) {
68     global $CFG, $DB;
69     require_once($CFG->dirroot . '/mod/assign/locallib.php');
71     $status = array();
72     $params = array('courseid'=>$data->courseid);
73     $sql = "SELECT a.id FROM {assign} a WHERE a.course=:courseid";
74     $course = $DB->get_record('course', array('id'=>$data->courseid), '*', MUST_EXIST);
75     if ($assigns = $DB->get_records_sql($sql, $params)) {
76         foreach ($assigns as $assign) {
77             $cm = get_coursemodule_from_instance('assign',
78                                                  $assign->id,
79                                                  $data->courseid,
80                                                  false,
81                                                  MUST_EXIST);
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 }
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  */
99 function assign_refresh_events($courseid = 0) {
100     global $CFG, $DB;
101     require_once($CFG->dirroot . '/mod/assign/locallib.php');
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) {
121         // Get course and course module for the assignment.
122         list($course, $cm) = get_course_and_cm_from_instance($assign->id, 'assign', $assign->course);
124         // Refresh the assignment's calendar events.
125         $context = context_module::instance($cm->id);
126         $assignment = new assign($context, $cm, $course);
127         $assignment->update_calendar($cm->id);
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         }
140     }
142     return true;
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  */
151 function assign_reset_gradebook($courseid, $type='') {
152     global $CFG, $DB;
154     $params = array('moduletype'=>'assign', 'courseid'=>$courseid);
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';
159     if ($assignments = $DB->get_records_sql($sql, $params)) {
160         foreach ($assignments as $assignment) {
161             assign_grade_item_update($assignment, 'reset');
162         }
163     }
166 /**
167  * Implementation of the function for printing the form elements that control
168  * whether the course reset functionality affects the assignment.
169  * @param moodleform $mform form passed by reference
170  */
171 function assign_reset_course_form_definition(&$mform) {
172     $mform->addElement('header', 'assignheader', get_string('modulenameplural', 'assign'));
173     $name = get_string('deleteallsubmissions', 'assign');
174     $mform->addElement('advcheckbox', 'reset_assign_submissions', $name);
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'));
181 /**
182  * Course reset form defaults.
183  * @param  object $course
184  * @return array
185  */
186 function assign_reset_course_form_defaults($course) {
187     return array('reset_assign_submissions' => 1,
188             'reset_assign_group_overrides' => 1,
189             'reset_assign_user_overrides' => 1);
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
197  * @param stdClass $form - unused
198  * @return object
199  */
200 function assign_update_instance(stdClass $data, $form) {
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);
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  *
213  * @param assign $assign the assign object.
214  * @param object $override (optional) limit to a specific override
215  */
216 function assign_update_events($assign, $override = null) {
217     global $CFG, $DB;
219     require_once($CFG->dirroot . '/calendar/lib.php');
221     $assigninstance = $assign->get_instance();
223     // Load the old events relating to this assign.
224     $conds = array('modulename' => 'assign', 'instance' => $assigninstance->id);
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);
235     // Now make a to-do list of all that needs to be updated.
236     if (empty($override)) {
237         // We are updating the primary settings for the assign, so we need to add all the overrides.
238         $overrides = $DB->get_records('assign_overrides', array('assignid' => $assigninstance->id));
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     }
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     }
252     foreach ($overrides as $current) {
253         $groupid   = isset($current->groupid) ? $current->groupid : 0;
254         $userid    = isset($current->userid) ? $current->userid : 0;
255         $duedate = isset($current->duedate) ? $current->duedate : $assigninstance->duedate;
257         // Only add 'due' events for an override if they differ from the assign default.
258         $addclose = empty($current->id) || !empty($current->duedate);
260         $event = new stdClass();
261         $event->type = CALENDAR_EVENT_TYPE_ACTION;
262         $event->description = format_module_intro('assign', $assigninstance, $cmid);
263         // Events module won't show user events when the courseid is nonzero.
264         $event->courseid    = ($userid) ? 0 : $assigninstance->course;
265         $event->groupid     = $groupid;
266         $event->userid      = $userid;
267         $event->modulename  = 'assign';
268         $event->instance    = $assigninstance->id;
269         $event->timestart   = $duedate;
270         $event->timeduration = 0;
271         $event->timesort    = $event->timestart + $event->timeduration;
272         $event->visible     = instance_is_visible('assign', $assigninstance);
273         $event->eventtype   = ASSIGN_EVENT_TYPE_DUE;
275         // Determine the event name and priority.
276         if ($groupid) {
277             // Group override event.
278             $params = new stdClass();
279             $params->assign = $assigninstance->name;
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);
286             // Set group override priority.
287             if (isset($current->sortorder)) {
288                 $event->priority = $current->sortorder;
289             }
290         } else if ($userid) {
291             // User override event.
292             $params = new stdClass();
293             $params->assign = $assigninstance->name;
294             $eventname = get_string('overrideusereventname', 'assign', $params);
295             // Set user override priority.
296             $event->priority = CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
297         } else {
298             // The parent event.
299             $eventname = $assigninstance->name;
300         }
302         if ($duedate && $addclose) {
303             if ($oldevent = array_shift($oldevents)) {
304                 $event->id = $oldevent->id;
305             } else {
306                 unset($event->id);
307             }
308             $event->name      = $eventname.' ('.get_string('duedate', 'assign').')';
309             calendar_event::create($event);
310         }
311     }
313     // Delete any leftover events.
314     foreach ($oldevents as $badevent) {
315         $badevent = calendar_event::load($badevent);
316         $badevent->delete();
317     }
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  */
326 function assign_supports($feature) {
327     switch($feature) {
328         case FEATURE_GROUPS:
329             return true;
330         case FEATURE_GROUPINGS:
331             return true;
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;
350         case FEATURE_COMMENT:
351             return true;
353         default:
354             return null;
355     }
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  */
363 function assign_grading_areas_list() {
364     return array('submissions'=>get_string('submissions', 'assign'));
368 /**
369  * extend an assigment navigation settings
370  *
371  * @param settings_navigation $settings
372  * @param navigation_node $navref
373  * @return void
374  */
375 function assign_extend_settings_navigation(settings_navigation $settings, navigation_node $navref) {
376     global $PAGE, $DB;
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     }
389     $cm = $PAGE->cm;
390     if (!$cm) {
391         return;
392     }
394     $context = $cm->context;
395     $course = $PAGE->course;
397     if (!$course) {
398         return;
399     }
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);
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     }
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     }
422     // Link to download all submissions.
423     if (has_any_capability(array('mod/assign:grade', 'mod/assign:viewgrades'), $context)) {
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);
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     }
431     if (has_capability('mod/assign:revealidentities', $context)) {
432         $dbparams = array('id'=>$cm->instance);
433         $assignment = $DB->get_record('assign', $dbparams, 'blindmarking, revealidentities');
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     }
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).
452  * @return cached_cm_info An object on information that the courses
453  *                        will know about (most noticeably, an icon).
454  */
455 function assign_get_coursemodule_info($coursemodule) {
456     global $CFG, $DB;
458     $dbparams = array('id'=>$coursemodule->instance);
459     $fields = 'id, name, alwaysshowdescription, allowsubmissionsfromdate, intro, introformat';
460     if (! $assignment = $DB->get_record('assign', $dbparams, $fields)) {
461         return false;
462     }
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;
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  */
481 function assign_page_type_list($pagetype, $parentcontext, $currentcontext) {
482     $modulepagetype = array(
483         'mod-assign-*' => get_string('page-mod-assign-x', 'assign'),
484         'mod-assign-view' => get_string('page-mod-assign-view', 'assign'),
485     );
486     return $modulepagetype;
489 /**
490  * Print an overview of all assignments
491  * for the courses.
492  *
493  * @deprecated since 3.3
494  *
495  * @param mixed $courses The list of courses to print the overview for
496  * @param array $htmlarray The array of html to return
497  * @return true
498  */
499 function assign_print_overview($courses, &$htmlarray) {
500     global $CFG, $DB;
502     debugging('The function assign_print_overview() is now deprecated.', DEBUG_DEVELOPER);
504     if (empty($courses) || !is_array($courses) || count($courses) == 0) {
505         return true;
506     }
508     if (!$assignments = get_all_instances_in_courses('assign', $courses)) {
509         return true;
510     }
512     $assignmentids = array();
514     // Do assignment_base::isopen() here without loading the whole thing for speed.
515     foreach ($assignments as $key => $assignment) {
516         $time = time();
517         $isopen = false;
518         if ($assignment->duedate) {
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);
527             }
528         }
529         if ($isopen) {
530             $assignmentids[] = $assignment->id;
531         }
532     }
534     if (empty($assignmentids)) {
535         // No assignments to look at - we're done.
536         return true;
537     }
539     // Definitely something to print, now include the constants we need.
540     require_once($CFG->dirroot . '/mod/assign/locallib.php');
542     $strduedate = get_string('duedate', 'assign');
543     $strcutoffdate = get_string('nosubmissionsacceptedafter', 'assign');
544     $strnolatesubmissions = get_string('nolatesubmissions', 'assign');
545     $strduedateno = get_string('duedateno', 'assign');
546     $strassignment = get_string('modulename', 'assign');
548     // We do all possible database work here *outside* of the loop to ensure this scales.
549     list($sqlassignmentids, $assignmentidparams) = $DB->get_in_or_equal($assignmentids);
551     $mysubmissions = null;
552     $unmarkedsubmissions = null;
554     foreach ($assignments as $assignment) {
556         // Do not show assignments that are not open.
557         if (!in_array($assignment->id, $assignmentids)) {
558             continue;
559         }
561         $context = context_module::instance($assignment->coursemodule);
563         // Does the submission status of the assignment require notification?
564         if (has_capability('mod/assign:submit', $context, null, false)) {
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         }
572         if (has_capability('mod/assign:grade', $context, null, false)) {
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         }
580         if (empty($submitdetails) && empty($gradedetails)) {
581             // There is no need to display this assignment as there is nothing to notify.
582             continue;
583         }
585         $dimmedclass = '';
586         if (!$assignment->visible) {
587             $dimmedclass = ' class="dimmed"';
588         }
589         $href = $CFG->wwwroot . '/mod/assign/view.php?id=' . $assignment->coursemodule;
590         $basestr = '<div class="assign overview">' .
591                '<div class="name">' .
592                $strassignment . ': '.
593                '<a ' . $dimmedclass .
594                    'title="' . $strassignment . '" ' .
595                    'href="' . $href . '">' .
596                format_string($assignment->name) .
597                '</a></div>';
598         if ($assignment->duedate) {
599             $userdate = userdate($assignment->duedate);
600             $basestr .= '<div class="info">' . $strduedate . ': ' . $userdate . '</div>';
601         } else {
602             $basestr .= '<div class="info">' . $strduedateno . '</div>';
603         }
604         if ($assignment->cutoffdate) {
605             if ($assignment->cutoffdate == $assignment->duedate) {
606                 $basestr .= '<div class="info">' . $strnolatesubmissions . '</div>';
607             } else {
608                 $userdate = userdate($assignment->cutoffdate);
609                 $basestr .= '<div class="info">' . $strcutoffdate . ': ' . $userdate . '</div>';
610             }
611         }
613         // Show only relevant information.
614         if (!empty($submitdetails)) {
615             $basestr .= $submitdetails;
616         }
618         if (!empty($gradedetails)) {
619             $basestr .= $gradedetails;
620         }
621         $basestr .= '</div>';
623         if (empty($htmlarray[$assignment->course]['assign'])) {
624             $htmlarray[$assignment->course]['assign'] = $basestr;
625         } else {
626             $htmlarray[$assignment->course]['assign'] .= $basestr;
627         }
628     }
629     return true;
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  *
636  * @deprecated since 3.3
637  *
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  */
646 function assign_get_mysubmission_details_for_print_overview(&$mysubmissions, $sqlassignmentids, $assignmentidparams,
647                                                             $assignment) {
648     global $USER, $DB;
650     debugging('The function assign_get_mysubmission_details_for_print_overview() is now deprecated.', DEBUG_DEVELOPER);
652     if ($assignment->nosubmissions) {
653         // Offline assignment. No need to display alerts for offline assignments.
654         return false;
655     }
657     $strnotsubmittedyet = get_string('notsubmittedyet', 'assign');
659     if (!isset($mysubmissions)) {
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);
678     }
680     $submitdetails = '';
681     $submitdetails .= '<div class="details">';
682     $submitdetails .= get_string('mysubmission', 'assign');
683     $submission = false;
685     if (isset($mysubmissions[$assignment->id])) {
686         $submission = $mysubmissions[$assignment->id];
687     }
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     }
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;
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  *
726  * @deprecated since 3.3
727  *
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  */
737 function assign_get_grade_details_for_print_overview(&$unmarkedsubmissions, $sqlassignmentids, $assignmentidparams,
738                                                      $assignment, $context) {
739     global $DB;
741     debugging('The function assign_get_grade_details_for_print_overview() is now deprecated.', DEBUG_DEVELOPER);
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
759                                              s.timemodified >= g.timemodified OR
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);
766         $unmarkedsubmissions = array();
767         foreach ($rs as $rd) {
768             $unmarkedsubmissions[$rd->assignment][$rd->userid] = $rd->id;
769         }
770         $rs->close();
771     }
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     }
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     }
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
803  * @param int $timestart the time the rendering started
804  * @return bool true if activity was printed, false otherwise.
805  */
806 function assign_print_recent_activity($course, $viewfullnames, $timestart) {
807     global $CFG, $USER, $DB, $OUTPUT;
808     require_once($CFG->dirroot . '/mod/assign/locallib.php');
810     // Do not use log table if possible, it may be huge.
812     $dbparams = array($timestart, $course->id, 'assign', ASSIGN_SUBMISSION_STATUS_SUBMITTED);
813     $namefields = user_picture::fields('u', null, 'userid');
814     if (!$submissions = $DB->get_records_sql("SELECT asb.id, asb.timemodified, cm.id AS cmid, um.id as recordid,
815                                                      $namefields
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
821                                                 LEFT JOIN {assign_user_mapping} um ON um.userid = u.id AND um.assignment = a.id
822                                                WHERE asb.timemodified > ? AND
823                                                      asb.latest = 1 AND
824                                                      a.course = ? AND
825                                                      md.name = ? AND
826                                                      asb.status = ?
827                                             ORDER BY asb.timemodified ASC", $dbparams)) {
828          return false;
829     }
831     $modinfo = get_fast_modinfo($course);
832     $show    = array();
833     $grader  = array();
835     $showrecentsubmissions = get_config('assign', 'showrecentsubmissions');
837     foreach ($submissions as $submission) {
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         }
850         $context = context_module::instance($submission->cmid);
851         // The act of submitting of assignment may be considered private -
852         // only graders will see it if specified.
853         if (empty($showrecentsubmissions)) {
854             if (!array_key_exists($cm->id, $grader)) {
855                 $grader[$cm->id] = has_capability('moodle/grade:viewall', $context);
856             }
857             if (!$grader[$cm->id]) {
858                 continue;
859             }
860         }
862         $groupmode = groups_get_activity_groupmode($cm, $course);
864         if ($groupmode == SEPARATEGROUPS &&
865                 !has_capability('moodle/site:accessallgroups',  $context)) {
866             if (isguestuser()) {
867                 // Shortcut - guest user does not belong into any group.
868                 continue;
869             }
871             // This will be slow - show only users that share group with me in this cm.
872             if (!$modinfo->get_groups($cm->groupingid)) {
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);
878                 $intersect = array_intersect($usersgroups, $modinfo->get_groups($cm->groupingid));
879                 if (empty($intersect)) {
880                     continue;
881                 }
882             }
883         }
884         $show[] = $submission;
885     }
887     if (empty($show)) {
888         return false;
889     }
891     echo $OUTPUT->heading(get_string('newsubmissions', 'assign').':', 3);
893     foreach ($show as $submission) {
894         $cm = $modinfo->get_cm($submission->cmid);
895         $context = context_module::instance($submission->cmid);
896         $assign = new assign($context, $cm, $cm->course);
897         $link = $CFG->wwwroot.'/mod/assign/view.php?id='.$cm->id;
898         // Obscure first and last name if blind marking enabled.
899         if ($assign->is_blind_marking()) {
900             $submission->firstname = get_string('participant', 'mod_assign');
901             if (empty($submission->recordid)) {
902                 $submission->recordid = $assign->get_uniqueid_for_user($submission->userid);
903             }
904             $submission->lastname = $submission->recordid;
905         }
906         print_recent_activity_note($submission->timemodified,
907                                    $submission,
908                                    $cm->name,
909                                    $link,
910                                    false,
911                                    $viewfullnames);
912     }
914     return true;
917 /**
918  * Returns all assignments since a given time.
919  *
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  */
929 function assign_get_recent_mod_activity(&$activities,
930                                         &$index,
931                                         $timestart,
932                                         $courseid,
933                                         $cmid,
934                                         $userid=0,
935                                         $groupid=0) {
936     global $CFG, $COURSE, $USER, $DB;
938     require_once($CFG->dirroot . '/mod/assign/locallib.php');
940     if ($COURSE->id == $courseid) {
941         $course = $COURSE;
942     } else {
943         $course = $DB->get_record('course', array('id'=>$courseid));
944     }
946     $modinfo = get_fast_modinfo($course);
948     $cm = $modinfo->get_cm($cmid);
949     $params = array();
950     if ($userid) {
951         $userselect = 'AND u.id = :userid';
952         $params['userid'] = $userid;
953     } else {
954         $userselect = '';
955     }
957     if ($groupid) {
958         $groupselect = 'AND gm.groupid = :groupid';
959         $groupjoin   = 'JOIN {groups_members} gm ON  gm.userid=u.id';
960         $params['groupid'] = $groupid;
961     } else {
962         $groupselect = '';
963         $groupjoin   = '';
964     }
966     $params['cminstance'] = $cm->instance;
967     $params['timestart'] = $timestart;
968     $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
970     $userfields = user_picture::fields('u', null, 'userid');
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
979                                                      asb.status = :submitted AND
980                                                      a.id = :cminstance
981                                                      ' . $userselect . ' ' . $groupselect .
982                                             ' ORDER BY asb.timemodified ASC', $params)) {
983          return;
984     }
986     $groupmode       = groups_get_activity_groupmode($cm, $course);
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);
993     $showrecentsubmissions = get_config('assign', 'showrecentsubmissions');
994     $show = array();
995     foreach ($submissions as $submission) {
996         if ($submission->userid == $USER->id) {
997             $show[] = $submission;
998             continue;
999         }
1000         // The act of submitting of assignment may be considered private -
1001         // only graders will see it if specified.
1002         if (empty($showrecentsubmissions)) {
1003             if (!$grader) {
1004                 continue;
1005             }
1006         }
1008         if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
1009             if (isguestuser()) {
1010                 // Shortcut - guest user does not belong into any group.
1011                 continue;
1012             }
1014             // This will be slow - show only users that share group with me in this cm.
1015             if (!$modinfo->get_groups($cm->groupingid)) {
1016                 continue;
1017             }
1018             $usersgroups =  groups_get_all_groups($course->id, $submission->userid, $cm->groupingid);
1019             if (is_array($usersgroups)) {
1020                 $usersgroups = array_keys($usersgroups);
1021                 $intersect = array_intersect($usersgroups, $modinfo->get_groups($cm->groupingid));
1022                 if (empty($intersect)) {
1023                     continue;
1024                 }
1025             }
1026         }
1027         $show[] = $submission;
1028     }
1030     if (empty($show)) {
1031         return;
1032     }
1034     if ($grader) {
1035         require_once($CFG->libdir.'/gradelib.php');
1036         $userids = array();
1037         foreach ($show as $id => $submission) {
1038             $userids[] = $submission->userid;
1039         }
1040         $grades = grade_get_grades($courseid, 'mod', 'assign', $cm->instance, $userids);
1041     }
1043     $aname = format_string($cm->name, true);
1044     foreach ($show as $submission) {
1045         $activity = new stdClass();
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         }
1057         $userfields = explode(',', user_picture::fields());
1058         foreach ($userfields as $userfield) {
1059             if ($userfield == 'id') {
1060                 // Aliased in SQL above.
1061                 $activity->user->{$userfield} = $submission->userid;
1062             } else {
1063                 $activity->user->{$userfield} = $submission->{$userfield};
1064             }
1065         }
1066         $activity->user->fullname = fullname($submission, $viewfullnames);
1068         $activities[$index++] = $activity;
1069     }
1071     return;
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  */
1083 function assign_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
1084     global $CFG, $OUTPUT;
1086     echo '<table border="0" cellpadding="3" cellspacing="0" class="assignment-recent">';
1088     echo '<tr><td class="userpicture" valign="top">';
1089     echo $OUTPUT->user_picture($activity->user);
1090     echo '</td><td>';
1092     if ($detail) {
1093         $modname = $modnames[$activity->type];
1094         echo '<div class="title">';
1095         echo $OUTPUT->image_icon('icon', $modname, 'assign');
1096         echo '<a href="' . $CFG->wwwroot . '/mod/assign/view.php?id=' . $activity->cmid . '">';
1097         echo $activity->name;
1098         echo '</a>';
1099         echo '</div>';
1100     }
1102     if (isset($activity->grade)) {
1103         echo '<div class="grade">';
1104         echo get_string('grade').': ';
1105         echo $activity->grade;
1106         echo '</div>';
1107     }
1109     echo '<div class="user">';
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);
1112     echo '</div>';
1114     echo '</td></tr></table>';
1117 /**
1118  * Checks if a scale is being used by an assignment.
1119  *
1120  * This is used by the backup code to decide whether to back up a scale
1121  * @param int $assignmentid
1122  * @param int $scaleid
1123  * @return boolean True if the scale is used by the assignment
1124  */
1125 function assign_scale_used($assignmentid, $scaleid) {
1126     global $DB;
1128     $return = false;
1129     $rec = $DB->get_record('assign', array('id'=>$assignmentid, 'grade'=>-$scaleid));
1131     if (!empty($rec) && !empty($scaleid)) {
1132         $return = true;
1133     }
1135     return $return;
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
1142  * @param int $scaleid
1143  * @return boolean True if the scale is used by any assignment
1144  */
1145 function assign_scale_used_anywhere($scaleid) {
1146     global $DB;
1148     if ($scaleid and $DB->record_exists('assign', array('grade'=>-$scaleid))) {
1149         return true;
1150     } else {
1151         return false;
1152     }
1155 /**
1156  * List the actions that correspond to a view of this module.
1157  * This is used by the participation report.
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  *
1163  * @return array
1164  */
1165 function assign_get_view_actions() {
1166     return array('view submission', 'view feedback');
1169 /**
1170  * List the actions that correspond to a post of this module.
1171  * This is used by the participation report.
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  *
1177  * @return array
1178  */
1179 function assign_get_post_actions() {
1180     return array('upload', 'submit', 'submit for grading');
1183 /**
1184  * Call cron on the assign module.
1185  */
1186 function assign_cron() {
1187     global $CFG;
1189     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1190     assign::cron();
1192     $plugins = core_component::get_plugin_list('assignsubmission');
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     }
1202     $plugins = core_component::get_plugin_list('assignfeedback');
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     }
1213     return true;
1216 /**
1217  * Returns all other capabilities used by this module.
1218  * @return array Array of capability strings
1219  */
1220 function assign_get_extra_capabilities() {
1221     return array('gradereport/grader:view',
1222                  'moodle/grade:viewall',
1223                  'moodle/site:viewfullnames',
1224                  'moodle/site:config');
1227 /**
1228  * Create grade item for given assignment.
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  */
1234 function assign_grade_item_update($assign, $grades=null) {
1235     global $CFG;
1236     require_once($CFG->libdir.'/gradelib.php');
1238     if (!isset($assign->courseid)) {
1239         $assign->courseid = $assign->course;
1240     }
1242     $params = array('itemname'=>$assign->name, 'idnumber'=>$assign->cmidnumber);
1244     // Check if feedback plugin for gradebook is enabled, if yes then
1245     // gradetype = GRADE_TYPE_TEXT else GRADE_TYPE_NONE.
1246     $gradefeedbackenabled = false;
1248     if (isset($assign->gradefeedbackenabled)) {
1249         $gradefeedbackenabled = $assign->gradefeedbackenabled;
1250     } else if ($assign->grade == 0) { // Grade feedback is needed only when grade == 0.
1251         require_once($CFG->dirroot . '/mod/assign/locallib.php');
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     }
1258     if ($assign->grade > 0) {
1259         $params['gradetype'] = GRADE_TYPE_VALUE;
1260         $params['grademax']  = $assign->grade;
1261         $params['grademin']  = 0;
1263     } else if ($assign->grade < 0) {
1264         $params['gradetype'] = GRADE_TYPE_SCALE;
1265         $params['scaleid']   = -$assign->grade;
1267     } else if ($gradefeedbackenabled) {
1268         // $assign->grade == 0 and feedback enabled.
1269         $params['gradetype'] = GRADE_TYPE_TEXT;
1270     } else {
1271         // $assign->grade == 0 and no feedback enabled.
1272         $params['gradetype'] = GRADE_TYPE_NONE;
1273     }
1275     if ($grades  === 'reset') {
1276         $params['reset'] = true;
1277         $grades = null;
1278     }
1280     return grade_update('mod/assign',
1281                         $assign->courseid,
1282                         'mod',
1283                         'assign',
1284                         $assign->id,
1285                         0,
1286                         $grades,
1287                         $params);
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  */
1297 function assign_get_user_grades($assign, $userid=0) {
1298     global $CFG;
1300     require_once($CFG->dirroot . '/mod/assign/locallib.php');
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);
1305     $assignment->set_instance($assign);
1306     return $assignment->get_user_grades_for_gradebook($userid);
1309 /**
1310  * Update activity grades.
1311  *
1312  * @param stdClass $assign database record
1313  * @param int $userid specific user only, 0 means all
1314  * @param bool $nullifnone - not used
1315  */
1316 function assign_update_grades($assign, $userid=0, $nullifnone=true) {
1317     global $CFG;
1318     require_once($CFG->libdir.'/gradelib.php');
1320     if ($assign->grade == 0) {
1321         assign_grade_item_update($assign);
1323     } else if ($grades = assign_get_user_grades($assign, $userid)) {
1324         foreach ($grades as $k => $v) {
1325             if ($v->rawgrade == -1) {
1326                 $grades[$k]->rawgrade = null;
1327             }
1328         }
1329         assign_grade_item_update($assign, $grades);
1331     } else {
1332         assign_grade_item_update($assign);
1333     }
1336 /**
1337  * List the file areas that can be browsed.
1338  *
1339  * @param stdClass $course
1340  * @param stdClass $cm
1341  * @param stdClass $context
1342  * @return array
1343  */
1344 function assign_get_file_areas($course, $cm, $context) {
1345     global $CFG;
1346     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1348     $areas = array(ASSIGN_INTROATTACHMENT_FILEAREA => get_string('introattachments', 'mod_assign'));
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();
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();
1364             if ($pluginareas) {
1365                 $areas = array_merge($areas, $pluginareas);
1366             }
1367         }
1368     }
1370     return $areas;
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  */
1387 function assign_get_file_info($browser,
1388                               $areas,
1389                               $course,
1390                               $cm,
1391                               $context,
1392                               $filearea,
1393                               $itemid,
1394                               $filepath,
1395                               $filename) {
1396     global $CFG;
1397     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1399     if ($context->contextlevel != CONTEXT_MODULE) {
1400         return null;
1401     }
1403     $urlbase = $CFG->wwwroot.'/pluginfile.php';
1404     $fs = get_file_storage();
1405     $filepath = is_null($filepath) ? '/' : $filepath;
1406     $filename = is_null($filename) ? '.' : $filename;
1408     // Need to find where this belongs to.
1409     $assignment = new assign($context, $cm, $course);
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     }
1430     $pluginowner = null;
1431     foreach ($assignment->get_submission_plugins() as $plugin) {
1432         if ($plugin->is_visible()) {
1433             $pluginareas = $plugin->get_file_areas();
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();
1446                 if (array_key_exists($filearea, $pluginareas)) {
1447                     $pluginowner = $plugin;
1448                     break;
1449                 }
1450             }
1451         }
1452     }
1454     if (!$pluginowner) {
1455         return null;
1456     }
1458     $result = $pluginowner->get_file_info($browser, $filearea, $itemid, $filepath, $filename);
1459     return $result;
1462 /**
1463  * Prints the complete info about a user's interaction with an assignment.
1464  *
1465  * @param stdClass $course
1466  * @param stdClass $user
1467  * @param stdClass $coursemodule
1468  * @param stdClass $assign the database assign record
1469  *
1470  * This prints the submission summary and feedback summary for this student.
1471  */
1472 function assign_user_complete($course, $user, $coursemodule, $assign) {
1473     global $CFG;
1474     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1476     $context = context_module::instance($coursemodule->id);
1478     $assignment = new assign($context, $coursemodule, $course);
1480     echo $assignment->view_student_summary($user, false);
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  */
1493 function assign_rescale_activity_grades($course, $cm, $oldmin, $oldmax, $newmin, $newmax) {
1494     global $DB;
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     }
1506     $params = array(
1507         'p1' => $oldmin,
1508         'p2' => $scale,
1509         'p3' => $newmin,
1510         'a' => $cm->instance
1511     );
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     }
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;
1524     assign_update_grades($assign);
1526     return true;
1529 /**
1530  * Print the grade information for the assignment for this user.
1531  *
1532  * @param stdClass $course
1533  * @param stdClass $user
1534  * @param stdClass $coursemodule
1535  * @param stdClass $assignment
1536  */
1537 function 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');
1542     $gradinginfo = grade_get_grades($course->id,
1543                                         'mod',
1544                                         'assign',
1545                                         $assignment->id,
1546                                         $user->id);
1548     $gradingitem = $gradinginfo->items[0];
1549     $gradebookgrade = $gradingitem->grades[$user->id];
1551     if (empty($gradebookgrade->str_long_grade)) {
1552         return null;
1553     }
1554     $result = new stdClass();
1555     $result->info = get_string('outlinegrade', 'assign', $gradebookgrade->str_long_grade);
1556     $result->time = $gradebookgrade->dategraded;
1558     return $result;
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  */
1571 function assign_get_completion_state($course, $cm, $userid, $type) {
1572     global $CFG, $DB;
1573     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1575     $assign = new assign(null, $cm, $course);
1577     // If completion option is enabled, evaluate it and return true/false.
1578     if ($assign->get_instance()->completionsubmit) {
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         }
1584         return $submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1585     } else {
1586         // Completion option is not enabled so just return $type.
1587         return $type;
1588     }
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  */
1603 function assign_pluginfile($course,
1604                 $cm,
1605                 context $context,
1606                 $filearea,
1607                 $args,
1608                 $forcedownload,
1609                 array $options=array()) {
1610     global $CFG;
1612     if ($context->contextlevel != CONTEXT_MODULE) {
1613         return false;
1614     }
1616     require_login($course, false, $cm);
1617     if (!has_capability('mod/assign:view', $context)) {
1618         return false;
1619     }
1621     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1622     $assign = new assign($context, $cm, $course);
1624     if ($filearea !== ASSIGN_INTROATTACHMENT_FILEAREA) {
1625         return false;
1626     }
1627     if (!$assign->show_intro()) {
1628         return false;
1629     }
1631     $itemid = (int)array_shift($args);
1632     if ($itemid != 0) {
1633         return false;
1634     }
1636     $relativepath = implode('/', $args);
1638     $fullpath = "/{$context->id}/mod_assign/$filearea/$itemid/$relativepath";
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);
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  */
1653 function mod_assign_output_fragment_gradingpanel($args) {
1654     global $CFG;
1656     $context = $args['context'];
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);
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     );
1677     return $assign->view('gradingpanel', $viewargs);
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  */
1689 function assign_check_updates_since(cm_info $cm, $from, $filter = array()) {
1690     global $DB, $USER, $CFG;
1691     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1693     $updates = new stdClass();
1694     $updates = course_check_module_updates_since($cm, $from, array(ASSIGN_INTROATTACHMENT_FILEAREA), $filter);
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);
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     }
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     }
1713     return $updates;
1716 /**
1717  * Is the event visible?
1718  *
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  *
1723  * @param calendar_event $event
1724  * @return bool Returns true if the event is visible to the current user, false otherwise.
1725  */
1726 function mod_assign_core_calendar_is_event_visible(calendar_event $event) {
1727     global $CFG, $USER;
1729     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1731     $cm = get_fast_modinfo($event->courseid)->instances['assign'][$event->instance];
1732     $context = context_module::instance($cm->id);
1734     $assign = new assign($context, $cm, null);
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     }
1743 /**
1744  * Handles creating actions for events.
1745  *
1746  * @param calendar_event $event
1747  * @param \core_calendar\action_factory $factory
1748  * @return \core_calendar\local\event\value_objects\action|\core_calendar\local\interfaces\action_interface|null
1749  */
1750 function mod_assign_core_calendar_provide_event_action(calendar_event $event,
1751                                                        \core_calendar\action_factory $factory) {
1753     global $CFG, $USER;
1755     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1757     $cm = get_fast_modinfo($event->courseid)->instances['assign'][$event->instance];
1758     $context = context_module::instance($cm->id);
1760     $assign = new assign($context, $cm, null);
1762     // Apply overrides.
1763     $assign->update_effective_access($USER->id);
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 {
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         }
1781         // The user has not yet submitted anything. Show the addsubmission link.
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     }
1791     return $factory->create_instance(
1792         $name,
1793         $url,
1794         $itemcount,
1795         $actionable
1796     );
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  *
1803  * @param calendar_event $event The calendar event.
1804  * @param int $itemcount The item count associated with the action event.
1805  * @return bool
1806  */
1807 function mod_assign_core_calendar_event_action_shows_item_count(calendar_event $event, $itemcount = 0) {
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;