e0bfc1d18851cc01d521db38b7f7780f4f221ea4
[moodle.git] / mod / assign / locallib.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * This file contains the 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  */
27 defined('MOODLE_INTERNAL') || die();
29 /**
30  * Assignment submission statuses
31  */
32 define('ASSIGN_SUBMISSION_STATUS_DRAFT', 'draft'); // student thinks it is a draft
33 define('ASSIGN_SUBMISSION_STATUS_SUBMITTED', 'submitted'); // student thinks it is finished
35 /**
36  * Search filters for grading page
37  */
38 define('ASSIGN_FILTER_SUBMITTED', 'submitted');
39 define('ASSIGN_FILTER_SINGLE_USER', 'singleuser');
40 define('ASSIGN_FILTER_REQUIRE_GRADING', 'require_grading');
42 /** Include accesslib.php */
43 require_once($CFG->libdir.'/accesslib.php');
44 /** Include formslib.php */
45 require_once($CFG->libdir.'/formslib.php');
46 /** Include repository/lib.php */
47 require_once($CFG->dirroot . '/repository/lib.php');
48 /** Include local mod_form.php */
49 require_once($CFG->dirroot.'/mod/assign/mod_form.php');
50 /** gradelib.php */
51 require_once($CFG->libdir.'/gradelib.php');
52 /** grading lib.php */
53 require_once($CFG->dirroot.'/grade/grading/lib.php');
54 /** Include feedbackplugin.php */
55 require_once($CFG->dirroot.'/mod/assign/feedbackplugin.php');
56 /** Include submissionplugin.php */
57 require_once($CFG->dirroot.'/mod/assign/submissionplugin.php');
58 /** Include renderable.php */
59 require_once($CFG->dirroot.'/mod/assign/renderable.php');
60 /** Include gradingtable.php */
61 require_once($CFG->dirroot.'/mod/assign/gradingtable.php');
62 /** Include eventslib.php */
63 require_once($CFG->libdir.'/eventslib.php');
66 /**
67  * Standard base class for mod_assign (assignment types).
68  *
69  * @package   mod_assign
70  * @copyright 2012 NetSpot {@link http://www.netspot.com.au}
71  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
72  */
73 class assign {
76     /** @var stdClass the assignment record that contains the global settings for this assign instance */
77     private $instance;
79     /** @var context the context of the course module for this assign instance (or just the course if we are
80         creating a new one) */
81     private $context;
83     /** @var stdClass the course this assign instance belongs to */
84     private $course;
86     /** @var stdClass the admin config for all assign instances  */
87     private $adminconfig;
90     /** @var assign_renderer the custom renderer for this module */
91     private $output;
93     /** @var stdClass the course module for this assign instance */
94     private $coursemodule;
96     /** @var array cache for things like the coursemodule name or the scale menu - only lives for a single
97         request */
98     private $cache;
100     /** @var array list of the installed submission plugins */
101     private $submissionplugins;
103     /** @var array list of the installed feedback plugins */
104     private $feedbackplugins;
106     /** @var string action to be used to return to this page (without repeating any form submissions etc.) */
107     private $returnaction = 'view';
109     /** @var array params to be used to return to this page */
110     private $returnparams = array();
112     /** @var string modulename prevents excessive calls to get_string */
113     private static $modulename = null;
115     /** @var string modulenameplural prevents excessive calls to get_string */
116     private static $modulenameplural = null;
118     /**
119      * Constructor for the base assign class
120      *
121      * @param mixed $coursemodulecontext context|null the course module context (or the course context if the coursemodule has not been created yet)
122      * @param mixed $coursemodule the current course module if it was already loaded - otherwise this class will load one from the context as required
123      * @param mixed $course the current course  if it was already loaded - otherwise this class will load one from the context as required
124      */
125     public function __construct($coursemodulecontext, $coursemodule, $course) {
126         global $PAGE;
128         $this->context = $coursemodulecontext;
129         $this->coursemodule = $coursemodule;
130         $this->course = $course;
131         $this->cache = array(); // temporary cache only lives for a single request - used to reduce db lookups
133         $this->submissionplugins = $this->load_plugins('assignsubmission');
134         $this->feedbackplugins = $this->load_plugins('assignfeedback');
135         $this->output = $PAGE->get_renderer('mod_assign');
136     }
138     /**
139      * Set the action and parameters that can be used to return to the current page
140      *
141      * @param string $action The action for the current page
142      * @param array $params An array of name value pairs which form the parameters to return to the current page
143      * @return void
144      */
145     public function register_return_link($action, $params) {
146         $this->returnaction = $action;
147         $this->returnparams = $params;
148     }
150     /**
151      * Return an action that can be used to get back to the current page
152      * @return string action
153      */
154     public function get_return_action() {
155         return $this->returnaction;
156     }
158     /**
159      * Based on the current assignment settings should we display the intro
160      * @return bool showintro
161      */
162     private function show_intro() {
163         if ($this->get_instance()->alwaysshowdescription ||
164                 time() > $this->get_instance()->allowsubmissionsfromdate) {
165             return true;
166         }
167         return false;
168     }
170     /**
171      * Return a list of parameters that can be used to get back to the current page
172      * @return array params
173      */
174     public function get_return_params() {
175         return $this->returnparams;
176     }
178     /**
179      * Set the submitted form data
180      * @param stdClass $data The form data (instance)
181      */
182     public function set_instance(stdClass $data) {
183         $this->instance = $data;
184     }
186     /**
187      * Set the context
188      * @param context $context The new context
189      */
190     public function set_context(context $context) {
191         $this->context = $context;
192     }
194     /**
195      * Set the course data
196      * @param stdClass $course The course data
197      */
198     public function set_course(stdClass $course) {
199         $this->course = $course;
200     }
202     /**
203      * get list of feedback plugins installed
204      * @return array
205      */
206     public function get_feedback_plugins() {
207         return $this->feedbackplugins;
208     }
210     /**
211      * get list of submission plugins installed
212      * @return array
213      */
214     public function get_submission_plugins() {
215         return $this->submissionplugins;
216     }
219     /**
220      * get a specific submission plugin by its type
221      * @param string $subtype assignsubmission | assignfeedback
222      * @param string $type
223      * @return mixed assign_plugin|null
224      */
225     private function get_plugin_by_type($subtype, $type) {
226         $shortsubtype = substr($subtype, strlen('assign'));
227         $name = $shortsubtype . 'plugins';
228         $pluginlist = $this->$name;
229         foreach ($pluginlist as $plugin) {
230             if ($plugin->get_type() == $type) {
231                 return $plugin;
232             }
233         }
234         return null;
235     }
237     /**
238      * Get a feedback plugin by type
239      * @param string $type - The type of plugin e.g comments
240      * @return mixed assign_feedback_plugin|null
241      */
242     public function get_feedback_plugin_by_type($type) {
243         return $this->get_plugin_by_type('assignfeedback', $type);
244     }
246     /**
247      * Get a submission plugin by type
248      * @param string $type - The type of plugin e.g comments
249      * @return mixed assign_submission_plugin|null
250      */
251     public function get_submission_plugin_by_type($type) {
252         return $this->get_plugin_by_type('assignsubmission', $type);
253     }
255     /**
256      * Load the plugins from the sub folders under subtype
257      * @param string $subtype - either submission or feedback
258      * @return array - The sorted list of plugins
259      */
260     private function load_plugins($subtype) {
261         global $CFG;
262         $result = array();
264         $names = get_plugin_list($subtype);
266         foreach ($names as $name => $path) {
267             if (file_exists($path . '/locallib.php')) {
268                 require_once($path . '/locallib.php');
270                 $shortsubtype = substr($subtype, strlen('assign'));
271                 $pluginclass = 'assign_' . $shortsubtype . '_' . $name;
273                 $plugin = new $pluginclass($this, $name);
275                 if ($plugin instanceof assign_plugin) {
276                     $idx = $plugin->get_sort_order();
277                     while (array_key_exists($idx, $result)) $idx +=1;
278                     $result[$idx] = $plugin;
279                 }
280             }
281         }
282         ksort($result);
283         return $result;
284     }
287     /**
288      * Display the assignment, used by view.php
289      *
290      * The assignment is displayed differently depending on your role,
291      * the settings for the assignment and the status of the assignment.
292      * @param string $action The current action if any.
293      * @return void
294      */
295     public function view($action='') {
297         $o = '';
298         $mform = null;
299         $notices = array();
301         // Handle form submissions first.
302         if ($action == 'savesubmission') {
303             $action = 'editsubmission';
304             if ($this->process_save_submission($mform, $notices)) {
305                 $action = 'view';
306             }
307          } else if ($action == 'lock') {
308             $this->process_lock();
309             $action = 'grading';
310          } else if ($action == 'reverttodraft') {
311             $this->process_revert_to_draft();
312             $action = 'grading';
313          } else if ($action == 'unlock') {
314             $this->process_unlock();
315             $action = 'grading';
316          } else if ($action == 'confirmsubmit') {
317             $this->process_submit_for_grading();
318             // save and show next button
319          } else if ($action == 'batchgradingoperation') {
320             $this->process_batch_grading_operation();
321             $action = 'grading';
322          } else if ($action == 'submitgrade') {
323             if (optional_param('saveandshownext', null, PARAM_RAW)) {
324                 //save and show next
325                 $action = 'grade';
326                 if ($this->process_save_grade($mform)) {
327                     $action = 'nextgrade';
328                 }
329             } else if (optional_param('nosaveandprevious', null, PARAM_RAW)) {
330                 $action = 'previousgrade';
331             } else if (optional_param('nosaveandnext', null, PARAM_RAW)) {
332                 //show next button
333                 $action = 'nextgrade';
334             } else if (optional_param('savegrade', null, PARAM_RAW)) {
335                 //save changes button
336                 $action = 'grade';
337                 if ($this->process_save_grade($mform)) {
338                     $action = 'grading';
339                 }
340             } else {
341                 //cancel button
342                 $action = 'grading';
343             }
344         }else if ($action == 'quickgrade') {
345             $message = $this->process_save_quick_grades();
346             $action = 'quickgradingresult';
347         }else if ($action == 'saveoptions') {
348             $this->process_save_grading_options();
349             $action = 'grading';
350         }
352         $returnparams = array('rownum'=>optional_param('rownum', 0, PARAM_INT));
353         $this->register_return_link($action, $returnparams);
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 == 'quickgradingresult') {
360             $mform = null;
361             $o .= $this->view_quickgrading_result($message);
362         } else if ($action == 'nextgrade') {
363             $mform = null;
364             $o .= $this->view_single_grade_page($mform, 1);
365         } else if ($action == 'grade') {
366             $o .= $this->view_single_grade_page($mform);
367         } else if ($action == 'viewpluginassignfeedback') {
368             $o .= $this->view_plugin_content('assignfeedback');
369         } else if ($action == 'viewpluginassignsubmission') {
370             $o .= $this->view_plugin_content('assignsubmission');
371         } else if ($action == 'editsubmission') {
372             $o .= $this->view_edit_submission_page($mform, $notices);
373         } else if ($action == 'grading') {
374             $o .= $this->view_grading_page();
375         } else if ($action == 'downloadall') {
376             $o .= $this->download_submissions();
377         } else if ($action == 'submit') {
378             $o .= $this->check_submit_for_grading();
379         } else {
380             $o .= $this->view_submission_page();
381         }
383         return $o;
384     }
387     /**
388      * Add this instance to the database
389      *
390      * @param stdClass $formdata The data submitted from the form
391      * @param bool $callplugins This is used to skip the plugin code
392      *             when upgrading an old assignment to a new one (the plugins get called manually)
393      * @return mixed false if an error occurs or the int id of the new instance
394      */
395     public function add_instance(stdClass $formdata, $callplugins) {
396         global $DB;
398         $err = '';
400         // add the database record
401         $update = new stdClass();
402         $update->name = $formdata->name;
403         $update->timemodified = time();
404         $update->timecreated = time();
405         $update->course = $formdata->course;
406         $update->courseid = $formdata->course;
407         $update->intro = $formdata->intro;
408         $update->introformat = $formdata->introformat;
409         $update->alwaysshowdescription = $formdata->alwaysshowdescription;
410         $update->preventlatesubmissions = $formdata->preventlatesubmissions;
411         $update->submissiondrafts = $formdata->submissiondrafts;
412         $update->sendnotifications = $formdata->sendnotifications;
413         $update->sendlatenotifications = $formdata->sendlatenotifications;
414         $update->duedate = $formdata->duedate;
415         $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
416         $update->grade = $formdata->grade;
417         $returnid = $DB->insert_record('assign', $update);
418         $this->instance = $DB->get_record('assign', array('id'=>$returnid), '*', MUST_EXIST);
419         // cache the course record
420         $this->course = $DB->get_record('course', array('id'=>$formdata->course), '*', MUST_EXIST);
422         if ($callplugins) {
423             // call save_settings hook for submission plugins
424             foreach ($this->submissionplugins as $plugin) {
425                 if (!$this->update_plugin_instance($plugin, $formdata)) {
426                     print_error($plugin->get_error());
427                     return false;
428                 }
429             }
430             foreach ($this->feedbackplugins as $plugin) {
431                 if (!$this->update_plugin_instance($plugin, $formdata)) {
432                     print_error($plugin->get_error());
433                     return false;
434                 }
435             }
437             // in the case of upgrades the coursemodule has not been set so we need to wait before calling these two
438             // TODO: add event to the calendar
439             $this->update_calendar($formdata->coursemodule);
440             // TODO: add the item in the gradebook
441             $this->update_gradebook(false, $formdata->coursemodule);
443         }
445         $update = new stdClass();
446         $update->id = $this->get_instance()->id;
447         $update->nosubmissions = (!$this->is_any_submission_plugin_enabled()) ? 1: 0;
448         $DB->update_record('assign', $update);
450         return $returnid;
451     }
453     /**
454      * Delete all grades from the gradebook for this assignment
455      *
456      * @return bool
457      */
458     private function delete_grades() {
459         global $CFG;
461         return grade_update('mod/assign', $this->get_course()->id, 'mod', 'assign', $this->get_instance()->id, 0, NULL, array('deleted'=>1)) == GRADE_UPDATE_OK;
462     }
464     /**
465      * Delete this instance from the database
466      *
467      * @return bool false if an error occurs
468      */
469     public function delete_instance() {
470         global $DB;
471         $result = true;
473         foreach ($this->submissionplugins as $plugin) {
474             if (!$plugin->delete_instance()) {
475                 print_error($plugin->get_error());
476                 $result = false;
477             }
478         }
479         foreach ($this->feedbackplugins as $plugin) {
480             if (!$plugin->delete_instance()) {
481                 print_error($plugin->get_error());
482                 $result = false;
483             }
484         }
486         // delete files associated with this assignment
487         $fs = get_file_storage();
488         if (! $fs->delete_area_files($this->context->id) ) {
489             $result = false;
490         }
492         // delete_records will throw an exception if it fails - so no need for error checking here
494         $DB->delete_records('assign_submission', array('assignment'=>$this->get_instance()->id));
495         $DB->delete_records('assign_grades', array('assignment'=>$this->get_instance()->id));
496         $DB->delete_records('assign_plugin_config', array('assignment'=>$this->get_instance()->id));
498         // delete items from the gradebook
499         if (! $this->delete_grades()) {
500             $result = false;
501         }
503         // delete the instance
504         $DB->delete_records('assign', array('id'=>$this->get_instance()->id));
506         return $result;
507     }
509     /**
510     * Actual implementation of the reset course functionality, delete all the
511     * assignment submissions for course $data->courseid.
512     *
513     * @param $data the data submitted from the reset course.
514     * @return array status array
515     */
516     public function reset_userdata($data) {
517         global $CFG,$DB;
519         $componentstr = get_string('modulenameplural', 'assign');
520         $status = array();
522         $fs = get_file_storage();
523         if (!empty($data->reset_assign_submissions)) {
524             // Delete files associated with this assignment.
525             foreach ($this->submissionplugins as $plugin) {
526                 $fileareas = array();
527                 $plugincomponent = $plugin->get_subtype() . '_' . $plugin->get_type();
528                 $fileareas = $plugin->get_file_areas();
529                 foreach ($fileareas as $filearea) {
530                     $fs->delete_area_files($this->context->id, $plugincomponent, $filearea);
531                 }
533                 if (!$plugin->delete_instance()) {
534                     $status[] = array('component'=>$componentstr,
535                                       'item'=>get_string('deleteallsubmissions','assign'),
536                                       'error'=>$plugin->get_error());
537                 }
538             }
540             foreach ($this->feedbackplugins as $plugin) {
541                 $fileareas = array();
542                 $plugincomponent = $plugin->get_subtype() . '_' . $plugin->get_type();
543                 $fileareas = $plugin->get_file_areas();
544                 foreach ($fileareas as $filearea) {
545                     $fs->delete_area_files($this->context->id, $plugincomponent, $filearea);
546                 }
548                 if (!$plugin->delete_instance()) {
549                     $status[] = array('component'=>$componentstr,
550                                       'item'=>get_string('deleteallsubmissions','assign'),
551                                       'error'=>$plugin->get_error());
552                 }
553             }
555             $assignssql = "SELECT a.id
556                              FROM {assign} a
557                            WHERE a.course=:course";
558             $params = array ("course" => $data->courseid);
560             $DB->delete_records_select('assign_submission', "assignment IN ($assignssql)", $params);
561             $status[] = array('component'=>$componentstr,
562                               'item'=>get_string('deleteallsubmissions','assign'),
563                               'error'=>false);
565             if (empty($data->reset_gradebook_grades)) {
566                 // Remove all grades from gradebook.
567                 require_once($CFG->dirroot.'/mod/assign/lib.php');
568                 assign_reset_gradebook($data->courseid);
569             }
570         }
571         // Updating dates - shift may be negative too.
572         if ($data->timeshift) {
573             shift_course_mod_dates('assign',
574                                     array('duedate', 'allowsubmissionsfromdate'),
575                                     $data->timeshift,
576                                     $data->courseid);
577             $status[] = array('component'=>$componentstr,
578                               'item'=>get_string('datechanged'),
579                               'error'=>false);
580         }
582         return $status;
583     }
585     /**
586      * Update the settings for a single plugin
587      *
588      * @param assign_plugin $plugin The plugin to update
589      * @param stdClass $formdata The form data
590      * @return bool false if an error occurs
591      */
592     private function update_plugin_instance(assign_plugin $plugin, stdClass $formdata) {
593         if ($plugin->is_visible()) {
594             $enabledname = $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled';
595             if ($formdata->$enabledname) {
596                 $plugin->enable();
597                 if (!$plugin->save_settings($formdata)) {
598                     print_error($plugin->get_error());
599                     return false;
600                 }
601             } else {
602                 $plugin->disable();
603             }
604         }
605         return true;
606     }
608     /**
609      * Update the gradebook information for this assignment
610      *
611      * @param bool $reset If true, will reset all grades in the gradbook for this assignment
612      * @param int $coursemoduleid This is required because it might not exist in the database yet
613      * @return bool
614      */
615     public function update_gradebook($reset, $coursemoduleid) {
616          global $CFG;
617         /** Include lib.php */
618         require_once($CFG->dirroot.'/mod/assign/lib.php');
619         $assign = clone $this->get_instance();
620         $assign->cmidnumber = $coursemoduleid;
621         $param = null;
622         if ($reset) {
623             $param = 'reset';
624         }
626         return assign_grade_item_update($assign, $param);
627     }
629     /** Load and cache the admin config for this module
630      *
631      * @return stdClass the plugin config
632      */
633     public function get_admin_config() {
634         if ($this->adminconfig) {
635             return $this->adminconfig;
636         }
637         $this->adminconfig = get_config('assign');
638         return $this->adminconfig;
639     }
642     /**
643      * Update the calendar entries for this assignment
644      *
645      * @param int $coursemoduleid - Required to pass this in because it might not exist in the database yet
646      * @return bool
647      */
648     public function update_calendar($coursemoduleid) {
649         global $DB, $CFG;
650         require_once($CFG->dirroot.'/calendar/lib.php');
652         // special case for add_instance as the coursemodule has not been set yet.
654         if ($this->get_instance()->duedate) {
655             $event = new stdClass();
657             if ($event->id = $DB->get_field('event', 'id', array('modulename'=>'assign', 'instance'=>$this->get_instance()->id))) {
659                 $event->name        = $this->get_instance()->name;
661                 $event->description = format_module_intro('assign', $this->get_instance(), $coursemoduleid);
662                 $event->timestart   = $this->get_instance()->duedate;
664                 $calendarevent = calendar_event::load($event->id);
665                 $calendarevent->update($event);
666             } else {
667                 $event = new stdClass();
668                 $event->name        = $this->get_instance()->name;
669                 $event->description = format_module_intro('assign', $this->get_instance(), $coursemoduleid);
670                 $event->courseid    = $this->get_instance()->course;
671                 $event->groupid     = 0;
672                 $event->userid      = 0;
673                 $event->modulename  = 'assign';
674                 $event->instance    = $this->get_instance()->id;
675                 $event->eventtype   = 'due';
676                 $event->timestart   = $this->get_instance()->duedate;
677                 $event->timeduration = 0;
679                 calendar_event::create($event);
680             }
681         } else {
682             $DB->delete_records('event', array('modulename'=>'assign', 'instance'=>$this->get_instance()->id));
683         }
684     }
687     /**
688      * Update this instance in the database
689      *
690      * @param stdClass $formdata - the data submitted from the form
691      * @return bool false if an error occurs
692      */
693     public function update_instance($formdata) {
694         global $DB;
696         $update = new stdClass();
697         $update->id = $formdata->instance;
698         $update->name = $formdata->name;
699         $update->timemodified = time();
700         $update->course = $formdata->course;
701         $update->intro = $formdata->intro;
702         $update->introformat = $formdata->introformat;
703         $update->alwaysshowdescription = $formdata->alwaysshowdescription;
704         $update->preventlatesubmissions = $formdata->preventlatesubmissions;
705         $update->submissiondrafts = $formdata->submissiondrafts;
706         $update->sendnotifications = $formdata->sendnotifications;
707         $update->sendlatenotifications = $formdata->sendlatenotifications;
708         $update->duedate = $formdata->duedate;
709         $update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
710         $update->grade = $formdata->grade;
712         $result = $DB->update_record('assign', $update);
713         $this->instance = $DB->get_record('assign', array('id'=>$update->id), '*', MUST_EXIST);
715         // load the assignment so the plugins have access to it
717         // call save_settings hook for submission plugins
718         foreach ($this->submissionplugins as $plugin) {
719             if (!$this->update_plugin_instance($plugin, $formdata)) {
720                 print_error($plugin->get_error());
721                 return false;
722             }
723         }
724         foreach ($this->feedbackplugins as $plugin) {
725             if (!$this->update_plugin_instance($plugin, $formdata)) {
726                 print_error($plugin->get_error());
727                 return false;
728             }
729         }
732         // update the database record
735         // update all the calendar events
736         $this->update_calendar($this->get_course_module()->id);
738         $this->update_gradebook(false, $this->get_course_module()->id);
740         $update = new stdClass();
741         $update->id = $this->get_instance()->id;
742         $update->nosubmissions = (!$this->is_any_submission_plugin_enabled()) ? 1: 0;
743         $DB->update_record('assign', $update);
749         return $result;
750     }
752     /**
753      * add elements in grading plugin form
754      *
755      * @param mixed $grade stdClass|null
756      * @param MoodleQuickForm $mform
757      * @param stdClass $data
758      * @param int $userid - The userid we are grading
759      * @return void
760      */
761     private function add_plugin_grade_elements($grade, MoodleQuickForm $mform, stdClass $data, $userid) {
762         foreach ($this->feedbackplugins as $plugin) {
763             if ($plugin->is_enabled() && $plugin->is_visible()) {
764                 $mform->addElement('header', 'header_' . $plugin->get_type(), $plugin->get_name());
765                 if (!$plugin->get_form_elements_for_user($grade, $mform, $data, $userid)) {
766                     $mform->removeElement('header_' . $plugin->get_type());
767                 }
768             }
769         }
770     }
774     /**
775      * Add one plugins settings to edit plugin form
776      *
777      * @param assign_plugin $plugin The plugin to add the settings from
778      * @param MoodleQuickForm $mform The form to add the configuration settings to. This form is modified directly (not returned)
779      * @return void
780      */
781     private function add_plugin_settings(assign_plugin $plugin, MoodleQuickForm $mform) {
782         global $CFG;
783         if ($plugin->is_visible()) {
784             // enabled
785             //tied disableIf rule to this select element
786             $mform->addElement('selectyesno', $plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled', $plugin->get_name());
787             $mform->addHelpButton($plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled', 'enabled', $plugin->get_subtype() . '_' . $plugin->get_type());
790             $default = get_config($plugin->get_subtype() . '_' . $plugin->get_type(), 'default');
791             if ($plugin->get_config('enabled') !== false) {
792                 $default = $plugin->is_enabled();
793             }
794             $mform->setDefault($plugin->get_subtype() . '_' . $plugin->get_type() . '_enabled', $default);
796             $plugin->get_settings($mform);
798         }
800     }
803     /**
804      * Add settings to edit plugin form
805      *
806      * @param MoodleQuickForm $mform The form to add the configuration settings to. This form is modified directly (not returned)
807      * @return void
808      */
809     public function add_all_plugin_settings(MoodleQuickForm $mform) {
810         $mform->addElement('header', 'general', get_string('submissionsettings', 'assign'));
812         foreach ($this->submissionplugins as $plugin) {
813             $this->add_plugin_settings($plugin, $mform);
815         }
816         $mform->addElement('header', 'general', get_string('feedbacksettings', 'assign'));
817         foreach ($this->feedbackplugins as $plugin) {
818             $this->add_plugin_settings($plugin, $mform);
819         }
820     }
822     /**
823      * Allow each plugin an opportunity to update the defaultvalues
824      * passed in to the settings form (needed to set up draft areas for
825      * editor and filemanager elements)
826      * @param array $defaultvalues
827      */
828     public function plugin_data_preprocessing(&$defaultvalues) {
829         foreach ($this->submissionplugins as $plugin) {
830             if ($plugin->is_visible()) {
831                 $plugin->data_preprocessing($defaultvalues);
832             }
833         }
834         foreach ($this->feedbackplugins as $plugin) {
835             if ($plugin->is_visible()) {
836                 $plugin->data_preprocessing($defaultvalues);
837             }
838         }
839     }
841     /**
842      * Get the name of the current module.
843      *
844      * @return string the module name (Assignment)
845      */
846     protected function get_module_name() {
847         if (isset(self::$modulename)) {
848             return self::$modulename;
849         }
850         self::$modulename = get_string('modulename', 'assign');
851         return self::$modulename;
852     }
854     /**
855      * Get the plural name of the current module.
856      *
857      * @return string the module name plural (Assignments)
858      */
859     protected function get_module_name_plural() {
860         if (isset(self::$modulenameplural)) {
861             return self::$modulenameplural;
862         }
863         self::$modulenameplural = get_string('modulenameplural', 'assign');
864         return self::$modulenameplural;
865     }
867     /**
868      * Has this assignment been constructed from an instance?
869      *
870      * @return bool
871      */
872     public function has_instance() {
873         return $this->instance || $this->get_course_module();
874     }
876     /**
877      * Get the settings for the current instance of this assignment
878      *
879      * @return stdClass The settings
880      */
881     public function get_instance() {
882         global $DB;
883         if ($this->instance) {
884             return $this->instance;
885         }
886         if ($this->get_course_module()) {
887             $this->instance = $DB->get_record('assign', array('id' => $this->get_course_module()->instance), '*', MUST_EXIST);
888         }
889         if (!$this->instance) {
890             throw new coding_exception('Improper use of the assignment class. Cannot load the assignment record.');
891         }
892         return $this->instance;
893     }
895     /**
896      * Get the context of the current course
897      * @return mixed context|null The course context
898      */
899     public function get_course_context() {
900         if (!$this->context && !$this->course) {
901             throw new coding_exception('Improper use of the assignment class. Cannot load the course context.');
902         }
903         if ($this->context) {
904             return $this->context->get_course_context();
905         } else {
906             return context_course::instance($this->course->id);
907         }
908     }
911     /**
912      * Get the current course module
913      *
914      * @return mixed stdClass|null The course module
915      */
916     public function get_course_module() {
917         if ($this->coursemodule) {
918             return $this->coursemodule;
919         }
920         if (!$this->context) {
921             return null;
922         }
924         if ($this->context->contextlevel == CONTEXT_MODULE) {
925             $this->coursemodule = get_coursemodule_from_id('assign', $this->context->instanceid, 0, false, MUST_EXIST);
926             return $this->coursemodule;
927         }
928         return null;
929     }
931     /**
932      * Get context module
933      *
934      * @return context
935      */
936     public function get_context() {
937         return $this->context;
938     }
940     /**
941      * Get the current course
942      * @return mixed stdClass|null The course
943      */
944     public function get_course() {
945         global $DB;
946         if ($this->course) {
947             return $this->course;
948         }
950         if (!$this->context) {
951             return null;
952         }
953         $this->course = $DB->get_record('course', array('id' => $this->get_course_context()->instanceid), '*', MUST_EXIST);
954         return $this->course;
955     }
957     /**
958      * Return a grade in user-friendly form, whether it's a scale or not
959      *
960      * @param mixed $grade int|null
961      * @param boolean $editing Are we allowing changes to this grade?
962      * @param int $userid The user id the grade belongs to
963      * @param int $modified Timestamp from when the grade was last modified
964      * @return string User-friendly representation of grade
965      */
966     public function display_grade($grade, $editing, $userid=0, $modified=0) {
967         global $DB;
969         static $scalegrades = array();
971         $o = '';
973         if ($this->get_instance()->grade >= 0) {
974             // Normal number
975             if ($editing && $this->get_instance()->grade > 0) {
976                 if ($grade < 0) {
977                     $displaygrade = '';
978                 } else {
979                     $displaygrade = format_float($grade);
980                 }
981                 $o .= '<input type="text" name="quickgrade_' . $userid . '" value="' . $displaygrade . '" size="6" maxlength="10" class="quickgrade"/>';
982                 $o .= '&nbsp;/&nbsp;' . format_float($this->get_instance()->grade,2);
983                 $o .= '<input type="hidden" name="grademodified_' . $userid . '" value="' . $modified . '"/>';
984                 return $o;
985             } else {
986                 $o .= '<input type="hidden" name="grademodified_' . $userid . '" value="' . $modified . '"/>';
987                 if ($grade == -1 || $grade === null) {
988                     $o .= '-';
989                     return $o;
990                 } else {
991                     $o .= format_float(($grade),2) .'&nbsp;/&nbsp;'. format_float($this->get_instance()->grade,2);
992                     return $o;
993                 }
994             }
996         } else {
997             // Scale
998             if (empty($this->cache['scale'])) {
999                 if ($scale = $DB->get_record('scale', array('id'=>-($this->get_instance()->grade)))) {
1000                     $this->cache['scale'] = make_menu_from_list($scale->scale);
1001                 } else {
1002                     $o .= '-';
1003                     return $o;
1004                 }
1005             }
1006             if ($editing) {
1007                 $o .= '<select name="quickgrade_' . $userid . '" class="quickgrade">';
1008                 $o .= '<option value="-1">' . get_string('nograde') . '</option>';
1009                 foreach ($this->cache['scale'] as $optionid => $option) {
1010                     $selected = '';
1011                     if ($grade == $optionid) {
1012                         $selected = 'selected="selected"';
1013                     }
1014                     $o .= '<option value="' . $optionid . '" ' . $selected . '>' . $option . '</option>';
1015                 }
1016                 $o .= '</select>';
1017                 $o .= '<input type="hidden" name="grademodified_' . $userid . '" value="' . $modified . '"/>';
1018                 return $o;
1019             } else {
1020                 $scaleid = (int)$grade;
1021                 if (isset($this->cache['scale'][$scaleid])) {
1022                     $o .= $this->cache['scale'][$scaleid];
1023                     return $o;
1024                 }
1025                 $o .= '-';
1026                 return $o;
1027             }
1028         }
1029     }
1031     /**
1032      * Load a list of users enrolled in the current course with the specified permission and group (0 for no group)
1033      *
1034      * @param int $currentgroup
1035      * @param bool $idsonly
1036      * @return array List of user records
1037      */
1038     public function list_participants($currentgroup, $idsonly) {
1039         if ($idsonly) {
1040             return get_enrolled_users($this->context, "mod/assign:submit", $currentgroup, 'u.id');
1041         } else {
1042             return get_enrolled_users($this->context, "mod/assign:submit", $currentgroup);
1043         }
1044     }
1046     /**
1047      * Load a count of users enrolled in the current course with the specified permission and group (0 for no group)
1048      *
1049      * @param int $currentgroup
1050      * @return int number of matching users
1051      */
1052     public function count_participants($currentgroup) {
1053         return count_enrolled_users($this->context, "mod/assign:submit", $currentgroup);
1054     }
1056     /**
1057      * Load a count of users submissions in the current module that require grading
1058      * This means the submission modification time is more recent than the
1059      * grading modification time and the status is SUBMITTED.
1060      *
1061      * @return int number of matching submissions
1062      */
1063     public function count_submissions_need_grading() {
1064         global $DB;
1066         $currentgroup = groups_get_activity_group($this->get_course_module(), true);
1067         list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
1069         $params['assignid'] = $this->get_instance()->id;
1070         $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1072         $sql = 'SELECT COUNT(s.userid)
1073                    FROM {assign_submission} s
1074                    LEFT JOIN {assign_grades} g ON
1075                         s.assignment = g.assignment AND
1076                         s.userid = g.userid
1077                    JOIN(' . $esql . ') AS e ON e.id = s.userid
1078                    WHERE
1079                         s.assignment = :assignid AND
1080                         s.timemodified IS NOT NULL AND
1081                         s.status = :submitted AND
1082                         (s.timemodified > g.timemodified OR g.timemodified IS NULL)';
1084         return $DB->count_records_sql($sql, $params);
1085     }
1087     /**
1088      * Load a count of users enrolled in the current course with the specified permission and group (optional)
1089      *
1090      * @param string $status The submission status - should match one of the constants
1091      * @return int number of matching submissions
1092      */
1093     public function count_submissions_with_status($status) {
1094         global $DB;
1096         $currentgroup = groups_get_activity_group($this->get_course_module(), true);
1097         list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
1099         $params['assignid'] = $this->get_instance()->id;
1100         $params['submissionstatus'] = $status;
1102         $sql = 'SELECT COUNT(s.userid)
1103                    FROM {assign_submission} s
1104                    LEFT JOIN {assign_grades} g ON
1105                         s.assignment = g.assignment AND
1106                         s.userid = g.userid
1107                    JOIN(' . $esql . ') AS e ON e.id = s.userid
1108                    WHERE
1109                         s.assignment = :assignid AND
1110                         s.timemodified IS NOT NULL AND
1111                         s.status = :submissionstatus';
1113         return $DB->count_records_sql($sql, $params);
1114     }
1116     /**
1117      * Utility function to get the userid for every row in the grading table
1118      * so the order can be frozen while we iterate it
1119      *
1120      * @return array An array of userids
1121      */
1122     private function get_grading_userid_list(){
1123         $filter = get_user_preferences('assign_filter', '');
1124         $table = new assign_grading_table($this, 0, $filter, 0, false);
1126         $useridlist = $table->get_column_data('userid');
1128         return $useridlist;
1129     }
1132     /**
1133      * Utility function get the userid based on the row number of the grading table.
1134      * This takes into account any active filters on the table.
1135      *
1136      * @param int $num The row number of the user
1137      * @param bool $last This is set to true if this is the last user in the table
1138      * @return mixed The user id of the matching user or false if there was an error
1139      */
1140     private function get_userid_for_row($num, $last){
1141         if (!array_key_exists('userid_for_row', $this->cache)) {
1142             $this->cache['userid_for_row'] = array();
1143         }
1144         if (array_key_exists($num, $this->cache['userid_for_row'])) {
1145             list($userid, $last) = $this->cache['userid_for_row'][$num];
1146             return $userid;
1147         }
1149         $filter = get_user_preferences('assign_filter', '');
1150         $table = new assign_grading_table($this, 0, $filter, 0, false);
1152         $userid = $table->get_cell_data($num, 'userid', $last);
1154         $this->cache['userid_for_row'][$num] = array($userid, $last);
1155         return $userid;
1156     }
1158     /**
1159      * Return all assignment submissions by ENROLLED students (even empty)
1160      *
1161      * @param string $sort optional field names for the ORDER BY in the sql query
1162      * @param string $dir optional specifying the sort direction, defaults to DESC
1163      * @return array The submission objects indexed by id
1164      */
1165     private function get_all_submissions( $sort="", $dir="DESC") {
1166         global $DB;
1168         $currentgroup = groups_get_activity_group($this->get_course_module(), true);
1169         list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
1171         $params['assignid'] = $this->get_instance()->id;
1173         $sql = 'SELECT s.*, u.lastname, u.firstname, u.username
1174                    FROM {assign_submission} s
1175                    JOIN {user} u ON s.userid = u.id
1176                    JOIN(' . $esql . ') AS e ON e.id = s.userid
1177                    WHERE
1178                         s.assignment = :assignid AND
1179                         s.timemodified IS NOT NULL';
1181         if ($sort == "lastname" or $sort == "firstname") {
1182             $sort = "u.$sort $dir";
1183         } else if (empty($sort)) {
1184             $sort = "s.timemodified DESC";
1185         } else {
1186             $sort = "s.$sort $dir";
1187         }
1189         $sql .= ' ORDER BY ' . $sort;
1191         return $DB->get_records_sql($sql, $params);
1192     }
1194     /**
1195      * Generate zip file from array of given files
1196      *
1197      * @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
1198      * @return path of temp file - note this returned file does not have a .zip extension - it is a temp file.
1199      */
1200      private function pack_files($filesforzipping) {
1201          global $CFG;
1202          //create path for new zip file.
1203          $tempzip = tempnam($CFG->tempdir.'/', 'assignment_');
1204          //zip files
1205          $zipper = new zip_packer();
1206          if ($zipper->archive_to_pathname($filesforzipping, $tempzip)) {
1207              return $tempzip;
1208          }
1209          return false;
1210     }
1212     /**
1213      * Finds all assignment notifications that have yet to be mailed out, and mails them.
1214      *
1215      * Cron function to be run periodically according to the moodle cron
1216      *
1217      * @return bool
1218      */
1219     static function cron() {
1220         global $DB;
1222         // only ever send a max of one days worth of updates
1223         $yesterday = time() - (24 * 3600);
1224         $timenow   = time();
1226         // Collect all submissions from the past 24 hours that require mailing.
1227         $sql = "SELECT s.*, a.course, a.name, g.*, g.id as gradeid, g.timemodified as lastmodified
1228                  FROM {assign} a
1229                  JOIN {assign_grades} g ON g.assignment = a.id
1230             LEFT JOIN {assign_submission} s ON s.assignment = a.id AND s.userid = g.userid
1231                 WHERE g.timemodified >= :yesterday AND
1232                       g.timemodified <= :today AND
1233                       g.mailed = 0";
1234         $params = array('yesterday' => $yesterday, 'today' => $timenow);
1235         $submissions = $DB->get_records_sql($sql, $params);
1237         if (empty($submissions)) {
1238             mtrace('done.');
1239             return true;
1240         }
1242         mtrace('Processing ' . count($submissions) . ' assignment submissions ...');
1244         // Preload courses we are going to need those.
1245         $courseids = array();
1246         foreach ($submissions as $submission) {
1247             $courseids[] = $submission->course;
1248         }
1249         // Filter out duplicates
1250         $courseids = array_unique($courseids);
1251         $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
1252         list($courseidsql, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
1253         $sql = "SELECT c.*, {$ctxselect}
1254                   FROM {course} c
1255              LEFT JOIN {context} ctx ON ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel
1256                  WHERE c.id {$courseidsql}";
1257         $params['contextlevel'] = CONTEXT_COURSE;
1258         $courses = $DB->get_records_sql($sql, $params);
1259         // Clean up... this could go on for a while.
1260         unset($courseids);
1261         unset($ctxselect);
1262         unset($courseidsql);
1263         unset($params);
1265         // Simple array we'll use for caching modules.
1266         $modcache = array();
1268         // Message students about new feedback
1269         foreach ($submissions as $submission) {
1271             mtrace("Processing assignment submission $submission->id ...");
1273             // do not cache user lookups - could be too many
1274             if (!$user = $DB->get_record("user", array("id"=>$submission->userid))) {
1275                 mtrace("Could not find user $submission->userid");
1276                 continue;
1277             }
1279             // use a cache to prevent the same DB queries happening over and over
1280             if (!array_key_exists($submission->course, $courses)) {
1281                 mtrace("Could not find course $submission->course");
1282                 continue;
1283             }
1284             $course = $courses[$submission->course];
1285             if (isset($course->ctxid)) {
1286                 // Context has not yet been preloaded. Do so now.
1287                 context_helper::preload_from_record($course);
1288             }
1290             // Override the language and timezone of the "current" user, so that
1291             // mail is customised for the receiver.
1292             cron_setup_user($user, $course);
1294             // context lookups are already cached
1295             $coursecontext = context_course::instance($course->id);
1296             if (!is_enrolled($coursecontext, $user->id)) {
1297                 $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
1298                 mtrace(fullname($user)." not an active participant in " . $courseshortname);
1299                 continue;
1300             }
1302             if (!$grader = $DB->get_record("user", array("id"=>$submission->grader))) {
1303                 mtrace("Could not find grader $submission->grader");
1304                 continue;
1305             }
1307             if (!array_key_exists($submission->assignment, $modcache)) {
1308                 if (! $mod = get_coursemodule_from_instance("assign", $submission->assignment, $course->id)) {
1309                     mtrace("Could not find course module for assignment id $submission->assignment");
1310                     continue;
1311                 }
1312                 $modcache[$submission->assignment] = $mod;
1313             } else {
1314                 $mod = $modcache[$submission->assignment];
1315             }
1316             // context lookups are already cached
1317             $contextmodule = context_module::instance($mod->id);
1319             if (!$mod->visible) {
1320                 // Hold mail notification for hidden assignments until later
1321                 continue;
1322             }
1324             // need to send this to the student
1325             $messagetype = 'feedbackavailable';
1326             $eventtype = 'assign_notification';
1327             $updatetime = $submission->lastmodified;
1328             $modulename = get_string('modulename', 'assign');
1329             self::send_assignment_notification($grader, $user, $messagetype, $eventtype, $updatetime, $mod, $contextmodule, $course, $modulename, $submission->name);
1331             $grade = new stdClass();
1332             $grade->id = $submission->gradeid;
1333             $grade->mailed = 1;
1334             $DB->update_record('assign_grades', $grade);
1336             mtrace('Done');
1337         }
1338         mtrace('Done processing ' . count($submissions) . ' assignment submissions');
1340         cron_setup_user();
1342         // Free up memory just to be sure
1343         unset($courses);
1344         unset($modcache);
1346         return true;
1347     }
1349     /**
1350      * Update a grade in the grade table for the assignment and in the gradebook
1351      *
1352      * @param stdClass $grade a grade record keyed on id
1353      * @return bool true for success
1354      */
1355     private function update_grade($grade) {
1356         global $DB;
1358         $grade->timemodified = time();
1360         if ($grade->grade && $grade->grade != -1) {
1361             if ($this->get_instance()->grade > 0) {
1362                 if (!is_numeric($grade->grade)) {
1363                     return false;
1364                 } else if ($grade->grade > $this->get_instance()->grade) {
1365                     return false;
1366                 } else if ($grade->grade < 0) {
1367                     return false;
1368                 }
1369             } else {
1370                 // this is a scale
1371                 if ($scale = $DB->get_record('scale', array('id' => -($this->get_instance()->grade)))) {
1372                     $scaleoptions = make_menu_from_list($scale->scale);
1373                     if (!array_key_exists((int) $grade->grade, $scaleoptions)) {
1374                         return false;
1375                     }
1376                 }
1377             }
1378         }
1380         $result = $DB->update_record('assign_grades', $grade);
1381         if ($result) {
1382             $this->gradebook_item_update(null, $grade);
1383         }
1384         return $result;
1385     }
1387     /**
1388      * display the submission that is used by a plugin
1389      * Uses url parameters 'sid', 'gid' and 'plugin'
1390      * @param string $pluginsubtype
1391      * @return string
1392      */
1393     private function view_plugin_content($pluginsubtype) {
1394         global $USER;
1396         $o = '';
1398         $submissionid = optional_param('sid', 0, PARAM_INT);
1399         $gradeid = optional_param('gid', 0, PARAM_INT);
1400         $plugintype = required_param('plugin', PARAM_TEXT);
1401         $item = null;
1402         if ($pluginsubtype == 'assignsubmission') {
1403             $plugin = $this->get_submission_plugin_by_type($plugintype);
1404             if ($submissionid <= 0) {
1405                 throw new coding_exception('Submission id should not be 0');
1406             }
1407             $item = $this->get_submission($submissionid);
1409             // permissions
1410             if ($item->userid != $USER->id) {
1411                 require_capability('mod/assign:grade', $this->context);
1412             }
1413             $o .= $this->output->render(new assign_header($this->get_instance(),
1414                                                               $this->get_context(),
1415                                                               $this->show_intro(),
1416                                                               $this->get_course_module()->id,
1417                                                               $plugin->get_name()));
1418             $o .= $this->output->render(new assign_submission_plugin_submission($plugin,
1419                                                               $item,
1420                                                               assign_submission_plugin_submission::FULL,
1421                                                               $this->get_course_module()->id,
1422                                                               $this->get_return_action(),
1423                                                               $this->get_return_params()));
1425             $this->add_to_log('view submission', get_string('viewsubmissionforuser', 'assign', $item->userid));
1426         } else {
1427             $plugin = $this->get_feedback_plugin_by_type($plugintype);
1428             if ($gradeid <= 0) {
1429                 throw new coding_exception('Grade id should not be 0');
1430             }
1431             $item = $this->get_grade($gradeid);
1432             // permissions
1433             if ($item->userid != $USER->id) {
1434                 require_capability('mod/assign:grade', $this->context);
1435             }
1436             $o .= $this->output->render(new assign_header($this->get_instance(),
1437                                                               $this->get_context(),
1438                                                               $this->show_intro(),
1439                                                               $this->get_course_module()->id,
1440                                                               $plugin->get_name()));
1441             $o .= $this->output->render(new assign_feedback_plugin_feedback($plugin,
1442                                                               $item,
1443                                                               assign_feedback_plugin_feedback::FULL,
1444                                                               $this->get_course_module()->id,
1445                                                               $this->get_return_action(),
1446                                                               $this->get_return_params()));
1447             $this->add_to_log('view feedback', get_string('viewfeedbackforuser', 'assign', $item->userid));
1448         }
1451         $o .= $this->view_return_links();
1453         $o .= $this->view_footer();
1454         return $o;
1455     }
1457     /**
1458      * render the content in editor that is often used by plugin
1459      *
1460      * @param string $filearea
1461      * @param int  $submissionid
1462      * @param string $plugintype
1463      * @param string $editor
1464      * @param string $component
1465      * @return string
1466      */
1467     public function render_editor_content($filearea, $submissionid, $plugintype, $editor, $component) {
1468         global $CFG;
1470         $result = '';
1472         $plugin = $this->get_submission_plugin_by_type($plugintype);
1474         $text = $plugin->get_editor_text($editor, $submissionid);
1475         $format = $plugin->get_editor_format($editor, $submissionid);
1477         $finaltext = file_rewrite_pluginfile_urls($text, 'pluginfile.php', $this->get_context()->id, $component, $filearea, $submissionid);
1478         $result .= format_text($finaltext, $format, array('overflowdiv' => true, 'context' => $this->get_context()));
1482         if ($CFG->enableportfolios) {
1483             require_once($CFG->libdir . '/portfoliolib.php');
1485             $button = new portfolio_add_button();
1486             $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');
1487             $fs = get_file_storage();
1489             if ($files = $fs->get_area_files($this->context->id, $component,$filearea, $submissionid, "timemodified", false)) {
1490                 $button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
1491             } else {
1492                 $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
1493             }
1494             $result .= $button->to_html();
1495         }
1496         return $result;
1497     }
1499     /**
1500      * Display a grading error
1501      *
1502      * @param string $message - The description of the result
1503      * @return string
1504      */
1505     private function view_quickgrading_result($message) {
1506         $o = '';
1507         $o .= $this->output->render(new assign_header($this->get_instance(),
1508                                                       $this->get_context(),
1509                                                       $this->show_intro(),
1510                                                       $this->get_course_module()->id,
1511                                                       get_string('quickgradingresult', 'assign')));
1512         $o .= $this->output->render(new assign_quickgrading_result($message, $this->get_course_module()->id));
1513         $o .= $this->view_footer();
1514         return $o;
1515     }
1517     /**
1518      * Display the page footer
1519      *
1520      * @return string
1521      */
1522     private function view_footer() {
1523         return $this->output->render_footer();
1524     }
1526     /**
1527      * Does this user have grade permission for this assignment
1528      *
1529      * @return bool
1530      */
1531     private function can_grade() {
1532         // Permissions check
1533         if (!has_capability('mod/assign:grade', $this->context)) {
1534             return false;
1535         }
1537         return true;
1538     }
1540     /**
1541      * Download a zip file of all assignment submissions
1542      *
1543      * @return void
1544      */
1545     private function download_submissions() {
1546         global $CFG,$DB;
1548         // more efficient to load this here
1549         require_once($CFG->libdir.'/filelib.php');
1551         // load all submissions
1552         $submissions = $this->get_all_submissions('','');
1554         if (empty($submissions)) {
1555             print_error('errornosubmissions', 'assign');
1556             return;
1557         }
1559         // build a list of files to zip
1560         $filesforzipping = array();
1561         $fs = get_file_storage();
1563         $groupmode = groups_get_activity_groupmode($this->get_course_module());
1564         $groupid = 0;   // All users
1565         $groupname = '';
1566         if ($groupmode) {
1567             $groupid = groups_get_activity_group($this->get_course_module(), true);
1568             $groupname = groups_get_group_name($groupid).'-';
1569         }
1571         // construct the zip file name
1572         $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.
1574         // get all the files for each submission
1575         foreach ($submissions as $submission) {
1576             $userid = $submission->userid; //get userid
1577             if ((groups_is_member($groupid,$userid) or !$groupmode or !$groupid)) {
1578                 // get the plugins to add their own files to the zip
1580                 $prefix = clean_filename(fullname($submission) . "_" .$userid . "_");
1582                 foreach ($this->submissionplugins as $plugin) {
1583                     if ($plugin->is_enabled() && $plugin->is_visible()) {
1584                         $pluginfiles = $plugin->get_files($submission);
1587                         foreach ($pluginfiles as $zipfilename => $file) {
1588                             $filesforzipping[$prefix . $zipfilename] = $file;
1589                         }
1590                     }
1591                 }
1593             }
1594         } // end of foreach loop
1595         if ($zipfile = $this->pack_files($filesforzipping)) {
1596             $this->add_to_log('download all submissions', get_string('downloadall', 'assign'));
1597             send_temp_file($zipfile, $filename); //send file and delete after sending.
1598         }
1599     }
1601     /**
1602      * Util function to add a message to the log
1603      *
1604      * @param string $action The current action
1605      * @param string $info A detailed description of the change. But no more than 255 characters.
1606      * @param string $url The url to the assign module instance.
1607      * @return void
1608      */
1609     public function add_to_log($action = '', $info = '', $url='') {
1610         global $USER;
1612         $fullurl = 'view.php?id=' . $this->get_course_module()->id;
1613         if ($url != '') {
1614             $fullurl .= '&' . $url;
1615         }
1617         add_to_log($this->get_course()->id, 'assign', $action, $fullurl, $info, $this->get_course_module()->id, $USER->id);
1618     }
1620     /**
1621      * Load the submission object for a particular user, optionally creating it if required
1622      *
1623      * @param int $userid The id of the user whose submission we want or 0 in which case USER->id is used
1624      * @param bool $create optional Defaults to false. If set to true a new submission object will be created in the database
1625      * @return stdClass The submission
1626      */
1627     private function get_user_submission($userid, $create) {
1628         global $DB, $USER;
1630         if (!$userid) {
1631             $userid = $USER->id;
1632         }
1633         // if the userid is not null then use userid
1634         $submission = $DB->get_record('assign_submission', array('assignment'=>$this->get_instance()->id, 'userid'=>$userid));
1636         if ($submission) {
1637             return $submission;
1638         }
1639         if ($create) {
1640             $submission = new stdClass();
1641             $submission->assignment   = $this->get_instance()->id;
1642             $submission->userid       = $userid;
1643             $submission->timecreated = time();
1644             $submission->timemodified = $submission->timecreated;
1645             $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
1646             $sid = $DB->insert_record('assign_submission', $submission);
1647             $submission->id = $sid;
1648             return $submission;
1649         }
1650         return false;
1651     }
1653     /**
1654      * Load the submission object from it's id
1655      *
1656      * @param int $submissionid The id of the submission we want
1657      * @return stdClass The submission
1658      */
1659     private function get_submission($submissionid) {
1660         global $DB;
1662         return $DB->get_record('assign_submission', array('assignment'=>$this->get_instance()->id, 'id'=>$submissionid), '*', MUST_EXIST);
1663     }
1665     /**
1666      * This will retrieve a grade object from the db, optionally creating it if required
1667      *
1668      * @param int $userid The user we are grading
1669      * @param bool $create If true the grade will be created if it does not exist
1670      * @return stdClass The grade record
1671      */
1672     private function get_user_grade($userid, $create) {
1673         global $DB, $USER;
1675         if (!$userid) {
1676             $userid = $USER->id;
1677         }
1679         // if the userid is not null then use userid
1680         $grade = $DB->get_record('assign_grades', array('assignment'=>$this->get_instance()->id, 'userid'=>$userid));
1682         if ($grade) {
1683             return $grade;
1684         }
1685         if ($create) {
1686             $grade = new stdClass();
1687             $grade->assignment   = $this->get_instance()->id;
1688             $grade->userid       = $userid;
1689             $grade->timecreated = time();
1690             $grade->timemodified = $grade->timecreated;
1691             $grade->locked = 0;
1692             $grade->grade = -1;
1693             $grade->grader = $USER->id;
1694             $gid = $DB->insert_record('assign_grades', $grade);
1695             $grade->id = $gid;
1696             return $grade;
1697         }
1698         return false;
1699     }
1701     /**
1702      * This will retrieve a grade object from the db
1703      *
1704      * @param int $gradeid The id of the grade
1705      * @return stdClass The grade record
1706      */
1707     private function get_grade($gradeid) {
1708         global $DB;
1710         return $DB->get_record('assign_grades', array('assignment'=>$this->get_instance()->id, 'id'=>$gradeid), '*', MUST_EXIST);
1711     }
1713     /**
1714      * Print the grading page for a single user submission
1715      *
1716      * @param moodleform $mform
1717      * @param int $offset
1718      * @return string
1719      */
1720     private function view_single_grade_page($mform, $offset=0) {
1721         global $DB, $CFG;
1723         $o = '';
1725         // Include grade form
1726         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1728         // Need submit permission to submit an assignment
1729         require_capability('mod/assign:grade', $this->context);
1731         $o .= $this->output->render(new assign_header($this->get_instance(),
1732                                                       $this->get_context(), false, $this->get_course_module()->id,get_string('grading', 'assign')));
1734         $rownum = required_param('rownum', PARAM_INT) + $offset;
1735         $useridlist = optional_param('useridlist', '', PARAM_TEXT);
1736         if ($useridlist) {
1737             $useridlist = explode(',', $useridlist);
1738         } else {
1739             $useridlist = $this->get_grading_userid_list();
1740         }
1741         $last = false;
1742         $userid = $useridlist[$rownum];
1743         if ($rownum == count($useridlist) - 1) {
1744             $last = true;
1745         }
1746         if (!$userid) {
1747             throw new coding_exception('Row is out of bounds for the current grading table: ' . $rownum);
1748         }
1749         $user = $DB->get_record('user', array('id' => $userid));
1750         if ($user) {
1751             $o .= $this->output->render(new assign_user_summary($user, $this->get_course()->id, has_capability('moodle/site:viewfullnames', $this->get_course_context())));
1752         }
1753         $submission = $this->get_user_submission($userid, false);
1754         // get the current grade
1755         $grade = $this->get_user_grade($userid, false);
1756         if ($this->can_view_submission($userid)) {
1757             $gradelocked = ($grade && $grade->locked) || $this->grading_disabled($userid);
1758             $o .= $this->output->render(new assign_submission_status($this->get_instance()->allowsubmissionsfromdate,
1759                                                               $this->get_instance()->alwaysshowdescription,
1760                                                               $submission,
1761                                                               $this->is_any_submission_plugin_enabled(),
1762                                                               $gradelocked,
1763                                                               $this->is_graded($userid),
1764                                                               $this->get_instance()->duedate,
1765                                                               $this->get_submission_plugins(),
1766                                                               $this->get_return_action(),
1767                                                               $this->get_return_params(),
1768                                                               $this->get_course_module()->id,
1769                                                               assign_submission_status::GRADER_VIEW,
1770                                                               false,
1771                                                               false));
1772         }
1773         if ($grade) {
1774             $data = new stdClass();
1775             if ($grade->grade !== NULL && $grade->grade >= 0) {
1776                 $data->grade = format_float($grade->grade,2);
1777             }
1778         } else {
1779             $data = new stdClass();
1780             $data->grade = '';
1781         }
1783         // now show the grading form
1784         if (!$mform) {
1785             $mform = new mod_assign_grade_form(null, array($this, $data, array('rownum'=>$rownum, 'useridlist'=>$useridlist, 'last'=>$last)), 'post', '', array('class'=>'gradeform'));
1786         }
1787         $o .= $this->output->render(new assign_form('gradingform',$mform));
1789         $this->add_to_log('view grading form', get_string('viewgradingformforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))));
1791         $o .= $this->view_footer();
1792         return $o;
1793     }
1797     /**
1798      * View a link to go back to the previous page. Uses url parameters returnaction and returnparams.
1799      *
1800      * @return string
1801      */
1802     private function view_return_links() {
1804         $returnaction = optional_param('returnaction','', PARAM_ALPHA);
1805         $returnparams = optional_param('returnparams','', PARAM_TEXT);
1807         $params = array();
1808         parse_str($returnparams, $params);
1809         $params = array_merge( array('id' => $this->get_course_module()->id, 'action' => $returnaction), $params);
1811         return $this->output->single_button(new moodle_url('/mod/assign/view.php', $params), get_string('back'), 'get');
1813     }
1815     /**
1816      * View the grading table of all submissions for this assignment
1817      *
1818      * @return string
1819      */
1820     private function view_grading_table() {
1821         global $USER, $CFG;
1822         // Include grading options form
1823         require_once($CFG->dirroot . '/mod/assign/gradingoptionsform.php');
1824         require_once($CFG->dirroot . '/mod/assign/quickgradingform.php');
1825         require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
1826         $o = '';
1828         $links = array();
1829         if (has_capability('gradereport/grader:view', $this->get_course_context()) &&
1830                 has_capability('moodle/grade:viewall', $this->get_course_context())) {
1831             $gradebookurl = '/grade/report/grader/index.php?id=' . $this->get_course()->id;
1832             $links[$gradebookurl] = get_string('viewgradebook', 'assign');
1833         }
1834         if ($this->is_any_submission_plugin_enabled()) {
1835             $downloadurl = '/mod/assign/view.php?id=' . $this->get_course_module()->id . '&action=downloadall';
1836             $links[$downloadurl] = get_string('downloadall', 'assign');
1837         }
1839         $gradingactions = new url_select($links);
1840         $gradingactions->set_label(get_string('choosegradingaction', 'assign'));
1842         $gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
1844         $perpage = get_user_preferences('assign_perpage', 10);
1845         $filter = get_user_preferences('assign_filter', '');
1846         $controller = $gradingmanager->get_active_controller();
1847         $showquickgrading = empty($controller);
1848         if (optional_param('action', '', PARAM_ALPHA) == 'saveoptions') {
1849             $quickgrading = optional_param('quickgrading', false, PARAM_BOOL);
1850             set_user_preference('assign_quickgrading', $quickgrading);
1851         }
1852         $quickgrading = get_user_preferences('assign_quickgrading', false);
1854         // print options  for changing the filter and changing the number of results per page
1855         $gradingoptionsform = new mod_assign_grading_options_form(null,
1856                                                                   array('cm'=>$this->get_course_module()->id,
1857                                                                         'contextid'=>$this->context->id,
1858                                                                         'userid'=>$USER->id,
1859                                                                         'submissionsenabled'=>$this->is_any_submission_plugin_enabled(),
1860                                                                         'showquickgrading'=>$showquickgrading,
1861                                                                         'quickgrading'=>$quickgrading),
1862                                                                   'post', '',
1863                                                                   array('class'=>'gradingoptionsform'));
1865         $gradingbatchoperationsform = new mod_assign_grading_batch_operations_form(null,
1866                                                                   array('cm'=>$this->get_course_module()->id,
1867                                                                         'submissiondrafts'=>$this->get_instance()->submissiondrafts),
1868                                                                   'post', '',
1869                                                                   array('class'=>'gradingbatchoperationsform'));
1871         $gradingoptionsdata = new stdClass();
1872         $gradingoptionsdata->perpage = $perpage;
1873         $gradingoptionsdata->filter = $filter;
1874         $gradingoptionsform->set_data($gradingoptionsdata);
1876         $actionformtext = $this->output->render($gradingactions);
1877         $o .= $this->output->render(new assign_header($this->get_instance(),
1878                                                       $this->get_context(), false, $this->get_course_module()->id, get_string('grading', 'assign'), $actionformtext));
1879         $o .= groups_print_activity_menu($this->get_course_module(), $CFG->wwwroot . '/mod/assign/view.php?id=' . $this->get_course_module()->id.'&action=grading', true);
1881         // plagiarism update status apearring in the grading book
1882         if (!empty($CFG->enableplagiarism)) {
1883             /** Include plagiarismlib.php */
1884             require_once($CFG->libdir . '/plagiarismlib.php');
1885             $o .= plagiarism_update_status($this->get_course(), $this->get_course_module());
1886         }
1888         // load and print the table of submissions
1889         if ($showquickgrading && $quickgrading) {
1890             $table = $this->output->render(new assign_grading_table($this, $perpage, $filter, 0, true));
1891             $quickgradingform = new mod_assign_quick_grading_form(null,
1892                                                                   array('cm'=>$this->get_course_module()->id,
1893                                                                         'gradingtable'=>$table));
1894             $o .= $this->output->render(new assign_form('quickgradingform', $quickgradingform));
1895         } else {
1896             $o .= $this->output->render(new assign_grading_table($this, $perpage, $filter, 0, false));
1897         }
1899         $currentgroup = groups_get_activity_group($this->get_course_module(), true);
1900         $users = array_keys($this->list_participants($currentgroup, true));
1901         if (count($users) != 0) {
1902             // if no enrolled user in a course then don't display the batch operations feature
1903             $o .= $this->output->render(new assign_form('gradingbatchoperationsform', $gradingbatchoperationsform));
1904         }
1905         $o .= $this->output->render(new assign_form('gradingoptionsform', $gradingoptionsform, 'M.mod_assign.init_grading_options'));
1906         return $o;
1907     }
1909     /**
1910      * View entire grading page.
1911      *
1912      * @return string
1913      */
1914     private function view_grading_page() {
1915         global $CFG;
1917         $o = '';
1918         // Need submit permission to submit an assignment
1919         require_capability('mod/assign:grade', $this->context);
1920         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
1922         // only load this if it is
1924         $o .= $this->view_grading_table();
1926         $o .= $this->view_footer();
1927         $this->add_to_log('view submission grading table', get_string('viewsubmissiongradingtable', 'assign'));
1928         return $o;
1929     }
1931     /**
1932      * Capture the output of the plagiarism plugins disclosures and return it as a string
1933      *
1934      * @return void
1935      */
1936     private function plagiarism_print_disclosure() {
1937         global $CFG;
1938         $o = '';
1940         if (!empty($CFG->enableplagiarism)) {
1941             /** Include plagiarismlib.php */
1942             require_once($CFG->libdir . '/plagiarismlib.php');
1944             $o .= plagiarism_print_disclosure($this->get_course_module()->id);
1945         }
1947         return $o;
1948     }
1950     /**
1951      * message for students when assignment submissions have been closed
1952      *
1953      * @return string
1954      */
1955     private function view_student_error_message() {
1956         global $CFG;
1958         $o = '';
1959         // Need submit permission to submit an assignment
1960         require_capability('mod/assign:submit', $this->context);
1962         $o .= $this->output->render(new assign_header($this->get_instance(),
1963                                                       $this->get_context(),
1964                                                       $this->show_intro(),
1965                                                       $this->get_course_module()->id,
1966                                                       get_string('editsubmission', 'assign')));
1968         $o .= $this->output->notification(get_string('submissionsclosed', 'assign'));
1970         $o .= $this->view_footer();
1972         return $o;
1974     }
1976     /**
1977      * View edit submissions page.
1978      *
1979      * @param moodleform $mform
1980      * @param array $notices A list of notices to display at the top of the edit submission form (e.g. from plugins).
1981      * @return void
1982      */
1983     private function view_edit_submission_page($mform, $notices) {
1984         global $CFG;
1986         $o = '';
1987         // Include submission form
1988         require_once($CFG->dirroot . '/mod/assign/submission_form.php');
1989         // Need submit permission to submit an assignment
1990         require_capability('mod/assign:submit', $this->context);
1992         if (!$this->submissions_open()) {
1993             return $this->view_student_error_message();
1994         }
1995         $o .= $this->output->render(new assign_header($this->get_instance(),
1996                                                       $this->get_context(),
1997                                                       $this->show_intro(),
1998                                                       $this->get_course_module()->id,
1999                                                       get_string('editsubmission', 'assign')));
2000         $o .= $this->plagiarism_print_disclosure();
2001         $data = new stdClass();
2003         if (!$mform) {
2004             $mform = new mod_assign_submission_form(null, array($this, $data));
2005         }
2007         foreach ($notices as $notice) {
2008             $o .= $this->output->notification($notice);
2009         }
2011         $o .= $this->output->render(new assign_form('editsubmissionform',$mform));
2013         $o .= $this->view_footer();
2014         $this->add_to_log('view submit assignment form', get_string('viewownsubmissionform', 'assign'));
2016         return $o;
2017     }
2019     /**
2020      * See if this assignment has a grade yet
2021      *
2022      * @param int $userid
2023      * @return bool
2024      */
2025     private function is_graded($userid) {
2026         $grade = $this->get_user_grade($userid, false);
2027         if ($grade) {
2028             return ($grade->grade !== NULL && $grade->grade >= 0);
2029         }
2030         return false;
2031     }
2034     /**
2035      * Perform an access check to see if the current $USER can view this users submission
2036      *
2037      * @param int $userid
2038      * @return bool
2039      */
2040     public function can_view_submission($userid) {
2041         global $USER;
2043         if (!is_enrolled($this->get_course_context(), $userid)) {
2044             return false;
2045         }
2046         if ($userid == $USER->id && !has_capability('mod/assign:submit', $this->context)) {
2047             return false;
2048         }
2049         if ($userid != $USER->id && !has_capability('mod/assign:grade', $this->context)) {
2050             return false;
2051         }
2052         return true;
2053     }
2055     /**
2056      * Ask the user to confirm they want to perform this batch operation
2057      * @return string
2058      */
2059     private function process_batch_grading_operation() {
2060         global $CFG;
2061         require_once($CFG->dirroot . '/mod/assign/gradingbatchoperationsform.php');
2062         require_sesskey();
2064         $gradingbatchoperationsform = new mod_assign_grading_batch_operations_form(null,
2065                                                                   array('cm'=>$this->get_course_module()->id,
2066                                                                         'submissiondrafts'=>$this->get_instance()->submissiondrafts),
2067                                                                   'post', '',
2068                                                                   array('class'=>'gradingbatchoperationsform'));
2070         if ($data = $gradingbatchoperationsform->get_data()) {
2071             // get the list of users
2072             $users = $data->selectedusers;
2073             $userlist = explode(',', $users);
2075             foreach ($userlist as $userid) {
2076                 if ($data->operation == 'lock') {
2077                     $this->process_lock($userid);
2078                 } else if ($data->operation == 'unlock') {
2079                     $this->process_unlock($userid);
2080                 } else if ($data->operation == 'reverttodraft') {
2081                     $this->process_revert_to_draft($userid);
2082                 }
2083             }
2084         }
2086         return true;
2087     }
2089     /**
2090      * Ask the user to confirm they want to submit their work for grading
2091      * @return string
2092      */
2093     private function check_submit_for_grading() {
2094         global $USER;
2095         // Check that all of the submission plugins are ready for this submission
2096         $notifications = array();
2097         $submission = $this->get_user_submission($USER->id, false);
2098         $plugins = $this->get_submission_plugins();
2099         foreach ($plugins as $plugin) {
2100             if ($plugin->is_enabled() && $plugin->is_visible()) {
2101                 $check = $plugin->precheck_submission($submission);
2102                 if ($check !== true) {
2103                     $notifications[] = $check;
2104                 }
2105             }
2106         }
2108         $o = '';
2109         $o .= $this->output->header();
2110         $o .= $this->output->render(new assign_submit_for_grading_page($notifications, $this->get_course_module()->id));
2111         $o .= $this->view_footer();
2112         return $o;
2113     }
2115     /**
2116      * Print 2 tables of information with no action links -
2117      * the submission summary and the grading summary
2118      *
2119      * @param stdClass $user the user to print the report for
2120      * @param bool $showlinks - Return plain text or links to the profile
2121      * @return string - the html summary
2122      */
2123     public function view_student_summary($user, $showlinks) {
2124         global $CFG, $DB, $PAGE;
2126         $grade = $this->get_user_grade($user->id, false);
2127         $submission = $this->get_user_submission($user->id, false);
2128         $o = '';
2130         if ($this->can_view_submission($user->id)) {
2131             $showedit = has_capability('mod/assign:submit', $this->context) &&
2132                          $this->submissions_open() && ($this->is_any_submission_plugin_enabled()) && $showlinks;
2133             $showsubmit = $submission && ($submission->status == ASSIGN_SUBMISSION_STATUS_DRAFT) && $showlinks;
2134             if (!$this->get_instance()->submissiondrafts) {
2135                 $showsubmit = false;
2136             }
2137             $gradelocked = ($grade && $grade->locked) || $this->grading_disabled($user->id);
2139             $o .= $this->output->render(new assign_submission_status($this->get_instance()->allowsubmissionsfromdate,
2140                                                               $this->get_instance()->alwaysshowdescription,
2141                                                               $submission,
2142                                                               $this->is_any_submission_plugin_enabled(),
2143                                                               $gradelocked,
2144                                                               $this->is_graded($user->id),
2145                                                               $this->get_instance()->duedate,
2146                                                               $this->get_submission_plugins(),
2147                                                               $this->get_return_action(),
2148                                                               $this->get_return_params(),
2149                                                               $this->get_course_module()->id,
2150                                                               assign_submission_status::STUDENT_VIEW,
2151                                                               $showedit,
2152                                                               $showsubmit));
2153             require_once($CFG->libdir.'/gradelib.php');
2154             require_once($CFG->dirroot.'/grade/grading/lib.php');
2156             $gradinginfo = grade_get_grades($this->get_course()->id,
2157                                         'mod',
2158                                         'assign',
2159                                         $this->get_instance()->id,
2160                                         $user->id);
2162             $gradingitem = $gradinginfo->items[0];
2163             $gradebookgrade = $gradingitem->grades[$user->id];
2165             // check to see if all feedback plugins are empty
2166             $emptyplugins = true;
2167             if ($grade) {
2168                 foreach ($this->get_feedback_plugins() as $plugin) {
2169                     if ($plugin->is_visible() && $plugin->is_enabled()) {
2170                         if (!$plugin->is_empty($grade)) {
2171                             $emptyplugins = false;
2172                         }
2173                     }
2174                 }
2175             }
2178             if (!($gradebookgrade->hidden) && ($gradebookgrade->grade !== null || !$emptyplugins)) {
2180                 $gradefordisplay = '';
2181                 $gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
2183                 if ($controller = $gradingmanager->get_active_controller()) {
2184                     $controller->set_grade_range(make_grades_menu($this->get_instance()->grade));
2185                     $gradefordisplay = $controller->render_grade($PAGE,
2186                                                                  $grade->id,
2187                                                                  $gradingitem,
2188                                                                  $gradebookgrade->str_long_grade,
2189                                                                  has_capability('mod/assign:grade', $this->get_context()));
2190                 } else {
2191                     $gradefordisplay = $this->display_grade($gradebookgrade->grade, false);
2192                 }
2194                 $gradeddate = $gradebookgrade->dategraded;
2195                 $grader = $DB->get_record('user', array('id'=>$gradebookgrade->usermodified));
2197                 $feedbackstatus = new assign_feedback_status($gradefordisplay,
2198                                                       $gradeddate,
2199                                                       $grader,
2200                                                       $this->get_feedback_plugins(),
2201                                                       $grade,
2202                                                       $this->get_course_module()->id,
2203                                                       $this->get_return_action(),
2204                                                       $this->get_return_params());
2206                 $o .= $this->output->render($feedbackstatus);
2207             }
2209         }
2210         return $o;
2211     }
2213     /**
2214      * View submissions page (contains details of current submission).
2215      *
2216      * @return string
2217      */
2218     private function view_submission_page() {
2219         global $CFG, $DB, $USER, $PAGE;
2221         $o = '';
2222         $o .= $this->output->render(new assign_header($this->get_instance(),
2223                                                       $this->get_context(),
2224                                                       $this->show_intro(),
2225                                                       $this->get_course_module()->id));
2227         if ($this->can_grade()) {
2228             $o .= $this->output->render(new assign_grading_summary($this->count_participants(0),
2229                                                             $this->get_instance()->submissiondrafts,
2230                                                             $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT),
2231                                                             $this->is_any_submission_plugin_enabled(),
2232                                                             $this->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED),
2233                                                             $this->get_instance()->duedate,
2234                                                             $this->get_course_module()->id,
2235                                                             $this->count_submissions_need_grading()
2236                                                             ));
2237         }
2238         $grade = $this->get_user_grade($USER->id, false);
2239         $submission = $this->get_user_submission($USER->id, false);
2241         if ($this->can_view_submission($USER->id)) {
2242             $o .= $this->view_student_summary($USER, true);
2243         }
2246         $o .= $this->view_footer();
2247         $this->add_to_log('view', get_string('viewownsubmissionstatus', 'assign'));
2248         return $o;
2249     }
2251     /**
2252      * convert the final raw grade(s) in the  grading table for the gradebook
2253      *
2254      * @param stdClass $grade
2255      * @return array
2256      */
2257     private function convert_grade_for_gradebook(stdClass $grade) {
2258         $gradebookgrade = array();
2259         // trying to match those array keys in grade update function in gradelib.php
2260         // with keys in th database table assign_grades
2261         // starting around line 262
2262         if ($grade->grade >= 0) {
2263             $gradebookgrade['rawgrade'] = $grade->grade;
2264         }
2265         $gradebookgrade['userid'] = $grade->userid;
2266         $gradebookgrade['usermodified'] = $grade->grader;
2267         $gradebookgrade['datesubmitted'] = NULL;
2268         $gradebookgrade['dategraded'] = $grade->timemodified;
2269         if (isset($grade->feedbackformat)) {
2270             $gradebookgrade['feedbackformat'] = $grade->feedbackformat;
2271         }
2272         if (isset($grade->feedbacktext)) {
2273             $gradebookgrade['feedback'] = $grade->feedbacktext;
2274         }
2276         return $gradebookgrade;
2277     }
2279     /**
2280      * convert submission details for the gradebook
2281      *
2282      * @param stdClass $submission
2283      * @return array
2284      */
2285     private function convert_submission_for_gradebook(stdClass $submission) {
2286         $gradebookgrade = array();
2289         $gradebookgrade['userid'] = $submission->userid;
2290         $gradebookgrade['usermodified'] = $submission->userid;
2291         $gradebookgrade['datesubmitted'] = $submission->timemodified;
2293         return $gradebookgrade;
2294     }
2296     /**
2297      * update grades in the gradebook
2298      *
2299      * @param mixed $submission stdClass|null
2300      * @param mixed $grade stdClass|null
2301      * @return bool
2302      */
2303     private function gradebook_item_update($submission=NULL, $grade=NULL) {
2305         if($submission != NULL){
2306             $gradebookgrade = $this->convert_submission_for_gradebook($submission);
2307         }else{
2308             $gradebookgrade = $this->convert_grade_for_gradebook($grade);
2309         }
2310         // Grading is disabled, return.
2311         if ($this->grading_disabled($gradebookgrade['userid'])) {
2312             return false;
2313         }
2314         $assign = clone $this->get_instance();
2315         $assign->cmidnumber = $this->get_course_module()->id;
2317         return assign_grade_item_update($assign, $gradebookgrade);
2318     }
2320     /**
2321      * update grades in the gradebook based on submission time
2322      *
2323      * @param stdClass $submission
2324      * @param bool $updatetime
2325      * @return bool
2326      */
2327     private function update_submission(stdClass $submission, $updatetime=true) {
2328         global $DB;
2330         if ($updatetime) {
2331             $submission->timemodified = time();
2332         }
2333         $result= $DB->update_record('assign_submission', $submission);
2334         if ($result) {
2335             $this->gradebook_item_update($submission);
2336         }
2337         return $result;
2338     }
2340     /**
2341      * Is this assignment open for submissions?
2342      *
2343      * Check the due date,
2344      * prevent late submissions,
2345      * has this person already submitted,
2346      * is the assignment locked?
2347      *
2348      * @return bool
2349      */
2350     private function submissions_open() {
2351         global $USER;
2353         $time = time();
2354         $dateopen = true;
2355         if ($this->get_instance()->preventlatesubmissions && $this->get_instance()->duedate) {
2356             $dateopen = ($this->get_instance()->allowsubmissionsfromdate <= $time && $time <= $this->get_instance()->duedate);
2357         } else {
2358             $dateopen = ($this->get_instance()->allowsubmissionsfromdate <= $time);
2359         }
2361         if (!$dateopen) {
2362             return false;
2363         }
2365         // now check if this user has already submitted etc.
2366         if (!is_enrolled($this->get_course_context(), $USER)) {
2367             return false;
2368         }
2369         if ($submission = $this->get_user_submission($USER->id, false)) {
2370             if ($this->get_instance()->submissiondrafts && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
2371                 // drafts are tracked and the student has submitted the assignment
2372                 return false;
2373             }
2374         }
2375         if ($grade = $this->get_user_grade($USER->id, false)) {
2376             if ($grade->locked) {
2377                 return false;
2378             }
2379         }
2381         if ($this->grading_disabled($USER->id)) {
2382             return false;
2383         }
2385         return true;
2386     }
2388     /**
2389      * render the files in file area
2390      * @param string $component
2391      * @param string $area
2392      * @param int $submissionid
2393      * @return string
2394      */
2395     public function render_area_files($component, $area, $submissionid) {
2396         global $USER;
2398         if (!$submissionid) {
2399             $submission = $this->get_user_submission($USER->id,false);
2400             $submissionid = $submission->id;
2401         }
2403         $fs = get_file_storage();
2404         $browser = get_file_browser();
2405         $files = $fs->get_area_files($this->get_context()->id, $component, $area , $submissionid , "timemodified", false);
2406         return $this->output->assign_files($this->context, $submissionid, $area, $component);
2408     }
2410     /**
2411      * Returns a list of teachers that should be grading given submission
2412      *
2413      * @param int $userid
2414      * @return array
2415      */
2416     private function get_graders($userid) {
2417         //potential graders
2418         $potentialgraders = get_enrolled_users($this->context, "mod/assign:grade");
2420         $graders = array();
2421         if (groups_get_activity_groupmode($this->get_course_module()) == SEPARATEGROUPS) {   // Separate groups are being used
2422             if ($groups = groups_get_all_groups($this->get_course()->id, $userid)) {  // Try to find all groups
2423                 foreach ($groups as $group) {
2424                     foreach ($potentialgraders as $grader) {
2425                         if ($grader->id == $userid) {
2426                             continue; // do not send self
2427                         }
2428                         if (groups_is_member($group->id, $grader->id)) {
2429                             $graders[$grader->id] = $grader;
2430                         }
2431                     }
2432                 }
2433             } else {
2434                 // user not in group, try to find graders without group
2435                 foreach ($potentialgraders as $grader) {
2436                     if ($grader->id == $userid) {
2437                         continue; // do not send self
2438                     }
2439                     if (!groups_has_membership($this->get_course_module(), $grader->id)) {
2440                         $graders[$grader->id] = $grader;
2441                     }
2442                 }
2443             }
2444         } else {
2445             foreach ($potentialgraders as $grader) {
2446                 if ($grader->id == $userid) {
2447                     continue; // do not send self
2448                 }
2449                 // must be enrolled
2450                 if (is_enrolled($this->get_course_context(), $grader->id)) {
2451                     $graders[$grader->id] = $grader;
2452                 }
2453             }
2454         }
2455         return $graders;
2456     }
2458     /**
2459      * Format a notification for plain text
2460      *
2461      * @param string $messagetype
2462      * @param stdClass $info
2463      * @param stdClass $course
2464      * @param stdClass $context
2465      * @param string $modulename
2466      * @param string $assignmentname
2467      */
2468     private static function format_notification_message_text($messagetype, $info, $course, $context, $modulename, $assignmentname) {
2469         $posttext  = format_string($course->shortname, true, array('context' => $context->get_course_context())).' -> '.
2470                      $modulename.' -> '.
2471                      format_string($assignmentname, true, array('context' => $context))."\n";
2472         $posttext .= '---------------------------------------------------------------------'."\n";
2473         $posttext .= get_string($messagetype . 'text', "assign", $info)."\n";
2474         $posttext .= "\n---------------------------------------------------------------------\n";
2475         return $posttext;
2476     }
2478     /**
2479      * Format a notification for HTML
2480      *
2481      * @param string $messagetype
2482      * @param stdClass $info
2483      * @param stdClass $course
2484      * @param stdClass $context
2485      * @param string $modulename
2486      * @param stdClass $coursemodule
2487      * @param string $assignmentname
2488      * @param stdClass $info
2489      */
2490     private static function format_notification_message_html($messagetype, $info, $course, $context, $modulename, $coursemodule, $assignmentname) {
2491         global $CFG;
2492         $posthtml  = '<p><font face="sans-serif">'.
2493                      '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.format_string($course->shortname, true, array('context' => $context->get_course_context())).'</a> ->'.
2494                      '<a href="'.$CFG->wwwroot.'/mod/assign/index.php?id='.$course->id.'">'.$modulename.'</a> ->'.
2495                      '<a href="'.$CFG->wwwroot.'/mod/assign/view.php?id='.$coursemodule->id.'">'.format_string($assignmentname, true, array('context' => $context)).'</a></font></p>';
2496         $posthtml .= '<hr /><font face="sans-serif">';
2497         $posthtml .= '<p>'.get_string($messagetype . 'html', 'assign', $info).'</p>';
2498         $posthtml .= '</font><hr />';
2499         return $posthtml;
2500     }
2502     /**
2503      * Message someone about something (static so it can be called from cron)
2504      *
2505      * @param stdClass $userfrom
2506      * @param stdClass $userto
2507      * @param string $messagetype
2508      * @param string $eventtype
2509      * @param int $updatetime
2510      * @param stdClass $coursemodule
2511      * @param stdClass $context
2512      * @param stdClass $course
2513      * @param string $modulename
2514      * @param string $assignmentname
2515      * @return void
2516      */
2517     public static function send_assignment_notification($userfrom, $userto, $messagetype, $eventtype,
2518                                                         $updatetime, $coursemodule, $context, $course,
2519                                                         $modulename, $assignmentname) {
2520         global $CFG;
2522         $info = new stdClass();
2523         $info->username = fullname($userfrom, true);
2524         $info->assignment = format_string($assignmentname,true, array('context'=>$context));
2525         $info->url = $CFG->wwwroot.'/mod/assign/view.php?id='.$coursemodule->id;
2526         $info->timeupdated = userdate($updatetime, get_string('strftimerecentfull'));
2528         $postsubject = get_string($messagetype . 'small', 'assign', $info);
2529         $posttext = self::format_notification_message_text($messagetype, $info, $course, $context, $modulename, $assignmentname);
2530         $posthtml = ($userto->mailformat == 1) ? self::format_notification_message_html($messagetype, $info, $course, $context, $modulename, $coursemodule, $assignmentname) : '';
2532         $eventdata = new stdClass();
2533         $eventdata->modulename       = 'assign';
2534         $eventdata->userfrom         = $userfrom;
2535         $eventdata->userto           = $userto;
2536         $eventdata->subject          = $postsubject;
2537         $eventdata->fullmessage      = $posttext;
2538         $eventdata->fullmessageformat = FORMAT_PLAIN;
2539         $eventdata->fullmessagehtml  = $posthtml;
2540         $eventdata->smallmessage     = $postsubject;
2542         $eventdata->name            = $eventtype;
2543         $eventdata->component       = 'mod_assign';
2544         $eventdata->notification    = 1;
2545         $eventdata->contexturl      = $info->url;
2546         $eventdata->contexturlname  = $info->assignment;
2548         message_send($eventdata);
2549     }
2551     /**
2552      * Message someone about something
2553      *
2554      * @param stdClass $userfrom
2555      * @param stdClass $userto
2556      * @param string $messagetype
2557      * @param string $eventtype
2558      * @param int $updatetime
2559      * @return void
2560      */
2561     public function send_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime) {
2562         self::send_assignment_notification($userfrom, $userto, $messagetype, $eventtype, $updatetime, $this->get_course_module(), $this->get_context(), $this->get_course(), $this->get_module_name(), $this->get_instance()->name);
2563     }
2565     /**
2566      * Notify student upon successful submission
2567      *
2568      * @global moodle_database $DB
2569      * @param stdClass $submission
2570      * @return void
2571      */
2572     private function notify_student_submission_receipt(stdClass $submission) {
2573         global $DB;
2575         $adminconfig = $this->get_admin_config();
2576         if (!$adminconfig->submissionreceipts) {
2577             // No need to do anything
2578             return;
2579         }
2580         $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST);
2581         $this->send_notification($user, $user, 'submissionreceipt', 'assign_notification', $submission->timemodified);
2582     }
2584     /**
2585      * Send notifications to graders upon student submissions
2586      *
2587      * @global moodle_database $DB
2588      * @param stdClass $submission
2589      * @return void
2590      */
2591     private function notify_graders(stdClass $submission) {
2592         global $DB;
2594         $late = $this->get_instance()->duedate && ($this->get_instance()->duedate < time());
2596         if (!$this->get_instance()->sendnotifications && !($late && $this->get_instance()->sendlatenotifications)) {          // No need to do anything
2597             return;
2598         }
2600         $user = $DB->get_record('user', array('id'=>$submission->userid), '*', MUST_EXIST);
2601         if ($teachers = $this->get_graders($user->id)) {
2602             foreach ($teachers as $teacher) {
2603                 $this->send_notification($user, $teacher, 'gradersubmissionupdated', 'assign_notification', $submission->timemodified);
2604             }
2605         }
2606     }
2608     /**
2609      * assignment submission is processed before grading
2610      *
2611      * @return void
2612      */
2613     private function process_submit_for_grading() {
2614         global $USER;
2616         // Need submit permission to submit an assignment
2617         require_capability('mod/assign:submit', $this->context);
2618         require_sesskey();
2620         $submission = $this->get_user_submission($USER->id,true);
2621         if ($submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
2622             // Give each submission plugin a chance to process the submission
2623             $plugins = $this->get_submission_plugins();
2624             foreach ($plugins as $plugin) {
2625                 $plugin->submit_for_grading();
2626             }
2628             $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2629             $this->update_submission($submission);
2630             $this->add_to_log('submit for grading', $this->format_submission_for_log($submission));
2631             $this->notify_graders($submission);
2632             $this->notify_student_submission_receipt($submission);
2633         }
2634     }
2636     /**
2637      * save quick grades
2638      *
2639      * @global moodle_database $DB
2640      * @return string The result of the save operation
2641      */
2642     private function process_save_quick_grades() {
2643         global $USER, $DB, $CFG;
2645         // Need grade permission
2646         require_capability('mod/assign:grade', $this->context);
2648         // make sure advanced grading is disabled
2649         $gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
2650         $controller = $gradingmanager->get_active_controller();
2651         if (!empty($controller)) {
2652             return get_string('errorquickgradingvsadvancedgrading', 'assign');
2653         }
2655         $users = array();
2656         // first check all the last modified values
2657         $currentgroup = groups_get_activity_group($this->get_course_module(), true);
2658         $participants = $this->list_participants($currentgroup, true);
2660         // gets a list of possible users and look for values based upon that.
2661         foreach ($participants as $userid => $unused) {
2662             $modified = optional_param('grademodified_' . $userid, -1, PARAM_INT);
2663             // Gather the userid, updated grade and last modified value.
2664             $record = new stdClass();
2665             $record->userid = $userid;
2666             $gradevalue = optional_param('quickgrade_' . $userid, '', PARAM_TEXT);
2667             if($modified >= 0) {
2668                 $record->grade = unformat_float(optional_param('quickgrade_' . $record->userid, -1, PARAM_TEXT));
2669             }
2670             $record->lastmodified = $modified;
2671             $record->gradinginfo = grade_get_grades($this->get_course()->id, 'mod', 'assign', $this->get_instance()->id, array($userid));
2672             $users[$userid] = $record;
2673         }
2675         list($userids, $params) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED);
2676         $params['assignment'] = $this->get_instance()->id;
2677         // check them all for currency
2678         $sql = 'SELECT u.id as userid, g.grade as grade, g.timemodified as lastmodified
2679                   FROM {user} u
2680              LEFT JOIN {assign_grades} g ON u.id = g.userid AND g.assignment = :assignment
2681                  WHERE u.id ' . $userids;
2682         $currentgrades = $DB->get_recordset_sql($sql, $params);
2684         $modifiedusers = array();
2685         foreach ($currentgrades as $current) {
2686             $modified = $users[(int)$current->userid];
2687             $grade = $this->get_user_grade($modified->userid, false);
2689             // check to see if the outcomes were modified
2690             if ($CFG->enableoutcomes) {
2691                 foreach ($modified->gradinginfo->outcomes as $outcomeid => $outcome) {
2692                     $oldoutcome = $outcome->grades[$modified->userid]->grade;
2693                     $newoutcome = optional_param('outcome_' . $outcomeid . '_' . $modified->userid, -1, PARAM_FLOAT);
2694                     if ($oldoutcome != $newoutcome) {
2695                         // can't check modified time for outcomes because it is not reported
2696                         $modifiedusers[$modified->userid] = $modified;
2697                         continue;
2698                     }
2699                 }
2700             }
2702             // let plugins participate
2703             foreach ($this->feedbackplugins as $plugin) {
2704                 if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->supports_quickgrading()) {
2705                     if ($plugin->is_quickgrading_modified($modified->userid, $grade)) {
2706                         if ((int)$current->lastmodified > (int)$modified->lastmodified) {
2707                             return get_string('errorrecordmodified', 'assign');
2708                         } else {
2709                             $modifiedusers[$modified->userid] = $modified;
2710                             continue;
2711                         }
2712                     }
2713                 }
2714             }
2717             if (($current->grade < 0 || $current->grade === NULL) &&
2718                 ($modified->grade < 0 || $modified->grade === NULL)) {
2719                 // different ways to indicate no grade
2720                 continue;
2721             }
2722             // Treat 0 and null as different values
2723             if ($current->grade !== null) {
2724                 $current->grade = floatval($current->grade);
2725             }
2726             if ($current->grade !== $modified->grade) {
2727                 // grade changed
2728                 if ($this->grading_disabled($modified->userid)) {
2729                     continue;
2730                 }
2731                 if ((int)$current->lastmodified > (int)$modified->lastmodified) {
2732                     // error - record has been modified since viewing the page
2733                     return get_string('errorrecordmodified', 'assign');
2734                 } else {
2735                     $modifiedusers[$modified->userid] = $modified;
2736                 }
2737             }
2739         }
2740         $currentgrades->close();
2742         $adminconfig = $this->get_admin_config();
2743         $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
2745         // ok - ready to process the updates
2746         foreach ($modifiedusers as $userid => $modified) {
2747             $grade = $this->get_user_grade($userid, true);
2748             $grade->grade= grade_floatval(unformat_float($modified->grade));
2749             $grade->grader= $USER->id;
2751             // save plugins data
2752             foreach ($this->feedbackplugins as $plugin) {
2753                 if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->supports_quickgrading()) {
2754                     $plugin->save_quickgrading_changes($userid, $grade);
2755                     if (('assignfeedback_' . $plugin->get_type()) == $gradebookplugin) {
2756                         // This is the feedback plugin chose to push comments to the gradebook.
2757                         $grade->feedbacktext = $plugin->text_for_gradebook($grade);
2758                         $grade->feedbackformat = $plugin->format_for_gradebook($grade);
2759                     }
2760                 }
2761             }
2763             $this->update_grade($grade);
2765             // save outcomes
2766             if ($CFG->enableoutcomes) {
2767                 $data = array();
2768                 foreach ($modified->gradinginfo->outcomes as $outcomeid => $outcome) {
2769                     $oldoutcome = $outcome->grades[$modified->userid]->grade;
2770                     $newoutcome = optional_param('outcome_' . $outcomeid . '_' . $modified->userid, -1, PARAM_INT);
2771                     if ($oldoutcome != $newoutcome) {
2772                         $data[$outcomeid] = $newoutcome;
2773                     }
2774                 }
2775                 if (count($data) > 0) {
2776                     grade_update_outcomes('mod/assign', $this->course->id, 'mod', 'assign', $this->get_instance()->id, $userid, $data);
2777                 }
2778             }
2780             $this->add_to_log('grade submission', $this->format_grade_for_log($grade));
2781         }
2783         return get_string('quickgradingchangessaved', 'assign');
2784     }
2786     /**
2787      * save grading options
2788      *
2789      * @return void
2790      */
2791     private function process_save_grading_options() {
2792         global $USER, $CFG;
2794         // Include grading options form
2795         require_once($CFG->dirroot . '/mod/assign/gradingoptionsform.php');
2797         // Need submit permission to submit an assignment
2798         require_capability('mod/assign:grade', $this->context);
2800         $mform = new mod_assign_grading_options_form(null, array('cm'=>$this->get_course_module()->id,
2801                                                                  'contextid'=>$this->context->id,
2802                                                                  'userid'=>$USER->id,
2803                                                                  'submissionsenabled'=>$this->is_any_submission_plugin_enabled(),
2804                                                                  'showquickgrading'=>false));
2805         if ($formdata = $mform->get_data()) {
2806             set_user_preference('assign_perpage', $formdata->perpage);
2807             if (isset($formdata->filter)) {
2808                 set_user_preference('assign_filter', $formdata->filter);
2809             }
2810         }
2811     }
2813    /**
2814     * Take a grade object and print a short summary for the log file.
2815     * The size limit for the log file is 255 characters, so be careful not
2816     * to include too much information.
2817     *
2818     * @param stdClass $grade
2819     * @return string
2820     */
2821     private function format_grade_for_log(stdClass $grade) {
2822         global $DB;
2824         $user = $DB->get_record('user', array('id' => $grade->userid), '*', MUST_EXIST);
2826         $info = get_string('gradestudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user)));
2827         if ($grade->grade != '') {
2828             $info .= get_string('grade') . ': ' . $this->display_grade($grade->grade, false) . '. ';
2829         } else {
2830             $info .= get_string('nograde', 'assign');
2831         }
2832         if ($grade->locked) {
2833             $info .= get_string('submissionslocked', 'assign') . '. ';
2834         }
2835         return $info;
2836     }
2838     /**
2839      * Take a submission object and print a short summary for the log file.
2840      * The size limit for the log file is 255 characters, so be careful not
2841      * to include too much information.
2842      *
2843      * @param stdClass $submission
2844      * @return string
2845      */
2846     private function format_submission_for_log(stdClass $submission) {
2847         $info = '';
2848         $info .= get_string('submissionstatus', 'assign') . ': ' . get_string('submissionstatus_' . $submission->status, 'assign') . '. <br>';
2849         // format_for_log here iterating every single log INFO  from either submission or grade in every assignment plugin
2851         foreach ($this->submissionplugins as $plugin) {
2852             if ($plugin->is_enabled() && $plugin->is_visible()) {
2855                 $info .= "<br>" . $plugin->format_for_log($submission);
2856             }
2857         }
2860         return $info;
2861     }
2863     /**
2864      * save assignment submission
2865      *
2866      * @param  moodleform $mform
2867      * @param  array $notices Any error messages that should be shown to the user at the top of the edit submission form.
2868      * @return bool
2869      */
2870     private function process_save_submission(&$mform, &$notices) {
2871         global $USER, $CFG;
2873         // Include submission form.
2874         require_once($CFG->dirroot . '/mod/assign/submission_form.php');
2876         // Need submit permission to submit an assignment.
2877         require_capability('mod/assign:submit', $this->context);
2878         require_sesskey();
2880         $data = new stdClass();
2881         $mform = new mod_assign_submission_form(null, array($this, $data));
2882         if ($mform->is_cancelled()) {
2883             return true;
2884         }
2885         if ($data = $mform->get_data()) {
2886             $submission = $this->get_user_submission($USER->id, true); //create the submission if needed & its id
2887             if ($this->get_instance()->submissiondrafts) {
2888                 $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
2889             } else {
2890                 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
2891             }
2893             $grade = $this->get_user_grade($USER->id, false); // get the grade to check if it is locked
2894             if ($grade && $grade->locked) {
2895                 print_error('submissionslocked', 'assign');
2896                 return true;
2897             }
2899             $allempty = true;
2900             $pluginerror = false;
2901             foreach ($this->submissionplugins as $plugin) {
2902                 if ($plugin->is_enabled()) {
2903                     if (!$plugin->save($submission, $data)) {
2904                         $notices[] = $plugin->get_error();
2905                         $pluginerror = true;
2906                     }
2907                     if (!$allempty || !$plugin->is_empty($submission)) {
2908                         $allempty = false;
2909                     }
2910                 }
2911             }
2912             if ($pluginerror || $allempty) {
2913                 if ($allempty) {
2914                     $notices[] = get_string('submissionempty', 'mod_assign');
2915                 }
2916                 return false;
2917             }
2919             $this->update_submission($submission);
2921             // Logging
2922             $this->add_to_log('submit', $this->format_submission_for_log($submission));
2924             if (!$this->get_instance()->submissiondrafts) {
2925                 $this->notify_student_submission_receipt($submission);
2926                 $this->notify_graders($submission);
2927             }
2928             return true;
2929         }
2930         return false;
2931     }
2934     /**
2935      * Determine if this users grade is locked or overridden
2936      *
2937      * @param int $userid - The student userid
2938      * @return bool $gradingdisabled
2939      */
2940     public function grading_disabled($userid) {
2941         global $CFG;
2943         $gradinginfo = grade_get_grades($this->get_course()->id, 'mod', 'assign', $this->get_instance()->id, array($userid));
2944         if (!$gradinginfo) {
2945             return false;
2946         }
2948         if (!isset($gradinginfo->items[0]->grades[$userid])) {
2949             return false;
2950         }
2951         $gradingdisabled = $gradinginfo->items[0]->grades[$userid]->locked || $gradinginfo->items[0]->grades[$userid]->overridden;
2952         return $gradingdisabled;
2953     }
2956     /**
2957      * Get an instance of a grading form if advanced grading is enabled
2958      * This is specific to the assignment, marker and student
2959      *
2960      * @param int $userid - The student userid
2961      * @param bool $gradingdisabled
2962      * @return mixed gradingform_instance|null $gradinginstance
2963      */
2964     private function get_grading_instance($userid, $gradingdisabled) {
2965         global $CFG, $USER;
2967         $grade = $this->get_user_grade($userid, false);
2968         $grademenu = make_grades_menu($this->get_instance()->grade);
2970         $advancedgradingwarning = false;
2971         $gradingmanager = get_grading_manager($this->context, 'mod_assign', 'submissions');
2972         $gradinginstance = null;
2973         if ($gradingmethod = $gradingmanager->get_active_method()) {
2974             $controller = $gradingmanager->get_controller($gradingmethod);
2975             if ($controller->is_form_available()) {
2976                 $itemid = null;
2977                 if ($grade) {
2978                     $itemid = $grade->id;
2979                 }
2980                 if ($gradingdisabled && $itemid) {
2981                     $gradinginstance = ($controller->get_current_instance($USER->id, $itemid));
2982                 } else if (!$gradingdisabled) {
2983                     $instanceid = optional_param('advancedgradinginstanceid', 0, PARAM_INT);
2984                     $gradinginstance = ($controller->get_or_create_instance($instanceid, $USER->id, $itemid));
2985                 }
2986             } else {
2987                 $advancedgradingwarning = $controller->form_unavailable_notification();
2988             }
2989         }
2990         if ($gradinginstance) {
2991             $gradinginstance->get_controller()->set_grade_range($grademenu);
2992         }
2993         return $gradinginstance;
2994     }
2996     /**
2997      * add elements to grade form
2998      *
2999      * @param MoodleQuickForm $mform
3000      * @param stdClass $data
3001      * @param array $params
3002      * @return void
3003      */
3004     public function add_grade_form_elements(MoodleQuickForm $mform, stdClass $data, $params) {
3005         global $USER, $CFG;
3006         $settings = $this->get_instance();
3008         $rownum = $params['rownum'];
3009         $last = $params['last'];
3010         $useridlist = $params['useridlist'];
3011         $userid = $useridlist[$rownum];
3012         $grade = $this->get_user_grade($userid, false);
3014         // add advanced grading
3015         $gradingdisabled = $this->grading_disabled($userid);
3016         $gradinginstance = $this->get_grading_instance($userid, $gradingdisabled);
3018         if ($gradinginstance) {
3019             $gradingelement = $mform->addElement('grading', 'advancedgrading', get_string('grade').':', array('gradinginstance' => $gradinginstance));
3020             if ($gradingdisabled) {
3021                 $gradingelement->freeze();
3022             } else {
3023                 $mform->addElement('hidden', 'advancedgradinginstanceid', $gradinginstance->get_id());
3024             }
3025         } else {
3026             // use simple direct grading
3027             if ($this->get_instance()->grade > 0) {
3028                 $gradingelement = $mform->addElement('text', 'grade', get_string('gradeoutof', 'assign',$this->get_instance()->grade));
3029                 $mform->addHelpButton('grade', 'gradeoutofhelp', 'assign');
3030                 $mform->setType('grade', PARAM_TEXT);
3031                 if ($gradingdisabled) {
3032                     $gradingelement->freeze();
3033                 }
3034             } else {
3035                 $grademenu = make_grades_menu($this->get_instance()->grade);
3036                 if (count($grademenu) > 0) {
3037                     $gradingelement = $mform->addElement('select', 'grade', get_string('grade').':', $grademenu);
3039                     // The grade is already formatted with format_float so it needs to be converted back to an integer.
3040                     if (!empty($data->grade)) {
3041                         $data->grade = (int)unformat_float($data->grade);
3042                     }
3043                     $mform->setType('grade', PARAM_INT);
3044                     if ($gradingdisabled) {
3045                         $gradingelement->freeze();
3046                     }
3047                 }
3048             }
3049         }
3051         $gradinginfo = grade_get_grades($this->get_course()->id,
3052                                         'mod',
3053                                         'assign',
3054                                         $this->get_instance()->id,
3055                                         $userid);
3056         if (!empty($CFG->enableoutcomes)) {
3057             foreach($gradinginfo->outcomes as $index=>$outcome) {
3058                 $options = make_grades_menu(-$outcome->scaleid);
3059                 if ($outcome->grades[$userid]->locked) {
3060                     $options[0] = get_string('nooutcome', 'grades');
3061                     $mform->addElement('static', 'outcome_'.$index.'['.$userid.']', $outcome->name.':',
3062                             $options[$outcome->grades[$userid]->grade]);
3063                 } else {
3064                     $options[''] = get_string('nooutcome', 'grades');
3065                     $attributes = array('id' => 'menuoutcome_'.$index );
3066                     $mform->addElement('select', 'outcome_'.$index.'['.$userid.']', $outcome->name.':', $options, $attributes );
3067                     $mform->setType('outcome_'.$index.'['.$userid.']', PARAM_INT);
3068                     $mform->setDefault('outcome_'.$index.'['.$userid.']', $outcome->grades[$userid]->grade );
3069                 }
3070             }
3071         }
3073         if (has_all_capabilities(array('gradereport/grader:view', 'moodle/grade:viewall'), $this->get_course_context())) {
3074             $gradestring = $this->output->action_link(new moodle_url('/grade/report/grader/index.php',
3075                                                               array('id'=>$this->get_course()->id)),
3076                                                 $gradinginfo->items[0]->grades[$userid]->str_grade);
3077         } else {
3078             $gradestring = $gradinginfo->items[0]->grades[$userid]->str_grade;
3079         }
3080         $mform->addElement('static', 'finalgrade', get_string('currentgrade', 'assign').':', $gradestring);
3083         $mform->addElement('static', 'progress', '', get_string('gradingstudentprogress', 'assign', array('index'=>$rownum+1, 'count'=>count($useridlist))));
3085         // Let feedback plugins add elements to the grading form.
3086         $this->add_plugin_grade_elements($grade, $mform, $data, $userid);
3088         // hidden params
3089         $mform->addElement('hidden', 'id', $this->get_course_module()->id);
3090         $mform->setType('id', PARAM_INT);
3091         $mform->addElement('hidden', 'rownum', $rownum);
3092         $mform->setType('rownum', PARAM_INT);
3093         $mform->setConstant('rownum', $rownum);
3094         $mform->addElement('hidden', 'useridlist', implode(',', $useridlist));
3095         $mform->setType('useridlist', PARAM_TEXT);
3096         $mform->addElement('hidden', 'ajax', optional_param('ajax', 0, PARAM_INT));
3097         $mform->setType('ajax', PARAM_INT);
3099         $mform->addElement('hidden', 'action', 'submitgrade');
3100         $mform->setType('action', PARAM_ALPHA);
3103         $buttonarray=array();
3104         $buttonarray[] = $mform->createElement('submit', 'savegrade', get_string('savechanges', 'assign'));
3105         if (!$last){
3106             $buttonarray[] = $mform->createElement('submit', 'saveandshownext', get_string('savenext','assign'));
3107         }
3108         $buttonarray[] = $mform->createElement('cancel', 'cancelbutton', get_string('cancel'));
3109         $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
3110         $mform->closeHeaderBefore('buttonar');
3111         $buttonarray=array();
3113         if ($rownum > 0) {
3114             $buttonarray[] = $mform->createElement('submit', 'nosaveandprevious', get_string('previous','assign'));
3115         }
3117         if (!$last){
3118             $buttonarray[] = $mform->createElement('submit', 'nosaveandnext', get_string('nosavebutnext', 'assign'));
3119         }
3120         if (!empty($buttonarray)) {
3121             $mform->addGroup($buttonarray, 'navar', '', array(' '), false);
3122         }
3123     }
3126     /**
3127      * add elements in submission plugin form
3128      *
3129      * @param mixed $submission stdClass|null
3130      * @param MoodleQuickForm $mform
3131      * @param stdClass $data
3132      * @param int $userid The current userid (same as $USER->id)
3133      * @return void
3134      */
3135     private function add_plugin_submission_elements($submission, MoodleQuickForm $mform, stdClass $data, $userid) {
3136         foreach ($this->submissionplugins as $plugin) {
3137             if ($plugin->is_enabled() && $plugin->is_visible() && $plugin->allow_submissions()) {
3138                 $mform->addElement('header', 'header_' . $plugin->get_type(), $plugin->get_name());
3139                 if (!$plugin->get_form_elements_for_user($submission, $mform, $data, $userid)) {
3140                     $mform->removeElement('header_' . $plugin->get_type());
3141                 }
3142             }
3143         }
3144     }
3146     /**
3147      * check if feedback plugins installed are enabled
3148      *
3149      * @return bool
3150      */
3151     public function is_any_feedback_plugin_enabled() {
3152         if (!isset($this->cache['any_feedback_plugin_enabled'])) {
3153             $this->cache['any_feedback_plugin_enabled'] = false;
3154             foreach ($this->feedbackplugins as $plugin) {
3155                 if ($plugin->is_enabled() && $plugin->is_visible()) {
3156                     $this->cache['any_feedback_plugin_enabled'] = true;
3157                     break;
3158                 }
3159             }
3160         }
3162         return $this->cache['any_feedback_plugin_enabled'];
3164     }
3166     /**
3167      * check if submission plugins installed are enabled
3168      *
3169      * @return bool
3170      */
3171     public function is_any_submission_plugin_enabled() {
3172         if (!isset($this->cache['any_submission_plugin_enabled'])) {
3173             $this->cache['any_submission_plugin_enabled'] = false;
3174             foreach ($this->submissionplugins as $plugin) {
3175                 if ($plugin->is_enabled() && $plugin->is_visible() && $plugin->allow_submissions()) {
3176                     $this->cache['any_submission_plugin_enabled'] = true;
3177                     break;
3178                 }
3179             }
3180         }
3182         return $this->cache['any_submission_plugin_enabled'];
3184     }
3186     /**
3187      * add elements to submission form
3188      * @param MoodleQuickForm $mform
3189      * @param stdClass $data
3190      * @return void
3191      */
3192     public function add_submission_form_elements(MoodleQuickForm $mform, stdClass $data) {
3193         global $USER;
3195         // online text submissions
3197         $submission = $this->get_user_submission($USER->id, false);
3199         $this->add_plugin_submission_elements($submission, $mform, $data, $USER->id);
3201         // hidden params
3202         $mform->addElement('hidden', 'id', $this->get_course_module()->id);
3203         $mform->setType('id', PARAM_INT);
3205         $mform->addElement('hidden', 'action', 'savesubmission');
3206         $mform->setType('action', PARAM_TEXT);
3207         // buttons
3209     }
3211     /**
3212      * revert to draft
3213      * Uses url parameter userid
3214      *
3215      * @param int $userid
3216      * @return void
3217      */
3218     private function process_revert_to_draft($userid = 0) {
3219         global $USER, $DB;
3221         // Need grade permission
3222         require_capability('mod/assign:grade', $this->context);
3223         require_sesskey();
3225         if (!$userid) {
3226             $userid = required_param('userid', PARAM_INT);
3227         }
3229         $submission = $this->get_user_submission($userid, false);
3230         if (!$submission) {
3231             return;
3232         }
3233         $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
3234         $this->update_submission($submission, false);
3236         // update the modified time on the grade (grader modified)
3237         $grade = $this->get_user_grade($userid, true);
3238         $this->update_grade($grade);
3240         $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
3242         $this->add_to_log('revert submission to draft', get_string('reverttodraftforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))));
3244     }
3246     /**
3247      * lock  the process
3248      * Uses url parameter userid
3249      * @param int $userid
3250      * @return void
3251      */
3252     private function process_lock($userid = 0) {
3253         global $USER, $DB;
3255         // Need grade permission
3256         require_capability('mod/assign:grade', $this->context);
3257         require_sesskey();
3259         if (!$userid) {
3260             $userid = required_param('userid', PARAM_INT);
3261         }
3263         $grade = $this->get_user_grade($userid, true);
3264         $grade->locked = 1;
3265         $grade->grader = $USER->id;
3266         $this->update_grade($grade);
3268         $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
3270         $this->add_to_log('lock submission', get_string('locksubmissionforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))));
3271     }
3273     /**
3274      * unlock the process
3275      *
3276      * @param int $userid
3277      * @return void
3278      */
3279     private function process_unlock($userid = 0) {
3280         global $USER, $DB;
3282         // Need grade permission
3283         require_capability('mod/assign:grade', $this->context);
3284         require_sesskey();
3286         if (!$userid) {
3287             $userid = required_param('userid', PARAM_INT);
3288         }
3290         $grade = $this->get_user_grade($userid, true);
3291         $grade->locked = 0;
3292         $grade->grader = $USER->id;
3293         $this->update_grade($grade);
3295         $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
3297         $this->add_to_log('unlock submission', get_string('unlocksubmissionforstudent', 'assign', array('id'=>$user->id, 'fullname'=>fullname($user))));
3298     }
3300     /**
3301      * save outcomes submitted from grading form
3302      *
3303      * @param int $userid
3304      * @param stdClass $formdata
3305      */
3306     private function process_outcomes($userid, $formdata) {
3307         global $CFG, $USER;
3309         if (empty($CFG->enableoutcomes)) {
3310             return;
3311         }
3312         if ($this->grading_disabled($userid)) {
3313             return;
3314         }
3316         require_once($CFG->libdir.'/gradelib.php');
3318         $data = array();
3319         $gradinginfo = grade_get_grades($this->get_course()->id,
3320                                         'mod',
3321                                         'assign',
3322                                         $this->get_instance()->id,
3323                                         $userid);
3325         if (!empty($gradinginfo->outcomes)) {
3326             foreach($gradinginfo->outcomes as $index=>$oldoutcome) {
3327                 $name = 'outcome_'.$index;
3328                 if (isset($formdata->{$name}[$userid]) and $oldoutcome->grades[$userid]->grade != $formdata->{$name}[$userid]) {
3329                     $data[$index] = $formdata->{$name}[$userid];
3330                 }
3331             }
3332         }
3333         if (count($data) > 0) {
3334             grade_update_outcomes('mod/assign', $this->course->id, 'mod', 'assign', $this->get_instance()->id, $userid, $data);
3335         }
3337     }
3340     /**
3341      * save grade
3342      *
3343      * @param  moodleform $mform
3344      * @return bool - was the grade saved
3345      */
3346     private function process_save_grade(&$mform) {
3347         global $USER, $DB, $CFG;
3348         // Include grade form
3349         require_once($CFG->dirroot . '/mod/assign/gradeform.php');
3351         // Need submit permission to submit an assignment
3352         require_capability('mod/assign:grade', $this->context);
3353         require_sesskey();
3355         $rownum = required_param('rownum', PARAM_INT);
3356         $useridlist = optional_param('useridlist', '', PARAM_TEXT);
3357         if ($useridlist) {
3358             $useridlist = explode(',', $useridlist);
3359         } else {
3360             $useridlist = $this->get_grading_userid_list();
3361         }
3362         $last = false;
3363         $userid = $useridlist[$rownum];
3364         if ($rownum == count($useridlist) - 1) {
3365             $last = true;
3366         }
3368         $data = new stdClass();
3369         $mform = new mod_assign_grade_form(null, array($this, $data, array('rownum'=>$rownum, 'useridlist'=>$useridlist, 'last'=>false)), 'post', '', array('class'=>'gradeform'));
3371         if ($formdata = $mform->get_data()) {
3372             $grade = $this->get_user_grade($userid, true);
3373             $gradingdisabled = $this->grading_disabled($userid);
3374             $gradinginstance = $this->get_grading_instance($userid, $gradingdisabled);
3375             if (!$gradingdisabled) {
3376                 if ($gradinginstance) {
3377                     $grade->grade = $gradinginstance->submit_and_get_grade($formdata->advancedgrading, $grade->id);
3378                 } else {
3379                     // handle the case when grade is set to No Grade
3380                     if (isset($formdata->grade)) {
3381                         $grade->grade = grade_floatval(unformat_float($formdata->grade));
3382                     }
3383                 }
3384             }
3385             $grade->grader= $USER->id;
3387             $adminconfig = $this->get_admin_config();
3388             $gradebookplugin = $adminconfig->feedback_plugin_for_gradebook;
3390             // call save in plugins
3391             foreach ($this->feedbackplugins as $plugin) {
3392                 if ($plugin->is_enabled() && $plugin->is_visible()) {
3393                     if (!$plugin->save($grade, $formdata)) {
3394                         $result = false;
3395                         print_error($plugin->get_error());
3396                     }
3397                     if (('assignfeedback_' . $plugin->get_type()) == $gradebookplugin) {
3398                         // this is the feedback plugin chose to push comments to the gradebook
3399                         $grade->feedbacktext = $plugin->text_for_gradebook($grade);
3400                         $grade->feedbackformat = $plugin->format_for_gradebook($grade);
3401                     }
3402                 }
3403             }
3404             $this->process_outcomes($userid, $formdata);
3406             $grade->mailed = 0;
3408             $this->update_grade($grade);
3410             $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
3412             $this->add_to_log('grade submission', $this->format_grade_for_log($grade));
3415         } else {
3416             return false;
3417         }
3418         return true;
3419     }
3421     /**
3422      * This function is a static wrapper around can_upgrade
3423      *
3424      * @param string $type The plugin type
3425      * @param int $version The plugin version
3426      * @return bool
3427      */
3428     public static function can_upgrade_assignment($type, $version) {
3429         $assignment = new assign(null, null, null);
3430         return $assignment->can_upgrade($type, $version);
3431     }
3433     /**
3434      * This function returns true if it can upgrade an assignment from the 2.2
3435      * module.
3436      * @param string $type The plugin type
3437      * @param int $version The plugin version
3438      * @return bool
3439      */
3440     public function can_upgrade($type, $version) {
3441         if ($type == 'offline' && $version >= 2011112900) {
3442             return true;
3443         }
3444         foreach ($this->submissionplugins as $plugin) {
3445             if ($plugin->can_upgrade($type, $version)) {
3446                 return true;
3447             }
3448         }
3449         foreach ($this->feedbackplugins as $plugin) {
3450             if ($plugin->can_upgrade($type, $version)) {
3451                 return true;
3452             }
3453         }
3454         return false;
3455     }
3457     /**
3458      * Copy all the files from the old assignment files area to the new one.
3459      * This is used by the plugin upgrade code.
3460      *
3461      * @param int $oldcontextid The old assignment context id
3462      * @param int $oldcomponent The old assignment component ('assignment')
3463      * @param int $oldfilearea The old assignment filearea ('submissions')
3464      * @param int $olditemid The old submissionid (can be null e.g. intro)
3465      * @param int $newcontextid The new assignment context id
3466      * @param int $newcomponent The new assignment component ('assignment')
3467      * @param int $newfilearea The new assignment filearea ('submissions')
3468      * @param int $newitemid The new submissionid (can be null e.g. intro)
3469      * @return int The number of files copied
3470      */
3471     public function copy_area_files_for_upgrade($oldcontextid, $oldcomponent, $oldfilearea, $olditemid, $newcontextid, $newcomponent, $newfilearea, $newitemid) {
3472         // Note, this code is based on some code in filestorage - but that code
3473         // deleted the old files (which we don't want)
3474         $count = 0;
3476         $fs = get_file_storage();
3478         $oldfiles = $fs->get_area_files($oldcontextid, $oldcomponent, $oldfilearea, $olditemid, 'id', false);
3479         foreach ($oldfiles as $oldfile) {
3480             $filerecord = new stdClass();
3481             $filerecord->contextid = $newcontextid;
3482             $filerecord->component = $newcomponent;
3483             $filerecord->filearea = $newfilearea;
3484             $filerecord->itemid = $newitemid;
3485             $fs->create_file_from_storedfile($filerecord, $oldfile);
3486             $count += 1;
3487         }
3489         return $count;
3490     }
3492     /**
3493      * Get an upto date list of user grades and feedback for the gradebook
3494      *
3495      * @param int $userid int or 0 for all users
3496      * @return array of grade data formated for the gradebook api
3497      *         The data required by the gradebook api is userid,
3498      *                                                   rawgrade,
3499      *                                                   feedback,
3500      *                                                   feedbackformat,
3501      *                                                   usermodified,
3502      *                                                   dategraded,
3503      *                                                   datesubmitted
3504      */
3505     public function get_user_grades_for_gradebook($userid) {
3506         global $DB, $CFG;
3507         $grades = array();
3508         $assignmentid = $this->get_instance()->id;
3510         $adminconfig = $this->get_admin_config();
3511         $gradebookpluginname = $adminconfig->feedback_plugin_for_gradebook;
3512         $gradebookplugin = null;
3514         // find the gradebook plugin
3515         foreach ($this->feedbackplugins as $plugin) {
3516             if ($plugin->is_enabled() && $plugin->is_visible()) {
3517                 if (('assignfeedback_' . $plugin->get_type()) == $gradebookpluginname) {
3518                     $gradebookplugin = $plugin;
3519                 }
3520             }
3521         }
3522         if ($userid) {
3523             $where = ' WHERE u.id = ? ';
3524         } else {
3525             $where = ' WHERE u.id != ? ';
3526         }
3528         $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
3529                             FROM {user} u
3530                             LEFT JOIN {assign_submission} s ON u.id = s.userid and s.assignment = ?
3531                             JOIN {assign_grades} g ON u.id = g.userid and g.assignment = ?
3532                             ' . $where, array($assignmentid, $assignmentid, $userid));
3535         foreach ($graderesults as $result) {
3536             $gradebookgrade = clone $result;
3537             // now get the feedback
3538             if ($gradebookplugin) {
3539                 $grade = $this->get_user_grade($result->userid, false);
3540                 if ($grade) {
3541                     $gradebookgrade->feedbacktext = $gradebookplugin->text_for_gradebook($grade);
3542                     $gradebookgrade->feedbackformat = $gradebookplugin->format_for_gradebook($grade);
3543                 }
3544             }
3545             $grades[$gradebookgrade->userid] = $gradebookgrade;
3546         }
3548         $graderesults->close();
3549         return $grades;
3550     }