MDL-31270 mod_assign: introducing the assignment module
[moodle.git] / mod / assign / locallib.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 definition for the class assignment
19 *
20 * This class provides all the functionality for the new assign module.
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
27defined('MOODLE_INTERNAL') || die();
28
29/**
30 * Assignment submission statuses
31 */
32define('ASSIGN_SUBMISSION_STATUS_DRAFT', 'draft'); // student thinks it is a draft
33define('ASSIGN_SUBMISSION_STATUS_SUBMITTED', 'submitted'); // student thinks it is finished
34
35/**
36 * Search filters for grading page
37 */
38define('ASSIGN_FILTER_SUBMITTED', 'submitted');
39define('ASSIGN_FILTER_SINGLE_USER', 'singleuser');
40define('ASSIGN_FILTER_REQUIRE_GRADING', 'require_grading');
41
42/**
43 * File areas for assignment portfolio if enabled
44 */
45define('ASSIGN_FILEAREA_PORTFOLIO_FILES', 'portfolio_files');
46
47
48/** Include accesslib.php */
49require_once($CFG->libdir.'/accesslib.php');
50/** Include formslib.php */
51require_once($CFG->libdir.'/formslib.php');
52/** Include repository/lib.php */
53require_once($CFG->dirroot . '/repository/lib.php');
54/** Include local mod_form.php */
55require_once($CFG->dirroot.'/mod/assign/mod_form.php');
56/** Include portfoliolib.php */
57require_once($CFG->libdir . '/portfoliolib.php');
58/** gradelib.php */
59require_once($CFG->libdir.'/gradelib.php');
60/** grading lib.php */
61require_once($CFG->dirroot.'/grade/grading/lib.php');
62/** Include feedbackplugin.php */
63require_once($CFG->dirroot.'/mod/assign/feedbackplugin.php');
64/** Include submissionplugin.php */
65require_once($CFG->dirroot.'/mod/assign/submissionplugin.php');
66/** Include renderable.php */
67require_once($CFG->dirroot.'/mod/assign/renderable.php');
68/** Include gradingtable.php */
69require_once($CFG->dirroot.'/mod/assign/gradingtable.php');
70/** Include eventslib.php */
71require_once($CFG->libdir.'/eventslib.php');
72
73
74/**
75 * Standard base class for mod_assign (assignment types).
76 *
77 * @package mod_assign
78 * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
79 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
80 */
81class assign {
82
83
84 /** @var stdClass the assignment record that contains the global settings for this assign instance */
85 private $instance;
86
87 /** @var context the context of the course module for this assign instance (or just the course if we are
88 creating a new one) */
89 private $context;
90
91 /** @var stdClass the course this assign instance belongs to */
92 private $course;
93
94 /** @var assign_renderer the custom renderer for this module */
95 private $output;
96
97 /** @var stdClass the course module for this assign instance */
98 private $coursemodule;
99
100 /** @var array cache for things like the coursemodule name or the scale menu - only lives for a single
101 request */
102 private $cache;
103
104 /** @var array list of the installed submission plugins */
105 private $submissionplugins;
106
107 /** @var array list of the installed feedback plugins */
108 private $feedbackplugins;
109
110 /** @var string action to be used to return to this page (without repeating any form submissions etc.) */
111 private $returnaction = 'view';
112
113 /** @var array params to be used to return to this page */
114 private $returnparams = array();
115
116 /** @var string modulename prevents excessive calls to get_string */
117 private static $modulename = '';
118
119 /** @var string modulenameplural prevents excessive calls to get_string */
120 private static $modulenameplural = '';
121
122 /**
123 * Constructor for the base assign class
124 *
125 * @param mixed $coursemodulecontext context|null the course module context (or the course context if the coursemodule has not been created yet)
126 * @param mixed $coursemodule the current course module if it was already loaded - otherwise this class will load one from the context as required
127 * @param mixed $course the current course if it was already loaded - otherwise this class will load one from the context as required
128 */
129 public function __construct($coursemodulecontext, $coursemodule, $course) {
130 global $PAGE;
131
132 $this->context = $coursemodulecontext;
133 $this->coursemodule = $coursemodule;
134 $this->course = $course;
135 $this->cache = array(); // temporary cache only lives for a single request - used to reduce db lookups
136
137 $this->submissionplugins = $this->load_plugins('assignsubmission');
138 $this->feedbackplugins = $this->load_plugins('assignfeedback');
139 $this->output = $PAGE->get_renderer('mod_assign');
140 }
141
142 /**
143 * Set the action and parameters that can be used to return to the current page
144 *
145 * @param string $action The action for the current page
146 * @param array $params An array of name value pairs which form the parameters to return to the current page
147 * @return void
148 */
149 public function register_return_link($action, $params) {
150 $this->returnaction = $action;
151 $this->returnparams = $params;
152 }
153
154 /**
155 * Return an action that can be used to get back to the current page
156 * @return string action
157 */
158 public function get_return_action() {
159 return $this->returnaction;
160 }
161
162 /**
163 * Based on the current assignment settings should we display the intro
164 * @return bool showintro
165 */
166 private function show_intro() {
167 if ($this->get_instance()->alwaysshowdescription ||
168 time() > $this->get_instance()->allowsubmissionsfromdate) {
169 return true;
170 }
171 return false;
172 }
173
174 /**
175 * Return a list of parameters that can be used to get back to the current page
176 * @return array params
177 */
178 public function get_return_params() {
179 return $this->returnparams;
180 }
181
182 /**
183 * Set the submitted form data
184 * @param stdClass $data The form data (instance)
185 */
186 public function set_instance(stdClass $data) {
187 $this->instance = $data;
188 }
189
190 /**
191 * Set the context
192 * @param context $context The new context
193 */
194 public function set_context(context $context) {
195 $this->context = $context;
196 }
197
198 /**
199 * Set the course data
200 * @param stdClass $course The course data
201 */
202 public function set_course(stdClass $course) {
203 $this->course = $course;
204 }
205
206 /**
207 * get list of feedback plugins installed
208 * @return array
209 */
210 public function get_feedback_plugins() {
211 return $this->feedbackplugins;
212 }
213
214 /**
215 * get list of submission plugins installed
216 * @return array
217 */
218 public function get_submission_plugins() {
219 return $this->submissionplugins;
220 }
221
222
223 /**
224 * get a specific submission plugin by its type
225 * @param string $subtype assignsubmission | assignfeedback
226 * @param string $type
227 * @return mixed assign_plugin|null
228 */
229 private function get_plugin_by_type($subtype, $type) {
230 $shortsubtype = substr($subtype, strlen('assign'));
231 $name = $shortsubtype . 'plugins';
232 $pluginlist = $this->$name;
233 foreach ($pluginlist as $plugin) {
234 if ($plugin->get_type() == $type) {
235 return $plugin;
236 }
237 }
238 return null;
239 }
240
241 /**
242 * Get a feedback plugin by type
243 * @param string $type - The type of plugin e.g comments
244 * @return mixed assign_feedback_plugin|null
245 */
246 public function get_feedback_plugin_by_type($type) {
247 return $this->get_plugin_by_type('assignfeedback', $type);
248 }
249
250 /**
251 * Get a submission plugin by type
252 * @param string $type - The type of plugin e.g comments
253 * @return mixed assign_submission_plugin|null
254 */
255 public function get_submission_plugin_by_type($type) {
256 return $this->get_plugin_by_type('assignsubmission', $type);
257 }
258
259 /**
260 * Load the plugins from the sub folders under subtype
261 * @param string $subtype - either submission or feedback
262 * @return array - The sorted list of plugins
263 */
264 private function load_plugins($subtype) {
265 global $CFG;
266 $result = array();
267
268 $names = get_plugin_list($subtype);
269
270 foreach ($names as $name => $path) {
271 if (file_exists($path . '/locallib.php')) {
272 require_once($path . '/locallib.php');
273
274 $shortsubtype = substr($subtype, strlen('assign'));
275 $pluginclass = 'assign_' . $shortsubtype . '_' . $name;
276
277 $plugin = new $pluginclass($this, $name);
278
279 if ($plugin instanceof assign_plugin) {
280 $idx = $plugin->get_sort_order();
281 while (array_key_exists($idx, $result)) $idx +=1;
282 $result[$idx] = $plugin;
283 }
284 }
285 }
286 ksort($result);
287 return $result;
288 }
289
290
291 /**
292 * Display the assignment, used by view.php
293 *
294 * The assignment is displayed differently depending on your role,
295 * the settings for the assignment and the status of the assignment.
296 * @param string $action The current action if any.
297 * @return void
298 */
299 public function view($action='') {
300
301 $o = '';
302 $mform = null;
303
304 // handle form submissions first
305 if ($action == 'savesubmission') {
306 $action = 'editsubmission';
307 if ($this->process_save_submission($mform)) {
308 $action = 'view';
309 }
310 } else if ($action == 'lock') {
311 $this->process_lock();
312 $action = 'grading';
313 } else if ($action == 'reverttodraft') {
314 $this->process_revert_to_draft();
315 $action = 'grading';
316 } else if ($action == 'unlock') {
317 $this->process_unlock();
318 $action = 'grading';
319 } else if ($action == 'confirmsubmit') {
320 $this->process_submit_for_grading();
321 // save and show next button
322 } else if ($action == 'batchgradingoperation') {
323 $this->process_batch_grading_operation();
324 $action = 'grading';
325 } else if ($action == 'submitgrade') {
326 if (optional_param('saveandshownext', null, PARAM_ALPHA)) {
327 //save and show next
328 $action = 'grade';
329 if ($this->process_save_grade($mform)) {
330 $action = 'nextgrade';
331 }
332 } else if (optional_param('nosaveandprevious', null, PARAM_ALPHA)) {
333 $action = 'previousgrade';
334 } else if (optional_param('nosaveandnext', null, PARAM_ALPHA)) {
335 //show next button
336 $action = 'nextgrade';
337 } else if (optional_param('savegrade', null, PARAM_ALPHA)) {
338 //save changes button
339 $action = 'grade';
340 if ($this->process_save_grade($mform)) {
341 $action = 'grading';
342 }
343 } else {
344 //cancel button
345 $action = 'grading';
346 }
347 }else if ($action == 'saveoptions') {
348 $this->process_save_grading_options();
349 $action = 'grading';
350 }
351
352 $returnparams = array('rownum'=>optional_param('rownum', 0, PARAM_INT));
353 $this->register_return_link($action, $returnparams);
354
355 // now show the right view page
356 if ($action == 'previousgrade') {
357 $mform = null;
358 $o .= $this->view_single_grade_page($mform, -1);
359 } else if ($action == 'nextgrade') {
360 $mform = null;
361 $o .= $this->view_single_grade_page($mform, 1);
362 } else if ($action == 'redirect') {
363 redirect(required_param('url', PARAM_TEXT));
364 } else if ($action == 'grade') {
365 $o .= $this->view_single_grade_page($mform);
366 } else if ($action == 'viewpluginassignfeedback') {
367 $o .= $this->view_plugin_content('assignfeedback');
368 } else if ($action == 'viewpluginassignsubmission') {
369 $o .= $this->view_plugin_content('assignsubmission');
370 } else if ($action == 'editsubmission') {
371 $o .= $this->view_edit_submission_page($mform);
372 } else if ($action == 'grading') {
373 $o .= $this->view_grading_page();
374 } else if ($action == 'downloadall') {
375 $o .= $this->download_submissions();
376 } else if ($action == 'submit') {
377 $o .= $this->check_submit_for_grading();
378 } else {
379 $o .= $this->view_submission_page();
380 }
381
382 return $o;
383 }
384
385
386 /**
387 * Add this instance to the database
388 *
389 * @param stdClass $formdata The data submitted from the form
390 * @param bool $callplugins This is used to skip the plugin code
391 * when upgrading an old assignment to a new one (the plugins get called manually)
392 * @return mixed false if an error occurs or the int id of the new instance
393 */
394 public function add_instance(stdClass $formdata, $callplugins) {
395 global $DB;
396
397 $err = '';
398
399 // add the database record
400 $update = new stdClass();
401 $update->name = $formdata->name;
402 $update->timemodified = time();
403 $update->timecreated = time();
404 $update->course = $formdata->course;
405 $update->courseid = $formdata->course;
406 $update->intro = $formdata->intro;
407 $update->introformat = $formdata->introformat;
408 $update->alwaysshowdescription = $formdata->alwaysshowdescription;
409 $update->preventlatesubmissions = $formdata->preventlatesubmissions;
410 $update->submissiondrafts = $formdata->submissiondrafts;
411 $update->sendnotifications = $formdata->sendnotifications;
412 $update->duedate = $formdata->duedate;
413 $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
414 $update->grade = $formdata->grade;
415 $returnid = $DB->insert_record('assign', $update);
416 $this->instance = $DB->get_record('assign', array('id'=>$returnid), '*', MUST_EXIST);
417 // cache the course record
418 $this->course = $DB->get_record('course', array('id'=>$formdata->course), '*', MUST_EXIST);
419
420 if ($callplugins) {
421 // call save_settings hook for submission plugins
422 foreach ($this->submissionplugins as $plugin) {
423 if (!$this->update_plugin_instance($plugin, $formdata)) {
424 print_error($plugin->get_error());
425 return false;
426 }
427 }
428 foreach ($this->feedbackplugins as $plugin) {
429 if (!$this->update_plugin_instance($plugin, $formdata)) {
430 print_error($plugin->get_error());
431 return false;
432 }
433 }
434
435 // in the case of upgrades the coursemodule has not been set so we need to wait before calling these two
436 // TODO: add event to the calendar
437 $this->update_calendar($formdata->coursemodule);
438 // TODO: add the item in the gradebook
439 $this->update_gradebook(false, $formdata->coursemodule);
440
441 }
442
443 $update = new stdClass();
444 $update->id = $this->get_instance()->id;
445 $update->nosubmissions = (!$this->is_any_submission_plugin_enabled()) ? 1: 0;
446 $DB->update_record('assign', $update);
447
448 return $returnid;
449 }
450
451 /**
452 * Delete all grades from the gradebook for this assignment
453 *
454 * @return bool
455 */
456 private function delete_grades() {
457 global $CFG;
458
459 return grade_update('mod/assign', $this->get_course()->id, 'mod', 'assign', $this->get_instance()->id, 0, NULL, array('deleted'=>1)) == GRADE_UPDATE_OK;
460 }
461
462 /**
463 * Delete this instance from the database
464 *
465 * @return bool false if an error occurs
466 */
467 public function delete_instance() {
468 global $DB;
469 $result = true;
470
471 foreach ($this->submissionplugins as $plugin) {
472 if (!$plugin->delete_instance()) {
473 print_error($plugin->get_error());
474 $result = false;
475 }
476 }
477 foreach ($this->feedbackplugins as $plugin) {
478 if (!$plugin->delete_instance()) {
479 print_error($plugin->get_error());
480 $result = false;
481 }
482 }
483
484 // delete files associated with this assignment
485 $fs = get_file_storage();
486 if (! $fs->delete_area_files($this->context->id) ) {
487 $result = false;
488 }
489
490 // delete_records will throw an exception if it fails - so no need for error checking here
491
492 $DB->delete_records('assign_submission', array('assignment'=>$this->get_instance()->id));
493 $DB->delete_records('assign_grades', array('assignment'=>$this->get_instance()->id));
494 $DB->delete_records('assign_plugin_config', array('assignment'=>$this->get_instance()->id));
495
496 // delete items from the gradebook
497 if (! $this->delete_grades()) {
498 $result = false;
499 }
500
501 // delete the instance
502 $DB->delete_records('assign', array('id'=>$this->get_instance()->id));
503
504 return $result;
505 }
506
507 /**
508 * Update the settings for a single plugin
509 *
510 * @param assign_plugin $plugin The plugin to update
511 * @param stdClass $formdata The form data
512 * @return bool false if an error occurs
513 */
514 private function update_plugin_instance(assign_plugin $plugin, stdClass $formdata) {
515 if ($plugin->is_visible()) {
516 $enabledname = $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled';
517 if ($formdata->$enabledname) {
518 $plugin->enable();
519 if (!$plugin->save_settings($formdata)) {
520 print_error($plugin->get_error());
521 return false;
522 }
523 } else {
524 $plugin->disable();
525 }
526 }
527 return true;
528 }
529
530 /**
531 * Update the gradebook information for this assignment
532 *
533 * @param bool $reset If true, will reset all grades in the gradbook for this assignment
534 * @param int $coursemoduleid This is required because it might not exist in the database yet
535 * @return bool
536 */
537 public function update_gradebook($reset, $coursemoduleid) {
538 global $CFG;
539 /** Include lib.php */
540 require_once($CFG->dirroot.'/mod/assign/lib.php');
541 $assign = clone $this->get_instance();
542 $assign->cmidnumber = $coursemoduleid;
543 $param = null;
544 if ($reset) {
545 $param = 'reset';
546 }
547
548 return assign_grade_item_update($assign, $param);
549 }
550
551
552 /**
553 * Update the calendar entries for this assignment
554 *
555 * @param int $coursemoduleid - Required to pass this in because it might not exist in the database yet
556 * @return bool
557 */
558 public function update_calendar($coursemoduleid) {
559 global $DB, $CFG;
560 require_once($CFG->dirroot.'/calendar/lib.php');
561
562 // special case for add_instance as the coursemodule has not been set yet.
563
564 if ($this->get_instance()->duedate) {
565 $event = new stdClass();
566
567 if ($event->id = $DB->get_field('event', 'id', array('modulename'=>'assign', 'instance'=>$this->get_instance()->id))) {
568
569 $event->name = $this->get_instance()->name;
570
571 $event->description = format_module_intro('assign', $this->get_instance(), $coursemoduleid);
572 $event->timestart = $this->get_instance()->duedate;
573
574 $calendarevent = calendar_event::load($event->id);
575 $calendarevent->update($event);
576 } else {
577 $event = new stdClass();
578 $event->name = $this->get_instance()->name;
579 $event->description = format_module_intro('assign', $this->get_instance(), $coursemoduleid);
580 $event->courseid = $this->get_instance()->course;
581 $event->groupid = 0;
582 $event->userid = 0;
583 $event->modulename = 'assign';
584 $event->instance = $this->get_instance()->id;
585 $event->eventtype = 'due';
586 $event->timestart = $this->get_instance()->duedate;
587 $event->timeduration = 0;
588
589 calendar_event::create($event);
590 }
591 } else {
592 $DB->delete_records('event', array('modulename'=>'assign', 'instance'=>$this->get_instance()->id));
593 }
594 }
595
596
597 /**
598 * Update this instance in the database
599 *
600 * @param stdClass $formdata - the data submitted from the form
601 * @return bool false if an error occurs
602 */
603 public function update_instance($formdata) {
604 global $DB;
605
606 $update = new stdClass();
607 $update->id = $formdata->instance;
608 $update->name = $formdata->name;
609 $update->timemodified = time();
610 $update->course = $formdata->course;
611 $update->intro = $formdata->intro;
612 $update->introformat = $formdata->introformat;
613 $update->alwaysshowdescription = $formdata->alwaysshowdescription;
614 $update->preventlatesubmissions = $formdata->preventlatesubmissions;
615 $update->submissiondrafts = $formdata->submissiondrafts;
616 $update->sendnotifications = $formdata->sendnotifications;
617 $update->duedate = $formdata->duedate;
618 $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
619 $update->grade = $formdata->grade;
620
621 $result = $DB->update_record('assign', $update);
622 $this->instance = $DB->get_record('assign', array('id'=>$update->id), '*', MUST_EXIST);
623
624 // load the assignment so the plugins have access to it
625
626 // call save_settings hook for submission plugins
627 foreach ($this->submissionplugins as $plugin) {
628 if (!$this->update_plugin_instance($plugin, $formdata)) {
629 print_error($plugin->get_error());
630 return false;
631 }
632 }
633 foreach ($this->feedbackplugins as $plugin) {
634 if (!$this->update_plugin_instance($plugin, $formdata)) {
635 print_error($plugin->get_error());
636 return false;
637 }
638 }
639
640
641 // update the database record
642
643
644 // update all the calendar events
645 $this->update_calendar($this->get_course_module()->id);
646
647 $this->update_gradebook(false, $this->get_course_module()->id);
648
649 $update = new stdClass();
650 $update->id = $this->get_instance()->id;
651 $update->nosubmissions = (!$this->is_any_submission_plugin_enabled()) ? 1: 0;
652 $DB->update_record('assign', $update);
653
654
655
656
657
658 return $result;
659 }
660
661 /**
662 * add elements in grading plugin form
663 *
664 * @param mixed $grade stdClass|null
665 * @param MoodleQuickForm $mform
666 * @param stdClass $data
667 * @return void
668 */
669 private function add_plugin_grade_elements($grade, MoodleQuickForm $mform, stdClass $data) {
670 foreach ($this->feedbackplugins as $plugin) {
671 if ($plugin->is_enabled() && $plugin->is_visible()) {
672 $mform->addElement('header', 'header_' . $plugin->get_type(), $plugin->get_name());
673 if (!$plugin->get_form_elements($grade, $mform, $data)) {
674 $mform->removeElement('header_' . $plugin->get_type());
675 }
676 }
677 }
678 }
679
680
681
682 /**
683 * Add one plugins settings to edit plugin form
684 *
685 * @param assign_plugin $plugin The plugin to add the settings from
686 * @param MoodleQuickForm $mform The form to add the configuration settings to. This form is modified directly (not returned)
687 * @return void
688 */
689 private function add_plugin_settings(assign_plugin $plugin, MoodleQuickForm $mform) {
690 global $CFG;
691 if ($plugin->is_visible()) {
692 // enabled
693 //tied disableIf rule to this select element
694 $mform->addElement('selectyesno', $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled', $plugin->get_name());
695 $mform->addHelpButton($plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled', 'enabled', $plugin->get_subtype() . '_' . $plugin->get_type());
696
697 $setting = $plugin->get_subtype() . '_' . $plugin->get_type() . '_default';
698
699 $default = $CFG->$setting;
700 if ($plugin->get_config('enabled') !== false) {
701 $default = $plugin->is_enabled();
702 }
703 $mform->setDefault($plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled', $default);
704
705 $plugin->get_settings($mform);
706
707 }
708
709 }
710
711
712 /**
713 * Add settings to edit plugin form
714 *
715 * @param MoodleQuickForm $mform The form to add the configuration settings to. This form is modified directly (not returned)
716 * @return void
717 */
718 public function add_all_plugin_settings(MoodleQuickForm $mform) {
719 $mform->addElement('header', 'general', get_string('submissionsettings', 'assign'));
720
721 foreach ($this->submissionplugins as $plugin) {
722 $this->add_plugin_settings($plugin, $mform);
723
724 }
725 $mform->addElement('header', 'general', get_string('feedbacksettings', 'assign'));
726 foreach ($this->feedbackplugins as $plugin) {
727 $this->add_plugin_settings($plugin, $mform);
728 }
729 }
730
731 /**
732 * Allow each plugin an opportunity to update the defaultvalues
733 * passed in to the settings form (needed to set up draft areas for
734 * editor and filemanager elements)
735 * @param array $defaultvalues
736 */
737 public function plugin_data_preprocessing(&$defaultvalues) {
738 foreach ($this->submissionplugins as $plugin) {
739 if ($plugin->is_visible()) {
740 $plugin->data_preprocessing($defaultvalues);
741 }
742 }
743 foreach ($this->feedbackplugins as $plugin) {
744 if ($plugin->is_visible()) {
745 $plugin->data_preprocessing($defaultvalues);
746 }
747 }
748 }
749
750 /**
751 * Get the name of the current module.
752 *
753 * @return string the module name (Assignment)
754 */
755 protected function get_module_name() {
756 if (isset(self::$modulename)) {
757 return self::$modulename;
758 }
759 self::$modulename = get_string('modulename', 'assign');
760 return self::$modulename;
761 }
762
763 /**
764 * Get the plural name of the current module.
765 *
766 * @return string the module name plural (Assignments)
767 */
768 protected function get_module_name_plural() {
769 if (isset(self::$modulenameplural)) {
770 return self::$modulenameplural;
771 }
772 self::$modulenameplural = get_string('modulenameplural', 'assign');
773 return self::$modulenameplural;
774 }
775
776 /**
777 * Has this assignment been constructed from an instance?
778 *
779 * @return bool
780 */
781 public function has_instance() {
782 return $this->instance || $this->get_course_module();
783 }
784
785 /**
786 * Get the settings for the current instance of this assignment
787 *
788 * @return stdClass The settings
789 */
790 public function get_instance() {
791 global $DB;
792 if ($this->instance) {
793 return $this->instance;
794 }
795 if ($this->get_course_module()) {
796 $this->instance = $DB->get_record('assign', array('id' => $this->get_course_module()->instance), '*', MUST_EXIST);
797 }
798 if (!$this->instance) {
799 throw new coding_exception('Improper use of the assignment class. Cannot load the assignment record.');
800 }
801 return $this->instance;
802 }
803
804 /**
805 * Get the context of the current course
806 * @return mixed context|null The course context
807 */
808 public function get_course_context() {
809 if (!$this->context && !$this->course) {
810 throw new coding_exception('Improper use of the assignment class. Cannot load the course context.');
811 }
812 if ($this->context) {
813 return $this->context->get_course_context();
814 } else {
815 return context_course::instance($this->course->id);
816 }
817 }
818
819
820 /**
821 * Get the current course module
822 *
823 * @return mixed stdClass|null The course module
824 */
825 public function get_course_module() {
826 if ($this->coursemodule) {
827 return $this->coursemodule;
828 }
829 if (!$this->context) {
830 return null;
831 }
832
833 if ($this->context->contextlevel == CONTEXT_MODULE) {
834 $this->coursemodule = get_coursemodule_from_id('assign', $this->context->instanceid, 0, false, MUST_EXIST);
835 return $this->coursemodule;
836 }
837 return null;
838 }
839
840 /**
841 * Get context module
842 *
843 * @return context
844 */
845 public function get_context() {
846 return $this->context;
847 }
848
849 /**
850 * Get the current course
851 * @return mixed stdClass|null The course
852 */
853 public function get_course() {
854 global $DB;
855 if ($this->course) {
856 return $this->course;
857 }
858
859 if (!$this->context) {
860 return null;
861 }
862 $this->course = $DB->get_record('course', array('id' => $this->get_course_context()->instanceid), '*', MUST_EXIST);
863 return $this->course;
864 }
865
866 /**
867 * Return a grade in user-friendly form, whether it's a scale or not
868 *
869 * @param mixed $grade int|null
870 * @return string User-friendly representation of grade
871 */
872 public function display_grade($grade) {
873 global $DB;
874
875 static $scalegrades = array();
876
877
878
879 if ($this->get_instance()->grade >= 0) { // Normal number
880 if ($grade == -1 || $grade === null) {
881 return '-';
882 } else {
883 return format_float(($grade),2) .'&nbsp;/&nbsp;'. format_float($this->get_instance()->grade,2);
884 }
885
886 } else { // Scale
887 if (empty($this->cache['scale'])) {
888 if ($scale = $DB->get_record('scale', array('id'=>-($this->get_instance()->grade)))) {
889 $this->cache['scale'] = make_menu_from_list($scale->scale);
890 } else {
891 return '-';
892 }
893 }
894 $scaleid = (int)$grade;
895 if (isset($this->cache['scale'][$scaleid])) {
896 return $this->cache['scale'][$scaleid];
897 }
898 return '-';
899 }
900 }
901
902 /**
903 * Load a list of users enrolled in the current course with the specified permission and group (0 for no group)
904 *
905 * @param int $currentgroup
906 * @param bool $idsonly
907 * @return array List of user records
908 */
909 public function list_participants($currentgroup, $idsonly) {
910 if ($idsonly) {
911 return get_enrolled_users($this->context, "mod/assign:submit", $currentgroup, 'u.id');
912 } else {
913 return get_enrolled_users($this->context, "mod/assign:submit", $currentgroup);
914 }
915 }
916
917 /**
918 * Load a count of users enrolled in the current course with the specified permission and group (0 for no group)
919 *
920 * @param int $currentgroup
921 * @return int number of matching users
922 */
923 public function count_participants($currentgroup) {
924 return count_enrolled_users($this->context, "mod/assign:submit", $currentgroup);
925 }
926
927 /**
928 * Load a count of users enrolled in the current course with the specified permission and group (optional)
929 *
930 * @param string $status The submission status - should match one of the constants
931 * @return int number of matching submissions
932 */
933 public function count_submissions_with_status($status) {
934 global $DB;
935 return $DB->count_records_sql("SELECT COUNT('x')
936 FROM {assign_submission}
937 WHERE assignment = ? AND
938 status = ?", array($this->get_course_module()->instance, $status));
939 }
940
941 /**
942 * Utility function to get the userid for every row in the grading table
943 * so the order can be frozen while we iterate it
944 *
945 * @return array An array of userids
946 */
947 private function get_grading_userid_list(){
948 $filter = get_user_preferences('assign_filter', '');
949 $table = new assign_grading_table($this, 0, $filter);
950
951 $useridlist = $table->get_column_data('userid');
952
953 return $useridlist;
954 }
955
956
957 /**
958 * Utility function get the userid based on the row number of the grading table.
959 * This takes into account any active filters on the table.
960 *
961 * @param int $num The row number of the user
962 * @param bool $last This is set to true if this is the last user in the table
963 * @return mixed The user id of the matching user or false if there was an error
964 */
965 private function get_userid_for_row($num, $last){
966 if (!array_key_exists('userid_for_row', $this->cache)) {
967 $this->cache['userid_for_row'] = array();
968 }
969 if (array_key_exists($num, $this->cache['userid_for_row'])) {
970 list($userid, $last) = $this->cache['userid_for_row'][$num];
971 return $userid;
972 }
973
974 $filter = get_user_preferences('assign_filter', '');
975 $table = new assign_grading_table($this, 0, $filter);
976
977 $userid = $table->get_cell_data($num, 'userid', $last);
978
979 $this->cache['userid_for_row'][$num] = array($userid, $last);
980 return $userid;
981 }
982
983 /**
984 * Return all assignment submissions by ENROLLED students (even empty)
985 *
986 * @param string $sort optional field names for the ORDER BY in the sql query
987 * @param string $dir optional specifying the sort direction, defaults to DESC
988 * @return array The submission objects indexed by id
989 */
990 private function get_all_submissions( $sort="", $dir="DESC") {
991 global $CFG, $DB;
992
993 if ($sort == "lastname" or $sort == "firstname") {
994 $sort = "u.$sort $dir";
995 } else if (empty($sort)) {
996 $sort = "a.timemodified DESC";
997 } else {
998 $sort = "a.$sort $dir";
999 }
1000
1001 return $DB->get_records_sql("SELECT a.*
1002 FROM {assign_submission} a, {user} u
1003 WHERE u.id = a.userid
1004 AND a.assignment = ?
1005 ORDER BY $sort", array($this->get_instance()->id));
1006
1007 }
1008
1009 /**
1010 * Generate zip file from array of given files
1011 *
1012 * @param array $filesforzipping - array of files to pass into archive_to_pathname - this array is indexed by the final file name and each element in the array is an instance of a stored_file object
1013 * @return path of temp file - note this returned file does not have a .zip extension - it is a temp file.
1014 */
1015 private function pack_files($filesforzipping) {
1016 global $CFG;
1017 //create path for new zip file.
1018 $tempzip = tempnam($CFG->tempdir.'/', 'assignment_');
1019 //zip files
1020 $zipper = new zip_packer();
1021 if ($zipper->archive_to_pathname($filesforzipping, $tempzip)) {
1022 return $tempzip;
1023 }
1024 return false;
1025 }
1026
1027
1028 /**
1029 * Cron function to be run periodically according to the moodle cron
1030 * Finds all assignment notifications that have yet to be mailed out, and mails them
1031 *
1032 * @return bool
1033 */
1034 static function cron() {
1035 global $CFG, $USER, $DB;
1036
1037
1038 return true;
1039 }
1040
1041 /**
1042 * Update a grade in the grade table for the assignment and in the gradebook
1043 *
1044 * @param stdClass $grade a grade record keyed on id
1045 * @return bool true for success
1046 */
1047 private function update_grade($grade) {
1048 global $DB;
1049
1050 $grade->timemodified = time();
1051
1052 if ($grade->grade && $grade->grade != -1) {
1053 if ($this->get_instance()->grade > 0) {
1054 if (!is_numeric($grade->grade)) {
1055 return false;
1056 } else if ($grade->grade > $this->get_instance()->grade) {
1057 return false;
1058 } else if ($grade->grade < 0) {
1059 return false;
1060 }
1061 } else {
1062 // this is a scale
1063 if ($scale = $DB->get_record('scale', array('id' => -($this->get_instance()->grade)))) {
1064 $scaleoptions = make_menu_from_list($scale->scale);
1065 if (!array_key_exists((int) $grade->grade, $scaleoptions)) {
1066 return false;
1067 }
1068 }
1069 }
1070 }
1071
1072 $result = $DB->update_record('assign_grades', $grade);
1073 if ($result) {
1074 $this->gradebook_item_update(null, $grade);
1075 }
1076 return $result;
1077 }
1078
1079 /**
1080 * display the submission that is used by a plugin
1081 * Uses url parameters 'sid', 'gid' and 'plugin'
1082 * @param string $pluginsubtype
1083 * @return string
1084 */
1085 private function view_plugin_content($pluginsubtype) {
1086 global $USER;
1087
1088 $o = '';
1089
1090 $submissionid = optional_param('sid', 0, PARAM_INT);
1091 $gradeid = optional_param('gid', 0, PARAM_INT);
1092 $plugintype = required_param('plugin', PARAM_TEXT);
1093 $item = null;
1094 if ($pluginsubtype == 'assignsubmission') {
1095 $plugin = $this->get_submission_plugin_by_type($plugintype);
1096 if ($submissionid <= 0) {
1097 throw new coding_exception('Submission id should not be 0');
1098 }
1099 $item = $this->get_submission($submissionid);
1100
1101 // permissions
1102 if ($item->userid != $USER->id) {
1103 require_capability('mod/assign:grade', $this->context);
1104 }
1105 $o .= $this->output->render(new assign_header($this->get_instance(),
1106 $this->get_context(),
1107 $this->show_intro(),
1108 $this->get_course_module()->id,
1109 $plugin->get_name()));
1110 $o .= $this->output->render(new assign_submission_plugin_submission($plugin,
1111 $item,
1112 assign_submission_plugin_submission::FULL,
1113 $this->get_course_module()->id,
1114 $this->get_return_action(),
1115 $this->get_return_params()));
1116
1117 $this->add_to_log('view submission', get_string('viewsubmissionforuser', 'assign', $item->userid));
1118 } else {
1119 $plugin = $this->get_feedback_plugin_by_type($plugintype);
1120 if ($gradeid <= 0) {
1121 throw new coding_exception('Grade id should not be 0');
1122 }
1123 $item = $this->get_grade($gradeid);
1124 // permissions
1125 if ($item->userid != $USER->id) {
1126 require_capability('mod/assign:grade', $this->context);
1127 }
1128 $o .= $this->output->render(new assign_header($this->get_instance(),
1129 $this->get_context(),
1130 $this->show_intro(),
1131 $this->get_course_module()->id,
1132 $plugin->get_name()));
1133 $o .= $this->output->render(new assign_feedback_plugin_feedback($plugin,
1134 $item,
1135 assign_feedback_plugin_feedback::FULL,
1136 $this->get_course_module()->id,
1137 $this->get_return_action(),
1138 $this->get_return_params()));
1139 $this->add_to_log('view feedback', get_string('viewfeedbackforuser', 'assign', $item->userid));
1140 }
1141
1142
1143 $o .= $this->view_return_links();
1144
1145 $o .= $this->view_footer();
1146 return $o;
1147 }
1148
1149 /**
1150 * render the content in editor that is often used by plugin
1151 *
1152 * @param string $filearea
1153 * @param int $submissionid
1154 * @param string $plugintype
1155 * @param string $editor
1156 * @param string $component
1157 * @return string
1158 */
1159 public function render_editor_content($filearea, $submissionid, $plugintype, $editor, $component) {
1160 global $CFG;
1161
1162 $result = '';
1163
1164 $plugin = $this->get_submission_plugin_by_type($plugintype);
1165
1166 $text = $plugin->get_editor_text($editor, $submissionid);
1167 $format = $plugin->get_editor_format($editor, $submissionid);
1168
1169 $finaltext = file_rewrite_pluginfile_urls($text, 'pluginfile.php', $this->get_context()->id, $component, $filearea, $submissionid);
1170 $result .= format_text($finaltext, $format, array('overflowdiv' => true, 'context' => $this->get_context()));
1171
1172
1173
1174 if ($CFG->enableportfolios) {
1175 require_once($CFG->libdir . '/portfoliolib.php');
1176
1177 $button = new portfolio_add_button();
1178 $button->set_callback_options('assign_portfolio_caller', array('cmid' => $this->get_course_module()->id, 'sid' => $submissionid, 'plugin' => $plugintype, 'editor' => $editor, 'area'=>$filearea), '/mod/assign/portfolio_callback.php');
1179 $fs = get_file_storage();
1180
1181 if ($files = $fs->get_area_files($this->context->id, $component,$filearea, $submissionid, "timemodified", false)) {
1182 $button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
1183 } else {
1184 $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
1185 }
1186 $result .= $button->to_html();
1187 }
1188 return $result;
1189 }
1190
1191
1192 /**
1193 * Display the page footer
1194 *
1195 * @return None
1196 */
1197 private function view_footer() {
1198 return $this->output->render_footer();
1199 }
1200
1201 /**
1202 * Does this user have grade permission for this assignment
1203 *
1204 * @return bool
1205 */
1206 private function can_grade() {
1207 // Permissions check
1208 if (!has_capability('mod/assign:grade', $this->context)) {
1209 return false;
1210 }
1211
1212 return true;
1213 }
1214
1215 /**
1216 * Download a zip file of all assignment submissions
1217 *
1218 * @return void
1219 */
1220 private function download_submissions() {
1221 global $CFG,$DB;
1222
1223 // more efficient to load this here
1224 require_once($CFG->libdir.'/filelib.php');
1225
1226 // load all submissions
1227 $submissions = $this->get_all_submissions('','');
1228
1229 if (empty($submissions)) {
1230 print_error('errornosubmissions', 'assign');
1231 return;
1232 }
1233
1234 // build a list of files to zip
1235 $filesforzipping = array();
1236 $fs = get_file_storage();
1237
1238 $groupmode = groups_get_activity_groupmode($this->get_course_module());
1239 $groupid = 0; // All users
1240 $groupname = '';
1241 if ($groupmode) {
1242 $groupid = groups_get_activity_group($this->get_course_module(), true);
1243 $groupname = groups_get_group_name($groupid).'-';
1244 }
1245
1246 // construct the zip file name
1247 $filename = str_replace(' ', '_', clean_filename($this->get_course()->shortname.'-'.$this->get_instance()->name.'-'.$groupname.$this->get_course_module()->id.".zip")); //name of new zip file.
1248
1249 // get all the files for each submission
1250 foreach ($submissions as $submission) {
1251 $userid = $submission->userid; //get userid
1252 if ((groups_is_member($groupid,$userid) or !$groupmode or !$groupid)) {
1253 // get the plugins to add their own files to the zip
1254
1255 $user = $DB->get_record("user", array("id"=>$userid),'id,username,firstname,lastname', MUST_EXIST);
1256
1257 $prefix = clean_filename(fullname($user) . "_" .$userid . "_");
1258
1259 foreach ($this->submissionplugins as $plugin) {
1260 if ($plugin->is_enabled() && $plugin->is_visible()) {
1261 $pluginfiles = $plugin->get_files($submission);
1262
1263
1264 foreach ($pluginfiles as $zipfilename => $file) {
1265 $filesforzipping[$prefix . $zipfilename] = $file;
1266 }
1267 }
1268 }
1269
1270 }
1271 } // end of foreach loop
1272 if ($zipfile = $this->pack_files($filesforzipping)) {
1273 $this->add_to_log('download all submissions', get_string('downloadall', 'assign'));
1274 send_temp_file($zipfile, $filename); //send file and delete after sending.
1275 }
1276 }
1277
1278 /**
1279 * Util function to add a message to the log
1280 *
1281 * @param string $action The current action
1282 * @param string $info A detailed description of the change. But no more than 255 characters.
1283 * @param string $url The url to the assign module instance.
1284 * @return void
1285 */
1286 public function add_to_log($action = '', $info = '', $url='') {
1287 global $USER;
1288
1289 $fullurl = 'view.php?id=' . $this->get_course_module()->id;
1290 if ($url != '') {
1291 $fullurl .= '&' . $url;
1292 }
1293
1294 add_to_log($this->get_course()->id, 'assign', $action, $fullurl, $info, $this->get_course_module()->id, $USER->id);
1295 }
1296
1297 /**
1298 * Load the submission object for a particular user, optionally creating it if required
1299 *
1300 * @param int $userid The id of the user whose submission we want or 0 in which case USER->id is used
1301 * @param bool $create optional Defaults to false. If set to true a new submission object will be created in the database
1302 * @return stdClass The submission
1303 */
1304 private function get_user_submission($userid, $create) {
1305 global $DB, $USER;
1306
1307 if (!$userid) {
1308 $userid = $USER->id;
1309 }
1310 // if the userid is not null then use userid
1311 $submission = $DB->get_record('assign_submission', array('assignment'=>$this->get_instance()->id, 'userid'=>$userid));
1312
1313 if ($submission) {
1314 return $submission;
1315 }
1316 if ($create) {
1317 $submission = new stdClass();
1318 $submission->assignment = $this->get_instance()->id;
1319 $submission->userid = $userid;
1320 $submission->timecreated = time();
1321 $submission->timemodified = $submission->timecreated;
1322
1323 if ($this->get_instance()->submissiondrafts) {
1324 $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
1325 } else {
1326 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1327 }
1328 $sid = $DB->insert_record('assign_submission', $submission);
1329 $submission->id = $sid;
1330 return $submission;
1331 }
1332 return false;
1333 }
1334
1335 /**
1336 * Load the submission object from it's id
1337 *
1338 * @param int $submissionid The id of the submission we want
1339 * @return stdClass The submission
1340 */
1341 private function get_submission($submissionid) {
1342 global $DB;
1343
1344 return $DB->get_record('assign_submission', array('assignment'=>$this->get_instance()->id, 'id'=>$submissionid), '*', MUST_EXIST);
1345 }
1346
1347 /**
1348 * This will retrieve a grade object from the db, optionally creating it if required
1349 *
1350 * @param int $userid The user we are grading
1351 * @param bool $create If true the grade will be created if it does not exist
1352 * @return stdClass The grade record
1353 */
1354 private function get_user_grade($userid, $create) {
1355 global $DB, $USER;
1356
1357 if (!$userid) {
1358 $userid = $USER->id;
1359 }
1360
1361 // if the userid is not null then use userid
1362 $grade = $DB->get_record('assign_grades', array('assignment'=>$this->get_instance()->id, 'userid'=>$userid));
1363
1364 if ($grade) {
1365 return $grade;
1366 }
1367 if ($create) {
1368 $grade = new stdClass();
1369 $grade->assignment = $this->get_instance()->id;
1370 $grade->userid = $userid;
1371 $grade->timecreated = time();
1372 $grade->timemodified = $grade->timecreated;
1373 $grade->locked = 0;
1374 $grade->grade = -1;
1375 $grade->grader = $USER->id;
1376 $gid = $DB->insert_record('assign_grades', $grade);
1377 $grade->id = $gid;
1378 return $grade;
1379 }
1380 return false;
1381 }
1382
1383 /**
1384 * This will retrieve a grade object from the db
1385 *
1386 * @param int $gradeid The id of the grade
1387 * @return stdClass The grade record
1388 */
1389 private function get_grade($gradeid) {
1390 global $DB;
1391
1392 return $DB->get_record('assign_grades', array('assignment'=>$this->get_instance()->id, 'id'=>$gradeid), '*', MUST_EXIST);
1393 }
1394
1395 /**
1396 * Print the grading page for a single user submission
1397 *
1398 * @param moodleform $mform
1399 * @param int $offset
1400 * @return string
1401 */
1402 private function view_single_grade_page($mform, $offset=0) {
1403 global $DB, $CFG;
1404
1405 $o = '';
1406
1407 // Include grade form
1408 require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1409
1410 // Need submit permission to submit an assignment
1411 require_capability('mod/assign:grade', $this->context);
1412
1413 $o .= $this->output->render(new assign_header($this->get_instance(),
1414 $this->get_context(), false, $this->get_course_module()->id,get_string('grading', 'assign')));
1415
1416 $rownum = required_param('rownum', PARAM_INT) + $offset;
1417 $useridlist = optional_param('useridlist', '', PARAM_TEXT);
1418 if ($useridlist) {
1419 $useridlist = explode(',', $useridlist);
1420 } else {
1421 $useridlist = $this->get_grading_userid_list();
1422 }
1423 $last = false;
1424 $userid = $useridlist[$rownum];
1425 if ($rownum == count($useridlist) - 1) {
1426 $last = true;
1427 }
1428 // the placement of this is important so can pass the list of userids above
1429 if ($offset) {
1430 $_POST = array();
1431 }
1432 if(!$userid){
1433 throw new coding_exception('Row is out of bounds for the current grading table: ' . $rownum);
1434 }
1435 $user = $DB->get_record('user', array('id' => $userid));
1436 if ($user) {
1437 $o .= $this->output->render(new assign_user_summary($user, $this->get_course()->id, has_capability('moodle/site:viewfullnames', $this->get_course_context())));
1438 }
1439 $submission = $this->get_user_submission($userid, false);
1440 // get the current grade
1441 $grade = $this->get_user_grade($userid, false);
1442 if ($this->can_view_submission($userid)) {
1443 $gradelocked = ($grade && $grade->locked) || $this->grading_disabled($userid);
1444 $o .= $this->output->render(new assign_submission_status($this->get_instance()->allowsubmissionsfromdate,
1445 $this->get_instance()->alwaysshowdescription,
1446 $submission,
1447 $this->is_any_submission_plugin_enabled(),
1448 $gradelocked,
1449 $this->is_graded($userid),
1450 $this->get_instance()->duedate,
1451 $this->get_submission_plugins(),
1452 $this->get_return_action(),
1453 $this->get_return_params(),
1454 $this->get_course_module()->id,
1455 assign_submission_status::GRADER_VIEW,
1456 false,
1457 false));
1458 }
1459 if ($grade) {
1460 $data = new stdClass();
1461 if ($grade->grade >= 0) {
1462 $data->grade = format_float($grade->grade,2);
1463 }
1464 } else {
1465 $data = new stdClass();
1466 $data->grade = '';
1467 }
1468
1469 // now show the grading form
1470 if (!$mform) {
1471 $mform = new mod_assign_grade_form(null, array($this, $data, array('rownum'=>$rownum, 'useridlist'=>$useridlist, 'last'=>$last)), 'post', '', array('class'=>'gradeform'));
1472 }
1473 $o .= $this->output->render(new assign_form('gradingform',$mform));
1474
1475 $this->add_to_log('view grading form', get_string('viewgradingformforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))));
1476
1477 $o .= $this->view_footer();
1478 return $o;
1479 }
1480
1481
1482
1483 /**
1484 * View a link to go back to the previous page. Uses url parameters returnaction and returnparams.
1485 *
1486 * @return string
1487 */
1488 private function view_return_links() {
1489
1490 $returnaction = optional_param('returnaction','', PARAM_ALPHA);
1491 $returnparams = optional_param('returnparams','', PARAM_TEXT);
1492
1493 $params = array();
1494 parse_str($returnparams, $params);
1495 $params = array_merge( array('id' => $this->get_course_module()->id, 'action' => $returnaction), $params);
1496
1497 return $this->output->single_button(new moodle_url('/mod/assign/view.php', $params), get_string('back'), 'get');
1498
1499 }
1500
1501 /**
1502 * View the grading table of all submissions for this assignment
1503 *
1504 * @return string
1505 */
1506 private function view_grading_table() {
1507 global $USER, $CFG;
1508 // Include grading options form
1509 require_once($CFG->dirroot . '/mod/assign/gradingoptionsform.php');
1510 require_once($CFG->dirroot . '/mod/assign/gradingactionsform.php');
1511 require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
1512 $o = '';
1513
1514 $links = array();
1515 $selecturl = (string)(new moodle_url('/mod/assign/view.php',
1516 array('action'=>'grading', 'id'=>$this->get_course_module()->id)));
1517 $links[$selecturl] = get_string('selectlink', 'assign');
1518 if (has_capability('gradereport/grader:view', $this->get_course_context()) &&
1519 has_capability('moodle/grade:viewall', $this->get_course_context())) {
1520 $gradebookurl = (string) (new moodle_url('/grade/report/grader/index.php',
1521 array('id' => $this->get_course()->id)));
1522 $links[$gradebookurl] = get_string('viewgradebook', 'assign');
1523 }
1524 if ($this->is_any_submission_plugin_enabled()) {
1525 $downloadurl = (string) (new moodle_url('/mod/assign/view.php',
1526 array('id' => $this->get_course_module()->id,
1527 'action' => 'downloadall')));
1528 $links[$downloadurl] = get_string('downloadall', 'assign');
1529 }
1530 $gradingactionsform = new mod_assign_grading_actions_form(null,
1531 array('links'=>$links,
1532 'cm'=>$this->get_course_module()->id),
1533 'post', '',
1534 array('class'=>'gradingactionsform'));
1535
1536 $perpage = get_user_preferences('assign_perpage', 10);
1537 $filter = get_user_preferences('assign_filter', '');
1538 // print options for changing the filter and changing the number of results per page
1539 $gradingoptionsform = new mod_assign_grading_options_form(null,
1540 array('cm'=>$this->get_course_module()->id,
1541 'contextid'=>$this->context->id,
1542 'userid'=>$USER->id),
1543 'post', '',
1544 array('class'=>'gradingoptionsform'));
1545
1546 $gradingbatchoperationsform = new mod_assign_grading_batch_operations_form(null,
1547 array('cm'=>$this->get_course_module()->id,
1548 'submissiondrafts'=>$this->get_instance()->submissiondrafts),
1549 'post', '',
1550 array('class'=>'gradingbatchoperationsform'));
1551
1552 $gradingoptionsdata = new stdClass();
1553 $gradingoptionsdata->perpage = $perpage;
1554 $gradingoptionsdata->filter = $filter;
1555 $gradingoptionsform->set_data($gradingoptionsdata);
1556
1557 // plagiarism update status apearring in the grading book
1558 if (!empty($CFG->enableplagiarism)) {
1559 /** Include plagiarismlib.php */
1560 require_once($CFG->libdir . '/plagiarismlib.php');
1561 plagiarism_update_status($this->get_course(), $this->get_course_module());
1562 }
1563
1564 $o .= $this->output->render(new assign_form('gradingactionsform', $gradingactionsform));
1565 $o .= $this->output->render(new assign_form('gradingoptionsform', $gradingoptionsform));
1566
1567 // load and print the table of submissions
1568 $o .= $this->output->render(new assign_grading_table($this, $perpage, $filter));
1569
1570 $currentgroup = groups_get_activity_group($this->get_course_module(), true);
1571 $users = array_keys($this->list_participants($currentgroup, true));
1572 if (count($users) != 0) {
1573 // if no enrolled user in a course then don't display the batch operations feature
1574 $o .= $this->output->render(new assign_form('gradingbatchoperationsform', $gradingbatchoperationsform));
1575 }
1576 return $o;
1577 }
1578
1579 /**
1580 * View entire grading page.
1581 *
1582 * @return string
1583 */
1584 private function view_grading_page() {
1585 global $CFG;
1586
1587 $o = '';
1588 // Need submit permission to submit an assignment
1589 require_capability('mod/assign:grade', $this->context);
1590 require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1591
1592 // only load this if it is
1593
1594 $o .= $this->output->render(new assign_header($this->get_instance(),
1595 $this->get_context(), false, $this->get_course_module()->id, get_string('grading', 'assign')));
1596 $o .= groups_print_activity_menu($this->get_course_module(), $CFG->wwwroot . '/mod/assign/view.php?id=' . $this->get_course_module()->id.'&action=grading', true);
1597
1598
1599 $o .= $this->view_grading_table();
1600
1601 $o .= $this->view_footer();
1602 $this->add_to_log('view submission grading table', get_string('viewsubmissiongradingtable', 'assign'));
1603 return $o;
1604 }
1605
1606 /**
1607 * Capture the output of the plagiarism plugins disclosures and return it as a string
1608 *
1609 * @return void
1610 */
1611 private function plagiarism_print_disclosure() {
1612 global $CFG;
1613 $o = '';
1614
1615 if (!empty($CFG->enableplagiarism)) {
1616 /** Include plagiarismlib.php */
1617 require_once($CFG->libdir . '/plagiarismlib.php');
1618 ob_start();
1619
1620 plagiarism_print_disclosure($this->get_course_module()->id);
1621 $o = ob_get_contents();
1622 ob_end_clean();
1623 }
1624
1625 return $o;
1626 }
1627
1628 /**
1629 * message for students when assignment submissions have been closed
1630 *
1631 * @return string
1632 */
1633 private function view_student_error_message() {
1634 global $CFG;
1635
1636 $o = '';
1637 // Need submit permission to submit an assignment
1638 require_capability('mod/assign:submit', $this->context);
1639
1640 $o .= $this->output->render(new assign_header($this->get_instance(),
1641 $this->get_context(),
1642 $this->show_intro(),
1643 $this->get_course_module()->id,
1644 get_string('editsubmission', 'assign')));
1645
1646 $o .= $this->output->notification('This assignment is no longer accepting submissions');
1647
1648 $o .= $this->view_footer();
1649
1650 return $o;
1651
1652 }
1653
1654 /**
1655 * View edit submissions page.
1656 *
1657 * @param moodleform $mform
1658 * @return void
1659 */
1660 private function view_edit_submission_page($mform) {
1661 global $CFG;
1662
1663 $o = '';
1664 // Include submission form
1665 require_once($CFG->dirroot . '/mod/assign/submission_form.php');
1666 // Need submit permission to submit an assignment
1667 require_capability('mod/assign:submit', $this->context);
1668
1669 if (!$this->submissions_open()) {
1670 $subclosed = '';
1671 $subclosed .= $this->view_student_error_message();
1672 return $subclosed;
1673 }
1674 $o .= $this->output->render(new assign_header($this->get_instance(),
1675 $this->get_context(),
1676 $this->show_intro(),
1677 $this->get_course_module()->id,
1678 get_string('editsubmission', 'assign')));
1679 $o .= $this->plagiarism_print_disclosure();
1680 $data = new stdClass();
1681
1682 if (!$mform) {
1683 $mform = new mod_assign_submission_form(null, array($this, $data));
1684 }
1685
1686 $o .= $this->output->render(new assign_form('editsubmissionform',$mform));
1687
1688 $o .= $this->view_footer();
1689 $this->add_to_log('view submit assignment form', get_string('viewownsubmissionform', 'assign'));
1690
1691 return $o;
1692 }
1693
1694 /**
1695 * See if this assignment has a grade yet
1696 *
1697 * @param int $userid
1698 * @return bool
1699 */
1700 private function is_graded($userid) {
1701 $grade = $this->get_user_grade($userid, false);
1702 if ($grade) {
1703 return ($grade->grade != '');
1704 }
1705 return false;
1706 }
1707
1708
1709 /**
1710 * Perform an access check to see if the current $USER can view this users submission
1711 *
1712 * @param int $userid
1713 * @return bool
1714 */
1715 public function can_view_submission($userid) {
1716 global $USER;
1717
1718 if (!is_enrolled($this->get_course_context(), $userid)) {
1719 return false;
1720 }
1721 if ($userid == $USER->id && !has_capability('mod/assign:submit', $this->context)) {
1722 return false;
1723 }
1724 if ($userid != $USER->id && !has_capability('mod/assign:grade', $this->context)) {
1725 return false;
1726 }
1727 return true;
1728 }
1729
1730 /**
1731 * Ask the user to confirm they want to perform this batch operation
1732 * @return string
1733 */
1734 private function process_batch_grading_operation() {
1735 global $CFG;
1736 require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
1737 require_sesskey();
1738
1739 $gradingbatchoperationsform = new mod_assign_grading_batch_operations_form(null,
1740 array('cm'=>$this->get_course_module()->id,
1741 'submissiondrafts'=>$this->get_instance()->submissiondrafts),
1742 'post', '',
1743 array('class'=>'gradingbatchoperationsform'));
1744
1745 if ($data = $gradingbatchoperationsform->get_data()) {
1746 // get the list of users
1747 $users = $data->selectedusers;
1748 $userlist = explode(',', $users);
1749
1750 foreach ($userlist as $userid) {
1751 if ($data->operation == 'lock') {
1752 $this->process_lock($userid);
1753 } else if ($data->operation == 'unlock') {
1754 $this->process_unlock($userid);
1755 } else if ($data->operation == 'reverttodraft') {
1756 $this->process_revert_to_draft($userid);
1757 }
1758 }
1759 }
1760
1761 return true;
1762 }
1763
1764 /**
1765 * Ask the user to confirm they want to submit their work for grading
1766 * @return string
1767 */
1768 private function check_submit_for_grading() {
1769 global $USER;
1770 // Check that all of the submission plugins are ready for this submission
1771 $notifications = array();
1772 $submission = $this->get_user_submission($USER->id, false);
1773 $plugins = $this->get_submission_plugins();
1774 foreach ($plugins as $plugin) {
1775 if ($plugin->is_enabled() && $plugin->is_visible()) {
1776 $check = $plugin->precheck_submission($submission);
1777 if ($check !== true) {
1778 $notifications[] = $check;
1779 }
1780 }
1781 }
1782
1783 $o = '';
1784 $o .= $this->output->header();
1785 $o .= $this->output->render(new assign_submit_for_grading_page($notifications, $this->get_course_module()->id));
1786 $o .= $this->view_footer();
1787 return $o;
1788 }
1789
1790 /**
1791 * Print 2 tables of information with no action links -
1792 * the submission summary and the grading summary
1793 *
1794 * @param stdClass $user the user to print the report for
1795 * @param bool $showlinks - Return plain text or links to the profile
1796 * @return string - the html summary
1797 */
1798 public function view_student_summary($user, $showlinks) {
1799 global $CFG, $DB, $PAGE;
1800
1801 $grade = $this->get_user_grade($user->id, false);
1802 $submission = $this->get_user_submission($user->id, false);
1803 $o = '';
1804
1805 if ($this->can_view_submission($user->id)) {
1806 $showedit = has_capability('mod/assign:submit', $this->context) &&
1807 $this->submissions_open() && ($this->is_any_submission_plugin_enabled()) && $showlinks;
1808 $showsubmit = $submission && ($submission->status == ASSIGN_SUBMISSION_STATUS_DRAFT) && $showlinks;
1809 $gradelocked = ($grade && $grade->locked) || $this->grading_disabled($user->id);
1810
1811 $o .= $this->output->render(new assign_submission_status($this->get_instance()->allowsubmissionsfromdate,
1812 $this->get_instance()->alwaysshowdescription,
1813 $submission,
1814 $this->is_any_submission_plugin_enabled(),
1815 $gradelocked,
1816 $this->is_graded($user->id),
1817 $this->get_instance()->duedate,
1818 $this->get_submission_plugins(),
1819 $this->get_return_action(),
1820 $this->get_return_params(),
1821 $this->get_course_module()->id,
1822 assign_submission_status::STUDENT_VIEW,
1823 $showedit,
1824 $showsubmit));
1825 require_once($CFG->libdir.'/gradelib.php');
1826 require_once($CFG->dirroot.'/grade/grading/lib.php');
1827
1828 $gradinginfo = grade_get_grades($this->get_course()->id,
1829 'mod',
1830 'assign',
1831 $this->get_instance()->id,
1832 $user->id);
1833
1834 $gradingitem = $gradinginfo->items[0];
1835 $gradebookgrade = $gradingitem->grades[$user->id];
1836
1837 // check to see if all feedback plugins are empty
1838 $emptyplugins = true;
1839 if ($grade) {
1840 foreach ($this->get_feedback_plugins() as $plugin) {
1841 if ($plugin->is_visible() && $plugin->is_enabled()) {
1842 if (!$plugin->is_empty($grade)) {
1843 $emptyplugins = false;
1844 }
1845 }
1846 }
1847 }
1848
1849
1850 if (!($gradebookgrade->hidden) && ($gradebookgrade->grade !== null || !$emptyplugins)) {
1851
1852 $gradefordisplay = '';
1853 $gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
1854
1855 if ($controller = $gradingmanager->get_active_controller()) {
1856 $controller->set_grade_range(make_grades_menu($this->get_instance()->grade));
1857 $gradefordisplay = $controller->render_grade($PAGE,
1858 $grade->id,
1859 $gradingitem,
1860 $gradebookgrade->str_long_grade,
1861 has_capability('mod/assign:grade', $this->get_context()));
1862 } else {
1863 $gradefordisplay = $this->display_grade($gradebookgrade->grade);
1864 }
1865
1866 $gradeddate = $gradebookgrade->dategraded;
1867 $grader = $DB->get_record('user', array('id'=>$gradebookgrade->usermodified));
1868
1869 $feedbackstatus = new assign_feedback_status($gradefordisplay,
1870 $gradeddate,
1871 $grader,
1872 $this->get_feedback_plugins(),
1873 $grade,
1874 $this->get_course_module()->id,
1875 $this->get_return_action(),
1876 $this->get_return_params());
1877
1878 $o .= $this->output->render($feedbackstatus);
1879 }
1880
1881 }
1882 return $o;
1883 }
1884
1885 /**
1886 * View submissions page (contains details of current submission).
1887 *
1888 * @return string
1889 */
1890 private function view_submission_page() {
1891 global $CFG, $DB, $USER, $PAGE;
1892
1893 $o = '';
1894 $o .= $this->output->render(new assign_header($this->get_instance(),
1895 $this->get_context(),
1896 $this->show_intro(),
1897 $this->get_course_module()->id));
1898
1899 if ($this->can_grade()) {
1900 $o .= $this->output->render(new assign_grading_summary($this->count_participants(0),
1901 $this->get_instance()->submissiondrafts,
1902 $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT),
1903 $this->is_any_submission_plugin_enabled(),
1904 $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED),
1905 $this->get_instance()->duedate,
1906 $this->get_course_module()->id
1907 ));
1908 }
1909 $grade = $this->get_user_grade($USER->id, false);
1910 $submission = $this->get_user_submission($USER->id, false);
1911
1912 if ($this->can_view_submission($USER->id)) {
1913 $o .= $this->view_student_summary($USER, true);
1914 }
1915
1916
1917 $o .= $this->view_footer();
1918 $this->add_to_log('view', get_string('viewownsubmissionstatus', 'assign'));
1919 return $o;
1920 }
1921
1922 /**
1923 * convert the final raw grade(s) in the grading table for the gradebook
1924 *
1925 * @param stdClass $grade
1926 * @return array
1927 */
1928 private function convert_grade_for_gradebook(stdClass $grade) {
1929 $gradebookgrade = array();
1930 // trying to match those array keys in grade update function in gradelib.php
1931 // with keys in th database table assign_grades
1932 // starting around line 262
1933 $gradebookgrade['rawgrade'] = $grade->grade;
1934 $gradebookgrade['userid'] = $grade->userid;
1935 $gradebookgrade['usermodified'] = $grade->grader;
1936 $gradebookgrade['datesubmitted'] = NULL;
1937 $gradebookgrade['dategraded'] = $grade->timemodified;
1938 if (isset($grade->feedbackformat)) {
1939 $gradebookgrade['feedbackformat'] = $grade->feedbackformat;
1940 }
1941 if (isset($grade->feedbacktext)) {
1942 $gradebookgrade['feedback'] = $grade->feedbacktext;
1943 }
1944
1945 return $gradebookgrade;
1946 }
1947
1948 /**
1949 * convert submission details for the gradebook
1950 *
1951 * @param stdClass $submission
1952 * @return array
1953 */
1954 private function convert_submission_for_gradebook(stdClass $submission) {
1955 $gradebookgrade = array();
1956
1957
1958 $gradebookgrade['userid'] = $submission->userid;
1959 $gradebookgrade['usermodified'] = $submission->userid;
1960 $gradebookgrade['datesubmitted'] = $submission->timemodified;
1961
1962 return $gradebookgrade;
1963 }
1964
1965 /**
1966 * update grades in the gradebook
1967 *
1968 * @param mixed $submission stdClass|null
1969 * @param mixed $grade stdClass|null
1970 * @return bool
1971 */
1972 private function gradebook_item_update($submission=NULL, $grade=NULL) {
1973
1974 if($submission != NULL){
1975
1976 $gradebookgrade = $this->convert_submission_for_gradebook($submission);
1977
1978
1979 }else{
1980
1981
1982 $gradebookgrade = $this->convert_grade_for_gradebook($grade);
1983 }
1984 $assign = clone $this->get_instance();
1985 $assign->cmidnumber = $this->get_course_module()->id;
1986
1987 return assign_grade_item_update($assign, $gradebookgrade);
1988 }
1989
1990 /**
1991 * update grades in the gradebook based on submission time
1992 *
1993 * @param stdClass $submission
1994 * @param bool $updatetime
1995 * @return bool
1996 */
1997 private function update_submission(stdClass $submission, $updatetime=true) {
1998 global $DB;
1999
2000 if ($updatetime) {
2001 $submission->timemodified = time();
2002 }
2003 $result= $DB->update_record('assign_submission', $submission);
2004 if ($result) {
2005 $this->gradebook_item_update($submission);
2006 }
2007 return $result;
2008 }
2009
2010 /**
2011 * Is this assignment open for submissions?
2012 *
2013 * Check the due date,
2014 * prevent late submissions,
2015 * has this person already submitted,
2016 * is the assignment locked?
2017 *
2018 * @return bool
2019 */
2020 private function submissions_open() {
2021 global $USER;
2022
2023 $time = time();
2024 $dateopen = true;
2025 if ($this->get_instance()->preventlatesubmissions && $this->get_instance()->duedate) {
2026 $dateopen = ($this->get_instance()->allowsubmissionsfromdate <= $time && $time <= $this->get_instance()->duedate);
2027 } else {
2028 $dateopen = ($this->get_instance()->allowsubmissionsfromdate <= $time);
2029 }
2030
2031 if (!$dateopen) {
2032 return false;
2033 }
2034
2035 // now check if this user has already submitted etc.
2036 if (!is_enrolled($this->get_course_context(), $USER)) {
2037 return false;
2038 }
2039 if ($submission = $this->get_user_submission($USER->id, false)) {
2040 if ($this->get_instance()->submissiondrafts && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
2041 // drafts are tracked and the student has submitted the assignment
2042 return false;
2043 }
2044 }
2045 if ($grade = $this->get_user_grade($USER->id, false)) {
2046 if ($grade->locked) {
2047 return false;
2048 }
2049 }
2050
2051 if ($this->grading_disabled($USER->id)) {
2052 return false;
2053 }
2054
2055 return true;
2056 }
2057
2058 /**
2059 * render the files in file area
2060 * @param string $component
2061 * @param string $area
2062 * @param int $submissionid
2063 * @return string
2064 */
2065 public function render_area_files($component, $area, $submissionid) {
2066 global $USER;
2067
2068 if (!$submissionid) {
2069 $submission = $this->get_user_submission($USER->id,false);
2070 $submissionid = $submission->id;
2071 }
2072
2073
2074
2075 $fs = get_file_storage();
2076 $browser = get_file_browser();
2077 $files = $fs->get_area_files($this->get_context()->id, $component, $area , $submissionid , "timemodified", false);
2078 return $this->output->assign_files($this->context, $submissionid, $area, $component);
2079
2080 }
2081
2082 /**
2083 * Returns a list of teachers that should be grading given submission
2084 *
2085 * @param stdClass $user
2086 * @return array
2087 */
2088 private function get_graders(stdClass $user) {
2089 //potential graders
2090 $potentialgraders = get_users_by_capability($this->context, 'mod/assign:grade', '', '', '', '', '', '', false, false);
2091
2092 $graders = array();
2093 if (groups_get_activity_groupmode($this->get_course_module()) == SEPARATEGROUPS) { // Separate groups are being used
2094 if ($groups = groups_get_all_groups($this->get_course()->id, $user->id)) { // Try to find all groups
2095 foreach ($groups as $group) {
2096 foreach ($potentialgraders as $grader) {
2097 if ($grader->id == $user->id) {
2098 continue; // do not send self
2099 }
2100 if (groups_is_member($group->id, $grader->id)) {
2101 $graders[$grader->id] = $grader;
2102 }
2103 }
2104 }
2105 } else {
2106 // user not in group, try to find graders without group
2107 foreach ($potentialgraders as $grader) {
2108 if ($grader->id == $user->id) {
2109 continue; // do not send self
2110 }
2111 if (!groups_has_membership($this->get_course_module(), $grader->id)) {
2112 $graders[$grader->id] = $grader;
2113 }
2114 }
2115 }
2116 } else {
2117 foreach ($potentialgraders as $grader) {
2118 if ($grader->id == $user->id) {
2119 continue; // do not send self
2120 }
2121 // must be enrolled
2122 if (is_enrolled($this->get_course_context(), $grader->id)) {
2123 $graders[$grader->id] = $grader;
2124 }
2125 }
2126 }
2127 return $graders;
2128 }
2129
2130 /**
2131 * Creates the text content for emails to grader
2132 *
2133 * @param array $info The info used by the 'emailgradermail' language string
2134 * @return string
2135 */
2136 private function format_email_grader_text($info) {
2137 $posttext = format_string($this->get_course()->shortname, true, array('context' => $this->get_course_context())).' -> '.
2138 $this->get_module_name().' -> '.
2139 format_string($this->get_instance()->name, true, array('context' => $this->context))."\n";
2140 $posttext .= '---------------------------------------------------------------------'."\n";
2141 $posttext .= get_string("emailgradermail", "assign", $info)."\n";
2142 $posttext .= "\n---------------------------------------------------------------------\n";
2143 return $posttext;
2144 }
2145
2146 /**
2147 * Creates the html content for emails to graders
2148 *
2149 * @param array $info The info used by the 'emailgradermailhtml' language string
2150 * @return string
2151 */
2152 private function format_email_grader_html($info) {
2153 global $CFG;
2154 $posthtml = '<p><font face="sans-serif">'.
2155 '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$this->get_course()->id.'">'.format_string($this->get_course()->shortname, true, array('context' => $this->get_course_context())).'</a> ->'.
2156 '<a href="'.$CFG->wwwroot.'/mod/assignment/index.php?id='.$this->get_course()->id.'">'.$this->get_module_name().'</a> ->'.
2157 '<a href="'.$CFG->wwwroot.'/mod/assignment/view.php?id='.$this->get_course_module()->id.'">'.format_string($this->get_instance()->name, true, array('context' => $this->context)).'</a></font></p>';
2158 $posthtml .= '<hr /><font face="sans-serif">';
2159 $posthtml .= '<p>'.get_string('emailgradermailhtml', 'assign', $info).'</p>';
2160 $posthtml .= '</font><hr />';
2161 return $posthtml;
2162 }
2163
2164 /**
2165 * email graders upon student submissions
2166 *
2167 * @param stdClass $submission
2168 * @return void
2169 */
2170 private function email_graders(stdClass $submission) {
2171 global $CFG, $DB;
2172
2173 if (empty($this->get_instance()->sendnotifications)) { // No need to do anything
2174 return;
2175 }
2176
2177 $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST);
2178
2179 if ($teachers = $this->get_graders($user)) {
2180
2181 $strassignments = $this->get_module_name_plural();
2182 $strassignment = $this->get_module_name();
2183 $strsubmitted = get_string('submitted', 'assign');
2184
2185 foreach ($teachers as $teacher) {
2186 $info = new stdClass();
2187 $info->username = fullname($user, true);
2188 $info->assignment = format_string($this->get_instance()->name,true, array('context'=>$this->get_context()));
2189 $info->url = $CFG->wwwroot.'/mod/assign/view.php?id='.$this->get_course_module()->id;
2190 $info->timeupdated = strftime('%c',$submission->timemodified);
2191
2192 $postsubject = $strsubmitted.': '.$info->username.' -> '.$this->get_instance()->name;
2193 $posttext = $this->format_email_grader_text($info);
2194 $posthtml = ($teacher->mailformat == 1) ? $this->format_email_grader_html($info) : '';
2195
2196 $eventdata = new stdClass();
2197 $eventdata->modulename = 'assign';
2198 $eventdata->userfrom = $user;
2199 $eventdata->userto = $teacher;
2200 $eventdata->subject = $postsubject;
2201 $eventdata->fullmessage = $posttext;
2202 $eventdata->fullmessageformat = FORMAT_PLAIN;
2203 $eventdata->fullmessagehtml = $posthtml;
2204 $eventdata->smallmessage = $postsubject;
2205
2206 $eventdata->name = 'assign_updates';
2207 $eventdata->component = 'mod_assign';
2208 $eventdata->notification = 1;
2209 $eventdata->contexturl = $info->url;
2210 $eventdata->contexturlname = $info->assignment;
2211
2212 message_send($eventdata);
2213 }
2214 }
2215 }
2216
2217 /**
2218 * assignment submission is processed before grading
2219 *
2220 * @return void
2221 */
2222 private function process_submit_for_grading() {
2223 global $USER;
2224
2225 // Need submit permission to submit an assignment
2226 require_capability('mod/assign:submit', $this->context);
2227 require_sesskey();
2228
2229 $submission = $this->get_user_submission($USER->id,true);
2230 if ($submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
2231 // Give each submission plugin a chance to process the submission
2232 $plugins = $this->get_submission_plugins();
2233 foreach ($plugins as $plugin) {
2234 $plugin->submit_for_grading();
2235 }
2236
2237 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2238 $this->update_submission($submission);
2239 $this->add_to_log('submit for grading', $this->format_submission_for_log($submission));
2240 $this->email_graders($submission);
2241 }
2242 }
2243
2244 /**
2245 * save grading options
2246 *
2247 * @return void
2248 */
2249 private function process_save_grading_options() {
2250 global $USER, $CFG;
2251
2252 // Include grading options form
2253 require_once($CFG->dirroot . '/mod/assign/gradingoptionsform.php');
2254
2255 // Need submit permission to submit an assignment
2256 require_capability('mod/assign:grade', $this->context);
2257
2258
2259
2260 $mform = new mod_assign_grading_options_form(null, array('cm'=>$this->get_course_module()->id, 'contextid'=>$this->context->id, 'userid'=>$USER->id));
2261
2262 if ($formdata = $mform->get_data()) {
2263 set_user_preference('assign_perpage', $formdata->perpage);
2264 set_user_preference('assign_filter', $formdata->filter);
2265 }
2266 }
2267
2268 /**
2269 * Take a grade object and print a short summary for the log file.
2270 * The size limit for the log file is 255 characters, so be careful not
2271 * to include too much information.
2272 *
2273 * @param stdClass $grade
2274 * @return string
2275 */
2276 private function format_grade_for_log(stdClass $grade) {
2277 global $DB;
2278
2279 $user = $DB->get_record('user', array('id' => $grade->userid), '*', MUST_EXIST);
2280
2281 $info = get_string('gradestudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user)));
2282 if ($grade->grade != '') {
2283 $info .= get_string('grade') . ': ' . $this->display_grade($grade->grade) . '. ';
2284 } else {
2285 $info .= get_string('nograde', 'assign');
2286 }
2287 if ($grade->locked) {
2288 $info .= get_string('submissionslocked', 'assign') . '. ';
2289 }
2290 return $info;
2291 }
2292
2293 /**
2294 * Take a submission object and print a short summary for the log file.
2295 * The size limit for the log file is 255 characters, so be careful not
2296 * to include too much information.
2297 *
2298 * @param stdClass $submission
2299 * @return string
2300 */
2301 private function format_submission_for_log(stdClass $submission) {
2302 $info = '';
2303 $info .= get_string('submissionstatus', 'assign') . ': ' . get_string('submissionstatus_' . $submission->status, 'assign') . '. <br>';
2304 // format_for_log here iterating every single log INFO from either submission or grade in every assignment plugin
2305
2306 foreach ($this->submissionplugins as $plugin) {
2307 if ($plugin->is_enabled() && $plugin->is_visible()) {
2308
2309
2310 $info .= "<br>" . $plugin->format_for_log($submission);
2311 }
2312 }
2313
2314
2315 return $info;
2316 }
2317
2318 /**
2319 * save assignment submission
2320 *
2321 * @param moodleform $mform
2322 * @return bool
2323 */
2324 private function process_save_submission(&$mform) {
2325 global $USER, $CFG;
2326
2327 // Include submission form
2328 require_once($CFG->dirroot . '/mod/assign/submission_form.php');
2329
2330 // Need submit permission to submit an assignment
2331 require_capability('mod/assign:submit', $this->context);
2332 require_sesskey();
2333
2334 $data = new stdClass();
2335 $mform = new mod_assign_submission_form(null, array($this, $data));
2336 if ($mform->is_cancelled()) {
2337 return true;
2338 }
2339 if ($data = $mform->get_data()) {
2340 $submission = $this->get_user_submission($USER->id, true); //create the submission if needed & its id
2341 $grade = $this->get_user_grade($USER->id, false); // get the grade to check if it is locked
2342 if ($grade && $grade->locked) {
2343 print_error('submissionslocked', 'assign');
2344 return true;
2345 }
2346
2347
2348 foreach ($this->submissionplugins as $plugin) {
2349 if ($plugin->is_enabled()) {
2350 if (!$plugin->save($submission, $data)) {
2351 print_error($plugin->get_error());
2352 }
2353 }
2354 }
2355
2356 $this->update_submission($submission);
2357
2358 // Logging
2359 $this->add_to_log('submit', $this->format_submission_for_log($submission));
2360
2361 if (!$this->get_instance()->submissiondrafts) {
2362 $this->email_graders($submission);
2363 }
2364 return true;
2365 }
2366 return false;
2367 }
2368
2369 /**
2370 * count the number of files in the file area
2371 *
2372 * @param int $userid
2373 * @param string $area
2374 * @return int
2375 */
2376 private function count_files($userid = 0, $area = ASSIGN_FILEAREA_SUBMISSION_FILES) {
2377 global $USER;
2378
2379 if (!$userid) {
2380 $userid = $USER->id;
2381 }
2382
2383 $fs = get_file_storage();
2384 $files = $fs->get_area_files($this->context->id, 'mod_assign', $area, $userid, "id", false);
2385
2386 return count($files);
2387 }
2388
2389 /**
2390 * Determine if this users grade is locked or overridden
2391 *
2392 * @param int $userid - The student userid
2393 * @return bool $gradingdisabled
2394 */
2395 private function grading_disabled($userid) {
2396 global $CFG;
2397
2398 $gradinginfo = grade_get_grades($this->get_course()->id, 'mod', 'assign', $this->get_instance()->id, array($userid));
2399 if (!$gradinginfo) {
2400 return false;
2401 }
2402
2403 if (!isset($gradinginfo->items[0]->grades[$userid])) {
2404 return false;
2405 }
2406 $gradingdisabled = $gradinginfo->items[0]->grades[$userid]->locked || $gradinginfo->items[0]->grades[$userid]->overridden;
2407 return $gradingdisabled;
2408 }
2409
2410
2411 /**
2412 * Get an instance of a grading form if advanced grading is enabled
2413 * This is specific to the assignment, marker and student
2414 *
2415 * @param int $userid - The student userid
2416 * @param bool $gradingdisabled
2417 * @return mixed gradingform_instance|null $gradinginstance
2418 */
2419 private function get_grading_instance($userid, $gradingdisabled) {
2420 global $CFG, $USER;
2421
2422 $grade = $this->get_user_grade($userid, false);
2423 $grademenu = make_grades_menu($this->get_instance()->grade);
2424
2425 $advancedgradingwarning = false;
2426 $gradingmanager = get_grading_manager($this->context, 'mod_assign', 'submissions');
2427 $gradinginstance = null;
2428 if ($gradingmethod = $gradingmanager->get_active_method()) {
2429 $controller = $gradingmanager->get_controller($gradingmethod);
2430 if ($controller->is_form_available()) {
2431 $itemid = null;
2432 if ($grade) {
2433 $itemid = $grade->id;
2434 }
2435 if ($gradingdisabled && $itemid) {
2436 $gradinginstance = ($controller->get_current_instance($USER->id, $itemid));
2437 } else if (!$gradingdisabled) {
2438 $instanceid = optional_param('advancedgradinginstanceid', 0, PARAM_INT);
2439 $gradinginstance = ($controller->get_or_create_instance($instanceid, $USER->id, $itemid));
2440 }
2441 } else {
2442 $advancedgradingwarning = $controller->form_unavailable_notification();
2443 }
2444 }
2445 if ($gradinginstance) {
2446 $gradinginstance->get_controller()->set_grade_range($grademenu);
2447 }
2448 return $gradinginstance;
2449 }
2450
2451 /**
2452 * add elements to grade form
2453 *
2454 * @param MoodleQuickForm $mform
2455 * @param stdClass $data
2456 * @param array $params
2457 * @return void
2458 */
2459 public function add_grade_form_elements(MoodleQuickForm $mform, stdClass $data, $params) {
2460 global $USER, $CFG;
2461 $settings = $this->get_instance();
2462
2463 $rownum = $params['rownum'];
2464 $last = $params['last'];
2465 $useridlist = $params['useridlist'];
2466 $userid = $useridlist[$rownum];
2467 $grade = $this->get_user_grade($userid, false);
2468
2469 // add advanced grading
2470 $gradingdisabled = $this->grading_disabled($userid);
2471 $gradinginstance = $this->get_grading_instance($userid, $gradingdisabled);
2472
2473 if ($gradinginstance) {
2474 $gradingelement = $mform->addElement('grading', 'advancedgrading', get_string('grade').':', array('gradinginstance' => $gradinginstance));
2475 if ($gradingdisabled) {
2476 $gradingelement->freeze();
2477 } else {
2478 $mform->addElement('hidden', 'advancedgradinginstanceid', $gradinginstance->get_id());
2479 }
2480 } else {
2481 // use simple direct grading
2482 if ($this->get_instance()->grade > 0) {
2483 $mform->addElement('text', 'grade', get_string('gradeoutof', 'assign',$this->get_instance()->grade));
2484 $mform->addHelpButton('grade', 'gradeoutofhelp', 'assign');
2485 $mform->setType('grade', PARAM_TEXT);
2486 } else {
2487 $grademenu = make_grades_menu($this->get_instance()->grade);
2488 if (count($grademenu) > 0) {
2489 $mform->addElement('select', 'grade', get_string('grade').':', $grademenu);
2490 $mform->setType('grade', PARAM_INT);
2491 }
2492 }
2493 }
2494 $mform->addElement('static', 'progress', '', get_string('gradingstudentprogress', 'assign', array('index'=>$rownum+1, 'count'=>count($useridlist))));
2495
2496
2497 // plugins
2498 $this->add_plugin_grade_elements($grade, $mform, $data);
2499
2500 // hidden params
2501 $mform->addElement('hidden', 'id', $this->get_course_module()->id);
2502 $mform->setType('id', PARAM_INT);
2503 $mform->addElement('hidden', 'rownum', $rownum);
2504 $mform->setType('rownum', PARAM_INT);
2505 $mform->addElement('hidden', 'useridlist', implode(',', $useridlist));
2506 $mform->setType('useridlist', PARAM_TEXT);
2507 $mform->addElement('hidden', 'ajax', optional_param('ajax', 0, PARAM_INT));
2508 $mform->setType('ajax', PARAM_INT);
2509
2510 $mform->addElement('hidden', 'action', 'submitgrade');
2511 $mform->setType('action', PARAM_ALPHA);
2512
2513
2514 $buttonarray=array();
2515 $buttonarray[] = $mform->createElement('submit', 'savegrade', get_string('savechanges', 'assign'));
2516 if (!$last){
2517 $buttonarray[] = $mform->createElement('submit', 'saveandshownext', get_string('savenext','assign'));
2518 }
2519 $buttonarray[] = $mform->createElement('cancel', 'cancelbutton', get_string('cancel'));
2520 $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
2521 $mform->closeHeaderBefore('buttonar');
2522 $buttonarray=array();
2523
2524 if ($rownum > 0) {
2525 $buttonarray[] = $mform->createElement('submit', 'nosaveandprevious', get_string('previous','assign'));
2526 }
2527
2528 if (!$last){
2529 $buttonarray[] = $mform->createElement('submit', 'nosaveandnext', get_string('nosavebutnext', 'assign'));
2530 }
2531 $mform->addGroup($buttonarray, 'navar', '', array(' '), false);
2532 }
2533
2534
2535 /**
2536 * add elements in submission plugin form
2537 *
2538 * @param mixed $submission stdClass|null
2539 * @param MoodleQuickForm $mform
2540 * @param stdClass $data
2541 * @return void
2542 */
2543 private function add_plugin_submission_elements($submission, MoodleQuickForm $mform, stdClass $data) {
2544 foreach ($this->submissionplugins as $plugin) {
2545 if ($plugin->is_enabled() && $plugin->is_visible() && $plugin->allow_submissions()) {
2546 $mform->addElement('header', 'header_' . $plugin->get_type(), $plugin->get_name());
2547 if (!$plugin->get_form_elements($submission, $mform, $data)) {
2548 $mform->removeElement('header_' . $plugin->get_type());
2549 }
2550 }
2551 }
2552 }
2553
2554 /**
2555 * check if feedback plugins installed are enabled
2556 *
2557 * @return bool
2558 */
2559 public function is_any_feedback_plugin_enabled() {
2560 if (!isset($this->cache['any_feedback_plugin_enabled'])) {
2561 $this->cache['any_feedback_plugin_enabled'] = false;
2562 foreach ($this->feedbackplugins as $plugin) {
2563 if ($plugin->is_enabled() && $plugin->is_visible()) {
2564 $this->cache['any_feedback_plugin_enabled'] = true;
2565 break;
2566 }
2567 }
2568 }
2569
2570 return $this->cache['any_feedback_plugin_enabled'];
2571
2572 }
2573
2574 /**
2575 * check if submission plugins installed are enabled
2576 *
2577 * @return bool
2578 */
2579 public function is_any_submission_plugin_enabled() {
2580 if (!isset($this->cache['any_submission_plugin_enabled'])) {
2581 $this->cache['any_submission_plugin_enabled'] = false;
2582 foreach ($this->submissionplugins as $plugin) {
2583 if ($plugin->is_enabled() && $plugin->is_visible() && $plugin->allow_submissions()) {
2584 $this->cache['any_submission_plugin_enabled'] = true;
2585 break;
2586 }
2587 }
2588 }
2589
2590 return $this->cache['any_submission_plugin_enabled'];
2591
2592 }
2593
2594 /**
2595 * add elements to submission form
2596 * @param MoodleQuickForm $mform
2597 * @param stdClass $data
2598 * @return void
2599 */
2600 public function add_submission_form_elements(MoodleQuickForm $mform, stdClass $data) {
2601 global $USER;
2602
2603 // online text submissions
2604
2605 $submission = $this->get_user_submission($USER->id, false);
2606
2607 $this->add_plugin_submission_elements($submission, $mform, $data);
2608
2609 // hidden params
2610 $mform->addElement('hidden', 'id', $this->get_course_module()->id);
2611 $mform->setType('id', PARAM_INT);
2612
2613 $mform->addElement('hidden', 'action', 'savesubmission');
2614 $mform->setType('action', PARAM_TEXT);
2615 // buttons
2616
2617 }
2618
2619 /**
2620 * revert to draft
2621 * Uses url parameter userid
2622 *
2623 * @param int $userid
2624 * @return void
2625 */
2626 private function process_revert_to_draft($userid = 0) {
2627 global $USER, $DB;
2628
2629 // Need grade permission
2630 require_capability('mod/assign:grade', $this->context);
2631 require_sesskey();
2632
2633 if (!$userid) {
2634 $userid = required_param('userid', PARAM_INT);
2635 }
2636
2637 $submission = $this->get_user_submission($userid, false);
2638 if (!$submission) {
2639 return;
2640 }
2641 $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
2642 $this->update_submission($submission, false);
2643
2644 // update the modified time on the grade (grader modified)
2645 $grade = $this->get_user_grade($userid, true);
2646 $this->update_grade($grade);
2647
2648 $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
2649
2650 $this->add_to_log('revert submission to draft', get_string('reverttodraftforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))));
2651
2652 }
2653
2654 /**
2655 * lock the process
2656 * Uses url parameter userid
2657 * @param int $userid
2658 * @return void
2659 */
2660 private function process_lock($userid = 0) {
2661 global $USER, $DB;
2662
2663 // Need grade permission
2664 require_capability('mod/assign:grade', $this->context);
2665 require_sesskey();
2666
2667 if (!$userid) {
2668 $userid = required_param('userid', PARAM_INT);
2669 }
2670
2671 $grade = $this->get_user_grade($userid, true);
2672 $grade->locked = 1;
2673 $grade->grader = $USER->id;
2674 $this->update_grade($grade);
2675
2676 $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
2677
2678 $this->add_to_log('lock submission', get_string('locksubmissionforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))));
2679 }
2680
2681 /**
2682 * unlock the process
2683 *
2684 * @param int $userid
2685 * @return void
2686 */
2687 private function process_unlock($userid = 0) {
2688 global $USER, $DB;
2689
2690 // Need grade permission
2691 require_capability('mod/assign:grade', $this->context);
2692 require_sesskey();
2693
2694 if (!$userid) {
2695 $userid = required_param('userid', PARAM_INT);
2696 }
2697
2698 $grade = $this->get_user_grade($userid, true);
2699 $grade->locked = 0;
2700 $grade->grader = $USER->id;
2701 $this->update_grade($grade);
2702
2703 $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
2704
2705 $this->add_to_log('unlock submission', get_string('unlocksubmissionforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))));
2706 }
2707
2708 /**
2709 * save grade
2710 *
2711 * @param moodleform $mform
2712 * @return bool - was the grade saved
2713 */
2714 private function process_save_grade(&$mform) {
2715 global $USER, $DB, $CFG;
2716 // Include grade form
2717 require_once($CFG->dirroot . '/mod/assign/gradeform.php');
2718
2719 // Need submit permission to submit an assignment
2720 require_capability('mod/assign:grade', $this->context);
2721 require_sesskey();
2722
2723 $rownum = required_param('rownum', PARAM_INT);
2724 $useridlist = optional_param('useridlist', '', PARAM_TEXT);
2725 if ($useridlist) {
2726 $useridlist = explode(',', $useridlist);
2727 } else {
2728 $useridlist = $this->get_grading_userid_list();
2729 }
2730 $last = false;
2731 $userid = $useridlist[$rownum];
2732 if ($rownum == count($useridlist) - 1) {
2733 $last = true;
2734 }
2735
2736 $data = new stdClass();
2737 $mform = new mod_assign_grade_form(null, array($this, $data, array('rownum'=>$rownum, 'useridlist'=>$useridlist, 'last'=>false)), 'post', '', array('class'=>'gradeform'));
2738
2739 if ($formdata = $mform->get_data()) {
2740 $grade = $this->get_user_grade($userid, true);
2741 $gradingdisabled = $this->grading_disabled($userid);
2742 $gradinginstance = $this->get_grading_instance($userid, $gradingdisabled);
2743 if ($gradinginstance) {
2744 $grade->grade = $gradinginstance->submit_and_get_grade($formdata->advancedgrading, $grade->id);
2745 } else {
2746 // handle the case when grade is set to No Grade
2747 if (isset($formdata->grade)) {
2748 $grade->grade= grade_floatval($formdata->grade);
2749 }
2750 }
2751 $grade->grader= $USER->id;
2752
2753 $gradebookplugin = $CFG->assign_feedback_plugin_for_gradebook;
2754
2755 // call save in plugins
2756 foreach ($this->feedbackplugins as $plugin) {
2757 if ($plugin->is_enabled() && $plugin->is_visible()) {
2758 if (!$plugin->save($grade, $formdata)) {
2759 $result = false;
2760 print_error($plugin->get_error());
2761 }
2762 if (('assignfeedback_' . $plugin->get_type()) == $gradebookplugin) {
2763 // this is the feedback plugin chose to push comments to the gradebook
2764 $grade->feedbacktext = $plugin->text_for_gradebook($grade);
2765 $grade->feedbackformat = $plugin->format_for_gradebook($grade);
2766 }
2767 }
2768 }
2769 $this->update_grade($grade);
2770
2771 $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
2772
2773 $this->add_to_log('grade submission', $this->format_grade_for_log($grade));
2774
2775
2776 } else {
2777 return false;
2778 }
2779 return true;
2780 }
2781
2782 /**
2783 * This function is a static wrapper around can_upgrade
2784 *
2785 * @param string $type The plugin type
2786 * @param int $version The plugin version
2787 * @return bool
2788 */
2789 public static function can_upgrade_assignment($type, $version) {
2790 $assignment = new assign(null, null, null);
2791 return $assignment->can_upgrade($type, $version);
2792 }
2793
2794 /**
2795 * This function returns true if it can upgrade an assignment from the 2.2
2796 * module.
2797 * @param string $type The plugin type
2798 * @param int $version The plugin version
2799 * @return bool
2800 */
2801 public function can_upgrade($type, $version) {
2802 if ($type == 'offline' && $version >= 2011112900) {
2803 return true;
2804 }
2805 foreach ($this->submissionplugins as $plugin) {
2806 if ($plugin->can_upgrade($type, $version)) {
2807 return true;
2808 }
2809 }
2810 foreach ($this->feedbackplugins as $plugin) {
2811 if ($plugin->can_upgrade($type, $version)) {
2812 return true;
2813 }
2814 }
2815 return false;
2816 }
2817
2818 /**
2819 * Copy all the files from the old assignment files area to the new one.
2820 * This is used by the plugin upgrade code.
2821 *
2822 * @param int $oldcontextid The old assignment context id
2823 * @param int $oldcomponent The old assignment component ('assignment')
2824 * @param int $oldfilearea The old assignment filearea ('submissions')
2825 * @param int $olditemid The old submissionid (can be null e.g. intro)
2826 * @param int $newcontextid The new assignment context id
2827 * @param int $newcomponent The new assignment component ('assignment')
2828 * @param int $newfilearea The new assignment filearea ('submissions')
2829 * @param int $newitemid The new submissionid (can be null e.g. intro)
2830 * @return int The number of files copied
2831 */
2832 public function copy_area_files_for_upgrade($oldcontextid, $oldcomponent, $oldfilearea, $olditemid, $newcontextid, $newcomponent, $newfilearea, $newitemid) {
2833 // Note, this code is based on some code in filestorage - but that code
2834 // deleted the old files (which we don't want)
2835 $count = 0;
2836
2837 $fs = get_file_storage();
2838
2839 $oldfiles = $fs->get_area_files($oldcontextid, $oldcomponent, $oldfilearea, $olditemid, 'id', false);
2840 foreach ($oldfiles as $oldfile) {
2841 $filerecord = new stdClass();
2842 $filerecord->contextid = $newcontextid;
2843 $filerecord->component = $newcomponent;
2844 $filerecord->filearea = $newfilearea;
2845 $filerecord->itemid = $newitemid;
2846 $fs->create_file_from_storedfile($filerecord, $oldfile);
2847 $count += 1;
2848 }
2849
2850 return $count;
2851 }
2852
2853 /**
2854 * Get an upto date list of user grades and feedback for the gradebook
2855 *
2856 * @param int $userid int or 0 for all users
2857 * @return array of grade data formated for the gradebook api
2858 * The data required by the gradebook api is userid,
2859 * rawgrade,
2860 * feedback,
2861 * feedbackformat,
2862 * usermodified,
2863 * dategraded,
2864 * datesubmitted
2865 */
2866 public function get_user_grades_for_gradebook($userid) {
2867 global $DB, $CFG;
2868 $grades = array();
2869 $assignmentid = $this->get_instance()->id;
2870
2871 $gradebookpluginname = $CFG->assign_feedback_plugin_for_gradebook;
2872 $gradebookplugin = null;
2873
2874 // find the gradebook plugin
2875 foreach ($this->feedbackplugins as $plugin) {
2876 if ($plugin->is_enabled() && $plugin->is_visible()) {
2877 if (('assignfeedback_' . $plugin->get_type()) == $gradebookpluginname) {
2878 $gradebookplugin = $plugin;
2879 }
2880 }
2881 }
2882 if ($userid) {
2883 $where = ' WHERE u.id = ? ';
2884 } else {
2885 $where = ' WHERE u.id != ? ';
2886 }
2887
2888 $graderesults = $DB->get_recordset_sql('SELECT u.id as userid, s.timemodified as datesubmitted, g.grade as rawgrade, g.timemodified as dategraded, g.grader as usermodified
2889 FROM {user} u
2890 LEFT JOIN {assign_submission} s ON u.id = s.userid and s.assignment = ?
2891 LEFT JOIN {assign_grades} g ON u.id = g.userid and g.assignment = ?
2892 ' . $where, array($assignmentid, $assignmentid, $userid));
2893
2894
2895 foreach ($graderesults as $result) {
2896 $gradebookgrade = clone $result;
2897 // now get the feedback
2898 if ($gradebookplugin) {
2899 $grade = $this->get_user_grade($result->userid, false);
2900 $gradebookgrade->feedbacktext = $gradebookplugin->text_for_gradebook($grade);
2901 $gradebookgrade->feedbackformat = $gradebookplugin->format_for_gradebook($grade);
2902 }
2903 $grades[$gradebookgrade->userid] = $gradebookgrade;
2904 }
2905
2906 $graderesults->close();
2907 return $grades;
2908 }
2909
2910}
2911