Merge branch 'MDL-36804-master' of git://github.com/damyon/moodle

This commit is contained in:
Dan Poltawski 2013-04-03 16:18:50 +08:00
commit 5e45f6a569
28 changed files with 1934 additions and 268 deletions

View File

@ -627,4 +627,5 @@ abstract class assign_plugin {
return true;
}
}

View File

@ -62,7 +62,18 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
'requireallteammemberssubmit',
'teamsubmissiongroupingid',
'blindmarking',
'revealidentities'));
'revealidentities',
'attemptreopenmethod',
'maxattempts'));
$userflags = new backup_nested_element('userflags');
$userflag = new backup_nested_element('userflag', array('id'),
array('userid',
'assignment',
'mailed',
'locked',
'extensionduedate'));
$submissions = new backup_nested_element('submissions');
@ -71,7 +82,8 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
'timecreated',
'timemodified',
'status',
'groupid'));
'groupid',
'attemptnumber'));
$grades = new backup_nested_element('grades');
@ -81,9 +93,7 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
'timemodified',
'grader',
'grade',
'locked',
'mailed',
'extensionduedate'));
'attemptnumber'));
$pluginconfigs = new backup_nested_element('plugin_configs');
@ -94,7 +104,8 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
'value'));
// Build the tree.
$assign->add_child($userflags);
$userflags->add_child($userflag);
$assign->add_child($submissions);
$submissions->add_child($submission);
$assign->add_child($grades);
@ -108,6 +119,9 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
array('assignment' => backup::VAR_PARENTID));
if ($userinfo) {
$userflag->set_source_table('assign_user_flags',
array('assignment' => backup::VAR_PARENTID));
$submission->set_source_table('assign_submission',
array('assignment' => backup::VAR_PARENTID));
@ -120,6 +134,7 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
}
// Define id annotations.
$userflag->annotate_ids('user', 'userid');
$submission->annotate_ids('user', 'userid');
$submission->annotate_ids('group', 'groupid');
$grade->annotate_ids('user', 'userid');

View File

@ -130,6 +130,30 @@ class restore_assign_activity_structure_step extends restore_activity_structure_
$this->set_mapping('submission', $oldid, $newitemid, false, null, $this->task->get_old_contextid());
}
/**
* Process a user_flags restore
* @param object $data The data in object form
* @return void
*/
protected function process_assign_userflags($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->assignment = $this->get_new_parentid('assign');
$data->userid = $this->get_mappingid('user', $data->userid);
if (!empty($data->extensionduedate)) {
$data->extensionduedate = $this->apply_date_offset($data->extensionduedate);
} else {
$data->extensionduedate = 0;
}
// Flags mailed and locked need no translation on restore.
$newitemid = $DB->insert_record('assign_user_flags', $data);
}
/**
* Process a grade restore
* @param object $data The data in object form
@ -147,11 +171,20 @@ class restore_assign_activity_structure_step extends restore_activity_structure_
$data->timecreated = $this->apply_date_offset($data->timecreated);
$data->userid = $this->get_mappingid('user', $data->userid);
$data->grader = $this->get_mappingid('user', $data->grader);
// Handle flags restore to a different table.
$flags = new stdClass();
$flags->assignment = $this->get_new_parentid('assign');
if (!empty($data->extensionduedate)) {
$data->extensionduedate = $this->apply_date_offset($data->extensionduedate);
} else {
$data->extensionduedate = 0;
$flags->extensionduedate = $this->apply_date_offset($data->extensionduedate);
}
if (!empty($data->mailed)) {
$flags->mailed = $data->mailed;
}
if (!empty($data->locked)) {
$flags->locked = $data->locked;
}
$DB->insert_record('assign_user_flags', $flags);
$newitemid = $DB->insert_record('assign_grades', $data);

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/assign/db" VERSION="20120830" COMMENT="XMLDB file for Moodle mod/assign"
<XMLDB PATH="mod/assign/db" VERSION="20130308" COMMENT="XMLDB file for Moodle mod/assign"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
@ -23,11 +23,13 @@
<FIELD NAME="requiresubmissionstatement" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Forces the student to accept a submission statement when submitting an assignment"/>
<FIELD NAME="completionsubmit" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If this field is set to 1, then the activity will be automatically marked as 'complete' once the user submits their assignment."/>
<FIELD NAME="cutoffdate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The final date after which submissions will no longer be accepted for this assignment without an extensions."/>
<FIELD NAME="teamsubmission" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Do students submit in teams?"/>
<FIELD NAME="requireallteammemberssubmit" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="If enabled, a submission will not be accepted until all team members have submitted it."/>
<FIELD NAME="teamsubmissiongroupingid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="A grouping id to get groups for team submissions"/>
<FIELD NAME="teamsubmission" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Do students submit in teams?"/>
<FIELD NAME="requireallteammemberssubmit" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If enabled, a submission will not be accepted until all team members have submitted it."/>
<FIELD NAME="teamsubmissiongroupingid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="A grouping id to get groups for team submissions"/>
<FIELD NAME="blindmarking" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Hide student/grader identities until the reveal identities action is performed"/>
<FIELD NAME="revealidentities" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Show identities for a blind marking assignment"/>
<FIELD NAME="attemptreopenmethod" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="none" SEQUENCE="false" COMMENT="How to determine when students are allowed to open a new submission. Valid options are none, manual, untilpass"/>
<FIELD NAME="maxattempts" TYPE="int" LENGTH="6" NOTNULL="true" DEFAULT="-1" SEQUENCE="false" COMMENT="What is the maximum number of student attempts allowed for this assignment? -1 means unlimited."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="The unique id for this assignment instance."/>
@ -45,7 +47,8 @@
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The time of the first student submission to this assignment."/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The last time this assignment submission was modified by a student."/>
<FIELD NAME="status" TYPE="char" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The status of this assignment submission. The current statuses are DRAFT and SUBMITTED."/>
<FIELD NAME="groupid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="The group id for team submissions"/>
<FIELD NAME="groupid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The group id for team submissions"/>
<FIELD NAME="attemptnumber" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Used to track attempts for an assignment"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="The unique id for this assignment submission."/>
@ -53,6 +56,8 @@
</KEYS>
<INDEXES>
<INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
<INDEX NAME="attemptnumber" UNIQUE="false" FIELDS="attemptnumber"/>
<INDEX NAME="uniqueattemptsubmission" UNIQUE="true" FIELDS="assignment, userid, groupid, attemptnumber"/>
</INDEXES>
</TABLE>
<TABLE NAME="assign_grades" COMMENT="Grading information about a single assignment submission.">
@ -64,9 +69,7 @@
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The most recent modification time for the assignment submission by a grader."/>
<FIELD NAME="grader" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="grade" TYPE="number" LENGTH="10" NOTNULL="false" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The numerical grade for this assignment submission. Can be determined by scales/advancedgradingforms etc but will always be converted back to a floating point number."/>
<FIELD NAME="locked" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="A flag to prevent changes for a single student submission."/>
<FIELD NAME="mailed" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Has the student been sent a notification about this grade update?"/>
<FIELD NAME="extensionduedate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="An extension date assigned to an individual student"/>
<FIELD NAME="attemptnumber" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The attempt number that this grade relates to"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="The unique id for this grade."/>
@ -74,7 +77,8 @@
</KEYS>
<INDEXES>
<INDEX NAME="userid" UNIQUE="false" FIELDS="userid" COMMENT="The userid for the submission relating to this grade."/>
<INDEX NAME="mailed" UNIQUE="false" FIELDS="mailed" COMMENT="True if notifications have been sent about the most recent grader update about this submission."/>
<INDEX NAME="attemptnumber" UNIQUE="false" FIELDS="attemptnumber"/>
<INDEX NAME="uniqueattemptgrade" UNIQUE="true" FIELDS="assignment, userid, attemptnumber" COMMENT="This is a grade for a unique attempt."/>
</INDEXES>
</TABLE>
<TABLE NAME="assign_plugin_config" COMMENT="Config data for an instance of a plugin in an assignment.">
@ -108,5 +112,23 @@
<KEY NAME="user" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" COMMENT="The user to map to an id"/>
</KEYS>
</TABLE>
<TABLE NAME="assign_user_flags" COMMENT="List of flags that can be set for a single user in a single assignment.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The id of the user these flags apply to."/>
<FIELD NAME="assignment" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The assignment these flags apply to."/>
<FIELD NAME="locked" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Student cannot make any changes to their submission if this flag is set."/>
<FIELD NAME="mailed" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Has the student been sent a notification about this grade update?"/>
<FIELD NAME="extensionduedate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="An extension date assigned to an individual student."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" COMMENT="The id field of the user table."/>
<KEY NAME="assignment" TYPE="foreign" FIELDS="assignment" REFTABLE="assign" REFFIELDS="id" COMMENT="The assignment id these flags apply to."/>
</KEYS>
<INDEXES>
<INDEX NAME="mailed" UNIQUE="false" FIELDS="mailed" COMMENT="Has this user been mailed yet?"/>
</INDEXES>
</TABLE>
</TABLES>
</XMLDB>

View File

@ -208,6 +208,145 @@ function xmldb_assign_upgrade($oldversion) {
// Moodle v2.4.0 release upgrade line.
// Put any upgrade step following this.
if ($oldversion < 2013030600) {
// Define table assign_user_flags to be created.
$table = new xmldb_table('assign_user_flags');
// Adding fields to table assign_user_flags.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
$table->add_field('assignment', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
$table->add_field('locked', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
$table->add_field('mailed', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0');
$table->add_field('extensionduedate', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
// Adding keys to table assign_user_flags.
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
$table->add_key('assignment', XMLDB_KEY_FOREIGN, array('assignment'), 'assign', array('id'));
// Adding indexes to table assign_user_flags.
$table->add_index('mailed', XMLDB_INDEX_NOTUNIQUE, array('mailed'));
// Conditionally launch create table for assign_user_flags.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// Copy the flags from the old table to the new one.
$sql = 'INSERT INTO {assign_user_flags}
(assignment, userid, locked, mailed, extensionduedate)
SELECT assignment, userid, locked, mailed, extensionduedate
FROM {assign_grades}';
$DB->execute($sql);
// And delete the old columns.
// Define index mailed (not unique) to be dropped form assign_grades.
$table = new xmldb_table('assign_grades');
$index = new xmldb_index('mailed', XMLDB_INDEX_NOTUNIQUE, array('mailed'));
// Conditionally launch drop index mailed.
if ($dbman->index_exists($table, $index)) {
$dbman->drop_index($table, $index);
}
// Define field locked to be dropped from assign_grades.
$table = new xmldb_table('assign_grades');
$field = new xmldb_field('locked');
// Conditionally launch drop field locked.
if ($dbman->field_exists($table, $field)) {
$dbman->drop_field($table, $field);
}
// Define field mailed to be dropped from assign_grades.
$table = new xmldb_table('assign_grades');
$field = new xmldb_field('mailed');
// Conditionally launch drop field mailed.
if ($dbman->field_exists($table, $field)) {
$dbman->drop_field($table, $field);
}
// Define field extensionduedate to be dropped from assign_grades.
$table = new xmldb_table('assign_grades');
$field = new xmldb_field('extensionduedate');
// Conditionally launch drop field extensionduedate.
if ($dbman->field_exists($table, $field)) {
$dbman->drop_field($table, $field);
}
// Define field attemptreopenmethod to be added to assign.
$table = new xmldb_table('assign');
$field = new xmldb_field('attemptreopenmethod', XMLDB_TYPE_CHAR, '10', null,
XMLDB_NOTNULL, null, 'none', 'revealidentities');
// Conditionally launch add field attemptreopenmethod.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define field maxattempts to be added to assign.
$table = new xmldb_table('assign');
$field = new xmldb_field('maxattempts', XMLDB_TYPE_INTEGER, '6', null, XMLDB_NOTNULL, null, '-1', 'attemptreopenmethod');
// Conditionally launch add field maxattempts.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define field attemptnumber to be added to assign_submission.
$table = new xmldb_table('assign_submission');
$field = new xmldb_field('attemptnumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'groupid');
// Conditionally launch add field attemptnumber.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define field attemptnumber to be added to assign_grades.
$table = new xmldb_table('assign_grades');
$field = new xmldb_field('attemptnumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'grade');
// Conditionally launch add field attemptnumber.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define index attemptnumber (not unique) to be added to assign_grades.
$table = new xmldb_table('assign_grades');
$index = new xmldb_index('attemptnumber', XMLDB_INDEX_NOTUNIQUE, array('attemptnumber'));
// Conditionally launch add index attemptnumber.
if (!$dbman->index_exists($table, $index)) {
$dbman->add_index($table, $index);
}
// Define index uniqueattemptsubmission (unique) to be added to assign_submission.
$table = new xmldb_table('assign_submission');
$index = new xmldb_index('uniqueattemptsubmission',
XMLDB_INDEX_UNIQUE,
array('assignment', 'userid', 'groupid', 'attemptnumber'));
// Conditionally launch add index uniqueattempt.
if (!$dbman->index_exists($table, $index)) {
$dbman->add_index($table, $index);
}
// Define index uniqueattemptgrade (unique) to be added to assign_grades.
$table = new xmldb_table('assign_grades');
$index = new xmldb_index('uniqueattemptgrade', XMLDB_INDEX_UNIQUE, array('assignment', 'userid', 'attemptnumber'));
// Conditionally launch add index uniqueattempt.
if (!$dbman->index_exists($table, $index)) {
$dbman->add_index($table, $index);
}
// Module assign savepoint reached.
upgrade_mod_savepoint(true, 2013030600, 'assign');
}
return true;
}

View File

@ -95,13 +95,23 @@ class mod_assign_external extends external_api {
if (count ($requestedassignmentids) > 0) {
$placeholders = array();
list($inorequalsql, $placeholders) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
list($inorequalsql2, $placeholders2) = $DB->get_in_or_equal($requestedassignmentids, SQL_PARAMS_NAMED);
$grademaxattempt = 'SELECT mxg.userid, MAX(mxg.attemptnumber) AS maxattempt
FROM {assign_grades} mxg
WHERE mxg.assignment ' . $inorequalsql2 . ' GROUP BY mxg.userid';
$sql = "SELECT ag.id,ag.assignment,ag.userid,ag.timecreated,ag.timemodified,".
"ag.grader,ag.grade,ag.locked,ag.mailed ".
"ag.grader,ag.grade ".
"FROM {assign_grades} ag ".
"WHERE ag.assignment ".$inorequalsql.
"JOIN ( " . $grademaxattempt . " ) gmx ON ag.userid = gmx.userid".
" WHERE ag.assignment ".$inorequalsql.
" AND ag.timemodified >= :since".
" AND ag.attemptnumber = gmx.maxattempt" .
" ORDER BY ag.assignment, ag.id";
$placeholders['since'] = $params['since'];
// Combine the parameters.
$placeholders += $placeholders2;
$rs = $DB->get_recordset_sql($sql, $placeholders);
$currentassignmentid = null;
$assignment = null;
@ -113,8 +123,6 @@ class mod_assign_external extends external_api {
$grade['timemodified'] = $rd->timemodified;
$grade['grader'] = $rd->grader;
$grade['grade'] = (string)$rd->grade;
$grade['locked'] = $rd->locked;
$grade['mailed'] = $rd->mailed;
if (is_null($currentassignmentid) || ($rd->assignment != $currentassignmentid )) {
if (!is_null($assignment)) {
@ -165,9 +173,7 @@ class mod_assign_external extends external_api {
'timecreated' => new external_value(PARAM_INT, 'grade creation time'),
'timemodified' => new external_value(PARAM_INT, 'grade last modified time'),
'grader' => new external_value(PARAM_INT, 'grader'),
'grade' => new external_value(PARAM_TEXT, 'grade'),
'locked' => new external_value(PARAM_BOOL, 'locked'),
'mailed' => new external_value(PARAM_BOOL, 'mailed')
'grade' => new external_value(PARAM_TEXT, 'grade')
)
)
)
@ -504,11 +510,18 @@ class mod_assign_external extends external_api {
foreach ($assigns as $assign) {
$submissions = array();
$submissionplugins = $assign->get_submission_plugins();
$placeholders = array('assignment' => $assign->get_instance()->id);
$placeholders = array('assignid1' => $assign->get_instance()->id,
'assignid2' => $assign->get_instance()->id);
$submissionmaxattempt = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt
FROM {assign_submission} mxs
WHERE mxs.assignment = :assignid1 GROUP BY mxs.userid';
$sql = "SELECT mas.id, mas.assignment,mas.userid,".
"mas.timecreated,mas.timemodified,mas.status,mas.groupid ".
"FROM {assign_submission} mas ".
"WHERE mas.assignment = :assignment";
"JOIN ( " . $submissionmaxattempt . " ) smx ON mas.userid = smx.userid ".
"WHERE mas.assignment = :assignid2 AND mas.attemptnumber = smx.maxattempt";
if (!empty($params['status'])) {
$placeholders['status'] = $params['status'];

View File

@ -52,6 +52,9 @@ class mod_assign_grading_batch_operations_form extends moodleform {
if ($instance['duedate']) {
$options['grantextension'] = get_string('grantextension', 'assign');
}
if ($instance['attemptreopenmethod'] == ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL) {
$options['addattempt'] = get_string('addattempt', 'assign');
}
foreach ($instance['feedbackplugins'] as $plugin) {
if ($plugin->is_visible() && $plugin->is_enabled()) {

View File

@ -116,6 +116,9 @@ class assign_grading_table extends table_sql implements renderable {
$params = array();
$params['assignmentid1'] = (int)$this->assignment->get_instance()->id;
$params['assignmentid2'] = (int)$this->assignment->get_instance()->id;
$params['assignmentid3'] = (int)$this->assignment->get_instance()->id;
$params['assignmentid4'] = (int)$this->assignment->get_instance()->id;
$params['assignmentid5'] = (int)$this->assignment->get_instance()->id;
$extrauserfields = get_extra_user_fields($this->assignment->get_context());
@ -125,15 +128,33 @@ class assign_grading_table extends table_sql implements renderable {
$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 .= 'g.mailed as mailed, ';
$fields .= 'g.locked as locked, ';
$fields .= 'g.extensionduedate as extensionduedate';
$from = '{user} u LEFT JOIN {assign_submission} s ON u.id = s.userid AND s.assignment = :assignmentid1' .
' LEFT JOIN {assign_grades} g ON u.id = g.userid AND g.assignment = :assignmentid2';
$fields .= 'uf.mailed as mailed, ';
$fields .= 'uf.locked as locked, ';
$fields .= 'uf.extensionduedate as extensionduedate';
$submissionmaxattempt = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt
FROM {assign_submission} mxs
WHERE mxs.assignment = :assignmentid4 GROUP BY mxs.userid';
$grademaxattempt = 'SELECT mxg.userid, MAX(mxg.attemptnumber) AS maxattempt
FROM {assign_grades} mxg
WHERE mxg.assignment = :assignmentid5 GROUP BY mxg.userid';
$from = '{user} u
LEFT JOIN ( ' . $submissionmaxattempt . ' ) smx ON u.id = smx.userid
LEFT JOIN ( ' . $grademaxattempt . ' ) gmx ON u.id = gmx.userid
LEFT JOIN {assign_submission} s ON
u.id = s.userid AND
s.assignment = :assignmentid1 AND
s.attemptnumber = smx.maxattempt
LEFT JOIN {assign_grades} g ON
u.id = g.userid AND
g.assignment = :assignmentid2 AND
g.attemptnumber = gmx.maxattempt
LEFT JOIN {assign_user_flags} uf ON u.id = uf.userid AND uf.assignment = :assignmentid3';
$userparams = array();
$userindex = 0;
@ -159,6 +180,7 @@ class assign_grading_table extends table_sql implements renderable {
$params['userid'] = $userfilter;
}
}
$this->set_sql($fields, $from, $where, $params);
if ($downloadfilename) {
@ -829,6 +851,23 @@ class assign_grading_table extends table_sql implements renderable {
$actions[$url->out(false)] = $description;
}
$ismanual = $this->assignment->get_instance()->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL;
$hassubmission = !empty($row->status);
$notreopened = $hassubmission && $row->status != ASSIGN_SUBMISSION_STATUS_REOPENED;
$isunlimited = $this->assignment->get_instance()->maxattempts == ASSIGN_UNLIMITED_ATTEMPTS;
$hasattempts = $isunlimited || $row->attemptnumber < $this->assignment->get_instance()->maxattempts - 1;
if ($ismanual && $hassubmission && $notreopened && $hasattempts) {
$urlparams = array('id' => $this->assignment->get_course_module()->id,
'userid'=>$row->id,
'action'=>'addattempt',
'sesskey'=>sesskey(),
'page'=>$this->currpage);
$url = new moodle_url('/mod/assign/view.php', $urlparams);
$description = get_string('addattempt', 'assign');
$actions[$url->out(false)] = $description;
}
$edit .= $this->output->container_start(array('yui3-menu', 'actionmenu'), 'actionselect' . $row->id);
$edit .= $this->output->container_start(array('yui3-menu-content'));
$edit .= html_writer::start_tag('ul');

View File

@ -24,6 +24,11 @@
$string['activityoverview'] = 'You have assignments that need attention';
$string['addsubmission'] = 'Add submission';
$string['addattempt'] = 'Allow another attempt';
$string['addnewattempt'] = 'Add a new attempt';
$string['addnewattempt_help'] = 'This will create a new blank submission for you to work on.';
$string['addnewattemptfromprevious'] = 'Add a new attempt based on previous submission';
$string['addnewattemptfromprevious_help'] = 'This will copy the contents of your previous submission to a new submission for you to work on.';
$string['allowsubmissions'] = 'Allow the user to continue making submissions to this assignment.';
$string['allowsubmissionsshort'] = 'Allow submission changes';
$string['allowsubmissionsfromdate'] = 'Allow submissions from';
@ -59,6 +64,15 @@ $string['assignmentplugins'] = 'Assignment plugins';
$string['assignmentsperpage'] = 'Assignments per page';
$string['assignsubmission'] = 'Submission plugin';
$string['assignsubmissionpluginname'] = 'Submission plugin';
$string['attemptheading'] = 'Attempt {$a->attemptnumber}: {$a->submissionsummary}';
$string['attemptnumber'] = 'Attempt number';
$string['attempthistory'] = 'Previous attempts';
$string['attemptsettings'] = 'Attempt settings';
$string['attemptreopenmethod'] = 'Attempts reopened';
$string['attemptreopenmethod_help'] = 'Determines how student submission attempts are reopened. The available options are: <ul><li>Never - The student submission cannot be reopened.</li><li>Manually - The student submission can be reopened by a teacher.</li><li>Automatically until pass - The student submission is automatically reopened until the student achieves the grade to pass value that is set in the gradebook for this assignment.</li></ul>';
$string['attemptreopenmethod_manual'] = 'Manually';
$string['attemptreopenmethod_none'] = 'Never';
$string['attemptreopenmethod_untilpass'] = 'Automatically until pass';
$string['availability'] = 'Availability';
$string['backtoassignment'] = 'Back to assignment';
$string['batchoperationsdescription'] = 'With selected...';
@ -66,6 +80,7 @@ $string['batchoperationconfirmlock'] = 'Lock all selected submissions?';
$string['batchoperationconfirmgrantextension'] = 'Grant an extension to all selected submissions?';
$string['batchoperationconfirmunlock'] = 'Unlock all selected submissions?';
$string['batchoperationconfirmreverttodraft'] = 'Revert selected submissions to draft?';
$string['batchoperationconfirmaddattempt'] = 'Allow another attempt for selected submissions?';
$string['batchoperationlock'] = 'lock submissions';
$string['batchoperationunlock'] = 'unlock submissions';
$string['batchoperationreverttodraft'] = 'revert submissions to draft';
@ -78,7 +93,7 @@ $string['comment'] = 'Comment';
$string['completionsubmit'] = 'Student must submit to this activity to complete it';
$string['conversionexception'] = 'Could not convert assignment. Exception was: {$a}.';
$string['configshowrecentsubmissions'] = 'Everyone can see notifications of submissions in recent activity reports.';
$string['confirmsubmission'] = 'Are you sure you want to submit your work for grading? You will not be able to make any more changes';
$string['confirmsubmission'] = 'Are you sure you want to submit your work for grading? You will not be able to make any more changes.';
$string['confirmbatchgradingoperation'] = 'Are you sure you want to {$a->operation} for {$a->count} students?';
$string['couldnotconvertgrade'] = 'Could not convert assignment grade for user {$a}.';
$string['couldnotconvertsubmission'] = 'Could not convert assignment submission for user {$a}.';
@ -86,6 +101,8 @@ $string['couldnotcreatecoursemodule'] = 'Could not create course module.';
$string['couldnotcreatenewassignmentinstance'] = 'Could not create new assignment instance.';
$string['couldnotfindassignmenttoupgrade'] = 'Could not find old assignment instance to upgrade.';
$string['currentgrade'] = 'Current grade in gradebook';
$string['currentattempt'] = 'This is attempt {$a}.';
$string['currentattemptof'] = 'This is attempt {$a->attemptnumber} ( {$a->maxattempts} attempts allowed ).';
$string['cutoffdate'] = 'Cut-off date';
$string['cutoffdate_help'] = 'If set, the assignment will not accept submissions after this date without an extension.';
$string['cutoffdatevalidation'] = 'The cut-off date cannot be earlier than the due date.';
@ -106,7 +123,10 @@ $string['duedateno'] = 'No due date';
$string['submissionempty'] = 'Nothing was submitted';
$string['duedatereached'] = 'The due date for this assignment has now passed';
$string['duedatevalidation'] = 'Due date must be after the allow submissions from date.';
$string['editsubmission'] = 'Edit my submission';
$string['editattemptfeedback'] = 'Edit the grade and feedback for attempt number {$a}.';
$string['editingpreviousfeedbackwarning'] = 'You are editing the feedback for a previous attempt. This is attempt {$a->attemptnumber} out of {$a->totalattempts}.';
$string['editsubmission'] = 'Edit submission';
$string['editsubmission_help'] = 'Make changes to your submission';
$string['editingstatus'] = 'Editing status';
$string['editaction'] = 'Actions...';
$string['extensionduedate'] = 'Extension due date';
@ -160,10 +180,11 @@ $string['gradeoutofhelp'] = 'Grade';
$string['gradeoutofhelp_help'] = 'Enter the grade for the student\'s submission here. You may include decimals.';
$string['gradestudent'] = 'Grade student: (id={$a->id}, fullname={$a->fullname}). ';
$string['grading'] = 'Grading';
$string['gradingchangessaved'] = 'The grade changes were saved';
$string['gradingmethodpreview'] = 'Grading criteria';
$string['gradingoptions'] = 'Options';
$string['gradingstatus'] = 'Grading status';
$string['gradingstudentprogress'] = 'Grading student {$a->index} of {$a->count}';
$string['gradingstudent'] = 'Grading student';
$string['gradingsummary'] = 'Grading summary';
$string['hideshow'] = 'Hide/Show';
$string['hiddenuser'] = 'Participant ';
@ -178,7 +199,9 @@ $string['locksubmissionforstudent'] = 'Prevent any more submissions for student:
$string['locksubmissions'] = 'Lock submissions';
$string['manageassignfeedbackplugins'] = 'Manage assignment feedback plugins';
$string['manageassignsubmissionplugins'] = 'Manage assignment submission plugins';
$string['maxgrade'] = 'Maximum Grade';
$string['maxattempts'] = 'Maximum attempts';
$string['maxattempts_help'] = 'The maximum number of submissions attempts that can be made by a student. After this number of attempts has been made the student&apos;s submission will not be able to be reopened.';
$string['maxgrade'] = 'Maximum grade';
$string['messageprovider:assign_notification'] = 'Assignment notifications';
$string['modulename'] = 'Assignment';
$string['modulename_help'] = 'The assignment activity module enables a teacher to communicate tasks, collect work and provide grades and feedback.
@ -190,14 +213,15 @@ $string['modulename_link'] = 'mod/assignment/view';
$string['modulenameplural'] = 'Assignments';
$string['mysubmission'] = 'My submission: ';
$string['newsubmissions'] = 'Assignments submitted';
$string['noattempt'] = 'No attempt';
$string['nofiles'] = 'No files. ';
$string['nograde'] = 'No grade. ';
$string['nolatesubmissions'] = 'No late submissions accepted. ';
$string['nomoresubmissionsaccepted'] = 'No more submissions accepted';
$string['noonlinesubmissions'] = 'This assignment does not require you to submit anything online';
$string['nosavebutnext'] = 'Next';
$string['nosubmission'] = 'Nothing has been submitted for this assignment';
$string['nosubmissionsacceptedafter'] = 'No submissions accepted after ';
$string['nomoresubmissionsaccepted'] = 'No more submissions accepted';
$string['notgraded'] = 'Not graded';
$string['notgradedyet'] = 'Not graded yet';
$string['notsubmittedyet'] = 'Not submitted yet';
@ -210,6 +234,7 @@ $string['numberofsubmissionsneedgrading'] = 'Needs grading';
$string['numberofteams'] = 'Groups';
$string['offline'] = 'No online submissions required';
$string['open'] = 'Open';
$string['outof'] = '{$a->current} out of {$a->total}';
$string['overdue'] = '<font color="red">Assignment is overdue by: {$a}</font>';
$string['outlinegrade'] = 'Grade: {$a}';
$string['page-mod-assign-x'] = 'Any assignment module page';
@ -238,6 +263,7 @@ $string['reverttodraft'] = 'Revert the submission to draft status.';
$string['reverttodraftshort'] = 'Revert the submission to draft';
$string['reviewed'] = 'Reviewed';
$string['savechanges'] = 'Save changes';
$string['savegradingresult'] = 'Grade';
$string['saveallquickgradingchanges'] = 'Save all quick grading changes';
$string['savenext'] = 'Save and show next';
$string['scale'] = 'Scale';
@ -251,9 +277,20 @@ $string['sendsubmissionreceipts'] = 'Send submission receipt to students';
$string['sendsubmissionreceipts_help'] = 'This switch will enable submission receipts for students. Students will receive a notification every time they successfully submit an assignment';
$string['settings'] = 'Assignment settings';
$string['showrecentsubmissions'] = 'Show recent submissions';
$string['submissioncopiedtext'] = 'You have made a copy of your previous
assignment submission for \'{$a->assignment}\'
You can see the status of your assignment submission:
{$a->url}';
$string['submissioncopiedhtml'] = 'You have made a copy of your previous
assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
You can see the status of your <a href="{$a->url}">assignment submission</a>.';
$string['submissioncopiedsmall'] = 'You have copied your previous assignment submission for {$a->assignment}';
$string['submissiondrafts'] = 'Require students click submit button';
$string['submissiondrafts_help'] = 'If enabled, students will have to click a Submit button to declare their submission as final. This allows students to keep a draft version of the submission on the system. If this setting is changed from "No" to "Yes" after students have already submitted those submissions will be regarded as final.';
$string['submissioneditable'] = 'Student can edit this submission';
$string['submissionnotcopiedinvalidstatus'] = 'The submission was not copied because it has been edited since it was reopened.';
$string['submissionnoteditable'] = 'Student cannot edit this submission';
$string['submissionnotready'] = 'This assignment is not ready to submit:';
$string['submissionplugins'] = 'Submission plugins';
@ -282,13 +319,15 @@ $string['submissionstatus_draft'] = 'Draft (not submitted)';
$string['submissionstatusheading'] = 'Submission status';
$string['submissionstatus_marked'] = 'Graded';
$string['submissionstatus_new'] = 'New submission';
$string['submissionstatus_reopened'] = 'Reopened';
$string['submissionstatus_'] = 'No submission';
$string['submissionstatus'] = 'Submission status';
$string['submissionstatus_submitted'] = 'Submitted for grading';
$string['submissionsummary'] = '{$a->status}. Last modified on {$a->timemodified}';
$string['submissionteam'] = 'Group';
$string['submission'] = 'Submission';
$string['submitaction'] = 'Submit';
$string['submitassignment_help'] = 'Once this assignment is submitted you will not be able to make any more changes';
$string['submitassignment_help'] = 'Once this assignment is submitted you will not be able to make any more changes.';
$string['submitassignment'] = 'Submit assignment';
$string['submittedearly'] = 'Assignment was submitted {$a} early';
$string['submittedlate'] = 'Assignment was submitted {$a} late';
@ -304,6 +343,8 @@ $string['timemodified'] = 'Last modified';
$string['timeremaining'] = 'Time remaining';
$string['unlocksubmissionforstudent'] = 'Allow submissions for student: (id={$a->id}, fullname={$a->fullname}).';
$string['unlocksubmissions'] = 'Unlock submissions';
$string['unlimitedattempts'] = 'Unlimited';
$string['unlimitedattemptsallowed'] = 'Unlimited attempts allowed.';
$string['updategrade'] = 'Update grade';
$string['updatetable'] = 'Save and update table';
$string['upgradenotimplemented'] = 'Upgrade not implemented in plugin ({$a->type} {$a->subtype})';

View File

@ -150,20 +150,33 @@ function assign_update_instance(stdClass $data, $form) {
*/
function assign_supports($feature) {
switch($feature) {
case FEATURE_GROUPS: return true;
case FEATURE_GROUPINGS: return true;
case FEATURE_GROUPMEMBERSONLY: return true;
case FEATURE_MOD_INTRO: return true;
case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
case FEATURE_COMPLETION_HAS_RULES: return true;
case FEATURE_GRADE_HAS_GRADE: return true;
case FEATURE_GRADE_OUTCOMES: return true;
case FEATURE_BACKUP_MOODLE2: return true;
case FEATURE_SHOW_DESCRIPTION: return true;
case FEATURE_ADVANCED_GRADING: return true;
case FEATURE_PLAGIARISM: return true;
case FEATURE_GROUPS:
return true;
case FEATURE_GROUPINGS:
return true;
case FEATURE_GROUPMEMBERSONLY:
return true;
case FEATURE_MOD_INTRO:
return true;
case FEATURE_COMPLETION_TRACKS_VIEWS:
return true;
case FEATURE_COMPLETION_HAS_RULES:
return true;
case FEATURE_GRADE_HAS_GRADE:
return true;
case FEATURE_GRADE_OUTCOMES:
return true;
case FEATURE_BACKUP_MOODLE2:
return true;
case FEATURE_SHOW_DESCRIPTION:
return true;
case FEATURE_ADVANCED_GRADING:
return true;
case FEATURE_PLAGIARISM:
return true;
default: return null;
default:
return null;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -118,6 +118,22 @@ class mod_assign_mod_form extends moodleform_mod {
$mform->addElement('hidden', 'requiresubmissionstatement', 1);
}
$options = array(
ASSIGN_ATTEMPT_REOPEN_METHOD_NONE => get_string('attemptreopenmethod_none', 'mod_assign'),
ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL => get_string('attemptreopenmethod_manual', 'mod_assign'),
ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS => get_string('attemptreopenmethod_untilpass', 'mod_assign')
);
$mform->addElement('select', 'attemptreopenmethod', get_string('attemptreopenmethod', 'mod_assign'), $options);
$mform->setDefault('attemptreopenmethod', ASSIGN_ATTEMPT_REOPEN_METHOD_NONE);
$mform->addHelpButton('attemptreopenmethod', 'attemptreopenmethod', 'mod_assign');
$options = array(ASSIGN_UNLIMITED_ATTEMPTS => get_string('unlimitedattempts', 'mod_assign'));
$options += array_combine(range(1, 30), range(1, 30));
$mform->addElement('select', 'maxattempts', get_string('maxattempts', 'mod_assign'), $options);
$mform->addHelpButton('maxattempts', 'maxattempts', 'assign');
$mform->setDefault('maxattempts', -1);
$mform->disabledIf('maxattempts', 'attemptreopenmethod', 'eq', ASSIGN_ATTEMPT_REOPEN_METHOD_NONE);
$mform->addElement('header', 'groupsubmissionsettings', get_string('groupsubmissionsettings', 'assign'));
$name = get_string('teamsubmission', 'assign');

View File

@ -5,7 +5,7 @@ M.mod_assign.init_tree = function(Y, expand_all, htmlid) {
var tree = new Y.YUI2.widget.TreeView(htmlid);
tree.subscribe("clickEvent", function(node, event) {
// we want normal clicking which redirects to url
// We want normal clicking which redirects to url.
return false;
});
@ -169,12 +169,12 @@ M.mod_assign.init_plugin_summary = function(Y, subtype, type, submissionid) {
fullclassname = 'full_' + thissuffix;
full = Y.one('.' + fullclassname);
if (full) {
full.hide(true);
full.hide(false);
}
summaryclassname = 'summary_' + thissuffix;
summary = Y.one('.' + summaryclassname);
if (summary) {
summary.show(true);
summary.show(false);
}
});
}
@ -183,7 +183,7 @@ M.mod_assign.init_plugin_summary = function(Y, subtype, type, submissionid) {
full = Y.one('.full_' + suffix);
if (full) {
full.hide();
full.hide(false);
full.toggleClass('hidefull');
}
if (expand) {
@ -199,12 +199,12 @@ M.mod_assign.init_plugin_summary = function(Y, subtype, type, submissionid) {
summaryclassname = 'summary_' + thissuffix;
summary = Y.one('.' + summaryclassname);
if (summary) {
summary.hide(true);
summary.hide(false);
}
fullclassname = 'full_' + thissuffix;
full = Y.one('.' + fullclassname);
if (full) {
full.show(true);
full.show(false);
}
});
}

View File

@ -52,12 +52,14 @@ class assign_submit_for_grading_page implements renderable {
}
/**
* Implements a renderable grading error notification
* Implements a renderable message notification
* @package mod_assign
* @copyright 2012 NetSpot {@link http://www.netspot.com.au}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class assign_quickgrading_result implements renderable {
class assign_gradingmessage implements renderable {
/** @var string $heading is the heading to display to the user */
public $heading = '';
/** @var string $message is the message to display to the user */
public $message = '';
/** @var int $coursemoduleid */
@ -65,9 +67,11 @@ class assign_quickgrading_result implements renderable {
/**
* Constructor
* @param string $heading This is the heading to display
* @param string $message This is the message to display
*/
public function __construct($message, $coursemoduleid) {
public function __construct($heading, $message, $coursemoduleid) {
$this->heading = $heading;
$this->message = $message;
$this->coursemoduleid = $coursemoduleid;
}
@ -363,6 +367,10 @@ class assign_submission_status implements renderable {
public $blindmarking = false;
/** @var string gradingcontrollerpreview */
public $gradingcontrollerpreview = '';
/** @var string attemptreopenmethod */
public $attemptreopenmethod = 'none';
/** @var int maxattempts */
public $maxattempts = -1;
/**
* Constructor
@ -390,7 +398,9 @@ class assign_submission_status implements renderable {
* @param bool $canviewfullnames
* @param int $extensionduedate - Any extension to the due date granted for this user
* @param context $context - Any extension to the due date granted for this user
* @param blindmarking $blindmarking - Should we hide student identities from graders?
* @param bool $blindmarking - Should we hide student identities from graders?
* @param string $attemptreopenmethod - The method of reopening student attempts.
* @param int $maxattempts - How many attempts can a student make?
*/
public function __construct($allowsubmissionsfromdate,
$alwaysshowdescription,
@ -416,7 +426,9 @@ class assign_submission_status implements renderable {
$extensionduedate,
$context,
$blindmarking,
$gradingcontrollerpreview) {
$gradingcontrollerpreview,
$attemptreopenmethod,
$maxattempts) {
$this->allowsubmissionsfromdate = $allowsubmissionsfromdate;
$this->alwaysshowdescription = $alwaysshowdescription;
$this->submission = $submission;
@ -442,8 +454,66 @@ class assign_submission_status implements renderable {
$this->context = $context;
$this->blindmarking = $blindmarking;
$this->gradingcontrollerpreview = $gradingcontrollerpreview;
$this->attemptreopenmethod = $attemptreopenmethod;
$this->maxattempts = $maxattempts;
}
}
/**
* Used to output the attempt history for a particular assignment.
*
* @package mod_assign
* @copyright 2012 Davo Smith, Synergy Learning
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class assign_attempt_history implements renderable {
/** @var array submissions */
public $submissions = array();
/** @var array grades */
public $grades = array();
/** @var array submissionplugins */
public $submissionplugins = array();
/** @var array feedbackplugins */
public $feedbackplugins = array();
/** @var int coursemoduleid */
public $coursemoduleid = 0;
/** @var string returnaction */
public $returnaction = '';
/** @var string returnparams */
public $returnparams = array();
/** @var bool cangrade */
public $cangrade = false;
/**
* Constructor
*
* @param $submissions
* @param $grades
* @param $submissionplugins
* @param $feedbackplugins
* @param $coursemoduleid
* @param $returnaction
* @param $returnparams
* @param $cangrade
*/
public function __construct($submissions,
$grades,
$submissionplugins,
$feedbackplugins,
$coursemoduleid,
$returnaction,
$returnparams,
$cangrade) {
$this->submissions = $submissions;
$this->grades = $grades;
$this->submissionplugins = $submissionplugins;
$this->feedbackplugins = $feedbackplugins;
$this->coursemoduleid = $coursemoduleid;
$this->returnaction = $returnaction;
$this->returnparams = $returnparams;
$this->cangrade = $cangrade;
}
}
/**

View File

@ -85,16 +85,16 @@ class mod_assign_renderer extends plugin_renderer_base {
}
/**
* Render a grading error notification
* @param assign_quickgrading_result $result The result to render
* Render a grading message notification
* @param assign_gradingmessage $result The result to render
* @return string
*/
public function render_assign_quickgrading_result(assign_quickgrading_result $result) {
public function render_assign_gradingmessage(assign_gradingmessage $result) {
$urlparams = array('id' => $result->coursemoduleid, 'action'=>'grading');
$url = new moodle_url('/mod/assign/view.php', $urlparams);
$o = '';
$o .= $this->output->heading(get_string('quickgradingresult', 'assign'), 4);
$o .= $this->output->heading($result->heading, 4);
$o .= $this->output->notification($result->message);
$o .= $this->output->continue_button($url);
return $o;
@ -179,7 +179,9 @@ class mod_assign_renderer extends plugin_renderer_base {
$o .= $this->output->continue_button($cancelurl);
} else {
// All submission plugins ready - show the confirmation form.
$o .= $this->output->box_start('generalbox submitconfirm');
$o .= $this->moodleform($page->confirmform);
$o .= $this->output->box_end();
}
$o .= $this->output->container_end();
@ -426,6 +428,32 @@ class mod_assign_renderer extends plugin_renderer_base {
$t->data[] = $row;
}
if ($status->attemptreopenmethod != ASSIGN_ATTEMPT_REOPEN_METHOD_NONE) {
$currentattempt = 1;
if (!$status->teamsubmissionenabled) {
if ($status->submission) {
$currentattempt = $status->submission->attemptnumber + 1;
}
} else {
if ($status->teamsubmission) {
$currentattempt = $status->teamsubmission->attemptnumber + 1;
}
}
$row = new html_table_row();
$cell1 = new html_table_cell(get_string('attemptnumber', 'assign'));
$maxattempts = $status->maxattempts;
if ($maxattempts == ASSIGN_UNLIMITED_ATTEMPTS) {
$message = get_string('currentattempt', 'assign', $currentattempt);
} else {
$message = get_string('currentattemptof', 'assign', array('attemptnumber'=>$currentattempt,
'maxattempts'=>$maxattempts));
}
$cell2 = new html_table_cell($message);
$row->cells = array($cell1, $cell2);
$t->data[] = $row;
}
$row = new html_table_row();
$cell1 = new html_table_cell(get_string('submissionstatus', 'assign'));
if (!$status->teamsubmissionenabled) {
@ -437,7 +465,7 @@ class mod_assign_renderer extends plugin_renderer_base {
if (!$status->submissionsenabled) {
$cell2 = new html_table_cell(get_string('noonlinesubmissions', 'assign'));
} else {
$cell2 = new html_table_cell(get_string('nosubmission', 'assign'));
$cell2 = new html_table_cell(get_string('noattempt', 'assign'));
}
}
$row->cells = array($cell1, $cell2);
@ -637,23 +665,52 @@ class mod_assign_renderer extends plugin_renderer_base {
if ($status->view == assign_submission_status::STUDENT_VIEW) {
if ($status->canedit) {
if (!$submission) {
$o .= $this->output->box_start('generalbox submissionaction');
$urlparams = array('id' => $status->coursemoduleid, 'action' => 'editsubmission');
$o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', $urlparams),
get_string('addsubmission', 'assign'), 'get');
$o .= $this->output->box_start('boxaligncenter submithelp');
$o .= get_string('editsubmission_help', 'assign');
$o .= $this->output->box_end();
$o .= $this->output->box_end();
} else if ($submission->status == ASSIGN_SUBMISSION_STATUS_REOPENED) {
$o .= $this->output->box_start('generalbox submissionaction');
$urlparams = array('id' => $status->coursemoduleid, 'action' => 'editprevioussubmission');
$o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', $urlparams),
get_string('addnewattemptfromprevious', 'assign'), 'get');
$o .= $this->output->box_start('boxaligncenter submithelp');
$o .= get_string('addnewattemptfromprevious_help', 'assign');
$o .= $this->output->box_end();
$o .= $this->output->box_end();
$o .= $this->output->box_start('generalbox submissionaction');
$urlparams = array('id' => $status->coursemoduleid, 'action' => 'editsubmission');
$o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', $urlparams),
get_string('addnewattempt', 'assign'), 'get');
$o .= $this->output->box_start('boxaligncenter submithelp');
$o .= get_string('addnewattempt_help', 'assign');
$o .= $this->output->box_end();
$o .= $this->output->box_end();
} else {
$o .= $this->output->box_start('generalbox submissionaction');
$urlparams = array('id' => $status->coursemoduleid, 'action' => 'editsubmission');
$o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', $urlparams),
get_string('editsubmission', 'assign'), 'get');
$o .= $this->output->box_start('boxaligncenter submithelp');
$o .= get_string('editsubmission_help', 'assign');
$o .= $this->output->box_end();
$o .= $this->output->box_end();
}
}
if ($status->cansubmit) {
$urlparams = array('id' => $status->coursemoduleid, 'action'=>'submit');
$o .= $this->output->box_start('generalbox submissionaction');
$o .= $this->output->single_button(new moodle_url('/mod/assign/view.php', $urlparams),
get_string('submitassignment', 'assign'), 'get');
$o .= $this->output->box_start('boxaligncenter submithelp');
$o .= get_string('submitassignment_help', 'assign');
$o .= $this->output->box_end();
$o .= $this->output->box_end();
}
}
@ -661,6 +718,154 @@ class mod_assign_renderer extends plugin_renderer_base {
return $o;
}
/**
* Output the attempt history for this assignment
*
* @param assign_attempt_history $history
* @return string
*/
public function render_assign_attempt_history(assign_attempt_history $history) {
$o = '';
$submittedstr = get_string('submitted', 'assign');
$gradestr = get_string('grade');
$gradedonstr = get_string('gradedon', 'assign');
$gradedbystr = get_string('gradedby', 'assign');
// Don't show the last one because it is the current submission.
array_pop($history->submissions);
// Show newest to oldest.
$history->submissions = array_reverse($history->submissions);
if (empty($history->submissions)) {
return '';
}
$containerid = 'attempthistory' . uniqid();
$o .= $this->heading(get_string('attempthistory', 'assign'), 3);
$o .= $this->box_start('attempthistory', $containerid);
foreach ($history->submissions as $i => $submission) {
$grade = null;
foreach ($history->grades as $onegrade) {
if ($onegrade->attemptnumber == $submission->attemptnumber) {
$grade = $onegrade;
break;
}
}
$editbtn = '';
if ($submission) {
$submissionsummary = userdate($submission->timemodified);
} else {
$submissionsummary = get_string('nosubmission', 'assign');
}
$attemptsummaryparams = array('attemptnumber'=>$submission->attemptnumber+1,
'submissionsummary'=>$submissionsummary);
$o .= $this->heading(get_string('attemptheading', 'assign', $attemptsummaryparams), 4);
$t = new html_table();
if ($submission) {
$cell1 = new html_table_cell(get_string('submissionstatus', 'assign'));
$cell2 = new html_table_cell(get_string('submissionstatus_' . $submission->status, 'assign'));
$t->data[] = new html_table_row(array($cell1, $cell2));
foreach ($history->submissionplugins as $plugin) {
$pluginshowsummary = !$plugin->is_empty($submission) || !$plugin->allow_submissions();
if ($plugin->is_enabled() &&
$plugin->is_visible() &&
$plugin->has_user_summary() &&
$pluginshowsummary) {
$cell1 = new html_table_cell($plugin->get_name());
$pluginsubmission = new assign_submission_plugin_submission($plugin,
$submission,
assign_submission_plugin_submission::SUMMARY,
$history->coursemoduleid,
$history->returnaction,
$history->returnparams);
$cell2 = new html_table_cell($this->render($pluginsubmission));
$t->data[] = new html_table_row(array($cell1, $cell2));
}
}
}
if ($grade) {
// Heading 'feedback'.
$title = get_string('feedback', 'assign', $i);
$title .= $this->output->spacer(array('width'=>10));
if ($history->cangrade) {
// Edit previous feedback.
$returnparams = http_build_query($history->returnparams);
$urlparams = array('id' => $history->coursemoduleid,
'userid'=>$grade->userid,
'attemptnumber'=>$grade->attemptnumber,
'action'=>'grade',
'rownum'=>0,
'returnaction'=>$history->returnaction,
'returnparams'=>$returnparams);
$url = new moodle_url('/mod/assign/view.php', $urlparams);
$icon = new pix_icon('gradefeedback',
get_string('editattemptfeedback', 'assign', $grade->attemptnumber+1),
'mod_assign');
$title .= $this->output->action_icon($url, $icon);
}
$cell = new html_table_cell($title);
$cell->attributes['class'] = 'feedbacktitle';
$cell->colspan = 2;
$t->data[] = new html_table_row(array($cell));
// Grade.
$cell1 = new html_table_cell($gradestr);
$cell2 = $grade->gradefordisplay;
$t->data[] = new html_table_row(array($cell1, $cell2));
// Graded on.
$cell1 = new html_table_cell($gradedonstr);
$cell2 = new html_table_cell(userdate($grade->timemodified));
$t->data[] = new html_table_row(array($cell1, $cell2));
// Graded by.
$cell1 = new html_table_cell($gradedbystr);
$cell2 = new html_table_cell($this->output->user_picture($grade->grader) .
$this->output->spacer(array('width'=>30)) . fullname($grade->grader));
$t->data[] = new html_table_row(array($cell1, $cell2));
// Feedback from plugins.
foreach ($history->feedbackplugins as $plugin) {
if ($plugin->is_enabled() &&
$plugin->is_visible() &&
$plugin->has_user_summary() &&
!$plugin->is_empty($grade)) {
$cell1 = new html_table_cell($plugin->get_name());
$pluginfeedback = new assign_feedback_plugin_feedback(
$plugin, $grade, assign_feedback_plugin_feedback::SUMMARY, $history->coursemoduleid,
$history->returnaction, $history->returnparams
);
$cell2 = new html_table_cell($this->render($pluginfeedback));
$t->data[] = new html_table_row(array($cell1, $cell2));
}
}
}
$o .= html_writer::table($t);
}
$o .= $this->box_end();
$jsparams = array($containerid);
$this->page->requires->yui_module('moodle-mod_assign-history', 'Y.one("#' . $containerid . '").history');
return $o;
}
/**
* Render a submission plugin submission
*
@ -751,6 +956,7 @@ class mod_assign_renderer extends plugin_renderer_base {
$this->page->requires->string_for_js('batchoperationconfirmlock', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmreverttodraft', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmunlock', 'assign');
$this->page->requires->string_for_js('batchoperationconfirmaddattempt', 'assign');
$this->page->requires->string_for_js('editaction', 'assign');
foreach ($table->plugingradingbatchoperations as $plugin => $operations) {
foreach ($operations as $operation => $description) {

View File

@ -17,6 +17,8 @@ div.gradingsummary {
div.submissionstatus .generaltable,
div.submissionlinks .generaltable,
div.feedback .generaltable,
div.submissionsummarytable .generaltable,
div.attempthistory table,
div.gradingsummary .generaltable {
width: 100%;
}
@ -30,7 +32,6 @@ div.gradingsummary .generaltable {
margin-top: 1em;
}
div.submissionsummarytable table tbody tr td.c0 {
width: 30%;
}
@ -73,6 +74,12 @@ div.submissionlocked {
background-color: #efefcf;
}
td.submissionreopened,
div.submissionreopened {
color: black;
background-color: #efefef;
}
td.submissiongraded,
div.submissiongraded {
color: black;
@ -160,3 +167,90 @@ td.submissioneditable {
.jsenabled .quickgradingform form .commentscontainer textarea {
display: inline;
}
#page-mod-assign-view .previousfeedbackwarning {
font-size: 140%;
font-weight: bold;
text-align: center;
color: #500;
}
#page-mod-assign-view .submissionhistory {
background-color: #b0b0b0;
}
#page-mod-assign-view .submissionhistory .cell.historytitle {
background-color: #808080;
}
#page-mod-assign-view .submissionhistory .cell {
background-color: #d0d0d0;
}
#page-mod-assign-view .submissionhistory .singlebutton {
display: inline-block;
float: right;
}
#page-mod-assign-view.dir-rtl .submissionhistory .singlebutton {
float: left;
}
#page-mod-assign-view .submissionsummarytable .singlebutton {
display: inline-block;
}
.jsenabled .mod-assign-history-link {
display: block;
cursor: pointer;
margin-bottom: 7px;
}
.jsenabled .mod-assign-history-link h4 {
display: inline;
}
#page-mod-assign-view.jsenabled .attempthistory h4 {
margin-bottom: 7px;
text-align: left;
}
#page-mod-assign-view.jsenabled.dir_rtl .attempthistory h4 {
text-align: right;
}
.dir-rtl.jsenabled .mod-assign-history-link h4 {
text-align: right;
}
.jsenabled .mod-assign-history-link-open {
padding: 0 5px 0 20px; background: url([[pix:t/expanded]]) 2px center no-repeat;
}
.jsenabled .mod-assign-history-link-closed {
padding: 0 5px 0 20px; background: url([[pix:t/collapsed]]) 2px center no-repeat;
}
.dir-rtl.jsenabled .mod-assign-history-link-closed {
padding: 0 20px 0 5px; background: url([[pix:t/collapsed_rtl]]) 2px center no-repeat;
}
#page-mod-assign-view .submithelp {
padding: 1em;
}
#page-mod-assign-view .feedbacktitle {
font-weight: bold;
}
#page-mod-assign-view .submitconfirm,
#page-mod-assign-view .submissionlinks,
#page-mod-assign-view .submissionaction {
text-align: center;
}
#page-mod-assign-view .submissionsummarytable .c0,
#page-mod-assign-view .mod-assign-history-panel .c0 {
width: 150px;
}

View File

@ -449,4 +449,35 @@ class assign_submission_file extends assign_submission_plugin {
return array(ASSIGNSUBMISSION_FILE_FILEAREA=>$this->get_name());
}
/**
* Copy the student's submission from a previous submission. Used when a student opts to base their resubmission
* on the last submission.
* @param stdClass $sourcesubmission
* @param stdClass $destsubmission
*/
public function copy_submission(stdClass $sourcesubmission, stdClass $destsubmission) {
global $DB;
// Copy the files across.
$contextid = $this->assignment->get_context()->id;
$fs = get_file_storage();
$files = $fs->get_area_files($contextid,
'assignsubmission_file',
ASSIGNSUBMISSION_FILE_FILEAREA,
$sourcesubmission->id,
'id',
false);
foreach ($files as $file) {
$fieldupdates = array('itemid' => $destsubmission->id);
$fs->create_file_from_storedfile($fieldupdates, $file);
}
// Copy the assignsubmission_file record.
if ($filesubmission = $this->get_file_submission($sourcesubmission->id)) {
unset($filesubmission->id);
$filesubmission->submission = $destsubmission->id;
$DB->insert_record('assignsubmission_file', $filesubmission);
}
return true;
}
}

View File

@ -470,6 +470,34 @@ class assign_submission_onlinetext extends assign_submission_plugin {
return array(ASSIGNSUBMISSION_ONLINETEXT_FILEAREA=>$this->get_name());
}
/**
* Copy the student's submission from a previous submission. Used when a student opts to base their resubmission
* on the last submission.
* @param stdClass $sourcesubmission
* @param stdClass $destsubmission
*/
public function copy_submission(stdClass $sourcesubmission, stdClass $destsubmission) {
global $DB;
// Copy the files across (attached via the text editor).
$contextid = $this->assignment->get_context()->id;
$fs = get_file_storage();
$files = $fs->get_area_files($contextid, 'assignsubmission_onlinetext',
ASSIGNSUBMISSION_ONLINETEXT_FILEAREA, $sourcesubmission->id, 'id', false);
foreach ($files as $file) {
$fieldupdates = array('itemid' => $destsubmission->id);
$fs->create_file_from_storedfile($fieldupdates, $file);
}
// Copy the assignsubmission_onlinetext record.
$onlinetextsubmission = $this->get_onlinetext_submission($sourcesubmission->id);
if ($onlinetextsubmission) {
unset($onlinetextsubmission->id);
$onlinetextsubmission->submission = $destsubmission->id;
$DB->insert_record('assignsubmission_onlinetext', $onlinetextsubmission);
}
return true;
}
}

View File

@ -60,18 +60,28 @@ abstract class assign_submission_plugin extends assign_plugin {
/**
* Check if the submission plugin has all the required data to allow the work
* to be submitted for grading
* @param stdClass $submission the assign_submission record being submitted.
* @return bool|string 'true' if OK to proceed with submission, otherwise a
* a message to display to the user
*/
public function precheck_submission() {
public function precheck_submission($submission) {
return true;
}
/**
* Carry out any extra processing required when the work is submitted for grading
* @param stdClass $submission the assign_submission record being submitted.
* @return void
*/
public function submit_for_grading() {
public function submit_for_grading($submission) {
}
/**
* Copy the plugin specific submission data to a new submission record.
*
* @return bool
*/
public function copy_submission( stdClass $oldsubmission, stdClass $submission) {
return true;
}
}

View File

@ -134,7 +134,7 @@ class mod_assign_base_testcase extends advanced_testcase {
/*
* For tests that make sense to use alot of data, create extra students/teachers.
*/
protected function createExtraUsers() {
protected function create_extra_users() {
global $DB;
$this->extrateachers = array();
for ($i = 0; $i < self::EXTRA_TEACHER_COUNT; $i++) {
@ -217,8 +217,8 @@ class testable_assign extends assign {
return parent::delete_grades();
}
public function testable_apply_grade_to_user($formdata, $userid) {
return parent::apply_grade_to_user($formdata, $userid);
public function testable_apply_grade_to_user($formdata, $userid, $attemptnumber) {
return parent::apply_grade_to_user($formdata, $userid, $attemptnumber);
}
public function testable_get_grading_userid_list() {
@ -233,6 +233,10 @@ class testable_assign extends assign {
return parent::update_submission($submission, $userid, $updatetime, $teamsubmission);
}
public function testable_process_add_attempt($userid = 0) {
return parent::process_add_attempt($userid);
}
public function testable_submissions_open($userid = 0) {
return parent::submissions_open($userid);
}

View File

@ -75,8 +75,18 @@ class mod_assign_external_testcase extends externallib_advanced_testcase {
$user_enrolment_data['userid'] = $USER->id;
$DB->insert_record('user_enrolments', $user_enrolment_data);
// Create a student and give them a grade.
// Create a student and give them 2 grades (for 2 attempts).
$student = self::getDataGenerator()->create_user();
$grade = new stdClass();
$grade->assignment = $assign->id;
$grade->userid = $student->id;
$grade->timecreated = time();
$grade->timemodified = $grade->timecreated;
$grade->grader = $USER->id;
$grade->grade = 50;
$grade->attemptnumber = 0;
$DB->insert_record('assign_grades', $grade);
$grade = new stdClass();
$grade->assignment = $assign->id;
$grade->userid = $student->id;
@ -84,8 +94,7 @@ class mod_assign_external_testcase extends externallib_advanced_testcase {
$grade->timemodified = $grade->timecreated;
$grade->grader = $USER->id;
$grade->grade = 75;
$grade->locked = false;
$grade->mailed = true;
$grade->attemptnumber = 1;
$DB->insert_record('assign_grades', $grade);
$assignmentids[] = $assign->id;
@ -98,9 +107,11 @@ class mod_assign_external_testcase extends externallib_advanced_testcase {
$this->assertEquals(1, count($result['assignments']));
$assignment = $result['assignments'][0];
$this->assertEquals($assign->id, $assignment['assignmentid']);
// Should only get the last grade for this student.
$this->assertEquals(1, count($assignment['grades']));
$grade = $assignment['grades'][0];
$this->assertEquals($student->id, $grade['userid']);
// Should be the last grade (not the first)
$this->assertEquals(75, $grade['grade']);
}
@ -218,13 +229,25 @@ class mod_assign_external_testcase extends externallib_advanced_testcase {
$assign1 = self::getDataGenerator()->create_module('assign', $assigndata);
// Create a student with an online text submission.
// First attempt.
$student = self::getDataGenerator()->create_user();
$submission = new stdClass();
$submission->assignment = $assign1->id;
$submission->userid = $student->id;
$submission->timecreated = time();
$submission->timemodified = $submission->timecreated;
$submission->status = 'draft';
$submission->attemptnumber = 0;
$sid = $DB->insert_record('assign_submission', $submission);
// Second attempt.
$submission = new stdClass();
$submission->assignment = $assign1->id;
$submission->userid = $student->id;
$submission->timecreated = time();
$submission->timemodified = $submission->timecreated;
$submission->status = 'submitted';
$submission->attemptnumber = 1;
$sid = $DB->insert_record('assign_submission', $submission);
$submission->id = $sid;

View File

@ -63,7 +63,9 @@ class mod_assign_generator extends testing_module_generator {
'requireallteammemberssubmit' => 0,
'teamsubmissiongroupingid' => 0,
'blindmarking' => 0,
'cmidnumber' => ''
'cmidnumber' => '',
'attemptreopenmethod' => 'none',
'maxattempts' => -1
);
foreach ($defaultsettings as $name => $value) {

View File

@ -137,4 +137,3 @@ class mod_assign_lib_testcase extends mod_assign_base_testcase {
}
}

View File

@ -94,7 +94,6 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$assign->testable_process_reveal_identities();
// Test sesskey is required.
$nosesskey = true;
$this->setUser($this->editingteachers[0]);
$this->setExpectedException('moodle_exception');
$assign->testable_process_reveal_identities();
@ -165,7 +164,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$this->setUser($this->teachers[0]);
$data = new stdClass();
$data->grade = '50.0';
$assign->testable_apply_grade_to_user($data, $this->students[0]->id);
$assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
// Now see if the data is in the gradebook.
$gradinginfo = grade_get_grades($this->course->id,
@ -192,7 +191,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$this->setUser($this->teachers[0]);
$data = new stdClass();
$data->grade = '50.0';
$assign->testable_apply_grade_to_user($data, $this->students[0]->id);
$assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
// Simulate a submission.
$this->setUser($this->students[0]);
@ -220,7 +219,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$this->setUser($this->teachers[0]);
$data = new stdClass();
$data->grade = '50.0';
$assign->testable_apply_grade_to_user($data, $this->students[0]->id);
$assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
// Simulate a submission.
$this->setUser($this->students[0]);
@ -286,11 +285,6 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$instance->duedate = $now;
$instance->instance = $instance->id;
$instance->assignsubmission_onlinetext_enabled = 1;
$instance->assignsubmission_file_enabled = 0;
$instance->assignsubmission_comments_enabled = 0;
$instance->assignfeedback_comments_enabled = 0;
$instance->assignfeedback_file_enabled = 0;
$instance->assignfeedback_offline_enabled = 0;
$assign->update_instance($instance);
@ -299,7 +293,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
}
public function test_list_participants() {
$this->createExtraUsers();
$this->create_extra_users();
$this->setUser($this->editingteachers[0]);
$assign = $this->create_instance(array('grade'=>100));
@ -307,7 +301,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
}
public function test_count_teams() {
$this->createExtraUsers();
$this->create_extra_users();
$this->setUser($this->editingteachers[0]);
$assign = $this->create_instance(array('teamsubmission'=>1));
@ -315,7 +309,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
}
public function test_count_submissions() {
$this->createExtraUsers();
$this->create_extra_users();
$this->setUser($this->editingteachers[0]);
$assign = $this->create_instance(array('assignsubmission_onlinetext_enabled'=>1));
@ -334,7 +328,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$this->setUser($this->teachers[0]);
$data = new stdClass();
$data->grade = '50.0';
$assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id);
$assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
// Simulate a submission.
$this->setUser($this->extrastudents[1]);
@ -376,7 +370,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$this->setUser($this->teachers[0]);
$data = new stdClass();
$data->grade = '50.0';
$assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id);
$assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
$this->assertEquals(2, $assign->count_grades());
$this->assertEquals(4, $assign->count_submissions());
@ -386,7 +380,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
}
public function test_get_grading_userid_list() {
$this->createExtraUsers();
$this->create_extra_users();
$this->setUser($this->editingteachers[0]);
$assign = $this->create_instance();
@ -407,7 +401,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$this->setUser($this->teachers[0]);
$data = new stdClass();
$data->grade = '50.0';
$assign->testable_apply_grade_to_user($data, $this->students[0]->id);
$assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
// Now run cron and see that one message was sent.
$this->preventResetByRollback();
@ -430,7 +424,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$this->setUser($this->teachers[0]);
$data = new stdClass();
$data->grade = '50.0';
$assign->testable_apply_grade_to_user($data, $this->students[0]->id);
$assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
$this->assertEquals(true, $assign->testable_is_graded($this->students[0]->id));
$this->assertEquals(false, $assign->testable_is_graded($this->students[1]->id));
@ -456,7 +450,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
public function test_update_submission() {
$this->createExtraUsers();
$this->create_extra_users();
$this->setUser($this->editingteachers[0]);
$assign = $this->create_instance();
@ -564,7 +558,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
}
public function test_get_graders() {
$this->createExtraUsers();
$this->create_extra_users();
$this->setUser($this->editingteachers[0]);
$assign = $this->create_instance();
@ -614,7 +608,7 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$this->setUser($this->teachers[0]);
$data = new stdClass();
$data->grade = '50.0';
$assign->testable_apply_grade_to_user($data, $this->students[0]->id);
$assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
// Now we should see the feedback.
$this->setUser($this->students[0]);
@ -656,6 +650,98 @@ class mod_assign_locallib_testcase extends mod_assign_base_testcase {
$this->assertEquals(false, strpos($output, 'Graded on'), 'Do not show graded date when there is no grade.');
}
public function test_attempt_reopen_method_manual() {
global $PAGE;
$this->setUser($this->editingteachers[0]);
$assign = $this->create_instance(array('attemptreopenmethod'=>ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
'maxattempts'=>3,
'submissiondrafts'=>1,
'assignsubmission_onlinetext_enabled'=>1));
$PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
// Student should be able to see an add submission button.
$this->setUser($this->students[0]);
$output = $assign->view_student_summary($this->students[0], true);
$this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
// Add a submission.
$now = time();
$submission = $assign->get_user_submission($this->students[0]->id, true);
$data = new stdClass();
$data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
'text'=>'Submission text',
'format'=>FORMAT_MOODLE);
$plugin = $assign->get_submission_plugin_by_type('onlinetext');
$plugin->save($submission, $data);
// And now submit it for marking.
$submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
$assign->testable_update_submission($submission, $this->students[0]->id, true, false);
// Verify the student cannot make changes to the submission.
$output = $assign->view_student_summary($this->students[0], true);
$this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
// Mark the submission.
$this->setUser($this->teachers[0]);
$data = new stdClass();
$data->grade = '50.0';
$assign->testable_apply_grade_to_user($data, $this->students[0]->id, 0);
// Check the student can see the grade.
$this->setUser($this->students[0]);
$output = $assign->view_student_summary($this->students[0], true);
$this->assertNotEquals(false, strpos($output, '50.0'));
// Allow the student another attempt.
$this->teachers[0]->ignoresesskey = true;
$this->setUser($this->teachers[0]);
$result = $assign->testable_process_add_attempt($this->students[0]->id);
$this->assertEquals(true, $result);
// Check that the previous attempt is now in the submission history table.
$this->setUser($this->students[0]);
$output = $assign->view_student_summary($this->students[0], true);
// Need a better check.
$this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
// Check that the student now has a button for Add a new attempt".
$this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
// Check that the student now does not have a button for Submit.
$this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
// Check that the student now has a submission history.
$this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
$this->setUser($this->teachers[0]);
// Check that the grading table loads correctly and contains this user.
// This is also testing that we do not get duplicate rows in the grading table.
$gradingtable = new assign_grading_table($assign, 100, '', 0, true);
$output = $assign->get_renderer()->render($gradingtable);
$this->assertEquals(true, strpos($output, $this->students[0]->lastname));
// Should be 1 not 2.
$this->assertEquals(1, $assign->count_submissions());
$this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
$this->assertEquals(0, $assign->count_submissions_need_grading());
$this->assertEquals(1, $assign->count_grades());
// Change max attempts to unlimited.
$formdata = clone($assign->get_instance());
$formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
$formdata->instance = $formdata->id;
$assign->update_instance($formdata);
// Check we can repopen still.
$result = $assign->testable_process_add_attempt($this->students[0]->id);
$this->assertEquals(true, $result);
$grades = $assign->get_user_grades_for_gradebook($this->students[0]->id);
$this->assertEquals(50, (int)$grades[$this->students[0]->id]->rawgrade);
}
}

43
mod/assign/upgrade.txt Normal file
View File

@ -0,0 +1,43 @@
This files describes API changes in the assign code.
=== 2.5 ===
* New feature - Attempt History
This adds settings so that a student can have build up a history of separate submission attempts and grades for the same
assignment.
Extra settings on the assign table are:
attemptreopenmethod - The way attempts can be reopened. One of:
ASSIGN_ATTEMPT_REOPEN_METHOD_NONE - Attempts cannot be reopened (default)
ASSIGN_ATTEMPT_REOPEN_METHOD_UNTIL_PASS - Attempts are reopened on grading until the gradebook
indicates this student has passed.
ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL - Attempts are reopened on manually by the grader.
maxattempts - The maximum allowed number of attempts per student for this assign instance.
Extra settings on the submission and grade records:
attemptnumber - Starting from 0 until the latest attempt.
New table assign_user_flags holds user specific flags that were stored in the assign_grade table. (contains assignment,
userid, extensionduedate, mailed and locked)
assign changes:
"get_user_grade" function has an extra optional parameter to retrieve a specific attempt number.
"get_user_submission" function has an extra optional parameter to retrieve a specific attempt number.
"get_group_submission" function has an extra optional parameter to retrieve a specific attempt number.
new "get_user_flags" function can retrieve the user flags for a specific user (extensionduedate, mailed and locked).
new "update_user_flags" function can update the user flags for a specific user (extensionduedate, mailed and locked).
assign_submission_plugin changes:
"precheck_submission" function now takes a submission record so you can determine which submission is being checked.
"submit_for_grading" function now takes a submission record so you can determine which submission is being submitted.
new function "copy_submission" can be implemented to copy submission data from one submission to a new one.
New renderable object "assign_attempt_history" for rendering the list of previous submissions.
New renderable object "assign_gradingmessage" for rendering a generic grading message.
=== Earlier changes ===
* Were not documented in this way. Sorry.

View File

@ -99,6 +99,8 @@ class assign_upgrade_manager {
$data->requireallteammemberssubmit = 0;
$data->teamsubmissiongroupingid = 0;
$data->blindmarking = 0;
$data->attemptreopenmethod = 'none';
$data->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
$newassignment = new assign(null, null, null);
@ -247,7 +249,14 @@ class assign_upgrade_manager {
$grade->timemodified = $oldsubmission->timemarked;
$grade->timecreated = $oldsubmission->timecreated;
$grade->grade = $oldsubmission->grade;
$grade->mailed = $oldsubmission->mailed;
if ($oldsubmission->mailed) {
// The mailed flag goes in the flags table.
$flags = new stdClass();
$flags->userid = $oldsubmission->userid;
$flags->assignment = $newassignment->get_instance()->id;
$flags->mailed = 1;
$DB->insert_record('assign_user_flags', $flags);
}
$grade->id = $DB->insert_record('assign_grades', $grade);
if (!$grade->id) {
$log .= get_string('couldnotinsertgrade', 'mod_assign', $grade->userid);

View File

@ -25,7 +25,7 @@
defined('MOODLE_INTERNAL') || die();
$module->component = 'mod_assign'; // Full name of the plugin (used for diagnostics).
$module->version = 2012112901; // The current module version (Date: YYYYMMDDXX).
$module->version = 2013030600; // The current module version (Date: YYYYMMDDXX).
$module->requires = 2012112900; // Requires this Moodle version.
$module->cron = 60;

71
mod/assign/yui/history/history.js vendored Normal file
View File

@ -0,0 +1,71 @@
YUI.add('moodle-mod_assign-history', function (Y) {
// Define a function that will run in the context of a
// Node instance:
var CSS = {
LINK: 'mod-assign-history-link',
OPEN: 'mod-assign-history-link-open',
CLOSED: 'mod-assign-history-link-closed',
PANEL: 'mod-assign-history-panel'
},
COUNT = 0,
TOGGLE = function() {
var id = this.get('for'),
panel = Y.one('#' + id);
console.log(this);
if (this.hasClass(CSS.OPEN)) {
this.removeClass(CSS.OPEN);
this.addClass(CSS.CLOSED);
this.setStyle('overflow', 'hidden');
panel.hide();
} else {
this.removeClass(CSS.CLOSED);
this.addClass(CSS.OPEN);
panel.show();
}
},
HISTORY = function() {
var link = null,
panel = null,
wrapper = null,
container = this;
// Loop through all the children of this container and turn
// every odd node to a link to open/close the following panel.
this.get('children').each(function () {
if (link) {
COUNT++;
// First convert the link to an anchor.
wrapper = Y.Node.create('<a/>');
panel = this;
container.insertBefore(wrapper, link);
link.remove(false);
wrapper.appendChild(link);
// Add a for attribute to the link to link to the id of the panel.
if (!panel.get('id')) {
panel.set('id', CSS.PANEL + COUNT);
}
wrapper.set('for', panel.get('id'));
// Add an aria attribute for the live region.
panel.set('aria-live', 'polite');
wrapper.addClass(CSS.LINK);
wrapper.addClass(CSS.CLOSED);
panel.addClass(CSS.PANEL);
panel.hide();
link = null;
} else {
link = this;
}
});
// Setup event listeners.
this.delegate('click', TOGGLE, '.' + CSS.LINK);
};
// Use addMethod to add history to the Node prototype:
Y.Node.addMethod("history", HISTORY);
// Extend this functionality to NodeLists.
Y.NodeList.importMethod(Y.Node.prototype, "history");
}, '@VERSION@', { requires: ['node', 'transition'] });