/mod/assignment/lib.php
PHP | 4044 lines | 3655 code | 136 blank | 253 comment | 113 complexity | b86c1dc381560e0982af409e73504077 MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-3.0, MPL-2.0-no-copyleft-exception, GPL-3.0, Apache-2.0, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- <?PHP
- // This file is part of Moodle - http://moodle.org/
- //
- // Moodle is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // Moodle is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
- /**
- * assignment_base is the base class for assignment types
- *
- * This class provides all the functionality for an assignment
- *
- * @package mod-assignment
- * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
- /** Include eventslib.php */
- require_once($CFG->libdir.'/eventslib.php');
- /** Include formslib.php */
- require_once($CFG->libdir.'/formslib.php');
- /** Include calendar/lib.php */
- require_once($CFG->dirroot.'/calendar/lib.php');
- /** ASSIGNMENT_COUNT_WORDS = 1 */
- define('ASSIGNMENT_COUNT_WORDS', 1);
- /** ASSIGNMENT_COUNT_LETTERS = 2 */
- define('ASSIGNMENT_COUNT_LETTERS', 2);
- /**
- * Standard base class for all assignment submodules (assignment types).
- *
- * @package mod-assignment
- * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
- class assignment_base {
- const FILTER_ALL = 0;
- const FILTER_SUBMITTED = 1;
- const FILTER_REQUIRE_GRADING = 2;
- /** @var object */
- var $cm;
- /** @var object */
- var $course;
- /** @var stdClass */
- var $coursecontext;
- /** @var object */
- var $assignment;
- /** @var string */
- var $strassignment;
- /** @var string */
- var $strassignments;
- /** @var string */
- var $strsubmissions;
- /** @var string */
- var $strlastmodified;
- /** @var string */
- var $pagetitle;
- /** @var bool */
- var $usehtmleditor;
- /**
- * @todo document this var
- */
- var $defaultformat;
- /**
- * @todo document this var
- */
- var $context;
- /** @var string */
- var $type;
- /**
- * Constructor for the base assignment class
- *
- * Constructor for the base assignment class.
- * If cmid is set create the cm, course, assignment objects.
- * If the assignment is hidden and the user is not a teacher then
- * this prints a page header and notice.
- *
- * @global object
- * @global object
- * @param int $cmid the current course module id - not set for new assignments
- * @param object $assignment usually null, but if we have it we pass it to save db access
- * @param object $cm usually null, but if we have it we pass it to save db access
- * @param object $course usually null, but if we have it we pass it to save db access
- */
- function assignment_base($cmid='staticonly', $assignment=NULL, $cm=NULL, $course=NULL) {
- global $COURSE, $DB;
- if ($cmid == 'staticonly') {
- //use static functions only!
- return;
- }
- global $CFG;
- if ($cm) {
- $this->cm = $cm;
- } else if (! $this->cm = get_coursemodule_from_id('assignment', $cmid)) {
- print_error('invalidcoursemodule');
- }
- $this->context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
- if ($course) {
- $this->course = $course;
- } else if ($this->cm->course == $COURSE->id) {
- $this->course = $COURSE;
- } else if (! $this->course = $DB->get_record('course', array('id'=>$this->cm->course))) {
- print_error('invalidid', 'assignment');
- }
- $this->coursecontext = get_context_instance(CONTEXT_COURSE, $this->course->id);
- $courseshortname = format_text($this->course->shortname, true, array('context' => $this->coursecontext));
- if ($assignment) {
- $this->assignment = $assignment;
- } else if (! $this->assignment = $DB->get_record('assignment', array('id'=>$this->cm->instance))) {
- print_error('invalidid', 'assignment');
- }
- $this->assignment->cmidnumber = $this->cm->idnumber; // compatibility with modedit assignment obj
- $this->assignment->courseid = $this->course->id; // compatibility with modedit assignment obj
- $this->strassignment = get_string('modulename', 'assignment');
- $this->strassignments = get_string('modulenameplural', 'assignment');
- $this->strsubmissions = get_string('submissions', 'assignment');
- $this->strlastmodified = get_string('lastmodified');
- $this->pagetitle = strip_tags($courseshortname.': '.$this->strassignment.': '.format_string($this->assignment->name, true, array('context' => $this->context)));
- // visibility handled by require_login() with $cm parameter
- // get current group only when really needed
- /// Set up things for a HTML editor if it's needed
- $this->defaultformat = editors_get_preferred_format();
- }
- /**
- * Display the assignment, used by view.php
- *
- * This in turn calls the methods producing individual parts of the page
- */
- function view() {
- $context = get_context_instance(CONTEXT_MODULE,$this->cm->id);
- require_capability('mod/assignment:view', $context);
- add_to_log($this->course->id, "assignment", "view", "view.php?id={$this->cm->id}",
- $this->assignment->id, $this->cm->id);
- $this->view_header();
- $this->view_intro();
- $this->view_dates();
- $this->view_feedback();
- $this->view_footer();
- }
- /**
- * Display the header and top of a page
- *
- * (this doesn't change much for assignment types)
- * This is used by the view() method to print the header of view.php but
- * it can be used on other pages in which case the string to denote the
- * page in the navigation trail should be passed as an argument
- *
- * @global object
- * @param string $subpage Description of subpage to be used in navigation trail
- */
- function view_header($subpage='') {
- global $CFG, $PAGE, $OUTPUT;
- if ($subpage) {
- $PAGE->navbar->add($subpage);
- }
- $PAGE->set_title($this->pagetitle);
- $PAGE->set_heading($this->course->fullname);
- echo $OUTPUT->header();
- groups_print_activity_menu($this->cm, $CFG->wwwroot . '/mod/assignment/view.php?id=' . $this->cm->id);
- echo '<div class="reportlink">'.$this->submittedlink().'</div>';
- echo '<div class="clearer"></div>';
- if (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
- echo $OUTPUT->notification(get_string('upgradenotification', 'assignment'));
- $adminurl = new moodle_url('/admin/tool/assignmentupgrade/listnotupgraded.php');
- echo $OUTPUT->single_button($adminurl, get_string('viewassignmentupgradetool', 'assignment'));
- }
- }
- /**
- * Display the assignment intro
- *
- * This will most likely be extended by assignment type plug-ins
- * The default implementation prints the assignment description in a box
- */
- function view_intro() {
- global $OUTPUT;
- echo $OUTPUT->box_start('generalbox boxaligncenter', 'intro');
- echo format_module_intro('assignment', $this->assignment, $this->cm->id);
- echo $OUTPUT->box_end();
- echo plagiarism_print_disclosure($this->cm->id);
- }
- /**
- * Display the assignment dates
- *
- * Prints the assignment start and end dates in a box.
- * This will be suitable for most assignment types
- */
- function view_dates() {
- global $OUTPUT;
- if (!$this->assignment->timeavailable && !$this->assignment->timedue) {
- return;
- }
- echo $OUTPUT->box_start('generalbox boxaligncenter', 'dates');
- echo '<table>';
- if ($this->assignment->timeavailable) {
- echo '<tr><td class="c0">'.get_string('availabledate','assignment').':</td>';
- echo ' <td class="c1">'.userdate($this->assignment->timeavailable).'</td></tr>';
- }
- if ($this->assignment->timedue) {
- echo '<tr><td class="c0">'.get_string('duedate','assignment').':</td>';
- echo ' <td class="c1">'.userdate($this->assignment->timedue).'</td></tr>';
- }
- echo '</table>';
- echo $OUTPUT->box_end();
- }
- /**
- * Display the bottom and footer of a page
- *
- * This default method just prints the footer.
- * This will be suitable for most assignment types
- */
- function view_footer() {
- global $OUTPUT;
- echo $OUTPUT->footer();
- }
- /**
- * Display the feedback to the student
- *
- * This default method prints the teacher picture and name, date when marked,
- * grade and teacher submissioncomment.
- * If advanced grading is used the method render_grade from the
- * advanced grading controller is called to display the grade.
- *
- * @global object
- * @global object
- * @global object
- * @param object $submission The submission object or NULL in which case it will be loaded
- */
- function view_feedback($submission=NULL) {
- global $USER, $CFG, $DB, $OUTPUT, $PAGE;
- require_once($CFG->libdir.'/gradelib.php');
- require_once("$CFG->dirroot/grade/grading/lib.php");
- if (!$submission) { /// Get submission for this assignment
- $userid = $USER->id;
- $submission = $this->get_submission($userid);
- } else {
- $userid = $submission->userid;
- }
- // Check the user can submit
- $canviewfeedback = ($userid == $USER->id && has_capability('mod/assignment:submit', $this->context, $USER->id, false));
- // If not then check if the user still has the view cap and has a previous submission
- $canviewfeedback = $canviewfeedback || (!empty($submission) && $submission->userid == $USER->id && has_capability('mod/assignment:view', $this->context));
- // Or if user can grade (is a teacher or admin)
- $canviewfeedback = $canviewfeedback || has_capability('mod/assignment:grade', $this->context);
- if (!$canviewfeedback) {
- // can not view or submit assignments -> no feedback
- return;
- }
- $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id, $userid);
- $item = $grading_info->items[0];
- $grade = $item->grades[$userid];
- if ($grade->hidden or $grade->grade === false) { // hidden or error
- return;
- }
- if ($grade->grade === null and empty($grade->str_feedback)) { /// Nothing to show yet
- return;
- }
- $graded_date = $grade->dategraded;
- $graded_by = $grade->usermodified;
- /// We need the teacher info
- if (!$teacher = $DB->get_record('user', array('id'=>$graded_by))) {
- print_error('cannotfindteacher');
- }
- /// Print the feedback
- echo $OUTPUT->heading(get_string('feedbackfromteacher', 'assignment', fullname($teacher)));
- echo '<table cellspacing="0" class="feedback">';
- echo '<tr>';
- echo '<td class="left picture">';
- if ($teacher) {
- echo $OUTPUT->user_picture($teacher);
- }
- echo '</td>';
- echo '<td class="topic">';
- echo '<div class="from">';
- if ($teacher) {
- echo '<div class="fullname">'.fullname($teacher).'</div>';
- }
- echo '<div class="time">'.userdate($graded_date).'</div>';
- echo '</div>';
- echo '</td>';
- echo '</tr>';
- echo '<tr>';
- echo '<td class="left side"> </td>';
- echo '<td class="content">';
- $gradestr = '<div class="grade">'. get_string("grade").': '.$grade->str_long_grade. '</div>';
- if (!empty($submission) && $controller = get_grading_manager($this->context, 'mod_assignment', 'submission')->get_active_controller()) {
- $controller->set_grade_range(make_grades_menu($this->assignment->grade));
- echo $controller->render_grade($PAGE, $submission->id, $item, $gradestr, has_capability('mod/assignment:grade', $this->context));
- } else {
- echo $gradestr;
- }
- echo '<div class="clearer"></div>';
- echo '<div class="comment">';
- echo $grade->str_feedback;
- echo '</div>';
- echo '</tr>';
- if ($this->type == 'uploadsingle') { //@TODO: move to overload view_feedback method in the class or is uploadsingle merging into upload?
- $responsefiles = $this->print_responsefiles($submission->userid, true);
- if (!empty($responsefiles)) {
- echo '<tr>';
- echo '<td class="left side"> </td>';
- echo '<td class="content">';
- echo $responsefiles;
- echo '</tr>';
- }
- }
- echo '</table>';
- }
- /**
- * Returns a link with info about the state of the assignment submissions
- *
- * This is used by view_header to put this link at the top right of the page.
- * For teachers it gives the number of submitted assignments with a link
- * For students it gives the time of their submission.
- * This will be suitable for most assignment types.
- *
- * @global object
- * @global object
- * @param bool $allgroup print all groups info if user can access all groups, suitable for index.php
- * @return string
- */
- function submittedlink($allgroups=false) {
- global $USER;
- global $CFG;
- $submitted = '';
- $urlbase = "{$CFG->wwwroot}/mod/assignment/";
- $context = get_context_instance(CONTEXT_MODULE,$this->cm->id);
- if (has_capability('mod/assignment:grade', $context)) {
- if ($allgroups and has_capability('moodle/site:accessallgroups', $context)) {
- $group = 0;
- } else {
- $group = groups_get_activity_group($this->cm);
- }
- if ($this->type == 'offline') {
- $submitted = '<a href="'.$urlbase.'submissions.php?id='.$this->cm->id.'">'.
- get_string('viewfeedback', 'assignment').'</a>';
- } else if ($count = $this->count_real_submissions($group)) {
- $submitted = '<a href="'.$urlbase.'submissions.php?id='.$this->cm->id.'">'.
- get_string('viewsubmissions', 'assignment', $count).'</a>';
- } else {
- $submitted = '<a href="'.$urlbase.'submissions.php?id='.$this->cm->id.'">'.
- get_string('noattempts', 'assignment').'</a>';
- }
- } else {
- if (isloggedin()) {
- if ($submission = $this->get_submission($USER->id)) {
- // If the submission has been completed
- if ($this->is_submitted_with_required_data($submission)) {
- if ($submission->timemodified <= $this->assignment->timedue || empty($this->assignment->timedue)) {
- $submitted = '<span class="early">'.userdate($submission->timemodified).'</span>';
- } else {
- $submitted = '<span class="late">'.userdate($submission->timemodified).'</span>';
- }
- }
- }
- }
- }
- return $submitted;
- }
- /**
- * Returns whether the assigment supports lateness information
- *
- * @return bool This assignment type supports lateness (true, default) or no (false)
- */
- function supports_lateness() {
- return true;
- }
- /**
- * @todo Document this function
- */
- function setup_elements(&$mform) {
- }
- /**
- * Any preprocessing needed for the settings form for
- * this assignment type
- *
- * @param array $default_values - array to fill in with the default values
- * in the form 'formelement' => 'value'
- * @param object $form - the form that is to be displayed
- * @return none
- */
- function form_data_preprocessing(&$default_values, $form) {
- }
- /**
- * Any extra validation checks needed for the settings
- * form for this assignment type
- *
- * See lib/formslib.php, 'validation' function for details
- */
- function form_validation($data, $files) {
- return array();
- }
- /**
- * Create a new assignment activity
- *
- * Given an object containing all the necessary data,
- * (defined by the form in mod_form.php) this function
- * will create a new instance and return the id number
- * of the new instance.
- * The due data is added to the calendar
- * This is common to all assignment types.
- *
- * @global object
- * @global object
- * @param object $assignment The data from the form on mod_form.php
- * @return int The id of the assignment
- */
- function add_instance($assignment) {
- global $COURSE, $DB;
- $assignment->timemodified = time();
- $assignment->courseid = $assignment->course;
- $returnid = $DB->insert_record("assignment", $assignment);
- $assignment->id = $returnid;
- if ($assignment->timedue) {
- $event = new stdClass();
- $event->name = $assignment->name;
- $event->description = format_module_intro('assignment', $assignment, $assignment->coursemodule);
- $event->courseid = $assignment->course;
- $event->groupid = 0;
- $event->userid = 0;
- $event->modulename = 'assignment';
- $event->instance = $returnid;
- $event->eventtype = 'due';
- $event->timestart = $assignment->timedue;
- $event->timeduration = 0;
- calendar_event::create($event);
- }
- assignment_grade_item_update($assignment);
- return $returnid;
- }
- /**
- * Deletes an assignment activity
- *
- * Deletes all database records, files and calendar events for this assignment.
- *
- * @global object
- * @global object
- * @param object $assignment The assignment to be deleted
- * @return boolean False indicates error
- */
- function delete_instance($assignment) {
- global $CFG, $DB;
- $assignment->courseid = $assignment->course;
- $result = true;
- // now get rid of all files
- $fs = get_file_storage();
- if ($cm = get_coursemodule_from_instance('assignment', $assignment->id)) {
- $context = get_context_instance(CONTEXT_MODULE, $cm->id);
- $fs->delete_area_files($context->id);
- }
- if (! $DB->delete_records('assignment_submissions', array('assignment'=>$assignment->id))) {
- $result = false;
- }
- if (! $DB->delete_records('event', array('modulename'=>'assignment', 'instance'=>$assignment->id))) {
- $result = false;
- }
- if (! $DB->delete_records('assignment', array('id'=>$assignment->id))) {
- $result = false;
- }
- $mod = $DB->get_field('modules','id',array('name'=>'assignment'));
- assignment_grade_item_delete($assignment);
- return $result;
- }
- /**
- * Updates a new assignment activity
- *
- * Given an object containing all the necessary data,
- * (defined by the form in mod_form.php) this function
- * will update the assignment instance and return the id number
- * The due date is updated in the calendar
- * This is common to all assignment types.
- *
- * @global object
- * @global object
- * @param object $assignment The data from the form on mod_form.php
- * @return bool success
- */
- function update_instance($assignment) {
- global $COURSE, $DB;
- $assignment->timemodified = time();
- $assignment->id = $assignment->instance;
- $assignment->courseid = $assignment->course;
- $DB->update_record('assignment', $assignment);
- if ($assignment->timedue) {
- $event = new stdClass();
- if ($event->id = $DB->get_field('event', 'id', array('modulename'=>'assignment', 'instance'=>$assignment->id))) {
- $event->name = $assignment->name;
- $event->description = format_module_intro('assignment', $assignment, $assignment->coursemodule);
- $event->timestart = $assignment->timedue;
- $calendarevent = calendar_event::load($event->id);
- $calendarevent->update($event);
- } else {
- $event = new stdClass();
- $event->name = $assignment->name;
- $event->description = format_module_intro('assignment', $assignment, $assignment->coursemodule);
- $event->courseid = $assignment->course;
- $event->groupid = 0;
- $event->userid = 0;
- $event->modulename = 'assignment';
- $event->instance = $assignment->id;
- $event->eventtype = 'due';
- $event->timestart = $assignment->timedue;
- $event->timeduration = 0;
- calendar_event::create($event);
- }
- } else {
- $DB->delete_records('event', array('modulename'=>'assignment', 'instance'=>$assignment->id));
- }
- // get existing grade item
- assignment_grade_item_update($assignment);
- return true;
- }
- /**
- * Update grade item for this submission.
- *
- * @param stdClass $submission The submission instance
- */
- function update_grade($submission) {
- assignment_update_grades($this->assignment, $submission->userid);
- }
- /**
- * Top-level function for handling of submissions called by submissions.php
- *
- * This is for handling the teacher interaction with the grading interface
- * This should be suitable for most assignment types.
- *
- * @global object
- * @param string $mode Specifies the kind of teacher interaction taking place
- */
- function submissions($mode) {
- ///The main switch is changed to facilitate
- ///1) Batch fast grading
- ///2) Skip to the next one on the popup
- ///3) Save and Skip to the next one on the popup
- //make user global so we can use the id
- global $USER, $OUTPUT, $DB, $PAGE;
- $mailinfo = optional_param('mailinfo', null, PARAM_BOOL);
- if (optional_param('next', null, PARAM_BOOL)) {
- $mode='next';
- }
- if (optional_param('saveandnext', null, PARAM_BOOL)) {
- $mode='saveandnext';
- }
- if (is_null($mailinfo)) {
- if (optional_param('sesskey', null, PARAM_BOOL)) {
- set_user_preference('assignment_mailinfo', 0);
- } else {
- $mailinfo = get_user_preferences('assignment_mailinfo', 0);
- }
- } else {
- set_user_preference('assignment_mailinfo', $mailinfo);
- }
- if (!($this->validate_and_preprocess_feedback())) {
- // form was submitted ('Save' or 'Save and next' was pressed, but validation failed)
- $this->display_submission();
- return;
- }
- switch ($mode) {
- case 'grade': // We are in a main window grading
- if ($submission = $this->process_feedback()) {
- $this->display_submissions(get_string('changessaved'));
- } else {
- $this->display_submissions();
- }
- break;
- case 'single': // We are in a main window displaying one submission
- if ($submission = $this->process_feedback()) {
- $this->display_submissions(get_string('changessaved'));
- } else {
- $this->display_submission();
- }
- break;
- case 'all': // Main window, display everything
- $this->display_submissions();
- break;
- case 'fastgrade':
- ///do the fast grading stuff - this process should work for all 3 subclasses
- $grading = false;
- $commenting = false;
- $col = false;
- if (isset($_POST['submissioncomment'])) {
- $col = 'submissioncomment';
- $commenting = true;
- }
- if (isset($_POST['menu'])) {
- $col = 'menu';
- $grading = true;
- }
- if (!$col) {
- //both submissioncomment and grade columns collapsed..
- $this->display_submissions();
- break;
- }
- foreach ($_POST[$col] as $id => $unusedvalue){
- $id = (int)$id; //clean parameter name
- $this->process_outcomes($id);
- if (!$submission = $this->get_submission($id)) {
- $submission = $this->prepare_new_submission($id);
- $newsubmission = true;
- } else {
- $newsubmission = false;
- }
- unset($submission->data1); // Don't need to update this.
- unset($submission->data2); // Don't need to update this.
- //for fast grade, we need to check if any changes take place
- $updatedb = false;
- if ($grading) {
- $grade = $_POST['menu'][$id];
- $updatedb = $updatedb || ($submission->grade != $grade);
- $submission->grade = $grade;
- } else {
- if (!$newsubmission) {
- unset($submission->grade); // Don't need to update this.
- }
- }
- if ($commenting) {
- $commentvalue = trim($_POST['submissioncomment'][$id]);
- $updatedb = $updatedb || ($submission->submissioncomment != $commentvalue);
- $submission->submissioncomment = $commentvalue;
- } else {
- unset($submission->submissioncomment); // Don't need to update this.
- }
- $submission->teacher = $USER->id;
- if ($updatedb) {
- $submission->mailed = (int)(!$mailinfo);
- }
- $submission->timemarked = time();
- //if it is not an update, we don't change the last modified time etc.
- //this will also not write into database if no submissioncomment and grade is entered.
- if ($updatedb){
- if ($newsubmission) {
- if (!isset($submission->submissioncomment)) {
- $submission->submissioncomment = '';
- }
- $sid = $DB->insert_record('assignment_submissions', $submission);
- $submission->id = $sid;
- } else {
- $DB->update_record('assignment_submissions', $submission);
- }
- // trigger grade event
- $this->update_grade($submission);
- //add to log only if updating
- add_to_log($this->course->id, 'assignment', 'update grades',
- 'submissions.php?id='.$this->cm->id.'&user='.$submission->userid,
- $submission->userid, $this->cm->id);
- }
- }
- $message = $OUTPUT->notification(get_string('changessaved'), 'notifysuccess');
- $this->display_submissions($message);
- break;
- case 'saveandnext':
- ///We are in pop up. save the current one and go to the next one.
- //first we save the current changes
- if ($submission = $this->process_feedback()) {
- //print_heading(get_string('changessaved'));
- //$extra_javascript = $this->update_main_listing($submission);
- }
- case 'next':
- /// We are currently in pop up, but we want to skip to next one without saving.
- /// This turns out to be similar to a single case
- /// The URL used is for the next submission.
- $offset = required_param('offset', PARAM_INT);
- $nextid = required_param('nextid', PARAM_INT);
- $id = required_param('id', PARAM_INT);
- $filter = optional_param('filter', self::FILTER_ALL, PARAM_INT);
- if ($mode == 'next' || $filter !== self::FILTER_REQUIRE_GRADING) {
- $offset = (int)$offset+1;
- }
- $redirect = new moodle_url('submissions.php',
- array('id' => $id, 'offset' => $offset, 'userid' => $nextid,
- 'mode' => 'single', 'filter' => $filter));
- redirect($redirect);
- break;
- case 'singlenosave':
- $this->display_submission();
- break;
- default:
- echo "something seriously is wrong!!";
- break;
- }
- }
- /**
- * Checks if grading method allows quickgrade mode. At the moment it is hardcoded
- * that advanced grading methods do not allow quickgrade.
- *
- * Assignment type plugins are not allowed to override this method
- *
- * @return boolean
- */
- public final function quickgrade_mode_allowed() {
- global $CFG;
- require_once("$CFG->dirroot/grade/grading/lib.php");
- if ($controller = get_grading_manager($this->context, 'mod_assignment', 'submission')->get_active_controller()) {
- return false;
- }
- return true;
- }
- /**
- * Helper method updating the listing on the main script from popup using javascript
- *
- * @global object
- * @global object
- * @param $submission object The submission whose data is to be updated on the main page
- */
- function update_main_listing($submission) {
- global $SESSION, $CFG, $OUTPUT;
- $output = '';
- $perpage = get_user_preferences('assignment_perpage', 10);
- $quickgrade = get_user_preferences('assignment_quickgrade', 0) && $this->quickgrade_mode_allowed();
- /// Run some Javascript to try and update the parent page
- $output .= '<script type="text/javascript">'."\n<!--\n";
- if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['submissioncomment'])) {
- if ($quickgrade){
- $output.= 'opener.document.getElementById("submissioncomment'.$submission->userid.'").value="'
- .trim($submission->submissioncomment).'";'."\n";
- } else {
- $output.= 'opener.document.getElementById("com'.$submission->userid.
- '").innerHTML="'.shorten_text(trim(strip_tags($submission->submissioncomment)), 15)."\";\n";
- }
- }
- if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['grade'])) {
- //echo optional_param('menuindex');
- if ($quickgrade){
- $output.= 'opener.document.getElementById("menumenu'.$submission->userid.
- '").selectedIndex="'.optional_param('menuindex', 0, PARAM_INT).'";'."\n";
- } else {
- $output.= 'opener.document.getElementById("g'.$submission->userid.'").innerHTML="'.
- $this->display_grade($submission->grade)."\";\n";
- }
- }
- //need to add student's assignments in there too.
- if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['timemodified']) &&
- $submission->timemodified) {
- $output.= 'opener.document.getElementById("ts'.$submission->userid.
- '").innerHTML="'.addslashes_js($this->print_student_answer($submission->userid)).userdate($submission->timemodified)."\";\n";
- }
- if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['timemarked']) &&
- $submission->timemarked) {
- $output.= 'opener.document.getElementById("tt'.$submission->userid.
- '").innerHTML="'.userdate($submission->timemarked)."\";\n";
- }
- if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['status'])) {
- $output.= 'opener.document.getElementById("up'.$submission->userid.'").className="s1";';
- $buttontext = get_string('update');
- $url = new moodle_url('/mod/assignment/submissions.php', array(
- 'id' => $this->cm->id,
- 'userid' => $submission->userid,
- 'mode' => 'single',
- 'offset' => (optional_param('offset', '', PARAM_INT)-1)));
- $button = $OUTPUT->action_link($url, $buttontext, new popup_action('click', $url, 'grade'.$submission->userid, array('height' => 450, 'width' => 700)), array('ttile'=>$buttontext));
- $output .= 'opener.document.getElementById("up'.$submission->userid.'").innerHTML="'.addslashes_js($button).'";';
- }
- $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id, $submission->userid);
- if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['finalgrade'])) {
- $output.= 'opener.document.getElementById("finalgrade_'.$submission->userid.
- '").innerHTML="'.$grading_info->items[0]->grades[$submission->userid]->str_grade.'";'."\n";
- }
- if (!empty($CFG->enableoutcomes) and empty($SESSION->flextable['mod-assignment-submissions']->collapse['outcome'])) {
- if (!empty($grading_info->outcomes)) {
- foreach($grading_info->outcomes as $n=>$outcome) {
- if ($outcome->grades[$submission->userid]->locked) {
- continue;
- }
- if ($quickgrade){
- $output.= 'opener.document.getElementById("outcome_'.$n.'_'.$submission->userid.
- '").selectedIndex="'.$outcome->grades[$submission->userid]->grade.'";'."\n";
- } else {
- $options = make_grades_menu(-$outcome->scaleid);
- $options[0] = get_string('nooutcome', 'grades');
- $output.= 'opener.document.getElementById("outcome_'.$n.'_'.$submission->userid.'").innerHTML="'.$options[$outcome->grades[$submission->userid]->grade]."\";\n";
- }
- }
- }
- }
- $output .= "\n-->\n</script>";
- return $output;
- }
- /**
- * Return a grade in user-friendly form, whether it's a scale or not
- *
- * @global object
- * @param mixed $grade
- * @return string User-friendly representation of grade
- */
- function display_grade($grade) {
- global $DB;
- static $scalegrades = array(); // Cache scales for each assignment - they might have different scales!!
- if ($this->assignment->grade >= 0) { // Normal number
- if ($grade == -1) {
- return '-';
- } else {
- return $grade.' / '.$this->assignment->grade;
- }
- } else { // Scale
- if (empty($scalegrades[$this->assignment->id])) {
- if ($scale = $DB->get_record('scale', array('id'=>-($this->assignment->grade)))) {
- $scalegrades[$this->assignment->id] = make_menu_from_list($scale->scale);
- } else {
- return '-';
- }
- }
- if (isset($scalegrades[$this->assignment->id][$grade])) {
- return $scalegrades[$this->assignment->id][$grade];
- }
- return '-';
- }
- }
- /**
- * Display a single submission, ready for grading on a popup window
- *
- * This default method prints the teacher info and submissioncomment box at the top and
- * the student info and submission at the bottom.
- * This method also fetches the necessary data in order to be able to
- * provide a "Next submission" button.
- * Calls preprocess_submission() to give assignment type plug-ins a chance
- * to process submissions before they are graded
- * This method gets its arguments from the page parameters userid and offset
- *
- * @global object
- * @global object
- * @param string $extra_javascript
- */
- function display_submission($offset=-1,$userid =-1, $display=true) {
- global $CFG, $DB, $PAGE, $OUTPUT, $USER;
- require_once($CFG->libdir.'/gradelib.php');
- require_once($CFG->libdir.'/tablelib.php');
- require_once("$CFG->dirroot/repository/lib.php");
- require_once("$CFG->dirroot/grade/grading/lib.php");
- if ($userid==-1) {
- $userid = required_param('userid', PARAM_INT);
- }
- if ($offset==-1) {
- $offset = required_param('offset', PARAM_INT);//offset for where to start looking for student.
- }
- $filter = optional_param('filter', 0, PARAM_INT);
- if (!$user = $DB->get_record('user', array('id'=>$userid))) {
- print_error('nousers');
- }
- if (!$submission = $this->get_submission($user->id)) {
- $submission = $this->prepare_new_submission($userid);
- }
- if ($submission->timemodified > $submission->timemarked) {
- $subtype = 'assignmentnew';
- } else {
- $subtype = 'assignmentold';
- }
- $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id, array($user->id));
- $gradingdisabled = $grading_info->items[0]->grades[$userid]->locked || $grading_info->items[0]->grades[$userid]->overridden;
- /// construct SQL, using current offset to find the data of the next student
- $course = $this->course;
- $assignment = $this->assignment;
- $cm = $this->cm;
- $context = get_context_instance(CONTEXT_MODULE, $cm->id);
- //reset filter to all for offline assignment
- if ($assignment->assignmenttype == 'offline' && $filter == self::FILTER_SUBMITTED) {
- $filter = self::FILTER_ALL;
- }
- /// Get all ppl that can submit assignments
- $currentgroup = groups_get_activity_group($cm);
- $users = get_enrolled_users($context, 'mod/assignment:submit', $currentgroup, 'u.id');
- if ($users) {
- $users = array_keys($users);
- // if groupmembersonly used, remove users who are not in any group
- if (!empty($CFG->enablegroupmembersonly) and $cm->groupmembersonly) {
- if ($groupingusers = groups_get_grouping_members($cm->groupingid, 'u.id', 'u.id')) {
- $users = array_intersect($users, array_keys($groupingusers));
- }
- }
- }
- $nextid = 0;
- $where = '';
- if($filter == self::FILTER_SUBMITTED) {
- $where .= 's.timemodified > 0 AND ';
- } else if($filter == self::FILTER_REQUIRE_GRADING) {
- $where .= 's.timemarked < s.timemodified AND ';
- }
- if ($users) {
- $userfields = user_picture::fields('u', array('lastaccess'));
- $select = "SELECT $userfields,
- s.id AS submissionid, s.grade, s.submissioncomment,
- s.timemodified, s.timemarked,
- CASE WHEN s.timemarked > 0 AND s.timemarked >= s.timemodified THEN 1
- ELSE 0 END AS status ";
- $sql = 'FROM {user} u '.
- 'LEFT JOIN {assignment_submissions} s ON u.id = s.userid
- AND s.assignment = '.$this->assignment->id.' '.
- 'WHERE '.$where.'u.id IN ('.implode(',', $users).') ';
- if ($sort = flexible_table::get_sort_for_table('mod-assignment-submissions')) {
- $sort = 'ORDER BY '.$sort.' ';
- }
- $auser = $DB->get_records_sql($select.$sql.$sort, null, $offset, 2);
- if (is_array($auser) && count($auser)>1) {
- $nextuser = next($auser);
- $nextid = $nextuser->id;
- }
- }
- if ($submission->teacher) {
- $teacher = $DB->get_record('user', array('id'=>$submission->teacher));
- } else {
- global $USER;
- $teacher = $USER;
- }
- $this->preprocess_submission($submission);
- $mformdata = new stdClass();
- $mformdata->context = $this->context;
- $mformdata->maxbytes = $this->course->maxbytes;
- $mformdata->courseid = $this->course->id;
- $mformdata->teacher = $teacher;
- $mformdata->assignment = $assignment;
- $mformdata->submission = $submission;
- $mformdata->lateness = $this->display_lateness($submission->timemodified);
- $mformdata->auser = $auser;
- $mformdata->user = $user;
- $mformdata->offset = $offset;
- $mformdata->userid = $userid;
- $mformdata->cm = $this->cm;
- $mformdata->grading_info = $grading_info;
- $mformdata->enableoutcomes = $CFG->enableoutcomes;
- $mformdata->grade = $this->assignment->grade;
- $mformdata->gradingdisabled = $gradingdisabled;
- $mformdata->nextid = $nextid;
- $mformdata->submissioncomment= $submission->submissioncomment;
- $mformdata->submissioncommentformat= FORMAT_HTML;
- $mformdata->submission_content= $this->print_user_files($user->id,true);
- $mformdata->filter = $filter;
- $mformdata->mailinfo = get_user_preferences('assignment_mailinfo', 0);
- if ($assignment->assignmenttype == 'upload') {
- $mformdata->fileui_options = array('subdirs'=>1, 'maxbytes'=>$assignment->maxbytes, 'maxfiles'=>$assignment->var1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL);
- } elseif ($assignment->assignmenttype == 'uploadsingle') {
- $mformdata->fileui_options = array('subdirs'=>0, 'maxbytes'=>$CFG->userquota, 'maxfiles'=>1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL);
- }
- $advancedgradingwarning = false;
- $gradingmanager = get_grading_manager($this->context, 'mod_assignment', 'submission');
- if ($gradingmethod = $gradingmanager->get_active_method()) {
- $controller = $gradingmanager->get_controller($gradingmethod);
- if ($controller->is_form_available()) {
- $itemid = null;
- if (!empty($submission->id)) {
- $itemid = $submission->id;
- }
- if ($gradingdisabled && $itemid) {
- $mformdata->advancedgradinginstance = $controller->get_current_instance($USER->id, $itemid);
- } else if (!$gradingdisabled) {
- $instanceid = optional_param('advancedgradinginstanceid', 0, PARAM_INT);
- $mformdata->advancedgradinginstance = $controller->get_or_create_instance($instanceid, $USER->id, $itemid);
- }
- } else {
- $advancedgradingwarning = $controller->form_unavailable_notification();
- }
- }
- $submitform = new assignment_grading_form( null, $mformdata );
- if (!$display) {
- $ret_data = new stdClass();
- $ret_data->mform = $submitform;
- if (isset($mformdata->fileui_options)) {
- $ret_data->fileui_options = $mformdata->fileui_options;
- }
- return $ret_data;
- }
- if ($submitform->is_cancelled()) {
- redirect('submissions.php?id='.$this->cm->id);
- }
- $submitform->set_data($mformdata);
- $PAGE->set_title($this->course->fullname . ': ' .get_string('feedback', 'assignment').' - '.fullname($user, true));
- $PAGE->set_heading($this->course->fullname);
- $PAGE->navbar->add(get_string('submissions', 'assignment'), new moodle_url('/mod/assignment/submissions.php', array('id'=>$cm->id)));
- $PAGE->navbar->add(fullname($user, true));
- echo $OUTPUT->header();
- echo $OUTPUT->heading(get_string('feedback', 'assignment').': '.fullname($user, true));
- // display mform here...
- if ($advancedgradingwarning) {
- echo $OUTPUT->notification($advancedgradingwarning, 'error');
- }
- $submitform->display();
- $customfeedback = $this->custom_feedbackform($submission, true);
- if (!empty($customfeedback)) {
- echo $customfeedback;
- }
- echo $OUTPUT->footer();
- }
- /**
- * Preprocess submission before grading
- *
- * Called by display_submission()
- * The default type does nothing here.
- *
- * @param object $submission The submission object
- */
- function preprocess_submission(&$submission) {
- }
- /**
- * Display all the submissions ready for grading
- *
- * @global object
- * @global object
- * @global object
- * @global object
- * @param string $message
- * @return bool|void
- */
- function display_submissions($message='') {
- global $CFG, $DB, $USER, $DB, $OUTPUT, $PAGE;
- require_once($CFG->libdir.'/gradelib.php');
- /* first we check to see if the form has just been submitted
- * to request user_preference updates
- */
- $filters = array(self::FILTER_ALL => get_string('all'),
- self::FILTER_REQUIRE_GRADING => get_string('requiregrading', 'assignment'));
- $updatepref = optional_param('updatepref', 0, PARAM_BOOL);
- if ($updatepref) {
- $perpage = optional_param('perpage', 10, PARAM_INT);
- $perpage = ($perpage <= 0) ? 10 : $perpage ;
- $filter = optional_param('filter', 0, PARAM_INT);
- set_user_preference('assignment_perpage', $perpage);
- set_user_preference('assignment_quickgrade', optional_param('quickgrade', 0, PARAM_BOOL));
- set_user_preference('assignment_filter', $filter);
- }
- /* next we get perpage and quickgrade (allow quick grade) params
- * from database
- */
- $perpage = get_user_preferences('assignment_perpage', 10);
- $quickgrade = get_user_preferences('assignment_quickgrade', 0) && $this->quickgrade_mode_allowed();
- $filter = get_user_preferences('assignment_filter', 0);
- $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id);
- if (!empty($CFG->enableoutcomes) and !empty($grading_info->outcomes)) {
- $uses_outcomes = true;
- } else {
- $uses_outcomes = false;
- }
- $page = optional_param('page', 0, PARAM_INT);
- $strsaveallfeedback = get_string('saveallfeedback', 'assignment');
- /// Some shortcuts to make the code read better
- $course = $this->course;
- $assignment = $this->assignment;
- $cm = $this->cm;
- $hassubmission = false;
- // reset filter to all for offline assignment only.
- if ($assignment->assignmenttype == 'offline') {
- if ($filter == self::FILTER_SUBMITTED) {
- $filter = self::FILTER_ALL;
- }
- } else {
- $filters[self::FILTER_SUBMITTED] = get_string('submitted', 'assignment');
- }
- $tabindex = 1; //tabindex for quick grading tabbing; Not working for dropdowns yet
- add_to_log($course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->cm->id, $this->assignment->id, $this->cm->id);
- $PAGE->set_title(format_string($this->assignment->name,true));
- $PAGE->set_heading($this->course->fullname);
- echo $OUTPUT->header();
- echo '<div class="usersubmissions">';
- //hook to allow plagiarism plugins to update status/print links.
- echo plagiarism_update_status($this->course, $this->cm);
- $course_context = get_context_instance(CONTEXT_COURSE, $course->id);
- if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
- echo '<div class="allcoursegrades"><a href="' . $CFG->wwwroot . '/grade/report/grader/index.php?id=' . $course->id . '">'
- . get_string('seeallcoursegrades', 'grades') . '</a></div>';
- }
- if (!empty($message)) {
- echo $message; // display messages here if any
- }
- $context = get_context_instance(CONTEXT_MODULE, $cm->id);
- /// Check to see if groups are being used in this assignment
- /// find out current groups mode
- $groupmode = groups_get_activity_groupmode($cm);
- $currentgroup = groups_get_activity_group($cm, true);
- groups_print_activity_menu($cm, $CFG->wwwroot . '/mod/assignment/submissions.php?id=' . $this->cm->id);
- /// Print quickgrade form around the table
- …
Large files files are truncated, but you can click here to view the full file