MDL-37619 Assign: Fix failed unit tests for mod/assign/lib.php
[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  * It delegates most functions to the assignment class.
20  *
21  * @package   mod_assign
22  * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
25 defined('MOODLE_INTERNAL') || die();
27 /**
28  * Adds an assignment instance
29  *
30  * This is done by calling the add_instance() method of the assignment type class
31  * @param stdClass $data
32  * @param mod_assign_mod_form $form
33  * @return int The instance id of the new assignment
34  */
35 function assign_add_instance(stdClass $data, mod_assign_mod_form $form = null) {
36     global $CFG;
37     require_once($CFG->dirroot . '/mod/assign/locallib.php');
39     $assignment = new assign(context_module::instance($data->coursemodule), null, null);
40     return $assignment->add_instance($data, true);
41 }
43 /**
44  * delete an assignment instance
45  * @param int $id
46  * @return bool
47  */
48 function assign_delete_instance($id) {
49     global $CFG;
50     require_once($CFG->dirroot . '/mod/assign/locallib.php');
51     $cm = get_coursemodule_from_instance('assign', $id, 0, false, MUST_EXIST);
52     $context = context_module::instance($cm->id);
54     $assignment = new assign($context, null, null);
55     return $assignment->delete_instance();
56 }
58 /**
59  * This function is used by the reset_course_userdata function in moodlelib.
60  * This function will remove all assignment submissions and feedbacks in the database
61  * and clean up any related data.
62  * @param $data the data submitted from the reset course.
63  * @return array status array
64  */
65 function assign_reset_userdata($data) {
66     global $CFG, $DB;
67     require_once($CFG->dirroot . '/mod/assign/locallib.php');
69     $status = array();
70     $params = array('courseid'=>$data->courseid);
71     $sql = "SELECT a.id FROM {assign} a WHERE a.course=:courseid";
72     $course = $DB->get_record('course', array('id'=>$data->courseid), '*', MUST_EXIST);
73     if ($assigns = $DB->get_records_sql($sql, $params)) {
74         foreach ($assigns as $assign) {
75             $cm = get_coursemodule_from_instance('assign',
76                                                  $assign->id,
77                                                  $data->courseid,
78                                                  false,
79                                                  MUST_EXIST);
80             $context = context_module::instance($cm->id);
81             $assignment = new assign($context, $cm, $course);
82             $status = array_merge($status, $assignment->reset_userdata($data));
83         }
84     }
85     return $status;
86 }
88 /**
89  * Removes all grades from gradebook
90  *
91  * @param int $courseid The ID of the course to reset
92  * @param string $type Optional type of assignment to limit the reset to a particular assignment type
93  */
94 function assign_reset_gradebook($courseid, $type='') {
95     global $CFG, $DB;
97     $params = array('moduletype'=>'assign', 'courseid'=>$courseid);
98     $sql = 'SELECT a.*, cm.idnumber as cmidnumber, a.course as courseid
99             FROM {assign} a, {course_modules} cm, {modules} m
100             WHERE m.name=:moduletype AND m.id=cm.module AND cm.instance=a.id AND a.course=:courseid';
102     if ($assignments = $DB->get_records_sql($sql, $params)) {
103         foreach ($assignments as $assignment) {
104             assign_grade_item_update($assignment, 'reset');
105         }
106     }
109 /**
110  * Implementation of the function for printing the form elements that control
111  * whether the course reset functionality affects the assignment.
112  * @param $mform form passed by reference
113  */
114 function assign_reset_course_form_definition(&$mform) {
115     $mform->addElement('header', 'assignheader', get_string('modulenameplural', 'assign'));
116     $name = get_string('deleteallsubmissions', 'assign');
117     $mform->addElement('advcheckbox', 'reset_assign_submissions', $name);
120 /**
121  * Course reset form defaults.
122  * @param  object $course
123  * @return array
124  */
125 function assign_reset_course_form_defaults($course) {
126     return array('reset_assign_submissions'=>1);
129 /**
130  * Update an assignment instance
131  *
132  * This is done by calling the update_instance() method of the assignment type class
133  * @param stdClass $data
134  * @param mod_assign_mod_form $form
135  * @return object
136  */
137 function assign_update_instance(stdClass $data, mod_assign_mod_form $form) {
138     global $CFG;
139     require_once($CFG->dirroot . '/mod/assign/locallib.php');
140     $context = context_module::instance($data->coursemodule);
141     $assignment = new assign($context, null, null);
142     return $assignment->update_instance($data);
145 /**
146  * Return the list if Moodle features this module supports
147  *
148  * @param string $feature FEATURE_xx constant for requested feature
149  * @return mixed True if module supports feature, null if doesn't know
150  */
151 function assign_supports($feature) {
152     switch($feature) {
153         case FEATURE_GROUPS:                  return true;
154         case FEATURE_GROUPINGS:               return true;
155         case FEATURE_GROUPMEMBERSONLY:        return true;
156         case FEATURE_MOD_INTRO:               return true;
157         case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
158         case FEATURE_COMPLETION_HAS_RULES:    return true;
159         case FEATURE_GRADE_HAS_GRADE:         return true;
160         case FEATURE_GRADE_OUTCOMES:          return true;
161         case FEATURE_BACKUP_MOODLE2:          return true;
162         case FEATURE_SHOW_DESCRIPTION:        return true;
163         case FEATURE_ADVANCED_GRADING:        return true;
164         case FEATURE_PLAGIARISM:              return true;
166         default: return null;
167     }
170 /**
171  * Lists all gradable areas for the advanced grading methods gramework
172  *
173  * @return array('string'=>'string') An array with area names as keys and descriptions as values
174  */
175 function assign_grading_areas_list() {
176     return array('submissions'=>get_string('submissions', 'assign'));
180 /**
181  * extend an assigment navigation settings
182  *
183  * @param settings_navigation $settings
184  * @param navigation_node $navref
185  * @return void
186  */
187 function assign_extend_settings_navigation(settings_navigation $settings, navigation_node $navref) {
188     global $PAGE, $DB;
190     $cm = $PAGE->cm;
191     if (!$cm) {
192         return;
193     }
195     $context = $cm->context;
196     $course = $PAGE->course;
198     if (!$course) {
199         return;
200     }
202     // Link to gradebook.
203     if (has_capability('gradereport/grader:view', $cm->context) &&
204             has_capability('moodle/grade:viewall', $cm->context)) {
205         $link = new moodle_url('/grade/report/grader/index.php', array('id' => $course->id));
206         $linkname = get_string('viewgradebook', 'assign');
207         $node = $navref->add($linkname, $link, navigation_node::TYPE_SETTING);
208     }
210     // Link to download all submissions.
211     if (has_capability('mod/assign:grade', $context)) {
212         $link = new moodle_url('/mod/assign/view.php', array('id' => $cm->id, 'action'=>'grading'));
213         $node = $navref->add(get_string('viewgrading', 'assign'), $link, navigation_node::TYPE_SETTING);
215         $link = new moodle_url('/mod/assign/view.php', array('id' => $cm->id, 'action'=>'downloadall'));
216         $node = $navref->add(get_string('downloadall', 'assign'), $link, navigation_node::TYPE_SETTING);
217     }
219     if (has_capability('mod/assign:revealidentities', $context)) {
220         $dbparams = array('id'=>$cm->instance);
221         $assignment = $DB->get_record('assign', $dbparams, 'blindmarking, revealidentities');
223         if ($assignment && $assignment->blindmarking && !$assignment->revealidentities) {
224             $urlparams = array('id' => $cm->id, 'action'=>'revealidentities');
225             $url = new moodle_url('/mod/assign/view.php', $urlparams);
226             $linkname = get_string('revealidentities', 'assign');
227             $node = $navref->add($linkname, $url, navigation_node::TYPE_SETTING);
228         }
229     }
232 /**
233  * Add a get_coursemodule_info function in case any assignment type wants to add 'extra' information
234  * for the course (see resource).
235  *
236  * Given a course_module object, this function returns any "extra" information that may be needed
237  * when printing this activity in a course listing.  See get_array_of_activities() in course/lib.php.
238  *
239  * @param stdClass $coursemodule The coursemodule object (record).
240  * @return cached_cm_info An object on information that the courses
241  *                        will know about (most noticeably, an icon).
242  */
243 function assign_get_coursemodule_info($coursemodule) {
244     global $CFG, $DB;
246     $dbparams = array('id'=>$coursemodule->instance);
247     $fields = 'id, name, alwaysshowdescription, allowsubmissionsfromdate, intro, introformat';
248     if (! $assignment = $DB->get_record('assign', $dbparams, $fields)) {
249         return false;
250     }
252     $result = new cached_cm_info();
253     $result->name = $assignment->name;
254     if ($coursemodule->showdescription) {
255         if ($assignment->alwaysshowdescription || time() > $assignment->allowsubmissionsfromdate) {
256             // Convert intro to html. Do not filter cached version, filters run at display time.
257             $result->content = format_module_intro('assign', $assignment, $coursemodule->id, false);
258         }
259     }
260     return $result;
263 /**
264  * Return a list of page types
265  * @param string $pagetype current page type
266  * @param stdClass $parentcontext Block's parent context
267  * @param stdClass $currentcontext Current context of block
268  */
269 function assign_page_type_list($pagetype, $parentcontext, $currentcontext) {
270     $module_pagetype = array(
271         'mod-assign-*' => get_string('page-mod-assign-x', 'assign'),
272         'mod-assign-view' => get_string('page-mod-assign-view', 'assign'),
273     );
274     return $module_pagetype;
277 /**
278  * Print an overview of all assignments
279  * for the courses.
280  *
281  * @param mixed $courses The list of courses to print the overview for
282  * @param array $htmlarray The array of html to return
283  */
284 function assign_print_overview($courses, &$htmlarray) {
285     global $USER, $CFG, $DB;
287     if (empty($courses) || !is_array($courses) || count($courses) == 0) {
288         return array();
289     }
291     if (!$assignments = get_all_instances_in_courses('assign', $courses)) {
292         return;
293     }
295     $assignmentids = array();
297     // Do assignment_base::isopen() here without loading the whole thing for speed.
298     foreach ($assignments as $key => $assignment) {
299         $time = time();
300         $isopen = false;
301         if ($assignment->duedate) {
302             $duedate = false;
303             if ($assignment->cutoffdate) {
304                 $duedate = $assignment->cutoffdate;
305             }
306             if ($duedate) {
307                 $isopen = ($assignment->allowsubmissionsfromdate <= $time && $time <= $duedate);
308             } else {
309                 $isopen = ($assignment->allowsubmissionsfromdate <= $time);
310             }
311         }
312         if ($isopen) {
313             $assignmentids[] = $assignment->id;
314         }
315     }
317     if (empty($assignmentids)) {
318         // No assignments to look at - we're done.
319         return true;
320     }
322     $strduedate = get_string('duedate', 'assign');
323     $strcutoffdate = get_string('nosubmissionsacceptedafter', 'assign');
324     $strnolatesubmissions = get_string('nolatesubmissions', 'assign');
325     $strduedateno = get_string('duedateno', 'assign');
326     $strduedateno = get_string('duedateno', 'assign');
327     $strgraded = get_string('graded', 'assign');
328     $strnotgradedyet = get_string('notgradedyet', 'assign');
329     $strnotsubmittedyet = get_string('notsubmittedyet', 'assign');
330     $strsubmitted = get_string('submitted', 'assign');
331     $strassignment = get_string('modulename', 'assign');
332     $strreviewed = get_string('reviewed', 'assign');
334     // We do all possible database work here *outside* of the loop to ensure this scales.
335     list($sqlassignmentids, $assignmentidparams) = $DB->get_in_or_equal($assignmentids);
337     // Build up and array of unmarked submissions indexed by assignment id/ userid
338     // for use where the user has grading rights on assignment.
339     $rs = $DB->get_recordset_sql('SELECT
340                                       s.assignment as assignment,
341                                       s.userid as userid,
342                                       s.id as id,
343                                       s.status as status,
344                                       g.timemodified as timegraded
345                                   FROM {assign_submission} s
346                                   LEFT JOIN {assign_grades} g ON
347                                       s.userid = g.userid AND
348                                       s.assignment = g.assignment
349                                   WHERE
350                                       g.timemodified = 0 OR
351                                       s.timemodified > g.timemodified AND
352                                       s.assignment ' . $sqlassignmentids, $assignmentidparams);
354     $unmarkedsubmissions = array();
355     foreach ($rs as $rd) {
356         $unmarkedsubmissions[$rd->assignment][$rd->userid] = $rd->id;
357     }
358     $rs->close();
360     // Get all user submissions, indexed by assignment id.
361     $dbparams = array_merge(array($USER->id, $USER->id), $assignmentidparams);
362     $mysubmissions = $DB->get_records_sql('SELECT
363                                                a.id AS assignment,
364                                                a.nosubmissions AS nosubmissions,
365                                                g.timemodified AS timemarked,
366                                                g.grader AS grader,
367                                                g.grade AS grade,
368                                                s.status AS status
369                                            FROM {assign} a
370                                            LEFT JOIN {assign_grades} g ON
371                                                g.assignment = a.id AND
372                                                g.userid = ?
373                                            LEFT JOIN {assign_submission} s ON
374                                                s.assignment = a.id AND
375                                                s.userid = ?
376                                            WHERE a.id ' . $sqlassignmentids, $dbparams);
378     foreach ($assignments as $assignment) {
379         // Do not show assignments that are not open.
380         if (!in_array($assignment->id, $assignmentids)) {
381             continue;
382         }
383         $dimmedclass = '';
384         if (!$assignment->visible) {
385             $dimmedclass = ' class="dimmed"';
386         }
387         $href = $CFG->wwwroot . '/mod/assign/view.php?id=' . $assignment->coursemodule;
388         $str = '<div class="assign overview">' .
389                '<div class="name">' .
390                $strassignment . ': '.
391                '<a ' . $dimmedclass .
392                    'title="' . $strassignment . '" ' .
393                    'href="' . $href . '">' .
394                format_string($assignment->name) .
395                '</a></div>';
396         if ($assignment->duedate) {
397             $userdate = userdate($assignment->duedate);
398             $str .= '<div class="info">' . $strduedate . ': ' . $userdate . '</div>';
399         } else {
400             $str .= '<div class="info">' . $strduedateno . '</div>';
401         }
402         if ($assignment->cutoffdate) {
403             if ($assignment->cutoffdate == $assignment->duedate) {
404                 $str .= '<div class="info">' . $strnolatesubmissions . '</div>';
405             } else {
406                 $userdate = userdate($assignment->cutoffdate);
407                 $str .= '<div class="info">' . $strcutoffdate . ': ' . $userdate . '</div>';
408             }
409         }
410         $context = context_module::instance($assignment->coursemodule);
411         if (has_capability('mod/assign:grade', $context)) {
412             // Count how many people can submit.
413             $submissions = 0;
414             if ($students = get_enrolled_users($context, 'mod/assign:view', 0, 'u.id')) {
415                 foreach ($students as $student) {
416                     if (isset($unmarkedsubmissions[$assignment->id][$student->id])) {
417                         $submissions++;
418                     }
419                 }
420             }
422             if ($submissions) {
423                 $urlparams = array('id'=>$assignment->coursemodule, 'action'=>'grading');
424                 $url = new moodle_url('/mod/assign/view.php', $urlparams);
425                 $str .= '<div class="details">' .
426                         '<a href="' . $url . '">' .
427                         get_string('submissionsnotgraded', 'assign', $submissions) .
428                         '</a></div>';
429             }
430         }
431         if (has_capability('mod/assign:submit', $context)) {
432             $str .= '<div class="details">';
433             $str .= get_string('mysubmission', 'assign');
434             $submission = $mysubmissions[$assignment->id];
435             if ($submission->nosubmissions) {
436                 $str .= get_string('offline', 'assign');
437             } else if (!$submission->status || $submission->status == 'draft') {
438                 $str .= $strnotsubmittedyet;
439             } else {
440                 $str .= get_string('submissionstatus_' . $submission->status, 'assign');
441             }
442             if (!$submission->grade || $submission->grade < 0) {
443                 $str .= ', ' . get_string('notgraded', 'assign');
444             } else {
445                 $str .= ', ' . get_string('graded', 'assign');
446             }
447             $str .= '</div>';
448         }
449         $str .= '</div>';
450         if (empty($htmlarray[$assignment->course]['assign'])) {
451             $htmlarray[$assignment->course]['assign'] = $str;
452         } else {
453             $htmlarray[$assignment->course]['assign'] .= $str;
454         }
455     }
458 /**
459  * Print recent activity from all assignments in a given course
460  *
461  * This is used by the recent activity block
462  * @param mixed $course the course to print activity for
463  * @param bool $viewfullnames boolean to determine whether to show full names or not
464  * @param int $timestart the time the rendering started
465  */
466 function assign_print_recent_activity($course, $viewfullnames, $timestart) {
467     global $CFG, $USER, $DB, $OUTPUT;
469     // Do not use log table if possible, it may be huge.
471     $dbparams = array($timestart, $course->id, 'assign');
472     if (!$submissions = $DB->get_records_sql('SELECT asb.id, asb.timemodified, cm.id AS cmid, asb.userid,
473                                                      u.firstname, u.lastname, u.email, u.picture
474                                                 FROM {assign_submission} asb
475                                                      JOIN {assign} a      ON a.id = asb.assignment
476                                                      JOIN {course_modules} cm ON cm.instance = a.id
477                                                      JOIN {modules} md        ON md.id = cm.module
478                                                      JOIN {user} u            ON u.id = asb.userid
479                                                WHERE asb.timemodified > ? AND
480                                                      a.course = ? AND
481                                                      md.name = ?
482                                             ORDER BY asb.timemodified ASC', $dbparams)) {
483          return false;
484     }
486     $modinfo = get_fast_modinfo($course);
487     $show    = array();
488     $grader  = array();
490     $showrecentsubmissions = get_config('mod_assign', 'showrecentsubmissions');
492     foreach ($submissions as $submission) {
493         if (!array_key_exists($submission->cmid, $modinfo->get_cms())) {
494             continue;
495         }
496         $cm = $modinfo->get_cm($submission->cmid);
497         if (!$cm->uservisible) {
498             continue;
499         }
500         if ($submission->userid == $USER->id) {
501             $show[] = $submission;
502             continue;
503         }
505         $context = context_module::instance($submission->cmid);
506         // The act of submitting of assignment may be considered private -
507         // only graders will see it if specified.
508         if (empty($showrecentsubmissions)) {
509             if (!array_key_exists($cm->id, $grader)) {
510                 $grader[$cm->id] = has_capability('moodle/grade:viewall', $context);
511             }
512             if (!$grader[$cm->id]) {
513                 continue;
514             }
515         }
517         $groupmode = groups_get_activity_groupmode($cm, $course);
519         if ($groupmode == SEPARATEGROUPS &&
520                 !has_capability('moodle/site:accessallgroups',  $context)) {
521             if (isguestuser()) {
522                 // Shortcut - guest user does not belong into any group.
523                 continue;
524             }
526             if (is_null($modinfo->get_groups())) {
527                 // Load all my groups and cache it in modinfo.
528                 $modinfo->groups = groups_get_user_groups($course->id);
529             }
531             // This will be slow - show only users that share group with me in this cm.
532             if (empty($modinfo->groups[$cm->id])) {
533                 continue;
534             }
535             $usersgroups =  groups_get_all_groups($course->id, $submission->userid, $cm->groupingid);
536             if (is_array($usersgroups)) {
537                 $usersgroups = array_keys($usersgroups);
538                 $intersect = array_intersect($usersgroups, $modinfo->groups[$cm->id]);
539                 if (empty($intersect)) {
540                     continue;
541                 }
542             }
543         }
544         $show[] = $submission;
545     }
547     if (empty($show)) {
548         return false;
549     }
551     echo $OUTPUT->heading(get_string('newsubmissions', 'assign').':', 3);
553     foreach ($show as $submission) {
554         $cm = $modinfo->get_cm($submission->cmid);
555         $link = $CFG->wwwroot.'/mod/assign/view.php?id='.$cm->id;
556         print_recent_activity_note($submission->timemodified,
557                                    $submission,
558                                    $cm->name,
559                                    $link,
560                                    false,
561                                    $viewfullnames);
562     }
564     return true;
567 /**
568  * Returns all assignments since a given time.
569  *
570  * @param array $activities The activity information is returned in this array
571  * @param int $index The current index in the activities array
572  * @param int $timestart The earliest activity to show
573  * @param int $courseid Limit the search to this course
574  * @param int $cmid The course module id
575  * @param int $userid Optional user id
576  * @param int $groupid Optional group id
577  * @return void
578  */
579 function assign_get_recent_mod_activity(&$activities,
580                                         &$index,
581                                         $timestart,
582                                         $courseid,
583                                         $cmid,
584                                         $userid=0,
585                                         $groupid=0) {
586     global $CFG, $COURSE, $USER, $DB;
588     if ($COURSE->id == $courseid) {
589         $course = $COURSE;
590     } else {
591         $course = $DB->get_record('course', array('id'=>$courseid));
592     }
594     $modinfo = get_fast_modinfo($course);
596     $cm = $modinfo->get_cm($cmid);
597     $params = array();
598     if ($userid) {
599         $userselect = 'AND u.id = :userid';
600         $params['userid'] = $userid;
601     } else {
602         $userselect = '';
603     }
605     if ($groupid) {
606         $groupselect = 'AND gm.groupid = :groupid';
607         $groupjoin   = 'JOIN {groups_members} gm ON  gm.userid=u.id';
608         $params['groupid'] = $groupid;
609     } else {
610         $groupselect = '';
611         $groupjoin   = '';
612     }
614     $params['cminstance'] = $cm->instance;
615     $params['timestart'] = $timestart;
617     $userfields = user_picture::fields('u', null, 'userid');
619     if (!$submissions = $DB->get_records_sql('SELECT asb.id, asb.timemodified, ' .
620                                                      $userfields .
621                                              '  FROM {assign_submission} asb
622                                                 JOIN {assign} a ON a.id = asb.assignment
623                                                 JOIN {user} u ON u.id = asb.userid ' .
624                                           $groupjoin .
625                                             '  WHERE asb.timemodified > :timestart AND
626                                                      a.id = :cminstance
627                                                      ' . $userselect . ' ' . $groupselect .
628                                             ' ORDER BY asb.timemodified ASC', $params)) {
629          return;
630     }
632     $groupmode       = groups_get_activity_groupmode($cm, $course);
633     $cm_context      = context_module::instance($cm->id);
634     $grader          = has_capability('moodle/grade:viewall', $cm_context);
635     $accessallgroups = has_capability('moodle/site:accessallgroups', $cm_context);
636     $viewfullnames   = has_capability('moodle/site:viewfullnames', $cm_context);
638     if (is_null($modinfo->get_groups())) {
639         // Load all my groups and cache it in modinfo.
640         $modinfo->groups = groups_get_user_groups($course->id);
641     }
643     $showrecentsubmissions = get_config('mod_assign', 'showrecentsubmissions');
644     $show = array();
645     $usersgroups = groups_get_all_groups($course->id, $USER->id, $cm->groupingid);
646     if (is_array($usersgroups)) {
647         $usersgroups = array_keys($usersgroups);
648     }
649     foreach ($submissions as $submission) {
650         if ($submission->userid == $USER->id) {
651             $show[] = $submission;
652             continue;
653         }
654         // The act of submitting of assignment may be considered private -
655         // only graders will see it if specified.
656         if (empty($showrecentsubmissions)) {
657             if (!$grader) {
658                 continue;
659             }
660         }
662         if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
663             if (isguestuser()) {
664                 // Shortcut - guest user does not belong into any group.
665                 continue;
666             }
668             // This will be slow - show only users that share group with me in this cm.
669             if (empty($modinfo->groups[$cm->id])) {
670                 continue;
671             }
672             if (is_array($usersgroups)) {
673                 $intersect = array_intersect($usersgroups, $modinfo->groups[$cm->id]);
674                 if (empty($intersect)) {
675                     continue;
676                 }
677             }
678         }
679         $show[] = $submission;
680     }
682     if (empty($show)) {
683         return;
684     }
686     if ($grader) {
687         require_once($CFG->libdir.'/gradelib.php');
688         $userids = array();
689         foreach ($show as $id => $submission) {
690             $userids[] = $submission->userid;
691         }
692         $grades = grade_get_grades($courseid, 'mod', 'assign', $cm->instance, $userids);
693     }
695     $aname = format_string($cm->name, true);
696     foreach ($show as $submission) {
697         $activity = new stdClass();
699         $activity->type         = 'assign';
700         $activity->cmid         = $cm->id;
701         $activity->name         = $aname;
702         $activity->sectionnum   = $cm->sectionnum;
703         $activity->timestamp    = $submission->timemodified;
704         $activity->user         = new stdClass();
705         if ($grader) {
706             $activity->grade = $grades->items[0]->grades[$submission->userid]->str_long_grade;
707         }
709         $userfields = explode(',', user_picture::fields());
710         foreach ($userfields as $userfield) {
711             if ($userfield == 'id') {
712                 // Aliased in SQL above.
713                 $activity->user->{$userfield} = $submission->userid;
714             } else {
715                 $activity->user->{$userfield} = $submission->{$userfield};
716             }
717         }
718         $activity->user->fullname = fullname($submission, $viewfullnames);
720         $activities[$index++] = $activity;
721     }
723     return;
726 /**
727  * Print recent activity from all assignments in a given course
728  *
729  * This is used by course/recent.php
730  * @param stdClass $activity
731  * @param int $courseid
732  * @param bool $detail
733  * @param array $modnames
734  */
735 function assign_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
736     global $CFG, $OUTPUT;
738     echo '<table border="0" cellpadding="3" cellspacing="0" class="assignment-recent">';
740     echo '<tr><td class="userpicture" valign="top">';
741     echo $OUTPUT->user_picture($activity->user);
742     echo '</td><td>';
744     if ($detail) {
745         $modname = $modnames[$activity->type];
746         echo '<div class="title">';
747         echo '<img src="' . $OUTPUT->pix_url('icon', 'assign') . '" '.
748              'class="icon" alt="' . $modname . '">';
749         echo '<a href="' . $CFG->wwwroot . '/mod/assign/view.php?id=' . $activity->cmid . '">';
750         echo $activity->name;
751         echo '</a>';
752         echo '</div>';
753     }
755     if (isset($activity->grade)) {
756         echo '<div class="grade">';
757         echo get_string('grade').': ';
758         echo $activity->grade;
759         echo '</div>';
760     }
762     echo '<div class="user">';
763     echo "<a href=\"$CFG->wwwroot/user/view.php?id={$activity->user->id}&amp;course=$courseid\">";
764     echo "{$activity->user->fullname}</a>  - " . userdate($activity->timestamp);
765     echo '</div>';
767     echo '</td></tr></table>';
770 /**
771  * Checks if a scale is being used by an assignment.
772  *
773  * This is used by the backup code to decide whether to back up a scale
774  * @param int $assignmentid
775  * @param int $scaleid
776  * @return boolean True if the scale is used by the assignment
777  */
778 function assign_scale_used($assignmentid, $scaleid) {
779     global $DB;
781     $return = false;
782     $rec = $DB->get_record('assign', array('id'=>$assignmentid, 'grade'=>-$scaleid));
784     if (!empty($rec) && !empty($scaleid)) {
785         $return = true;
786     }
788     return $return;
791 /**
792  * Checks if scale is being used by any instance of assignment
793  *
794  * This is used to find out if scale used anywhere
795  * @param int $scaleid
796  * @return boolean True if the scale is used by any assignment
797  */
798 function assign_scale_used_anywhere($scaleid) {
799     global $DB;
801     if ($scaleid and $DB->record_exists('assign', array('grade'=>-$scaleid))) {
802         return true;
803     } else {
804         return false;
805     }
808 /**
809  * List the actions that correspond to a view of this module.
810  * This is used by the participation report.
811  * @return array
812  */
813 function assign_get_view_actions() {
814     return array('view submission', 'view feedback');
817 /**
818  * List the actions that correspond to a post of this module.
819  * This is used by the participation report.
820  * @return array
821  */
822 function assign_get_post_actions() {
823     return array('upload', 'submit', 'submit for grading');
826 /**
827  * Call cron on the assign module.
828  */
829 function assign_cron() {
830     global $CFG;
832     require_once($CFG->dirroot . '/mod/assign/locallib.php');
833     assign::cron();
835     $plugins = get_plugin_list('assignsubmission');
837     foreach ($plugins as $name => $plugin) {
838         $disabled = get_config('assignsubmission_' . $name, 'disabled');
839         if (!$disabled) {
840             $class = 'assign_submission_' . $name;
841             require_once($CFG->dirroot . '/mod/assign/submission/' . $name . '/locallib.php');
842             $class::cron();
843         }
844     }
845     $plugins = get_plugin_list('assignfeedback');
847     foreach ($plugins as $name => $plugin) {
848         $disabled = get_config('assignfeedback_' . $name, 'disabled');
849         if (!$disabled) {
850             $class = 'assign_feedback_' . $name;
851             require_once($CFG->dirroot . '/mod/assign/feedback/' . $name . '/locallib.php');
852             $class::cron();
853         }
854     }
857 /**
858  * Returns all other capabilities used by this module.
859  * @return array Array of capability strings
860  */
861 function assign_get_extra_capabilities() {
862     return array('gradereport/grader:view',
863                  'moodle/grade:viewall',
864                  'moodle/site:viewfullnames',
865                  'moodle/site:config');
868 /**
869  * Create grade item for given assignment.
870  *
871  * @param stdClass $assign record with extra cmidnumber
872  * @param array $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
873  * @return int 0 if ok, error code otherwise
874  */
875 function assign_grade_item_update($assign, $grades=null) {
876     global $CFG;
877     require_once($CFG->libdir.'/gradelib.php');
879     if (!isset($assign->courseid)) {
880         $assign->courseid = $assign->course;
881     }
883     $params = array('itemname'=>$assign->name, 'idnumber'=>$assign->cmidnumber);
885     if ($assign->grade > 0) {
886         $params['gradetype'] = GRADE_TYPE_VALUE;
887         $params['grademax']  = $assign->grade;
888         $params['grademin']  = 0;
890     } else if ($assign->grade < 0) {
891         $params['gradetype'] = GRADE_TYPE_SCALE;
892         $params['scaleid']   = -$assign->grade;
894     } else {
895         // Allow text comments only.
896         $params['gradetype'] = GRADE_TYPE_TEXT;
897     }
899     if ($grades  === 'reset') {
900         $params['reset'] = true;
901         $grades = null;
902     }
904     return grade_update('mod/assign',
905                         $assign->courseid,
906                         'mod',
907                         'assign',
908                         $assign->id,
909                         0,
910                         $grades,
911                         $params);
914 /**
915  * Return grade for given user or all users.
916  *
917  * @param stdClass $assign record of assign with an additional cmidnumber
918  * @param int $userid optional user id, 0 means all users
919  * @return array array of grades, false if none
920  */
921 function assign_get_user_grades($assign, $userid=0) {
922     global $CFG;
924     require_once($CFG->dirroot . '/mod/assign/locallib.php');
926     $cm = get_coursemodule_from_instance('assign', $assign->id, 0, false, MUST_EXIST);
927     $context = context_module::instance($cm->id);
928     $assignment = new assign($context, null, null);
929     $assignment->set_instance($assign);
930     return $assignment->get_user_grades_for_gradebook($userid);
933 /**
934  * Update activity grades.
935  *
936  * @param stdClass $assign database record
937  * @param int $userid specific user only, 0 means all
938  * @param bool $nullifnone - not used
939  */
940 function assign_update_grades($assign, $userid=0, $nullifnone=true) {
941     global $CFG;
942     require_once($CFG->libdir.'/gradelib.php');
944     if ($assign->grade == 0) {
945         assign_grade_item_update($assign);
947     } else if ($grades = assign_get_user_grades($assign, $userid)) {
948         foreach ($grades as $k => $v) {
949             if ($v->rawgrade == -1) {
950                 $grades[$k]->rawgrade = null;
951             }
952         }
953         assign_grade_item_update($assign, $grades);
955     } else {
956         assign_grade_item_update($assign);
957     }
960 /**
961  * List the file areas that can be browsed.
962  *
963  * @param stdClass $course
964  * @param stdClass $cm
965  * @param stdClass $context
966  * @return array
967  */
968 function assign_get_file_areas($course, $cm, $context) {
969     global $CFG;
970     require_once($CFG->dirroot . '/mod/assign/locallib.php');
971     $areas = array();
973     $assignment = new assign($context, $cm, $course);
974     foreach ($assignment->get_submission_plugins() as $plugin) {
975         if ($plugin->is_visible()) {
976             $pluginareas = $plugin->get_file_areas();
978             if ($pluginareas) {
979                 $areas = array_merge($areas, $pluginareas);
980             }
981         }
982     }
983     foreach ($assignment->get_feedback_plugins() as $plugin) {
984         if ($plugin->is_visible()) {
985             $pluginareas = $plugin->get_file_areas();
987             if ($pluginareas) {
988                 $areas = array_merge($areas, $pluginareas);
989             }
990         }
991     }
993     return $areas;
996 /**
997  * File browsing support for assign module.
998  *
999  * @param file_browser $browser
1000  * @param object $areas
1001  * @param object $course
1002  * @param object $cm
1003  * @param object $context
1004  * @param string $filearea
1005  * @param int $itemid
1006  * @param string $filepath
1007  * @param string $filename
1008  * @return object file_info instance or null if not found
1009  */
1010 function assign_get_file_info($browser,
1011                               $areas,
1012                               $course,
1013                               $cm,
1014                               $context,
1015                               $filearea,
1016                               $itemid,
1017                               $filepath,
1018                               $filename) {
1019     global $CFG;
1020     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1022     if ($context->contextlevel != CONTEXT_MODULE) {
1023         return null;
1024     }
1026     $fs = get_file_storage();
1027     $filepath = is_null($filepath) ? '/' : $filepath;
1028     $filename = is_null($filename) ? '.' : $filename;
1030     // Need to find the plugin this belongs to.
1031     $assignment = new assign($context, $cm, $course);
1032     $pluginowner = null;
1033     foreach ($assignment->get_submission_plugins() as $plugin) {
1034         if ($plugin->is_visible()) {
1035             $pluginareas = $plugin->get_file_areas();
1037             if (array_key_exists($filearea, $pluginareas)) {
1038                 $pluginowner = $plugin;
1039                 break;
1040             }
1041         }
1042     }
1043     if (!$pluginowner) {
1044         foreach ($assignment->get_feedback_plugins() as $plugin) {
1045             if ($plugin->is_visible()) {
1046                 $pluginareas = $plugin->get_file_areas();
1048                 if (array_key_exists($filearea, $pluginareas)) {
1049                     $pluginowner = $plugin;
1050                     break;
1051                 }
1052             }
1053         }
1054     }
1056     if (!$pluginowner) {
1057         return null;
1058     }
1060     $result = $pluginowner->get_file_info($browser, $filearea, $itemid, $filepath, $filename);
1061     return $result;
1064 /**
1065  * Prints the complete info about a user's interaction with an assignment.
1066  *
1067  * @param stdClass $course
1068  * @param stdClass $user
1069  * @param stdClass $coursemodule
1070  * @param stdClass $assign the database assign record
1071  *
1072  * This prints the submission summary and feedback summary for this student.
1073  */
1074 function assign_user_complete($course, $user, $coursemodule, $assign) {
1075     global $CFG;
1076     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1078     $context = context_module::instance($coursemodule->id);
1080     $assignment = new assign($context, $coursemodule, $course);
1082     echo $assignment->view_student_summary($user, false);
1085 /**
1086  * Print the grade information for the assignment for this user.
1087  *
1088  * @param stdClass $course
1089  * @param stdClass $user
1090  * @param stdClass $coursemodule
1091  * @param stdClass $assignment
1092  */
1093 function assign_user_outline($course, $user, $coursemodule, $assignment) {
1094     global $CFG;
1095     require_once($CFG->libdir.'/gradelib.php');
1096     require_once($CFG->dirroot.'/grade/grading/lib.php');
1098     $gradinginfo = grade_get_grades($course->id,
1099                                         'mod',
1100                                         'assign',
1101                                         $assignment->id,
1102                                         $user->id);
1104     $gradingitem = $gradinginfo->items[0];
1105     $gradebookgrade = $gradingitem->grades[$user->id];
1107     if (!$gradebookgrade) {
1108         return null;
1109     }
1110     $result = new stdClass();
1111     $result->info = get_string('outlinegrade', 'assign', $gradebookgrade->grade);
1112     $result->time = $gradebookgrade->dategraded;
1114     return $result;
1117 /**
1118  * Obtains the automatic completion state for this module based on any conditions
1119  * in assign settings.
1120  *
1121  * @param object $course Course
1122  * @param object $cm Course-module
1123  * @param int $userid User ID
1124  * @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
1125  * @return bool True if completed, false if not, $type if conditions not set.
1126  */
1127 function assign_get_completion_state($course, $cm, $userid, $type) {
1128     global $CFG, $DB;
1129     require_once($CFG->dirroot . '/mod/assign/locallib.php');
1131     $assign = new assign(null, $cm, $course);
1133     // If completion option is enabled, evaluate it and return true/false.
1134     if ($assign->get_instance()->completionsubmit) {
1135         $dbparams = array('assignment'=>$assign->get_instance()->id, 'userid'=>$userid);
1136         $submission = $DB->get_record('assign_submission', $dbparams, '*', IGNORE_MISSING);
1137         return $submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1138     } else {
1139         // Completion option is not enabled so just return $type.
1140         return $type;
1141     }