Пример #1
0
 /**
  * Describes the parameters for save_grades
  * @return external_external_function_parameters
  * @since  Moodle 2.7
  */
 public static function save_grades_parameters()
 {
     global $CFG;
     require_once "{$CFG->dirroot}/mod/sepl/locallib.php";
     require_once "{$CFG->dirroot}/grade/grading/lib.php";
     $instance = new sepl(null, null, null);
     $pluginfeedbackparams = array();
     foreach ($instance->get_feedback_plugins() as $plugin) {
         $pluginparams = $plugin->get_external_parameters();
         if (!empty($pluginparams)) {
             $pluginfeedbackparams = array_merge($pluginfeedbackparams, $pluginparams);
         }
     }
     $advancedgradingdata = array();
     $methods = array_keys(grading_manager::available_methods(false));
     foreach ($methods as $method) {
         require_once $CFG->dirroot . '/grade/grading/form/' . $method . '/lib.php';
         $details = call_user_func('gradingform_' . $method . '_controller::get_external_instance_filling_details');
         if (!empty($details)) {
             $items = array();
             foreach ($details as $key => $value) {
                 $value->required = VALUE_OPTIONAL;
                 unset($value->content->keys['id']);
                 $items[$key] = new external_multiple_structure(new external_single_structure(array('criterionid' => new external_value(PARAM_INT, 'criterion id'), 'fillings' => $value)));
             }
             $advancedgradingdata[$method] = new external_single_structure($items, 'items', VALUE_OPTIONAL);
         }
     }
     return new external_function_parameters(array('seplmentid' => new external_value(PARAM_INT, 'The seplment id to operate on'), 'applytoall' => new external_value(PARAM_BOOL, 'If true, this grade will be applied ' . 'to all members ' . 'of the group (for group seplments).'), 'grades' => new external_multiple_structure(new external_single_structure(array('userid' => new external_value(PARAM_INT, 'The student id to operate on'), 'grade' => new external_value(PARAM_FLOAT, 'The new grade for this user. ' . 'Ignored if advanced grading used'), 'attemptnumber' => new external_value(PARAM_INT, 'The attempt number (-1 means latest attempt)'), 'addattempt' => new external_value(PARAM_BOOL, 'Allow another attempt if manual attempt reopen method'), 'workflowstate' => new external_value(PARAM_ALPHA, 'The next marking workflow state'), 'plugindata' => new external_single_structure($pluginfeedbackparams, 'plugin data', VALUE_DEFAULT, array()), 'advancedgradingdata' => new external_single_structure($advancedgradingdata, 'advanced grading data', VALUE_DEFAULT, array()))))));
 }
Пример #2
0
/**
 * File browsing support for sepl module.
 *
 * @param file_browser $browser
 * @param object $areas
 * @param object $course
 * @param object $cm
 * @param object $context
 * @param string $filearea
 * @param int $itemid
 * @param string $filepath
 * @param string $filename
 * @return object file_info instance or null if not found
 */
function sepl_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename)
{
    global $CFG;
    require_once $CFG->dirroot . '/mod/sepl/locallib.php';
    if ($context->contextlevel != CONTEXT_MODULE) {
        return null;
    }
    $urlbase = $CFG->wwwroot . '/pluginfile.php';
    $fs = get_file_storage();
    $filepath = is_null($filepath) ? '/' : $filepath;
    $filename = is_null($filename) ? '.' : $filename;
    // Need to find where this belongs to.
    $seplment = new sepl($context, $cm, $course);
    if ($filearea === ASSIGN_INTROATTACHMENT_FILEAREA) {
        if (!has_capability('moodle/course:managefiles', $context)) {
            // Students can not peak here!
            return null;
        }
        if (!($storedfile = $fs->get_file($seplment->get_context()->id, 'mod_sepl', $filearea, 0, $filepath, $filename))) {
            return null;
        }
        return new file_info_stored($browser, $seplment->get_context(), $storedfile, $urlbase, $filearea, $itemid, true, true, false);
    }
    $pluginowner = null;
    foreach ($seplment->get_submission_plugins() as $plugin) {
        if ($plugin->is_visible()) {
            $pluginareas = $plugin->get_file_areas();
            if (array_key_exists($filearea, $pluginareas)) {
                $pluginowner = $plugin;
                break;
            }
        }
    }
    if (!$pluginowner) {
        foreach ($seplment->get_feedback_plugins() as $plugin) {
            if ($plugin->is_visible()) {
                $pluginareas = $plugin->get_file_areas();
                if (array_key_exists($filearea, $pluginareas)) {
                    $pluginowner = $plugin;
                    break;
                }
            }
        }
    }
    if (!$pluginowner) {
        return null;
    }
    $result = $pluginowner->get_file_info($browser, $filearea, $itemid, $filepath, $filename);
    return $result;
}
Пример #3
0
 /**
  * This function converts all of the base settings for an instance of
  * the old seplment to the new format. Then it calls each of the plugins
  * to see if they can help upgrade this seplment.
  * @param int $oldseplmentid (don't rely on the old seplment type even being installed)
  * @param string $log This string gets appended to during the conversion process
  * @return bool true or false
  */
 public function upgrade_seplment($oldseplmentid, &$log)
 {
     global $DB, $CFG, $USER;
     // Steps to upgrade an seplment.
     core_php_time_limit::raise(ASSIGN_MAX_UPGRADE_TIME_SECS);
     // Get the module details.
     $oldmodule = $DB->get_record('modules', array('name' => 'seplment'), '*', MUST_EXIST);
     $params = array('module' => $oldmodule->id, 'instance' => $oldseplmentid);
     $oldcoursemodule = $DB->get_record('course_modules', $params, '*', MUST_EXIST);
     $oldcontext = context_module::instance($oldcoursemodule->id);
     // We used to check for admin capability, but since Moodle 2.7 this is called
     // during restore of a mod_seplment module.
     // Also note that we do not check for any mod_seplment capabilities, because they can
     // be removed so that users don't add new instances of the broken old thing.
     if (!has_capability('mod/sepl:addinstance', $oldcontext)) {
         $log = get_string('couldnotcreatenewseplmentinstance', 'mod_sepl');
         return false;
     }
     // First insert an sepl instance to get the id.
     $oldseplment = $DB->get_record('seplment', array('id' => $oldseplmentid), '*', MUST_EXIST);
     $oldversion = get_config('seplment_' . $oldseplment->seplmenttype, 'version');
     $data = new stdClass();
     $data->course = $oldseplment->course;
     $data->name = $oldseplment->name;
     $data->intro = $oldseplment->intro;
     $data->introformat = $oldseplment->introformat;
     $data->alwaysshowdescription = 1;
     $data->sendnotifications = $oldseplment->emailteachers;
     $data->sendlatenotifications = $oldseplment->emailteachers;
     $data->duedate = $oldseplment->timedue;
     $data->allowsubmissionsfromdate = $oldseplment->timeavailable;
     $data->grade = $oldseplment->grade;
     $data->submissiondrafts = $oldseplment->resubmit;
     $data->requiresubmissionstatement = 0;
     $data->markingworkflow = 0;
     $data->markingallocation = 0;
     $data->cutoffdate = 0;
     // New way to specify no late submissions.
     if ($oldseplment->preventlate) {
         $data->cutoffdate = $data->duedate;
     }
     $data->teamsubmission = 0;
     $data->requireallteammemberssubmit = 0;
     $data->teamsubmissiongroupingid = 0;
     $data->blindmarking = 0;
     $data->attemptreopenmethod = 'none';
     $data->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
     $newseplment = new sepl(null, null, null);
     if (!$newseplment->add_instance($data, false)) {
         $log = get_string('couldnotcreatenewseplmentinstance', 'mod_sepl');
         return false;
     }
     // Now create a new coursemodule from the old one.
     $newmodule = $DB->get_record('modules', array('name' => 'sepl'), '*', MUST_EXIST);
     $newcoursemodule = $this->duplicate_course_module($oldcoursemodule, $newmodule->id, $newseplment->get_instance()->id);
     if (!$newcoursemodule) {
         $log = get_string('couldnotcreatenewcoursemodule', 'mod_sepl');
         return false;
     }
     // Convert the base database tables (seplment, submission, grade).
     // These are used to store information in case a rollback is required.
     $gradingarea = null;
     $gradingdefinitions = null;
     $gradeidmap = array();
     $completiondone = false;
     $gradesdone = false;
     // From this point we want to rollback on failure.
     $rollback = false;
     try {
         $newseplment->set_context(context_module::instance($newcoursemodule->id));
         // The course module has now been created - time to update the core tables.
         // Copy intro files.
         $newseplment->copy_area_files_for_upgrade($oldcontext->id, 'mod_seplment', 'intro', 0, $newseplment->get_context()->id, 'mod_sepl', 'intro', 0);
         // Get the plugins to do their bit.
         foreach ($newseplment->get_submission_plugins() as $plugin) {
             if ($plugin->can_upgrade($oldseplment->seplmenttype, $oldversion)) {
                 $plugin->enable();
                 if (!$plugin->upgrade_settings($oldcontext, $oldseplment, $log)) {
                     $rollback = true;
                 }
             } else {
                 $plugin->disable();
             }
         }
         foreach ($newseplment->get_feedback_plugins() as $plugin) {
             if ($plugin->can_upgrade($oldseplment->seplmenttype, $oldversion)) {
                 $plugin->enable();
                 if (!$plugin->upgrade_settings($oldcontext, $oldseplment, $log)) {
                     $rollback = true;
                 }
             } else {
                 $plugin->disable();
             }
         }
         // See if there is advanced grading upgrades required.
         $gradingarea = $DB->get_record('grading_areas', array('contextid' => $oldcontext->id, 'areaname' => 'submission'), '*', IGNORE_MISSING);
         if ($gradingarea) {
             $params = array('id' => $gradingarea->id, 'contextid' => $newseplment->get_context()->id, 'component' => 'mod_sepl', 'areaname' => 'submissions');
             $DB->update_record('grading_areas', $params);
             $gradingdefinitions = $DB->get_records('grading_definitions', array('areaid' => $gradingarea->id));
         }
         // Upgrade availability data.
         \core_availability\info::update_dependency_id_across_course($newcoursemodule->course, 'course_modules', $oldcoursemodule->id, $newcoursemodule->id);
         // Upgrade completion data.
         $DB->set_field('course_modules_completion', 'coursemoduleid', $newcoursemodule->id, array('coursemoduleid' => $oldcoursemodule->id));
         $allcriteria = $DB->get_records('course_completion_criteria', array('moduleinstance' => $oldcoursemodule->id));
         foreach ($allcriteria as $criteria) {
             $criteria->module = 'sepl';
             $criteria->moduleinstance = $newcoursemodule->id;
             $DB->update_record('course_completion_criteria', $criteria);
         }
         $completiondone = true;
         // Migrate log entries so we don't lose them.
         $logparams = array('cmid' => $oldcoursemodule->id, 'course' => $oldcoursemodule->course);
         $DB->set_field('log', 'module', 'sepl', $logparams);
         $DB->set_field('log', 'cmid', $newcoursemodule->id, $logparams);
         // Copy all the submission data (and get plugins to do their bit).
         $oldsubmissions = $DB->get_records('seplment_submissions', array('seplment' => $oldseplmentid));
         foreach ($oldsubmissions as $oldsubmission) {
             $submission = new stdClass();
             $submission->seplment = $newseplment->get_instance()->id;
             $submission->userid = $oldsubmission->userid;
             $submission->timecreated = $oldsubmission->timecreated;
             $submission->timemodified = $oldsubmission->timemodified;
             $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
             // Because in mod_seplment there could only be one submission per student, it is always the latest.
             $submission->latest = 1;
             $submission->id = $DB->insert_record('sepl_submission', $submission);
             if (!$submission->id) {
                 $log .= get_string('couldnotinsertsubmission', 'mod_sepl', $submission->userid);
                 $rollback = true;
             }
             foreach ($newseplment->get_submission_plugins() as $plugin) {
                 if ($plugin->can_upgrade($oldseplment->seplmenttype, $oldversion)) {
                     if (!$plugin->upgrade($oldcontext, $oldseplment, $oldsubmission, $submission, $log)) {
                         $rollback = true;
                     }
                 }
             }
             if ($oldsubmission->timemarked) {
                 // Submission has been graded - create a grade record.
                 $grade = new stdClass();
                 $grade->seplment = $newseplment->get_instance()->id;
                 $grade->userid = $oldsubmission->userid;
                 $grade->grader = $oldsubmission->teacher;
                 $grade->timemodified = $oldsubmission->timemarked;
                 $grade->timecreated = $oldsubmission->timecreated;
                 $grade->grade = $oldsubmission->grade;
                 if ($oldsubmission->mailed) {
                     // The mailed flag goes in the flags table.
                     $flags = new stdClass();
                     $flags->userid = $oldsubmission->userid;
                     $flags->seplment = $newseplment->get_instance()->id;
                     $flags->mailed = 1;
                     $DB->insert_record('sepl_user_flags', $flags);
                 }
                 $grade->id = $DB->insert_record('sepl_grades', $grade);
                 if (!$grade->id) {
                     $log .= get_string('couldnotinsertgrade', 'mod_sepl', $grade->userid);
                     $rollback = true;
                 }
                 // Copy any grading instances.
                 if ($gradingarea) {
                     $gradeidmap[$grade->id] = $oldsubmission->id;
                     foreach ($gradingdefinitions as $definition) {
                         $params = array('definitionid' => $definition->id, 'itemid' => $oldsubmission->id);
                         $DB->set_field('grading_instances', 'itemid', $grade->id, $params);
                     }
                 }
                 foreach ($newseplment->get_feedback_plugins() as $plugin) {
                     if ($plugin->can_upgrade($oldseplment->seplmenttype, $oldversion)) {
                         if (!$plugin->upgrade($oldcontext, $oldseplment, $oldsubmission, $grade, $log)) {
                             $rollback = true;
                         }
                     }
                 }
             }
         }
         $newseplment->update_calendar($newcoursemodule->id);
         // Reassociate grade_items from the old seplment instance to the new sepl instance.
         // This includes outcome linked grade_items.
         $params = array('sepl', $newseplment->get_instance()->id, 'seplment', $oldseplment->id);
         $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?';
         $DB->execute($sql, $params);
         // Create a mapping record to map urls from the old to the new seplment.
         $mapping = new stdClass();
         $mapping->oldcmid = $oldcoursemodule->id;
         $mapping->oldinstance = $oldseplment->id;
         $mapping->newcmid = $newcoursemodule->id;
         $mapping->newinstance = $newseplment->get_instance()->id;
         $mapping->timecreated = time();
         $DB->insert_record('seplment_upgrade', $mapping);
         $gradesdone = true;
     } catch (Exception $exception) {
         $rollback = true;
         $log .= get_string('conversionexception', 'mod_sepl', $exception->getMessage());
     }
     if ($rollback) {
         // Roll back the grades changes.
         if ($gradesdone) {
             // Reassociate grade_items from the new sepl instance to the old seplment instance.
             $params = array('seplment', $oldseplment->id, 'sepl', $newseplment->get_instance()->id);
             $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?';
             $DB->execute($sql, $params);
         }
         // Roll back the completion changes.
         if ($completiondone) {
             $DB->set_field('course_modules_completion', 'coursemoduleid', $oldcoursemodule->id, array('coursemoduleid' => $newcoursemodule->id));
             $allcriteria = $DB->get_records('course_completion_criteria', array('moduleinstance' => $newcoursemodule->id));
             foreach ($allcriteria as $criteria) {
                 $criteria->module = 'seplment';
                 $criteria->moduleinstance = $oldcoursemodule->id;
                 $DB->update_record('course_completion_criteria', $criteria);
             }
         }
         // Roll back the log changes.
         $logparams = array('cmid' => $newcoursemodule->id, 'course' => $newcoursemodule->course);
         $DB->set_field('log', 'module', 'seplment', $logparams);
         $DB->set_field('log', 'cmid', $oldcoursemodule->id, $logparams);
         // Roll back the advanced grading update.
         if ($gradingarea) {
             foreach ($gradeidmap as $newgradeid => $oldsubmissionid) {
                 foreach ($gradingdefinitions as $definition) {
                     $DB->set_field('grading_instances', 'itemid', $oldsubmissionid, array('definitionid' => $definition->id, 'itemid' => $newgradeid));
                 }
             }
             $params = array('id' => $gradingarea->id, 'contextid' => $oldcontext->id, 'component' => 'mod_seplment', 'areaname' => 'submission');
             $DB->update_record('grading_areas', $params);
         }
         $newseplment->delete_instance();
         return false;
     }
     // Delete the old seplment (use object delete).
     $cm = get_coursemodule_from_id('', $oldcoursemodule->id, $oldcoursemodule->course);
     if ($cm) {
         course_delete_module($cm->id);
     }
     rebuild_course_cache($oldcoursemodule->course);
     return true;
 }
 /**
  * overridden constructor keeps a reference to the seplment class that is displaying this table
  *
  * @param sepl $seplment The seplment class
  * @param int $perpage how many per page
  * @param string $filter The current filter
  * @param int $rowoffset For showing a subsequent page of results
  * @param bool $quickgrading Is this table wrapped in a quickgrading form?
  * @param string $downloadfilename
  */
 public function __construct(sepl $seplment, $perpage, $filter, $rowoffset, $quickgrading, $downloadfilename = null)
 {
     global $CFG, $PAGE, $DB, $USER;
     parent::__construct('mod_sepl_grading');
     $this->is_persistent(true);
     $this->seplment = $seplment;
     // Check permissions up front.
     $this->hasgrantextension = has_capability('mod/sepl:grantextension', $this->seplment->get_context());
     $this->hasgrade = $this->seplment->can_grade();
     // Check if we have the elevated view capablities to see the blind details.
     $this->hasviewblind = has_capability('mod/sepl:viewblinddetails', $this->seplment->get_context());
     foreach ($seplment->get_feedback_plugins() as $plugin) {
         if ($plugin->is_visible() && $plugin->is_enabled()) {
             foreach ($plugin->get_grading_batch_operations() as $action => $description) {
                 if (empty($this->plugingradingbatchoperations)) {
                     $this->plugingradingbatchoperations[$plugin->get_type()] = array();
                 }
                 $this->plugingradingbatchoperations[$plugin->get_type()][$action] = $description;
             }
         }
     }
     $this->perpage = $perpage;
     $this->quickgrading = $quickgrading && $this->hasgrade;
     $this->output = $PAGE->get_renderer('mod_sepl');
     $urlparams = array('action' => 'grading', 'id' => $seplment->get_course_module()->id);
     $url = new moodle_url($CFG->wwwroot . '/mod/sepl/view.php', $urlparams);
     $this->define_baseurl($url);
     // Do some business - then set the sql.
     $currentgroup = groups_get_activity_group($seplment->get_course_module(), true);
     if ($rowoffset) {
         $this->rownum = $rowoffset - 1;
     }
     $users = array_keys($seplment->list_participants($currentgroup, true));
     if (count($users) == 0) {
         // Insert a record that will never match to the sql is still valid.
         $users[] = -1;
     }
     $params = array();
     $params['seplmentid1'] = (int) $this->seplment->get_instance()->id;
     $params['seplmentid2'] = (int) $this->seplment->get_instance()->id;
     $params['seplmentid3'] = (int) $this->seplment->get_instance()->id;
     $extrauserfields = get_extra_user_fields($this->seplment->get_context());
     $fields = user_picture::fields('u', $extrauserfields) . ', ';
     $fields .= 'u.id as userid, ';
     $fields .= 's.status as status, ';
     $fields .= 's.id as submissionid, ';
     $fields .= 's.timecreated as firstsubmission, ';
     $fields .= 's.timemodified as timesubmitted, ';
     $fields .= 's.attemptnumber as attemptnumber, ';
     $fields .= 'g.id as gradeid, ';
     $fields .= 'g.grade as grade, ';
     $fields .= 'g.timemodified as timemarked, ';
     $fields .= 'g.timecreated as firstmarked, ';
     $fields .= 'uf.mailed as mailed, ';
     $fields .= 'uf.locked as locked, ';
     $fields .= 'uf.extensionduedate as extensionduedate, ';
     $fields .= 'uf.workflowstate as workflowstate, ';
     $fields .= 'uf.allocatedmarker as allocatedmarker ';
     $from = '{user} u
                      LEFT JOIN {sepl_submission} s
                             ON u.id = s.userid
                            AND s.seplment = :seplmentid1
                            AND s.latest = 1
                      LEFT JOIN {sepl_grades} g
                             ON u.id = g.userid
                            AND g.seplment = :seplmentid2 ';
     // For group submissions we don't immediately create an entry in the sepl_submission table for each user,
     // instead the userid is set to 0. In this case we use a different query to retrieve the grade for the user.
     if ($this->seplment->get_instance()->teamsubmission) {
         $params['seplmentid4'] = (int) $this->seplment->get_instance()->id;
         $grademaxattempt = 'SELECT mxg.userid, MAX(mxg.attemptnumber) AS maxattempt
                               FROM {sepl_grades} mxg
                              WHERE mxg.seplment = :seplmentid4
                           GROUP BY mxg.userid';
         $from .= 'LEFT JOIN (' . $grademaxattempt . ') gmx
                          ON u.id = gmx.userid
                         AND g.attemptnumber = gmx.maxattempt ';
     } else {
         $from .= 'AND g.attemptnumber = s.attemptnumber ';
     }
     $from .= 'LEFT JOIN {sepl_user_flags} uf
                      ON u.id = uf.userid
                     AND uf.seplment = :seplmentid3';
     $userparams = array();
     $userindex = 0;
     list($userwhere, $userparams) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED, 'user');
     $where = 'u.id ' . $userwhere;
     $params = array_merge($params, $userparams);
     // The filters do not make sense when there are no submissions, so do not apply them.
     if ($this->seplment->is_any_submission_plugin_enabled()) {
         if ($filter == ASSIGN_FILTER_SUBMITTED) {
             $where .= ' AND (s.timemodified IS NOT NULL AND
                              s.status = :submitted) ';
             $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
         } else {
             if ($filter == ASSIGN_FILTER_NOT_SUBMITTED) {
                 $where .= ' AND (s.timemodified IS NULL OR s.status != :submitted) ';
                 $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
             } else {
                 if ($filter == ASSIGN_FILTER_REQUIRE_GRADING) {
                     $where .= ' AND (s.timemodified IS NOT NULL AND
                              s.status = :submitted AND
                              (s.timemodified >= g.timemodified OR g.timemodified IS NULL OR g.grade IS NULL))';
                     $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
                 } else {
                     if (strpos($filter, ASSIGN_FILTER_SINGLE_USER) === 0) {
                         $userfilter = (int) array_pop(explode('=', $filter));
                         $where .= ' AND (u.id = :userid)';
                         $params['userid'] = $userfilter;
                     }
                 }
             }
         }
     }
     if ($this->seplment->get_instance()->markingworkflow && $this->seplment->get_instance()->markingallocation) {
         if (has_capability('mod/sepl:manageallocations', $this->seplment->get_context())) {
             // Check to see if marker filter is set.
             $markerfilter = (int) get_user_preferences('sepl_markerfilter', '');
             if (!empty($markerfilter)) {
                 if ($markerfilter == ASSIGN_MARKER_FILTER_NO_MARKER) {
                     $where .= ' AND (uf.allocatedmarker IS NULL OR uf.allocatedmarker = 0)';
                 } else {
                     $where .= ' AND uf.allocatedmarker = :markerid';
                     $params['markerid'] = $markerfilter;
                 }
             }
         } else {
             // Only show users allocated to this marker.
             $where .= ' AND uf.allocatedmarker = :markerid';
             $params['markerid'] = $USER->id;
         }
     }
     if ($this->seplment->get_instance()->markingworkflow) {
         $workflowstates = $this->seplment->get_marking_workflow_states_for_current_user();
         if (!empty($workflowstates)) {
             $workflowfilter = get_user_preferences('sepl_workflowfilter', '');
             if ($workflowfilter == ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED) {
                 $where .= ' AND (uf.workflowstate = :workflowstate OR uf.workflowstate IS NULL OR ' . $DB->sql_isempty('sepl_user_flags', 'workflowstate', true, true) . ')';
                 $params['workflowstate'] = $workflowfilter;
             } else {
                 if (array_key_exists($workflowfilter, $workflowstates)) {
                     $where .= ' AND uf.workflowstate = :workflowstate';
                     $params['workflowstate'] = $workflowfilter;
                 }
             }
         }
     }
     $this->set_sql($fields, $from, $where, $params);
     if ($downloadfilename) {
         $this->is_downloading('csv', $downloadfilename);
     }
     $columns = array();
     $headers = array();
     // Select.
     if (!$this->is_downloading() && $this->hasgrade) {
         $columns[] = 'select';
         $headers[] = get_string('select') . '<div class="selectall"><label class="accesshide" for="selectall">' . get_string('selectall') . '</label>
                 <input type="checkbox" id="selectall" name="selectall" title="' . get_string('selectall') . '"/></div>';
     }
     // User picture.
     if ($this->hasviewblind || !$this->seplment->is_blind_marking()) {
         if (!$this->is_downloading()) {
             $columns[] = 'picture';
             $headers[] = get_string('pictureofuser');
         } else {
             $columns[] = 'recordid';
             $headers[] = get_string('recordid', 'sepl');
         }
         // Fullname.
         $columns[] = 'fullname';
         $headers[] = get_string('fullname');
         foreach ($extrauserfields as $extrafield) {
             $columns[] = $extrafield;
             $headers[] = get_user_field_name($extrafield);
         }
     } else {
         // Record ID.
         $columns[] = 'recordid';
         $headers[] = get_string('recordid', 'sepl');
     }
     // Submission status.
     $columns[] = 'status';
     $headers[] = get_string('status', 'sepl');
     // Team submission columns.
     if ($seplment->get_instance()->teamsubmission) {
         $columns[] = 'team';
         $headers[] = get_string('submissionteam', 'sepl');
     }
     // Allocated marker.
     if ($this->seplment->get_instance()->markingworkflow && $this->seplment->get_instance()->markingallocation && has_capability('mod/sepl:manageallocations', $this->seplment->get_context())) {
         // Add a column for the allocated marker.
         $columns[] = 'allocatedmarker';
         $headers[] = get_string('marker', 'sepl');
     }
     // Grade.
     $columns[] = 'grade';
     $headers[] = get_string('grade');
     if ($this->is_downloading()) {
         if ($this->seplment->get_instance()->grade >= 0) {
             $columns[] = 'grademax';
             $headers[] = get_string('maxgrade', 'sepl');
         } else {
             // This is a custom scale.
             $columns[] = 'scale';
             $headers[] = get_string('scale', 'sepl');
         }
         if ($this->seplment->get_instance()->markingworkflow) {
             // Add a column for the marking workflow state.
             $columns[] = 'workflowstate';
             $headers[] = get_string('markingworkflowstate', 'sepl');
         }
         // Add a column for the list of valid marking workflow states.
         $columns[] = 'gradecanbechanged';
         $headers[] = get_string('gradecanbechanged', 'sepl');
     }
     if (!$this->is_downloading() && $this->hasgrade) {
         // We have to call this column userid so we can use userid as a default sortable column.
         $columns[] = 'userid';
         $headers[] = get_string('edit');
     }
     // Submission plugins.
     if ($seplment->is_any_submission_plugin_enabled()) {
         $columns[] = 'timesubmitted';
         $headers[] = get_string('lastmodifiedsubmission', 'sepl');
         foreach ($this->seplment->get_submission_plugins() as $plugin) {
             if ($this->is_downloading()) {
                 if ($plugin->is_visible() && $plugin->is_enabled()) {
                     foreach ($plugin->get_editor_fields() as $field => $description) {
                         $index = 'plugin' . count($this->plugincache);
                         $this->plugincache[$index] = array($plugin, $field);
                         $columns[] = $index;
                         $headers[] = $plugin->get_name();
                     }
                 }
             } else {
                 if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
                     $index = 'plugin' . count($this->plugincache);
                     $this->plugincache[$index] = array($plugin);
                     $columns[] = $index;
                     $headers[] = $plugin->get_name();
                 }
             }
         }
     }
     // Time marked.
     $columns[] = 'timemarked';
     $headers[] = get_string('lastmodifiedgrade', 'sepl');
     // Feedback plugins.
     foreach ($this->seplment->get_feedback_plugins() as $plugin) {
         if ($this->is_downloading()) {
             if ($plugin->is_visible() && $plugin->is_enabled()) {
                 foreach ($plugin->get_editor_fields() as $field => $description) {
                     $index = 'plugin' . count($this->plugincache);
                     $this->plugincache[$index] = array($plugin, $field);
                     $columns[] = $index;
                     $headers[] = $description;
                 }
             }
         } else {
             if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
                 $index = 'plugin' . count($this->plugincache);
                 $this->plugincache[$index] = array($plugin);
                 $columns[] = $index;
                 $headers[] = $plugin->get_name();
             }
         }
     }
     // Exclude 'Final grade' column in downloaded grading worksheets.
     if (!$this->is_downloading()) {
         // Final grade.
         $columns[] = 'finalgrade';
         $headers[] = get_string('finalgrade', 'grades');
     }
     // Load the grading info for all users.
     $this->gradinginfo = grade_get_grades($this->seplment->get_course()->id, 'mod', 'sepl', $this->seplment->get_instance()->id, $users);
     if (!empty($CFG->enableoutcomes) && !empty($this->gradinginfo->outcomes)) {
         $columns[] = 'outcomes';
         $headers[] = get_string('outcomes', 'grades');
     }
     // Set the columns.
     $this->define_columns($columns);
     $this->define_headers($headers);
     foreach ($extrauserfields as $extrafield) {
         $this->column_class($extrafield, $extrafield);
     }
     // We require at least one unique column for the sort.
     $this->sortable(true, 'userid');
     $this->no_sorting('recordid');
     $this->no_sorting('finalgrade');
     $this->no_sorting('userid');
     $this->no_sorting('select');
     $this->no_sorting('outcomes');
     if ($seplment->get_instance()->teamsubmission) {
         $this->no_sorting('team');
     }
     $plugincolumnindex = 0;
     foreach ($this->seplment->get_submission_plugins() as $plugin) {
         if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
             $submissionpluginindex = 'plugin' . $plugincolumnindex++;
             $this->no_sorting($submissionpluginindex);
         }
     }
     foreach ($this->seplment->get_feedback_plugins() as $plugin) {
         if ($plugin->is_visible() && $plugin->is_enabled() && $plugin->has_user_summary()) {
             $feedbackpluginindex = 'plugin' . $plugincolumnindex++;
             $this->no_sorting($feedbackpluginindex);
         }
     }
     // When there is no data we still want the column headers printed in the csv file.
     if ($this->is_downloading()) {
         $this->start_output();
     }
 }