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