791ebcd62dc30028c4f78c5ac16b03991f835eee
[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         // Use assignment's course column if courseid parameter is not given.
122         if (!$courseid) {
123             $courseid = $assign->course;
124             if (!$course = $DB->get_record('course', array('id' => $courseid), '*')) {
125                 continue;
126             }
127         }
128         if (!$cm = get_coursemodule_from_instance('assign', $assign->id, $courseid, false)) {
129             continue;
130         }
131         $context = context_module::instance($cm->id);
132         $assignment = new assign($context, $cm, $course);
133         $assignment->update_calendar($cm->id);
134     }
136     return true;
139 /**
140  * Removes all grades from gradebook
141  *
142  * @param int $courseid The ID of the course to reset
143  * @param string $type Optional type of assignment to limit the reset to a particular assignment type
144  */
145 function assign_reset_gradebook($courseid, $type='') {
146     global $CFG, $DB;
148     $params = array('moduletype'=>'assign', 'courseid'=>$courseid);
149     $sql = 'SELECT a.*, cm.idnumber as cmidnumber, a.course as courseid
150             FROM {assign} a, {course_modules} cm, {modules} m
151             WHERE m.name=:moduletype AND m.id=cm.module AND cm.instance=a.id AND a.course=:courseid';
153     if ($assignments = $DB->get_records_sql($sql, $params)) {
154         foreach ($assignments as $assignment) {
155             assign_grade_item_update($assignment, 'reset');
156         }
157     }
160 /**
161  * Implementation of the function for printing the form elements that control
162  * whether the course reset functionality affects the assignment.
163  * @param moodleform $mform form passed by reference
164  */
165 function assign_reset_course_form_definition(&$mform) {
166     $mform->addElement('header', 'assignheader', get_string('modulenameplural', 'assign'));
167     $name = get_string('deleteallsubmissions', 'assign');
168     $mform->addElement('advcheckbox', 'reset_assign_submissions', $name);
169     $mform->addElement('advcheckbox', 'reset_assign_user_overrides',
170         get_string('removealluseroverrides', 'assign'));
171     $mform->addElement('advcheckbox', 'reset_assign_group_overrides',
172         get_string('removeallgroupoverrides', 'assign'));
175 /**
176  * Course reset form defaults.
177  * @param  object $course
178  * @return array
179  */
180 function assign_reset_course_form_defaults($course) {
181     return array('reset_assign_submissions' => 1,
182             'reset_assign_group_overrides' => 1,
183             'reset_assign_user_overrides' => 1);
186 /**
187  * Update an assignment instance
188  *
189  * This is done by calling the update_instance() method of the assignment type class
190  * @param stdClass $data
191  * @param stdClass $form - unused
192  * @return object
193  */
194 function assign_update_instance(stdClass $data, $form) {
195     global $CFG;
196     require_once($CFG->dirroot . '/mod/assign/locallib.php');
197     $context = context_module::instance($data->coursemodule);
198     $assignment = new assign($context, null, null);
199     return $assignment->update_instance($data);
202 /**
203  * This function updates the events associated to the assign.
204  * If $override is non-zero, then it updates only the events
205  * associated with the specified override.
206  *
207  * @uses ASSIGN_MAX_EVENT_LENGTH
208  * @param object $assign the assign object.
209  * @param object $override (optional) limit to a specific override
210  */
211 function assign_update_events($assign, $override = null) {
212     global $CFG, $DB;
214     require_once($CFG->dirroot . '/calendar/lib.php');
216     // Load the old events relating to this assign.
217     $conds = array('modulename' => 'assign',
218         'instance' => $assign->get_context()->id);
219     if (!empty($override)) {
220         // Only load events for this override.
221         if (isset($override->userid)) {
222             $conds['userid'] = $override->userid;
223         } else {
224             $conds['groupid'] = $override->groupid;
225         }
226     }
227     $oldevents = $DB->get_records('event', $conds);
229     // Now make a to-do list of all that needs to be updated.
230     if (empty($override)) {
231         // We are updating the primary settings for the assign, so we need to add all the overrides.
232         $overrides = $DB->get_records('assign_overrides', array('assignid' => $assign->id));
233         // As well as the original assign (empty override).
234         $overrides[] = new stdClass();
235     } else {
236         // Just do the one override.
237         $overrides = array($override);
238     }
240     foreach ($overrides as $current) {
241         $groupid   = isset($current->groupid) ? $current->groupid : 0;
242         $userid    = isset($current->userid) ? $current->userid : 0;
243         $allowsubmissionsfromdate  = isset($current->allowsubmissionsfromdate
244         ) ? $current->allowsubmissionsfromdate : $assign->get_context()->allowsubmissionsfromdate;
245         $duedate = isset($current->duedate) ? $current->duedate : $assign->get_context()->duedate;
247         // Only add open/close events for an override if they differ from the assign default.
248         $addopen  = empty($current->id) || !empty($current->allowsubmissionsfromdate);
249         $addclose = empty($current->id) || !empty($current->duedate);
251         if (!empty($assign->coursemodule)) {
252             $cmid = $assign->coursemodule;
253         } else {
254             $cmid = get_coursemodule_from_instance('assign', $assign->get_context()->id, $assign->get_context()->course)->id;
255         }
257         $event = new stdClass();
258         $event->description = format_module_intro('assign', $assign->get_context(), $cmid);
259         // Events module won't show user events when the courseid is nonzero.
260         $event->courseid    = ($userid) ? 0 : $assign->get_context()->course;
261         $event->groupid     = $groupid;
262         $event->userid      = $userid;
263         $event->modulename  = 'assign';
264         $event->instance    = $assign->get_context()->id;
265         $event->timestart   = $allowsubmissionsfromdate;
266         $event->timeduration = max($duedate - $allowsubmissionsfromdate, 0);
267         $event->visible     = instance_is_visible('assign', $assign);
268         $event->eventtype   = 'open';
270         // Determine the event name and priority.
271         if ($groupid) {
272             // Group override event.
273             $params = new stdClass();
274             $params->assign = $assign->get_context()->name;
275             $params->group = groups_get_group_name($groupid);
276             if ($params->group === false) {
277                 // Group doesn't exist, just skip it.
278                 continue;
279             }
280             $eventname = get_string('overridegroupeventname', 'assign', $params);
281             // Set group override priority.
282             if (isset($current->sortorder)) {
283                 $event->priority = $current->sortorder;
284             }
285         } else if ($userid) {
286             // User override event.
287             $params = new stdClass();
288             $params->assign = $assign->get_context()->name;
289             $eventname = get_string('overrideusereventname', 'assign', $params);
290             // Set user override priority.
291             $event->priority = CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
292         } else {
293             // The parent event.
294             $eventname = $assign->name;
295         }
297         if ($addopen or $addclose) {
298             // Separate start and end events.
299             $event->timeduration  = 0;
300             if ($allowsubmissionsfromdate && $addopen) {
301                 if ($oldevent = array_shift($oldevents)) {
302                     $event->id = $oldevent->id;
303                 } else {
304                     unset($event->id);
305                 }
306                 $event->name = $eventname.' ('.get_string('open', 'assign').')';
307                 // The method calendar_event::create will reuse a db record if the id field is set.
308                 calendar_event::create($event);
309             }
310             if ($duedate && $addclose) {
311                 if ($oldevent = array_shift($oldevents)) {
312                     $event->id = $oldevent->id;
313                 } else {
314                     unset($event->id);
315                 }
316                 $event->name      = $eventname.' ('.get_string('duedate', 'assign').')';
317                 $event->timestart = $duedate;
318                 $event->eventtype = 'due';
319                 calendar_event::create($event);
320             }
321         }
322     }
324     // Delete any leftover events.
325     foreach ($oldevents as $badevent) {
326         $badevent = calendar_event::load($badevent);
327         $badevent->delete();
328     }
331 /**
332  * Return the list if Moodle features this module supports
333  *
334  * @param string $feature FEATURE_xx constant for requested feature
335  * @return mixed True if module supports feature, null if doesn't know
336  */
337 function assign_supports($feature) {
338     switch($feature) {
339         case FEATURE_GROUPS:
340             return true;
341         case FEATURE_GROUPINGS:
342             return true;
343         case FEATURE_MOD_INTRO:
344             return true;
345         case FEATURE_COMPLETION_TRACKS_VIEWS:
346             return true;
347         case FEATURE_COMPLETION_HAS_RULES:
348             return true;
349         case FEATURE_GRADE_HAS_GRADE:
350             return true;
351         case FEATURE_GRADE_OUTCOMES:
352             return true;
353         case FEATURE_BACKUP_MOODLE2:
354             return true;
355         case FEATURE_SHOW_DESCRIPTION:
356             return true;
357         case FEATURE_ADVANCED_GRADING:
358             return true;
359         case FEATURE_PLAGIARISM:
360             return true;
361         case FEATURE_COMMENT:
362             return true;
364         default:
365             return null;
366     }
369 /**
370  * Lists all gradable areas for the advanced grading methods gramework
371  *
372  * @return array('string'=>'string') An array with area names as keys and descriptions as values
373  */
374 function assign_grading_areas_list() {
375     return array('submissions'=>get_string('submissions', 'assign'));
379 /**
380  * extend an assigment navigation settings
381  *
382  * @param settings_navigation $settings
383  * @param navigation_node $navref
384  * @return void
385  */
386 function assign_extend_settings_navigation(settings_navigation $settings, navigation_node $navref) {
387     global $PAGE, $DB;
389     // We want to add these new nodes after the Edit settings node, and before the
390     // Locally assigned roles node. Of course, both of those are controlled by capabilities.
391     $keys = $navref->get_children_key_list();
392     $beforekey = null;
393     $i = array_search('modedit', $keys);
394     if ($i === false and array_key_exists(0, $keys)) {
395         $beforekey = $keys[0];
396     } else if (array_key_exists($i + 1, $keys)) {
397         $beforekey = $keys[$i + 1];
398     }
400     $cm = $PAGE->cm;
401     if (!$cm) {
402         return;
403     }
405     $context = $cm->context;
406     $course = $PAGE->course;
408     if (!$course) {
409         return;
410     }
412     if (has_capability('mod/assign:manageoverrides', $PAGE->cm->context)) {
413         $url = new moodle_url('/mod/assign/overrides.php', array('cmid' => $PAGE->cm->id));
414         $node = navigation_node::create(get_string('groupoverrides', 'assign'),
415             new moodle_url($url, array('mode' => 'group')),
416             navigation_node::TYPE_SETTING, null, 'mod_assign_groupoverrides');
417         $navref->add_node($node, $beforekey);
419         $node = navigation_node::create(get_string('useroverrides', 'assign'),
420             new moodle_url($url, array('mode' => 'user')),
421             navigation_node::TYPE_SETTING, null, 'mod_assign_useroverrides');
422         $navref->add_node($node, $beforekey);
423     }
425     // Link to gradebook.
426     if (has_capability('gradereport/grader:view', $cm->context) &&
427             has_capability('moodle/grade:viewall', $cm->context)) {
428         $link = new moodle_url('/grade/report/grader/index.php', array('id' => $course->id));
429         $linkname = get_string('viewgradebook', 'assign');
430         $node = $navref->add($linkname, $link, navigation_node::TYPE_SETTING);
431     }
433     // Link to download all submissions.
434     if (has_any_capability(array('mod/assign:grade', 'mod/assign:viewgrades'), $context)) {
435         $link = new moodle_url('/mod/assign/view.php', array('id' => $cm->id, 'action'=>'grading'));
436         $node = $navref->add(get_string('viewgrading', 'assign'), $link, navigation_node::TYPE_SETTING);
438         $link = new moodle_url('/mod/assign/view.php', array('id' => $cm->id, 'action'=>'downloadall'));
439         $node = $navref->add(get_string('downloadall', 'assign'), $link, navigation_node::TYPE_SETTING);
440     }
442     if (has_capability('mod/assign:revealidentities', $context)) {
443         $dbparams = array('id'=>$cm->instance);
444         $assignment = $DB->get_record('assign', $dbparams, 'blindmarking, revealidentities');
446         if ($assignment && $assignment->blindmarking && !$assignment->revealidentities) {
447             $urlparams = array('id' => $cm->id, 'action'=>'revealidentities');
448             $url = new moodle_url('/mod/assign/view.php', $urlparams);
449             $linkname = get_string('revealidentities', 'assign');
450             $node = $navref->add($linkname, $url, navigation_node::TYPE_SETTING);
451         }
452     }
455 /**
456  * Add a get_coursemodule_info function in case any assignment type wants to add 'extra' information
457  * for the course (see resource).
458  *
459  * Given a course_module object, this function returns any "extra" information that may be needed
460  * when printing this activity in a course listing.  See get_array_of_activities() in course/lib.php.
461  *
462  * @param stdClass $coursemodule The coursemodule object (record).
463  * @return cached_cm_info An object on information that the courses
464  *                        will know about (most noticeably, an icon).
465  */
466 function assign_get_coursemodule_info($coursemodule) {
467     global $CFG, $DB;
469     $dbparams = array('id'=>$coursemodule->instance);
470     $fields = 'id, name, alwaysshowdescription, allowsubmissionsfromdate, intro, introformat';
471     if (! $assignment = $DB->get_record('assign', $dbparams, $fields)) {
472         return false;
473     }
475     $result = new cached_cm_info();
476     $result->name = $assignment->name;
477     if ($coursemodule->showdescription) {
478         if ($assignment->alwaysshowdescription || time() > $assignment->allowsubmissionsfromdate) {
479             // Convert intro to html. Do not filter cached version, filters run at display time.
480             $result->content = format_module_intro('assign', $assignment, $coursemodule->id, false);
481         }
482     }
483     return $result;
486 /**
487  * Return a list of page types
488  * @param string $pagetype current page type
489  * @param stdClass $parentcontext Block's parent context
490  * @param stdClass $currentcontext Current context of block
491  */
492 function assign_page_type_list($pagetype, $parentcontext, $currentcontext) {
493     $modulepagetype = array(
494         'mod-assign-*' => get_string('page-mod-assign-x', 'assign'),
495         'mod-assign-view' => get_string('page-mod-assign-view', 'assign'),
496     );
497     return $modulepagetype;
500 /**
501  * Print an overview of all assignments
502  * for the courses.
503  *
504  * @param mixed $courses The list of courses to print the overview for
505  * @param array $htmlarray The array of html to return
506  *
507  * @return true
508  */
509 function assign_print_overview($courses, &$htmlarray) {
510     global $CFG, $DB;
512     if (empty($courses) || !is_array($courses) || count($courses) == 0) {
513         return true;
514     }
516     if (!$assignments = get_all_instances_in_courses('assign', $courses)) {
517         return true;
518     }
520     $assignmentids = array();
522     // Do assignment_base::isopen() here without loading the whole thing for speed.
523     foreach ($assignments as $key => $assignment) {
524         $time = time();
525         $isopen = false;
526         if ($assignment->duedate) {
527             $duedate = false;
528             if ($assignment->cutoffdate) {
529                 $duedate = $assignment->cutoffdate;
530             }
531             if ($duedate) {
532                 $isopen = ($assignment->allowsubmissionsfromdate <= $time && $time <= $duedate);
533             } else {
534                 $isopen = ($assignment->allowsubmissionsfromdate <= $time);
535             }
536         }
537         if ($isopen) {
538             $assignmentids[] = $assignment->id;
539         }
540     }
542     if (empty($assignmentids)) {
543         // No assignments to look at - we're done.
544         return true;
545     }
547     // Definitely something to print, now include the constants we need.
548     require_once($CFG->dirroot . '/mod/assign/locallib.php');
550     $strduedate = get_string('duedate', 'assign');
551     $strcutoffdate = get_string('nosubmissionsacceptedafter', 'assign');
552     $strnolatesubmissions = get_string('nolatesubmissions', 'assign');
553     $strduedateno = get_string('duedateno', 'assign');
554     $strassignment = get_string('modulename', 'assign');
556     // We do all possible database work here *outside* of the loop to ensure this scales.
557     list($sqlassignmentids, $assignmentidparams) = $DB->get_in_or_equal($assignmentids);
559     $mysubmissions = null;
560     $unmarkedsubmissions = null;
562     foreach ($assignments as $assignment) {
564         // Do not show assignments that are not open.
565         if (!in_array($assignment->id, $assignmentids)) {
566             continue;
567         }
569         $context = context_module::instance($assignment->coursemodule);
571         // Does the submission status of the assignment require notification?
572         if (has_capability('mod/assign:submit', $context, null, false)) {
573             // Does the submission status of the assignment require notification?
574             $submitdetails = assign_get_mysubmission_details_for_print_overview($mysubmissions, $sqlassignmentids,
575                     $assignmentidparams, $assignment);
576         } else {
577             $submitdetails = false;
578         }
580         if (has_capability('mod/assign:grade', $context, null, false)) {
581             // Does the grading status of the assignment require notification ?
582             $gradedetails = assign_get_grade_details_for_print_overview($unmarkedsubmissions, $sqlassignmentids,
583                     $assignmentidparams, $assignment, $context);
584         } else {
585             $gradedetails = false;
586         }
588         if (empty($submitdetails) && empty($gradedetails)) {
589             // There is no need to display this assignment as there is nothing to notify.
590             continue;
591         }
593         $dimmedclass = '';
594         if (!$assignment->visible) {
595             $dimmedclass = ' class="dimmed"';
596         }
597         $href = $CFG->wwwroot . '/mod/assign/view.php?id=' . $assignment->coursemodule;
598         $basestr = '<div class="assign overview">' .
599                '<div class="name">' .
600                $strassignment . ': '.
601                '<a ' . $dimmedclass .
602                    'title="' . $strassignment . '" ' .
603                    'href="' . $href . '">' .
604                format_string($assignment->name) .
605                '</a></div>';
606         if ($assignment->duedate) {
607             $userdate = userdate($assignment->duedate);
608             $basestr .= '<div class="info">' . $strduedate . ': ' . $userdate . '</div>';
609         } else {
610             $basestr .= '<div class="info">' . $strduedateno . '</div>';
611         }
612         if ($assignment->cutoffdate) {
613             if ($assignment->cutoffdate == $assignment->duedate) {
614                 $basestr .= '<div class="info">' . $strnolatesubmissions . '</div>';
615             } else {
616                 $userdate = userdate($assignment->cutoffdate);
617                 $basestr .= '<div class="info">' . $strcutoffdate . ': ' . $userdate . '</div>';
618             }
619         }
621         // Show only relevant information.
622         if (!empty($submitdetails)) {
623             $basestr .= $submitdetails;
624         }
626         if (!empty($gradedetails)) {
627             $basestr .= $gradedetails;
628         }
629         $basestr .= '</div>';
631         if (empty($htmlarray[$assignment->course]['assign'])) {
632             $htmlarray[$assignment->course]['assign'] = $basestr;
633         } else {
634             $htmlarray[$assignment->course]['assign'] .= $basestr;
635         }
636     }
637     return true;
640 /**
641  * This api generates html to be displayed to students in print overview section, related to their submission status of the given
642  * assignment.
643  *
644  * @param array $mysubmissions list of submissions of current user indexed by assignment id.
645  * @param string $sqlassignmentids sql clause used to filter open assignments.
646  * @param array $assignmentidparams sql params used to filter open assignments.
647  * @param stdClass $assignment current assignment
648  *
649  * @return bool|string html to display , false if nothing needs to be displayed.
650  * @throws coding_exception
651  */
652 function assign_get_mysubmission_details_for_print_overview(&$mysubmissions, $sqlassignmentids, $assignmentidparams,
653                                                             $assignment) {
654     global $USER, $DB;
656     if ($assignment->nosubmissions) {
657         // Offline assignment. No need to display alerts for offline assignments.
658         return false;
659     }
661     $strnotsubmittedyet = get_string('notsubmittedyet', 'assign');
663     if (!isset($mysubmissions)) {
665         // Get all user submissions, indexed by assignment id.
666         $dbparams = array_merge(array($USER->id), $assignmentidparams, array($USER->id));
667         $mysubmissions = $DB->get_records_sql('SELECT a.id AS assignment,
668                                                       a.nosubmissions AS nosubmissions,
669                                                       g.timemodified AS timemarked,
670                                                       g.grader AS grader,
671                                                       g.grade AS grade,
672                                                       s.status AS status
673                                                  FROM {assign} a, {assign_submission} s
674                                             LEFT JOIN {assign_grades} g ON
675                                                       g.assignment = s.assignment AND
676                                                       g.userid = ? AND
677                                                       g.attemptnumber = s.attemptnumber
678                                                 WHERE a.id ' . $sqlassignmentids . ' AND
679                                                       s.latest = 1 AND
680                                                       s.assignment = a.id AND
681                                                       s.userid = ?', $dbparams);
682     }
684     $submitdetails = '';
685     $submitdetails .= '<div class="details">';
686     $submitdetails .= get_string('mysubmission', 'assign');
687     $submission = false;
689     if (isset($mysubmissions[$assignment->id])) {
690         $submission = $mysubmissions[$assignment->id];
691     }
693     if ($submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
694         // A valid submission already exists, no need to notify students about this.
695         return false;
696     }
698     // We need to show details only if a valid submission doesn't exist.
699     if (!$submission ||
700         !$submission->status ||
701         $submission->status == ASSIGN_SUBMISSION_STATUS_DRAFT ||
702         $submission->status == ASSIGN_SUBMISSION_STATUS_NEW
703     ) {
704         $submitdetails .= $strnotsubmittedyet;
705     } else {
706         $submitdetails .= get_string('submissionstatus_' . $submission->status, 'assign');
707     }
708     if ($assignment->markingworkflow) {
709         $workflowstate = $DB->get_field('assign_user_flags', 'workflowstate', array('assignment' =>
710                 $assignment->id, 'userid' => $USER->id));
711         if ($workflowstate) {
712             $gradingstatus = 'markingworkflowstate' . $workflowstate;
713         } else {
714             $gradingstatus = 'markingworkflowstate' . ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED;
715         }
716     } else if (!empty($submission->grade) && $submission->grade !== null && $submission->grade >= 0) {
717         $gradingstatus = ASSIGN_GRADING_STATUS_GRADED;
718     } else {
719         $gradingstatus = ASSIGN_GRADING_STATUS_NOT_GRADED;
720     }
721     $submitdetails .= ', ' . get_string($gradingstatus, 'assign');
722     $submitdetails .= '</div>';
723     return $submitdetails;
726 /**
727  * This api generates html to be displayed to teachers in print overview section, related to the grading status of the given
728  * assignment's submissions.
729  *
730  * @param array $unmarkedsubmissions list of submissions of that are currently unmarked indexed by assignment id.
731  * @param string $sqlassignmentids sql clause used to filter open assignments.
732  * @param array $assignmentidparams sql params used to filter open assignments.
733  * @param stdClass $assignment current assignment
734  * @param context $context context of the assignment.
735  *
736  * @return bool|string html to display , false if nothing needs to be displayed.
737  * @throws coding_exception
738  */
739 function assign_get_grade_details_for_print_overview(&$unmarkedsubmissions, $sqlassignmentids, $assignmentidparams,
740                                                      $assignment, $context) {
741     global $DB;
742     if (!isset($unmarkedsubmissions)) {
743         // Build up and array of unmarked submissions indexed by assignment id/ userid
744         // for use where the user has grading rights on assignment.
745         $dbparams = array_merge(array(ASSIGN_SUBMISSION_STATUS_SUBMITTED), $assignmentidparams);
746         $rs = $DB->get_recordset_sql('SELECT s.assignment as assignment,
747                                              s.userid as userid,
748                                              s.id as id,
749                                              s.status as status,
750                                              g.timemodified as timegraded
751                                         FROM {assign_submission} s
752                                    LEFT JOIN {assign_grades} g ON
753                                              s.userid = g.userid AND
754                                              s.assignment = g.assignment AND
755                                              g.attemptnumber = s.attemptnumber
756                                        WHERE
757                                              ( g.timemodified is NULL OR
758                                              s.timemodified >= g.timemodified OR
759                                              g.grade IS NULL ) AND
760                                              s.timemodified IS NOT NULL AND
761                                              s.status = ? AND
762                                              s.latest = 1 AND
763                                              s.assignment ' . $sqlassignmentids, $dbparams);
765         $unmarkedsubmissions = array();
766         foreach ($rs as $rd) {
767             $unmarkedsubmissions[$rd->assignment][$rd->userid] = $rd->id;
768         }
769         $rs->close();
770     }
772     // Count how many people can submit.
773     $submissions = 0;
774     if ($students = get_enrolled_users($context, 'mod/assign:view', 0, 'u.id')) {
775         foreach ($students as $student) {
776             if (isset($unmarkedsubmissions[$assignment->id][$student->id])) {
777                 $submissions++;
778             }
779         }
780     }
782     if ($submissions) {
783         $urlparams = array('id' => $assignment->coursemodule, 'action' => 'grading');
784         $url = new moodle_url('/mod/assign/view.php', $urlparams);
785         $gradedetails = '<div class="details">' .
786                 '<a href="' . $url . '">' .
787                 get_string('submissionsnotgraded', 'assign', $submissions) .
788                 '</a></div>';
789         return $gradedetails;
790     } else {
791         return false;
792     }
796 /**
797  * Print recent activity from all assignments in a given course
798  *
799  * This is used by the recent activity block
800  * @param mixed $course the course to print activity for
801  * @param bool $viewfullnames boolean to determine whether to show full names or not
802  * @param int $timestart the time the rendering started
803  * @return bool true if activity was printed, false otherwise.
804  */
805 function assign_print_recent_activity($course, $viewfullnames, $timestart) {
806     global $CFG, $USER, $DB, $OUTPUT;
807     require_once($CFG->dirroot . '/mod/assign/locallib.php');
809     // Do not use log table if possible, it may be huge.
811     $dbparams = array($timestart, $course->id, 'assign', ASSIGN_SUBMISSION_STATUS_SUBMITTED);
812     $namefields = user_picture::fields('u', null, 'userid');
813     if (!$submissions = $DB->get_records_sql("SELECT asb.id, asb.timemodified, cm.id AS cmid, um.id as recordid,
814                                                      $namefields
815                                                 FROM {assign_submission} asb
816                                                      JOIN {assign} a      ON a.id = asb.assignment
817                                                      JOIN {course_modules} cm ON cm.instance = a.id
818                                                      JOIN {modules} md        ON md.id = cm.module
819                                                      JOIN {user} u            ON u.id = asb.userid
820                                                 LEFT JOIN {assign_user_mapping} um ON um.userid = u.id AND um.assignment = a.id
821                                                WHERE asb.timemodified > ? AND
822                                                      asb.latest = 1 AND
823                                                      a.course = ? AND
824                                                      md.name = ? AND
825                                                      asb.status = ?
826                                             ORDER BY asb.timemodified ASC", $dbparams)) {
827          return false;
828     }
830     $modinfo = get_fast_modinfo($course);
831     $show    = array();
832     $grader  = array();
834     $showrecentsubmissions = get_config('assign', 'showrecentsubmissions');
836     foreach ($submissions as $submission) {
837         if (!array_key_exists($submission->cmid, $modinfo->get_cms())) {
838             continue;
839         }
840         $cm = $modinfo->get_cm($submission->cmid);
841         if (!$cm->uservisible) {
842             continue;
843         }
844         if ($submission->userid == $USER->id) {
845             $show[] = $submission;
846             continue;
847         }
849         $context = context_module::instance($submission->cmid);
850         // The act of submitting of assignment may be considered private -
851         // only graders will see it if specified.
852         if (empty($showrecentsubmissions)) {
853             if (!array_key_exists($cm->id, $grader)) {
854                 $grader[$cm->id] = has_capability('moodle/grade:viewall', $context);
855             }
856             if (!$grader[$cm->id]) {
857                 continue;
858             }
859         }
861         $groupmode = groups_get_activity_groupmode($cm, $course);
863         if ($groupmode == SEPARATEGROUPS &&
864                 !has_capability('moodle/site:accessallgroups',  $context)) {
865             if (isguestuser()) {
866                 // Shortcut - guest user does not belong into any group.
867                 continue;
868             }
870             // This will be slow - show only users that share group with me in this cm.
871             if (!$modinfo->get_groups($cm->groupingid)) {
872                 continue;
873             }
874             $usersgroups =  groups_get_all_groups($course->id, $submission->userid, $cm->groupingid);
875             if (is_array($usersgroups)) {
876                 $usersgroups = array_keys($usersgroups);
877                 $intersect = array_intersect($usersgroups, $modinfo->get_groups($cm->groupingid));
878                 if (empty($intersect)) {
879                     continue;
880                 }
881             }
882         }
883         $show[] = $submission;
884     }
886     if (empty($show)) {
887         return false;
888     }
890     echo $OUTPUT->heading(get_string('newsubmissions', 'assign').':', 3);
892     foreach ($show as $submission) {
893         $cm = $modinfo->get_cm($submission->cmid);
894         $context = context_module::instance($submission->cmid);
895         $assign = new assign($context, $cm, $cm->course);
896         $link = $CFG->wwwroot.'/mod/assign/view.php?id='.$cm->id;
897         // Obscure first and last name if blind marking enabled.
898         if ($assign->is_blind_marking()) {
899             $submission->firstname = get_string('participant', 'mod_assign');
900             if (empty($submission->recordid)) {
901                 $submission->recordid = $assign->get_uniqueid_for_user($submission->userid);
902             }
903             $submission->lastname = $submission->recordid;
904         }
905         print_recent_activity_note($submission->timemodified,
906                                    $submission,
907                                    $cm->name,
908                                    $link,
909                                    false,
910                                    $viewfullnames);
911     }
913     return true;
916 /**
917  * Returns all assignments since a given time.
918  *
919  * @param array $activities The activity information is returned in this array
920  * @param int $index The current index in the activities array
921  * @param int $timestart The earliest activity to show
922  * @param int $courseid Limit the search to this course
923  * @param int $cmid The course module id
924  * @param int $userid Optional user id
925  * @param int $groupid Optional group id
926  * @return void
927  */
928 function assign_get_recent_mod_activity(&$activities,
929                                         &$index,
930                                         $timestart,
931                                         $courseid,
932                                         $cmid,
933                                         $userid=0,
934                                         $groupid=0) {
935     global $CFG, $COURSE, $USER, $DB;
937     require_once($CFG->dirroot . '/mod/assign/locallib.php');
939     if ($COURSE->id == $courseid) {
940         $course = $COURSE;
941     } else {
942         $course = $DB->get_record('course', array('id'=>$courseid));
943     }
945     $modinfo = get_fast_modinfo($course);
947     $cm = $modinfo->get_cm($cmid);
948     $params = array();
949     if ($userid) {
950         $userselect = 'AND u.id = :userid';
951         $params['userid'] = $userid;
952     } else {
953         $userselect = '';
954     }
956     if ($groupid) {
957         $groupselect = 'AND gm.groupid = :groupid';
958         $groupjoin   = 'JOIN {groups_members} gm ON  gm.userid=u.id';
959         $params['groupid'] = $groupid;
960     } else {
961         $groupselect = '';
962         $groupjoin   = '';
963     }
965     $params['cminstance'] = $cm->instance;
966     $params['timestart'] = $timestart;
967     $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
969     $userfields = user_picture::fields('u', null, 'userid');
971     if (!$submissions = $DB->get_records_sql('SELECT asb.id, asb.timemodified, ' .
972                                                      $userfields .
973                                              '  FROM {assign_submission} asb
974                                                 JOIN {assign} a ON a.id = asb.assignment
975                                                 JOIN {user} u ON u.id = asb.userid ' .
976                                           $groupjoin .
977                                             '  WHERE asb.timemodified > :timestart AND
978                                                      asb.status = :submitted AND
979                                                      a.id = :cminstance
980                                                      ' . $userselect . ' ' . $groupselect .
981                                             ' ORDER BY asb.timemodified ASC', $params)) {
982          return;
983     }
985     $groupmode       = groups_get_activity_groupmode($cm, $course);
986     $cmcontext      = context_module::instance($cm->id);
987     $grader          = has_capability('moodle/grade:viewall', $cmcontext);
988     $accessallgroups = has_capability('moodle/site:accessallgroups', $cmcontext);
989     $viewfullnames   = has_capability('moodle/site:viewfullnames', $cmcontext);
992     $showrecentsubmissions = get_config('assign', 'showrecentsubmissions');
993     $show = array();
994     foreach ($submissions as $submission) {
995         if ($submission->userid == $USER->id) {
996             $show[] = $submission;
997             continue;
998         }
999         // The act of submitting of assignment may be considered private -
1000         // only graders will see it if specified.
1001         if (empty($showrecentsubmissions)) {
1002             if (!$grader) {
1003                 continue;
1004             }
1005         }
1007         if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
1008             if (isguestuser()) {
1009                 // Shortcut - guest user does not belong into any group.
1010                 continue;
1011             }
1013             // This will be slow - show only users that share group with me in this cm.
1014             if (!$modinfo->get_groups($cm->groupingid)) {
1015                 continue;
1016             }
1017             $usersgroups =  groups_get_all_groups($course->id, $submission->userid, $cm->groupingid);
1018             if (is_array($usersgroups)) {
1019                 $usersgroups = array_keys($usersgroups);
1020                 $intersect = array_intersect($usersgroups, $modinfo->get_groups($cm->groupingid));
1021                 if (empty($intersect)) {
1022                     continue;
1023                 }
1024             }
1025         }
1026         $show[] = $submission;
1027     }
1029     if (empty($show)) {
1030         return;
1031     }
1033     if ($grader) {
1034         require_once($CFG->libdir.'/gradelib.php');
1035         $userids = array();
1036         foreach ($show as $id => $submission) {
1037             $userids[] = $submission->userid;
1038         }
1039         $grades = grade_get_grades($courseid, 'mod', 'assign', $cm->instance, $userids);
1040     }
1042     $aname = format_string($cm->name, true);
1043     foreach ($show as $submission) {
1044         $activity = new stdClass();
1046         $activity->type         = 'assign';
1047         $activity->cmid         = $cm->id;
1048         $activity->name         = $aname;
1049         $activity->sectionnum   = $cm->sectionnum;
1050         $activity->timestamp    = $submission->timemodified;
1051         $activity->user         = new stdClass();
1052         if ($grader) {
1053             $activity->grade = $grades->items[0]->grades[$submission->userid]->str_long_grade;
1054         }
1056         $userfields = explode(',', user_picture::fields());
1057         foreach ($userfields as $userfield) {
1058             if ($userfield == 'id') {
1059                 // Aliased in SQL above.
1060                 $activity->user->{$userfield} = $submission->userid;
1061             } else {
1062                 $activity->user->{$userfield} = $submission->{$userfield};
1063             }
1064         }
1065         $activity->user->fullname = fullname($submission, $viewfullnames);
1067         $activities[$index++] = $activity;
1068     }
1070     return;
1073 /**
1074  * Print recent activity from all assignments in a given course
1075  *
1076  * This is used by course/recent.php
1077  * @param stdClass $activity
1078  * @param int $courseid
1079  * @param bool $detail
1080  * @param array $modnames
1081  */
1082 function assign_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
1083     global $CFG, $OUTPUT;
1085     echo '<table border="0" cellpadding="3" cellspacing="0" class="assignment-recent">';
1087     echo '<tr><td class="userpicture" valign="top">';
1088     echo $OUTPUT->user_picture($activity->user);
1089     echo '</td><td>';
1091     if ($detail) {
1092         $modname = $modnames[$activity->type];
1093         echo '<div class="title">';
1094         echo '<img src="' . $OUTPUT->pix_url('icon', 'assign') . '" '.
1095              'class="icon" alt="' . $modname . '">';
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;