mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 21:49:15 +01:00
MDL-68913 Assign: Per attempt timing
This patch provides functionality to allow the assign activity to have timed attempts for submissions. It includes the logic, UI and adminstration changes fot timed submissions.
This commit is contained in:
parent
d135a1200a
commit
eaa1f56704
2
mod/assign/amd/build/timer.min.js
vendored
Normal file
2
mod/assign/amd/build/timer.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
define ("mod_assign/timer",["exports","core/notification","core/str"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);var d=0,e=null,f=null,g=function(a){var b=Math.floor(a/3600),c=Math.floor(a/60)%60;return[b,c,a%60].filter(function(a,b){return 0!==a||0<b}).map(function(a){return"".concat(a).padStart(2,"0")}).join(":")},h=function(){if(e){clearTimeout(e)}},i=function(){var a=new Date().getTime(),j=Math.floor((d-a)/1e3);if(0>=j){f.classList.add("alert","alert-danger");f.innerHTML="00:00:00";if(document.getElementById("mod_assign_timelimit_block")){(0,c.get_string)("caneditsubmission","mod_assign").done(function(a){return b.default.addNotification({type:"error",message:a})}).fail(b.default.exception)}h();return}else if(300>j){f.classList.remove("alert-warning");f.classList.add("alert","alert-danger")}else if(900>j){f.classList.remove("alert-danger");f.classList.add("alert","alert-warning")}f.innerHTML=g(j);e=setTimeout(i,500)},j=function(a){f=document.getElementById(a);d=M.pageloadstarttime.getTime()+1e3*f.dataset.starttime;i()};a.init=j});
|
||||
//# sourceMappingURL=timer.min.js.map
|
1
mod/assign/amd/build/timer.min.js.map
Normal file
1
mod/assign/amd/build/timer.min.js.map
Normal file
File diff suppressed because one or more lines are too long
130
mod/assign/amd/src/timer.js
Normal file
130
mod/assign/amd/src/timer.js
Normal file
@ -0,0 +1,130 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Notification from 'core/notification';
|
||||
import {get_string as getString} from 'core/str';
|
||||
|
||||
/**
|
||||
* A javascript module for the time in the assign module.
|
||||
*
|
||||
* @copyright 2020 Matt Porritt <mattp@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Timestamp at which time runs out.
|
||||
*
|
||||
* @property {Number} endTime
|
||||
*/
|
||||
let endTime = 0;
|
||||
|
||||
/**
|
||||
* ID of the timeout that updates the clock.
|
||||
*
|
||||
* @property {Number} timeoutId
|
||||
*/
|
||||
let timeoutId = null;
|
||||
|
||||
/**
|
||||
* The timer element.
|
||||
*
|
||||
* @property {Element} timer
|
||||
*/
|
||||
let timer = null;
|
||||
|
||||
/**
|
||||
* Helper method to convert time remaining in seconds into HH:MM:SS format.
|
||||
*
|
||||
* @method formatSeconds
|
||||
* @param {Number} secs Time remaining in seconds to get value for.
|
||||
* @return {String} Time remaining in HH:MM:SS format.
|
||||
*/
|
||||
const formatSeconds = (secs) => {
|
||||
const hours = Math.floor(secs / 3600);
|
||||
const minutes = Math.floor(secs / 60) % 60;
|
||||
const seconds = secs % 60;
|
||||
|
||||
return [hours, minutes, seconds]
|
||||
// Remove the hours column if there is less than 1 hour left.
|
||||
.filter((value, index) => value !== 0 || index > 0)
|
||||
// Ensure that all fields are two digit numbers.
|
||||
.map(value => `${value}`.padStart(2, '0'))
|
||||
.join(":");
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop the timer, if it is running.
|
||||
*
|
||||
* @method stop
|
||||
*/
|
||||
const stop = () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to update the clock with the current time left.
|
||||
*
|
||||
* @method update
|
||||
*/
|
||||
const update = () => {
|
||||
const now = new Date().getTime();
|
||||
const secondsLeft = Math.floor((endTime - now) / 1000);
|
||||
|
||||
// If time has expired, set the hidden form field that says time has expired.
|
||||
if (secondsLeft <= 0) {
|
||||
timer.classList.add('alert', 'alert-danger');
|
||||
timer.innerHTML = '00:00:00';
|
||||
|
||||
// Only add a notification on the assign submission page.
|
||||
if (document.getElementById("mod_assign_timelimit_block")) {
|
||||
getString('caneditsubmission', 'mod_assign')
|
||||
.done(
|
||||
str => Notification.addNotification({
|
||||
type: "error",
|
||||
message: str
|
||||
})
|
||||
).fail(Notification.exception);
|
||||
}
|
||||
|
||||
stop();
|
||||
return;
|
||||
} else if (secondsLeft < 300) { // Add danger style when less than 5 minutes left.
|
||||
timer.classList.remove('alert-warning');
|
||||
timer.classList.add('alert', 'alert-danger');
|
||||
} else if (secondsLeft < 900) { // Add warning style when less than 15 minutes left.
|
||||
timer.classList.remove('alert-danger');
|
||||
timer.classList.add('alert', 'alert-warning');
|
||||
}
|
||||
|
||||
// Update the time display.
|
||||
timer.innerHTML = formatSeconds(secondsLeft);
|
||||
|
||||
// Arrange for this method to be called again soon.
|
||||
timeoutId = setTimeout(update, 500);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up the submission timer.
|
||||
*
|
||||
* @method init
|
||||
* @param {Number} timerId Unique ID of the timer element.
|
||||
*/
|
||||
export const init = (timerId) => {
|
||||
timer = document.getElementById(timerId);
|
||||
endTime = M.pageloadstarttime.getTime() + (timer.dataset.starttime * 1000);
|
||||
update();
|
||||
};
|
@ -75,6 +75,7 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
|
||||
'sendstudentnotifications',
|
||||
'duedate',
|
||||
'cutoffdate',
|
||||
'timelimit',
|
||||
'gradingduedate',
|
||||
'allowsubmissionsfromdate',
|
||||
'grade',
|
||||
@ -110,6 +111,7 @@ class backup_assign_activity_structure_step extends backup_activity_structure_st
|
||||
array('userid',
|
||||
'timecreated',
|
||||
'timemodified',
|
||||
'timestarted',
|
||||
'status',
|
||||
'groupid',
|
||||
'attemptnumber',
|
||||
|
@ -112,6 +112,9 @@ class restore_assign_activity_structure_step extends restore_activity_structure_
|
||||
$data->teamsubmissiongroupingid = 0;
|
||||
}
|
||||
|
||||
if (!isset($data->timelimit)) {
|
||||
$data->timelimit = 0;
|
||||
}
|
||||
if (!isset($data->cutoffdate)) {
|
||||
$data->cutoffdate = 0;
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ class assign_header implements \renderable {
|
||||
public $postfix;
|
||||
/** @var \moodle_url|null $subpageurl link for the sub page */
|
||||
public $subpageurl;
|
||||
/** @var bool $activity optional show activity text. */
|
||||
public $activity;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -59,6 +61,7 @@ class assign_header implements \renderable {
|
||||
* @param string $preface An optional preface to show before the heading.
|
||||
* @param string $postfix An optional postfix to show after the intro.
|
||||
* @param \moodle_url|null $subpageurl An optional sub page URL link for the subpage.
|
||||
* @param bool $activity Optional show activity text if true.
|
||||
*/
|
||||
public function __construct(
|
||||
\stdClass $assign,
|
||||
@ -68,7 +71,8 @@ class assign_header implements \renderable {
|
||||
$subpage = '',
|
||||
$preface = '',
|
||||
$postfix = '',
|
||||
\moodle_url $subpageurl = null
|
||||
\moodle_url $subpageurl = null,
|
||||
bool $activity = false
|
||||
) {
|
||||
$this->assign = $assign;
|
||||
$this->context = $context;
|
||||
@ -78,5 +82,6 @@ class assign_header implements \renderable {
|
||||
$this->preface = $preface;
|
||||
$this->postfix = $postfix;
|
||||
$this->subpageurl = $subpageurl;
|
||||
$this->activity = $activity;
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,8 @@ class assign_submission_status implements \renderable {
|
||||
public $preventsubmissionnotingroup = 0;
|
||||
/** @var array usergroups */
|
||||
public $usergroups = array();
|
||||
/** @var int The time limit for the assignment */
|
||||
public $timelimit = 0;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -130,6 +132,7 @@ class assign_submission_status implements \renderable {
|
||||
* @param string $gradingstatus The submission status (ie. Graded, Not Released etc).
|
||||
* @param bool $preventsubmissionnotingroup Prevent submission if user is not in a group.
|
||||
* @param array $usergroups Array containing all groups the user is assigned to.
|
||||
* @param int $timelimit The time limit for the assignment.
|
||||
*/
|
||||
public function __construct(
|
||||
$allowsubmissionsfromdate,
|
||||
@ -161,7 +164,8 @@ class assign_submission_status implements \renderable {
|
||||
$maxattempts,
|
||||
$gradingstatus,
|
||||
$preventsubmissionnotingroup,
|
||||
$usergroups
|
||||
$usergroups,
|
||||
$timelimit
|
||||
) {
|
||||
$this->allowsubmissionsfromdate = $allowsubmissionsfromdate;
|
||||
$this->alwaysshowdescription = $alwaysshowdescription;
|
||||
@ -193,5 +197,6 @@ class assign_submission_status implements \renderable {
|
||||
$this->gradingstatus = $gradingstatus;
|
||||
$this->preventsubmissionnotingroup = $preventsubmissionnotingroup;
|
||||
$this->usergroups = $usergroups;
|
||||
$this->timelimit = $timelimit;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@
|
||||
|
||||
namespace mod_assign\output;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
|
||||
use \mod_assign\output\grading_app;
|
||||
@ -263,6 +265,9 @@ class renderer extends \plugin_renderer_base {
|
||||
if ($header->showintro) {
|
||||
$o .= $this->output->box_start('generalbox boxaligncenter', 'intro');
|
||||
$o .= format_module_intro('assign', $header->assign, $header->coursemoduleid);
|
||||
if ($header->activity) {
|
||||
$o .= $this->format_activity_text($header->assign, $header->coursemoduleid);
|
||||
}
|
||||
$o .= $header->postfix;
|
||||
$o .= $this->output->box_end();
|
||||
}
|
||||
@ -467,7 +472,6 @@ class renderer extends \plugin_renderer_base {
|
||||
$o = '';
|
||||
$o .= $this->output->container_start('submissionstatustable');
|
||||
$o .= $this->output->heading(get_string('submission', 'assign'), 3);
|
||||
$time = time();
|
||||
|
||||
if ($status->teamsubmissionenabled) {
|
||||
$group = $status->submissiongroup;
|
||||
@ -567,33 +571,20 @@ class renderer extends \plugin_renderer_base {
|
||||
// Extension date.
|
||||
$duedate = $status->extensionduedate;
|
||||
}
|
||||
}
|
||||
|
||||
// Time remaining.
|
||||
$classname = 'timeremaining';
|
||||
if ($duedate - $time <= 0) {
|
||||
if (!$submission ||
|
||||
$submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
||||
if ($status->submissionsenabled) {
|
||||
$remaining = get_string('overdue', 'assign', format_time($time - $duedate));
|
||||
$classname = 'overdue';
|
||||
} else {
|
||||
$remaining = get_string('duedatereached', 'assign');
|
||||
}
|
||||
} else {
|
||||
if ($submission->timemodified > $duedate) {
|
||||
$remaining = get_string('submittedlate',
|
||||
'assign',
|
||||
format_time($submission->timemodified - $duedate));
|
||||
$classname = 'latesubmission';
|
||||
} else {
|
||||
$remaining = get_string('submittedearly',
|
||||
'assign',
|
||||
format_time($submission->timemodified - $duedate));
|
||||
$classname = 'earlysubmission';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$remaining = get_string('paramtimeremaining', 'assign', format_time($duedate - $time));
|
||||
// Time remaining.
|
||||
// Only add the row if there is a due date, or a countdown.
|
||||
if ($status->duedate > 0 || !empty($submission->timestarted)) {
|
||||
[$remaining, $classname] = $this->get_time_remaining($status);
|
||||
|
||||
// If the assignment is not submitted, and there is a submission in progress,
|
||||
// Add a heading for the time limit.
|
||||
if (!empty($submission) &&
|
||||
$submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED &&
|
||||
!empty($submission->timestarted)
|
||||
) {
|
||||
$o .= $this->output->container(get_string('timeremaining', 'assign'));
|
||||
}
|
||||
$o .= $this->output->container($remaining, $classname);
|
||||
}
|
||||
@ -801,36 +792,22 @@ class renderer extends \plugin_renderer_base {
|
||||
$this->add_table_row_tuple($t, $cell1content, $cell2content);
|
||||
$duedate = $status->extensionduedate;
|
||||
}
|
||||
}
|
||||
|
||||
// Time remaining.
|
||||
// Time remaining.
|
||||
// Only add the row if there is a due date, or a countdown.
|
||||
if ($status->duedate > 0 || !empty($submission->timestarted)) {
|
||||
$cell1content = get_string('timeremaining', 'assign');
|
||||
$cell2attributes = [];
|
||||
if ($duedate - $time <= 0) {
|
||||
if (!$submission ||
|
||||
$submission->status != ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
||||
if ($status->submissionsenabled) {
|
||||
$cell2content = get_string('overdue', 'assign', format_time($time - $duedate));
|
||||
$cell2attributes = array('class' => 'overdue');
|
||||
} else {
|
||||
$cell2content = get_string('duedatereached', 'assign');
|
||||
}
|
||||
} else {
|
||||
if ($submission->timemodified > $duedate) {
|
||||
$cell2content = get_string('submittedlate',
|
||||
'assign',
|
||||
format_time($submission->timemodified - $duedate));
|
||||
$cell2attributes = array('class' => 'latesubmission');
|
||||
} else {
|
||||
$cell2content = get_string('submittedearly',
|
||||
'assign',
|
||||
format_time($submission->timemodified - $duedate));
|
||||
$cell2attributes = array('class' => 'earlysubmission');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$cell2content = format_time($duedate - $time);
|
||||
}
|
||||
$this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes);
|
||||
[$cell2content, $cell2attributes] = $this->get_time_remaining($status);
|
||||
$this->add_table_row_tuple($t, $cell1content, $cell2content, [], ['class' => $cell2attributes]);
|
||||
}
|
||||
|
||||
// Add time limit info if there is one.
|
||||
$timelimitenabled = get_config('assign', 'enabletimelimit') && $status->timelimit > 0;
|
||||
if ($timelimitenabled && $status->timelimit > 0) {
|
||||
$cell1content = get_string('timelimit', 'assign');
|
||||
$cell2content = format_time($status->timelimit);
|
||||
$this->add_table_row_tuple($t, $cell1content, $cell2content, [], []);
|
||||
}
|
||||
|
||||
// Show graders whether this submission is editable by students.
|
||||
@ -850,7 +827,7 @@ class renderer extends \plugin_renderer_base {
|
||||
if (!empty($status->gradingcontrollerpreview)) {
|
||||
$cell1content = get_string('gradingmethodpreview', 'assign');
|
||||
$cell2content = $status->gradingcontrollerpreview;
|
||||
$this->add_table_row_tuple($t, $cell1content, $cell2content, [], $cell2attributes);
|
||||
$this->add_table_row_tuple($t, $cell1content, $cell2content, [], []);
|
||||
}
|
||||
|
||||
// Last modified.
|
||||
@ -899,8 +876,30 @@ class renderer extends \plugin_renderer_base {
|
||||
if (!$submission || $submission->status == ASSIGN_SUBMISSION_STATUS_NEW) {
|
||||
$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');
|
||||
|
||||
if ($timelimitenabled && empty($submission->timestarted)) {
|
||||
$confirmation = new \confirm_action(
|
||||
get_string(
|
||||
'confirmstart',
|
||||
'assign',
|
||||
format_time($status->timelimit)
|
||||
),
|
||||
null,
|
||||
get_string('beginassignment', 'assign')
|
||||
);
|
||||
$o .= $this->output->action_link(
|
||||
new \moodle_url('/mod/assign/view.php', $urlparams),
|
||||
get_string('beginassignment', 'assign'),
|
||||
$confirmation,
|
||||
array('class' => 'btn btn-primary')
|
||||
);
|
||||
} else {
|
||||
$o .= $this->output->single_button(
|
||||
new \moodle_url('/mod/assign/view.php', $urlparams),
|
||||
get_string('addsubmission', 'assign'), 'get', array('primary' => true)
|
||||
);
|
||||
}
|
||||
|
||||
$o .= $this->output->box_start('boxaligncenter submithelp');
|
||||
$o .= get_string('addsubmission_help', 'assign');
|
||||
$o .= $this->output->box_end();
|
||||
@ -1379,7 +1378,60 @@ class renderer extends \plugin_renderer_base {
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time remaining for a submission.
|
||||
*
|
||||
* @param \mod_assign\output\assign_submission_status $status
|
||||
* @return array The first element is the time remaining as a human readable
|
||||
* string and the second is a CSS class.
|
||||
*/
|
||||
protected function get_time_remaining(\mod_assign\output\assign_submission_status $status): array {
|
||||
$time = time();
|
||||
$submission = $status->teamsubmission ? $status->teamsubmission : $status->submission;
|
||||
$timelimitenabled = get_config('assign', 'enabletimelimit') && $status->timelimit > 0 && $submission->timestarted;
|
||||
$duedatereached = $status->duedate > 0 && $status->duedate - $time <= 0;
|
||||
$timelimitenabledbeforeduedate = $timelimitenabled && !$duedatereached;
|
||||
|
||||
// There is a submission, display the relevant early/late message.
|
||||
if ($submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
||||
$latecalculation = $submission->timemodified - ($timelimitenabledbeforeduedate ? $submission->timecreated : 0);
|
||||
$latethreshold = $timelimitenabledbeforeduedate ? $status->timelimit : $status->duedate;
|
||||
$earlystring = $timelimitenabledbeforeduedate ? 'submittedundertime' : 'submittedearly';
|
||||
$latestring = $timelimitenabledbeforeduedate ? 'submittedovertime' : 'submittedlate';
|
||||
$ontime = $latecalculation <= $latethreshold;
|
||||
return [
|
||||
get_string(
|
||||
$ontime ? $earlystring : $latestring,
|
||||
'assign',
|
||||
format_time($latecalculation - $latethreshold)
|
||||
),
|
||||
$ontime ? 'earlysubmission' : 'latesubmission'
|
||||
];
|
||||
}
|
||||
|
||||
// There is no submission, due date has passed, show assignment is overdue.
|
||||
if ($duedatereached) {
|
||||
return [
|
||||
get_string(
|
||||
$status->submissionsenabled ? 'overdue' : 'duedatereached',
|
||||
'assign',
|
||||
format_time($time - $status->duedate)
|
||||
),
|
||||
'overdue'
|
||||
];
|
||||
}
|
||||
|
||||
// An attempt has started and there is a time limit, display the time limit.
|
||||
if ($timelimitenabled && !empty($submission->timestarted)) {
|
||||
return [
|
||||
(new \assign($status->context, null, null))->get_timelimit_panel($submission),
|
||||
'timeremaining'
|
||||
];
|
||||
}
|
||||
|
||||
// Assignment is not overdue, and no submission has been made. Just display the due date.
|
||||
return [get_string('paramtimeremaining', 'assign', format_time($status->duedate - $time)), 'timeremaining'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function - creates htmls structure suitable for YUI tree.
|
||||
@ -1488,4 +1540,20 @@ class renderer extends \plugin_renderer_base {
|
||||
return $this->render_from_template('mod_assign/grading_app', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats activity intro text.
|
||||
*
|
||||
* @param object $assign Instance of assign.
|
||||
* @param int $cmid Course module ID.
|
||||
* @return string
|
||||
*/
|
||||
public function format_activity_text($assign, $cmid) {
|
||||
global $CFG;
|
||||
require_once("$CFG->libdir/filelib.php");
|
||||
$context = \context_module::instance($cmid);
|
||||
$options = array('noclean' => true, 'para' => false, 'filter' => true, 'context' => $context, 'overflowdiv' => true);
|
||||
$activity = file_rewrite_pluginfile_urls(
|
||||
$assign->activity, 'pluginfile.php', $context->id, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA, 0);
|
||||
return trim(format_text($activity, $assign->activityformat, $options, null));
|
||||
}
|
||||
}
|
||||
|
84
mod/assign/classes/output/timelimit_panel.php
Normal file
84
mod/assign/classes/output/timelimit_panel.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Represents the timer panel.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2020 Ilya Tregubov <ilyatregubov@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace mod_assign\output;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Represents the timer panel.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2020 Ilya Tregubov <ilyatregubov@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class timelimit_panel implements templatable, renderable {
|
||||
/** @var \stdClass assign submission attempt.*/
|
||||
protected $submission;
|
||||
/** @var object assign object.*/
|
||||
protected $assign;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \stdClass $submission assign submission.
|
||||
* @param \stdClass $assign assign object.
|
||||
*/
|
||||
public function __construct(\stdClass $submission, \stdClass $assign) {
|
||||
$this->submission = $submission;
|
||||
$this->assign = $assign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render timer.
|
||||
*
|
||||
* @param renderer_base $output The current page renderer.
|
||||
* @return stdClass - Flat list of exported data.
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
return (object)['timerstartvalue' => $this->end_time() - time()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute end time for this assign attempt.
|
||||
*
|
||||
* @return int the time when assign attempt is due.
|
||||
*/
|
||||
private function end_time(): int {
|
||||
$timedue = $this->submission->timestarted + $this->assign->timelimit;
|
||||
if ($this->assign->duedate) {
|
||||
return min($timedue, $this->assign->duedate);
|
||||
}
|
||||
|
||||
if ($this->assign->cutoffdate) {
|
||||
return min($timedue, $this->assign->cutoffdate);
|
||||
}
|
||||
|
||||
return $timedue;
|
||||
}
|
||||
}
|
@ -89,6 +89,7 @@ class provider implements
|
||||
'userid' => 'privacy:metadata:userid',
|
||||
'timecreated' => 'privacy:metadata:timecreated',
|
||||
'timemodified' => 'timemodified',
|
||||
'timestarted' => 'privacy:metadata:timestarted',
|
||||
'status' => 'gradingstatus',
|
||||
'groupid' => 'privacy:metadata:groupid',
|
||||
'attemptnumber' => 'attemptnumber',
|
||||
@ -566,6 +567,7 @@ class provider implements
|
||||
$submissiondata = (object)[
|
||||
'timecreated' => transform::datetime($submission->timecreated),
|
||||
'timemodified' => transform::datetime($submission->timemodified),
|
||||
'timestarted' => transform::datetime($submission->timestarted),
|
||||
'status' => get_string('submissionstatus_' . $submission->status, 'mod_assign'),
|
||||
'groupid' => $submission->groupid,
|
||||
'attemptnumber' => ($submission->attemptnumber + 1),
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="mod/assign/db" VERSION="20140724" COMMENT="XMLDB file for Moodle mod/assign"
|
||||
<XMLDB PATH="mod/assign/db" VERSION="20210930" COMMENT="XMLDB file for Moodle mod/assign"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
@ -36,6 +36,10 @@
|
||||
<FIELD NAME="markingallocation" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If enabled, marking allocation features will be used in this assignment"/>
|
||||
<FIELD NAME="sendstudentnotifications" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Default for send student notifications checkbox when grading."/>
|
||||
<FIELD NAME="preventsubmissionnotingroup" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If enabled a user will be unable to make a submission unless they are a member of a group."/>
|
||||
<FIELD NAME="activity" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="activityformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="submissionattachments" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="The unique id for this assignment instance."/>
|
||||
@ -52,6 +56,7 @@
|
||||
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<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="timestarted" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="The time when the student stared the submission."/>
|
||||
<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" 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"/>
|
||||
@ -150,6 +155,7 @@
|
||||
<FIELD NAME="allowsubmissionsfromdate" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time at which students may start attempting this assign. Can be null, in which case the assign default is used."/>
|
||||
<FIELD NAME="duedate" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time by which students must have completed their attempt. Can be null, in which case the assign default is used."/>
|
||||
<FIELD NAME="cutoffdate" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time by which students must have completed their attempt. Can be null, in which case the assign default is used."/>
|
||||
<FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time limit in seconds. Can be null, in which case the quiz default is used."/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
|
@ -59,5 +59,58 @@ function xmldb_assign_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.9.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2021093000) {
|
||||
// Define field activity to be added to assign.
|
||||
$table = new xmldb_table('assign');
|
||||
$field = new xmldb_field('activity', XMLDB_TYPE_TEXT, null, null, null, null, null, 'alwaysshowdescription');
|
||||
|
||||
// Conditionally launch add field activity.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
$field = new xmldb_field('activityformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'activity');
|
||||
|
||||
// Conditionally launch add field activityformat.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
$field = new xmldb_field('timelimit', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'cutoffdate');
|
||||
|
||||
// Conditionally launch add field timelimit.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
$field = new xmldb_field('submissionattachments', XMLDB_TYPE_INTEGER, '2',
|
||||
null, XMLDB_NOTNULL, null, '0', 'activityformat');
|
||||
|
||||
// Conditionally launch add field submissionattachments.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
$table = new xmldb_table('assign_submission');
|
||||
$field = new xmldb_field('timestarted', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'timemodified');
|
||||
|
||||
// Conditionally launch add field timestarted.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Define field timelimit to be added to assign_overrides.
|
||||
$table = new xmldb_table('assign_overrides');
|
||||
$field = new xmldb_field('timelimit', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'cutoffdate');
|
||||
|
||||
// Conditionally launch add field timelimit.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Assign savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2021093000, 'assign');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1078,6 +1078,7 @@ class assign_grading_table extends table_sql implements renderable {
|
||||
$o = '';
|
||||
|
||||
$instance = $this->assignment->get_instance($row->userid);
|
||||
$timelimitenabled = get_config('assign', 'enabletimelimit');
|
||||
|
||||
$due = $instance->duedate;
|
||||
if ($row->extensionduedate) {
|
||||
@ -1121,6 +1122,14 @@ class assign_grading_table extends table_sql implements renderable {
|
||||
'assign',
|
||||
$usertime);
|
||||
$o .= $this->output->container($latemessage, 'latesubmission');
|
||||
} else if ($timelimitenabled && $instance->timelimit && !empty($submission->timestarted)
|
||||
&& ($timesubmitted - $submission->timestarted > $instance->timelimit)
|
||||
&& $status != ASSIGN_SUBMISSION_STATUS_NEW) {
|
||||
$usertime = format_time($timesubmitted - $submission->timestarted - $instance->timelimit);
|
||||
$latemessage = get_string('submittedlateshort',
|
||||
'assign',
|
||||
$usertime);
|
||||
$o .= $this->output->container($latemessage, 'latesubmission');
|
||||
}
|
||||
if ($row->locked) {
|
||||
$lockedstr = get_string('submissionslockedshort', 'assign');
|
||||
|
@ -22,12 +22,13 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['activityattachments'] = 'Assignment activity attachments';
|
||||
$string['activitydate:submissionsdue'] = 'Due:';
|
||||
$string['activitydate:submissionsopen'] = 'Opens:';
|
||||
$string['activitydate:submissionsopened'] = 'Opened:';
|
||||
$string['activityeditor'] = 'Activity';
|
||||
$string['activityeditor_help'] = 'The actions you would like the student to complete for this assignment. This is only shown on the submission page where a students edits and submits their assignment.';
|
||||
$string['activityoverview'] = 'You have assignments that need attention';
|
||||
$string['addsubmission'] = 'Add submission';
|
||||
$string['addsubmission_help'] = 'You have not made a submission yet.';
|
||||
$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.';
|
||||
@ -35,6 +36,8 @@ $string['addnewattemptfromprevious'] = 'Add a new attempt based on previous subm
|
||||
$string['addnewattemptfromprevious_help'] = 'This will copy the contents of your previous submission to a new submission for you to work on.';
|
||||
$string['addnewgroupoverride'] = 'Add group override';
|
||||
$string['addnewuseroverride'] = 'Add user override';
|
||||
$string['addsubmission'] = 'Add submission';
|
||||
$string['addsubmission_help'] = 'You have not made a submission yet.';
|
||||
$string['allocatedmarker'] = 'Allocated Marker';
|
||||
$string['allocatedmarker_help'] = 'Marker allocated to this submission.';
|
||||
$string['allowsubmissions'] = 'Allow the user to continue making submissions to this assignment.';
|
||||
@ -81,6 +84,7 @@ $string['assignmentplugins'] = 'Assignment plugins';
|
||||
$string['assignmentsperpage'] = 'Assignments per page';
|
||||
$string['assignsubmission'] = 'Submission plugin';
|
||||
$string['assignsubmissionpluginname'] = 'Submission plugin';
|
||||
$string['assigntimeleft'] = 'Time left';
|
||||
$string['attemptheading'] = 'Attempt {$a->attemptnumber}: {$a->submissionsummary}';
|
||||
$string['attempthistory'] = 'Previous attempts';
|
||||
$string['attemptnumber'] = 'Attempt number';
|
||||
@ -111,12 +115,14 @@ $string['batchoperationunlock'] = 'unlock submissions';
|
||||
$string['batchoperationreverttodraft'] = 'revert submissions to draft';
|
||||
$string['batchsetallocatedmarker'] = 'Set allocated marker for {$a} selected user(s).';
|
||||
$string['batchsetmarkingworkflowstateforusers'] = 'Set marking workflow state for {$a} selected user(s).';
|
||||
$string['beginassignment'] = 'Begin assignment';
|
||||
$string['blindmarking'] = 'Anonymous submissions';
|
||||
$string['blindmarkingenabledwarning'] = 'Anonymous submissions are enabled for this activity. Grades will not be added to the gradebook until student identities are revealed via the grading action menu.';
|
||||
$string['blindmarking_help'] = 'Anonymous submissions hide the identity of students from markers. Anonymous submission settings will be locked once a submission or grade has been made in relation to this assignment.';
|
||||
$string['cachedef_overrides'] = 'User and group override information';
|
||||
$string['calendardue'] = '{$a} is due';
|
||||
$string['calendargradingdue'] = '{$a} is due to be graded';
|
||||
$string['caneditsubmission'] = 'You can submit/edit submission after time limit passed, but it will be marked as late.';
|
||||
$string['changeuser'] = 'Change user';
|
||||
$string['changefilters'] = 'Change filters';
|
||||
$string['choosegradingaction'] = 'Grading action';
|
||||
@ -179,6 +185,8 @@ $string['editsubmission_help'] = 'You can still make changes to your submission.
|
||||
$string['editingstatus'] = 'Editing status';
|
||||
$string['editaction'] = 'Actions...';
|
||||
$string['enabled'] = 'Enabled';
|
||||
$string['enabletimelimit'] = 'Enable timed assignments';
|
||||
$string['enabletimelimit_help'] = 'If enabled, you can set a time limit on assignment settings page.';
|
||||
$string['eventallsubmissionsdownloaded'] = 'All the submissions are being downloaded.';
|
||||
$string['eventassessablesubmitted'] = 'A submission has been submitted.';
|
||||
$string['eventbatchsetmarkerallocationviewed'] = 'Batch set marker allocation viewed';
|
||||
@ -314,7 +322,7 @@ $string['indicator:socialbreadthdef_help'] = 'The participant has reached this p
|
||||
$string['indicator:socialbreadthdef_link'] = 'Learning_analytics_indicators#Social_breadth';
|
||||
$string['instructionfiles'] = 'Instruction files';
|
||||
$string['introattachments'] = 'Additional files';
|
||||
$string['introattachments_help'] = 'Additional files for use in the assignment, such as answer templates, may be added. Download links for the files will then be displayed on the assignment page under the description.';
|
||||
$string['introattachments_help'] = 'Additional files for use in the assignment, such as answer templates, may be added. Download links for the files will then be displayed on the assignment page under the activty description.';
|
||||
$string['invalidgradeforscale'] = 'The grade supplied was not valid for the current scale';
|
||||
$string['invalidfloatforgrade'] = 'The grade provided could not be understood: {$a}';
|
||||
$string['invalidoverrideid'] = 'Invalid override id';
|
||||
@ -445,6 +453,7 @@ $string['privacy:metadata:groupid'] = 'Group ID that the user is a member of.';
|
||||
$string['privacy:metadata:latest'] = 'Greatly simplifies queries wanting to know information about only the latest attempt.';
|
||||
$string['privacy:metadata:mailed'] = 'Has this user been mailed yet?';
|
||||
$string['privacy:metadata:timecreated'] = 'Time created';
|
||||
$string['privacy:metadata:timestarted'] = 'Time started';
|
||||
$string['privacy:metadata:userid'] = 'ID of the user';
|
||||
$string['privacy:studentpath'] = 'studentsubmissions';
|
||||
$string['quickgrading'] = 'Quick grading';
|
||||
@ -502,6 +511,10 @@ $string['settings'] = 'Assignment settings';
|
||||
$string['showrecentsubmissions'] = 'Show recent submissions';
|
||||
$string['status'] = 'Status';
|
||||
$string['studentnotificationworkflowstateerror'] = 'Marking workflow state must be \'Released\' to notify students.';
|
||||
$string['submissionattachments'] = 'Only show files during submission.';
|
||||
$string['submissionattachments_help'] = 'When enabled files will only be shown on submission screen.
|
||||
When disabled files will be shown on both assignment view and submission screens.';
|
||||
$string['confirmstart'] = 'Your submission will have a time limit of {$a}. When you start, the timer will begin to count down and cannot be paused. You must finish your submission before it expires. Are you sure you wish to start now? ';
|
||||
$string['submissioncopiedtext'] = 'You have made a copy of your previous
|
||||
assignment submission for \'{$a->assignment}\'
|
||||
|
||||
@ -574,6 +587,8 @@ $string['submitassignment_help'] = 'Once this assignment is submitted you will n
|
||||
$string['submitassignment'] = 'Submit assignment';
|
||||
$string['submittedearly'] = 'Assignment was submitted {$a} early';
|
||||
$string['submittedlate'] = 'Assignment was submitted {$a} late';
|
||||
$string['submittedovertime'] = 'Assignment was submitted {$a} over the time limit';
|
||||
$string['submittedundertime'] = 'Assignment was submitted {$a} under the time limit';
|
||||
$string['submittedlateshort'] = '{$a} late';
|
||||
$string['submitted'] = 'Submitted';
|
||||
$string['subpagetitle'] = '{$a->contextname} - {$a->subpage}';
|
||||
@ -587,6 +602,9 @@ $string['teamsubmission_help'] = 'If enabled, students will be divided into grou
|
||||
$string['teamsubmissiongroupingid'] = 'Grouping for student groups';
|
||||
$string['teamsubmissiongroupingid_help'] = 'This is the grouping that the assignment will use to find groups for student groups. If not set, the default set of groups will be used.';
|
||||
$string['textinstructions'] = 'Assignment instructions';
|
||||
$string['timelimit'] = 'Time limit';
|
||||
$string['timelimit_help'] = 'If enabled, the time limit is stated on the assignment page and a countdown timer is displayed during the assignment.';
|
||||
$string['timelimitpassed'] = 'Time limit has been passed';
|
||||
$string['timemodified'] = 'Last modified';
|
||||
$string['timeremaining'] = 'Time remaining';
|
||||
$string['timeremainingcolon'] = 'Time remaining: {$a}';
|
||||
@ -646,3 +664,4 @@ $string['allowsubmissionsfromdatesummary'] = 'This assignment will accept submis
|
||||
$string['allowsubmissionsanddescriptionfromdatesummary'] = 'The assignment details and submission form will be available from <strong>{$a}</strong>';
|
||||
$string['relativedatessubmissionduedateafter'] = '{$a->datediffstr} after course start';
|
||||
$string['relativedatessubmissionduedatebefore'] = '{$a->datediffstr} before course start';
|
||||
|
||||
|
@ -164,7 +164,7 @@ function assign_prepare_update_events($assign, $course = null, $cm = null) {
|
||||
$assignment->update_calendar($cm->id);
|
||||
// Refresh the calendar events also for the assignment overrides.
|
||||
$overrides = $DB->get_records('assign_overrides', ['assignid' => $assign->id], '',
|
||||
'id, groupid, userid, duedate, sortorder');
|
||||
'id, groupid, userid, duedate, sortorder, timelimit');
|
||||
foreach ($overrides as $override) {
|
||||
if (empty($override->userid)) {
|
||||
unset($override->userid);
|
||||
@ -293,6 +293,7 @@ function assign_update_events($assign, $override = null) {
|
||||
$groupid = isset($current->groupid) ? $current->groupid : 0;
|
||||
$userid = isset($current->userid) ? $current->userid : 0;
|
||||
$duedate = isset($current->duedate) ? $current->duedate : $assigninstance->duedate;
|
||||
$timelimit = isset($current->timelimit) ? $current->timelimit : 0;
|
||||
|
||||
// Only add 'due' events for an override if they differ from the assign default.
|
||||
$addclose = empty($current->id) || !empty($current->duedate);
|
||||
@ -308,7 +309,7 @@ function assign_update_events($assign, $override = null) {
|
||||
$event->modulename = 'assign';
|
||||
$event->instance = $assigninstance->id;
|
||||
$event->timestart = $duedate;
|
||||
$event->timeduration = 0;
|
||||
$event->timeduration = $timelimit;
|
||||
$event->timesort = $event->timestart + $event->timeduration;
|
||||
$event->visible = instance_is_visible('assign', $assigninstance);
|
||||
$event->eventtype = ASSIGN_EVENT_TYPE_DUE;
|
||||
@ -1161,7 +1162,10 @@ function assign_get_file_areas($course, $cm, $context) {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
|
||||
$areas = array(ASSIGN_INTROATTACHMENT_FILEAREA => get_string('introattachments', 'mod_assign'));
|
||||
$areas = array(
|
||||
ASSIGN_INTROATTACHMENT_FILEAREA => get_string('introattachments', 'mod_assign'),
|
||||
ASSIGN_ACTIVITYATTACHMENT_FILEAREA => get_string('activityattachments', 'mod_assign'),
|
||||
);
|
||||
|
||||
$assignment = new assign($context, $cm, $course);
|
||||
foreach ($assignment->get_submission_plugins() as $plugin) {
|
||||
@ -1223,7 +1227,7 @@ function assign_get_file_info($browser,
|
||||
|
||||
// Need to find where this belongs to.
|
||||
$assignment = new assign($context, $cm, $course);
|
||||
if ($filearea === ASSIGN_INTROATTACHMENT_FILEAREA) {
|
||||
if ($filearea === ASSIGN_INTROATTACHMENT_FILEAREA || $filearea === ASSIGN_ACTIVITYATTACHMENT_FILEAREA) {
|
||||
if (!has_capability('moodle/course:managefiles', $context)) {
|
||||
// Students can not peak here!
|
||||
return null;
|
||||
@ -1412,7 +1416,7 @@ function assign_pluginfile($course,
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
$assign = new assign($context, $cm, $course);
|
||||
|
||||
if ($filearea !== ASSIGN_INTROATTACHMENT_FILEAREA) {
|
||||
if ($filearea !== ASSIGN_INTROATTACHMENT_FILEAREA && $filearea !== ASSIGN_ACTIVITYATTACHMENT_FILEAREA) {
|
||||
return false;
|
||||
}
|
||||
if (!$assign->show_intro()) {
|
||||
|
@ -73,6 +73,9 @@ define("ASSIGN_MAX_EVENT_LENGTH", "432000");
|
||||
// Name of file area for intro attachments.
|
||||
define('ASSIGN_INTROATTACHMENT_FILEAREA', 'introattachment');
|
||||
|
||||
// Name of file area for activity attachments.
|
||||
define('ASSIGN_ACTIVITYATTACHMENT_FILEAREA', 'activityattachment');
|
||||
|
||||
// Event types.
|
||||
define('ASSIGN_EVENT_TYPE_DUE', 'due');
|
||||
define('ASSIGN_EVENT_TYPE_GRADINGDUE', 'gradingdue');
|
||||
@ -94,6 +97,7 @@ require_once($CFG->libdir . '/portfolio/caller.php');
|
||||
use \mod_assign\output\grading_app;
|
||||
use \mod_assign\output\assign_header;
|
||||
use \mod_assign\output\assign_submission_status;
|
||||
use mod_assign\output\timelimit_panel;
|
||||
|
||||
/**
|
||||
* Standard base class for mod_assign (assignment types).
|
||||
@ -705,6 +709,13 @@ class assign {
|
||||
$update->intro = $formdata->intro;
|
||||
$update->introformat = $formdata->introformat;
|
||||
$update->alwaysshowdescription = !empty($formdata->alwaysshowdescription);
|
||||
if (isset($formdata->activityeditor)) {
|
||||
$update->activity = $this->save_editor_draft_files($formdata);
|
||||
$update->activityformat = $formdata->activityeditor['format'];
|
||||
}
|
||||
if (isset($formdata->submissionattachments)) {
|
||||
$update->submissionattachments = $formdata->submissionattachments;
|
||||
}
|
||||
$update->submissiondrafts = $formdata->submissiondrafts;
|
||||
$update->requiresubmissionstatement = $formdata->requiresubmissionstatement;
|
||||
$update->sendnotifications = $formdata->sendnotifications;
|
||||
@ -716,6 +727,9 @@ class assign {
|
||||
$update->duedate = $formdata->duedate;
|
||||
$update->cutoffdate = $formdata->cutoffdate;
|
||||
$update->gradingduedate = $formdata->gradingduedate;
|
||||
if (isset($formdata->timelimit)) {
|
||||
$update->timelimit = $formdata->timelimit;
|
||||
}
|
||||
$update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
|
||||
$update->grade = $formdata->grade;
|
||||
$update->completionsubmit = !empty($formdata->completionsubmit);
|
||||
@ -750,6 +764,7 @@ class assign {
|
||||
$this->course = $DB->get_record('course', array('id'=>$formdata->course), '*', MUST_EXIST);
|
||||
|
||||
$this->save_intro_draft_files($formdata);
|
||||
$this->save_editor_draft_files($formdata);
|
||||
|
||||
if ($callplugins) {
|
||||
// Call save_settings hook for submission plugins.
|
||||
@ -940,7 +955,7 @@ class assign {
|
||||
$override = $this->override_exists($userid);
|
||||
|
||||
// Merge with assign defaults.
|
||||
$keys = array('duedate', 'cutoffdate', 'allowsubmissionsfromdate');
|
||||
$keys = array('duedate', 'cutoffdate', 'allowsubmissionsfromdate', 'timelimit');
|
||||
foreach ($keys as $key) {
|
||||
if (isset($override->{$key})) {
|
||||
$this->get_instance($userid)->{$key} = $override->{$key};
|
||||
@ -1007,7 +1022,7 @@ class assign {
|
||||
// return arrays containing keys for only the defined overrides. So we get the
|
||||
// desired behaviour as per the algorithm.
|
||||
return (object)array_merge(
|
||||
['duedate' => null, 'cutoffdate' => null, 'allowsubmissionsfromdate' => null],
|
||||
['timelimit' => null, 'duedate' => null, 'cutoffdate' => null, 'allowsubmissionsfromdate' => null],
|
||||
$getgroupoverride($userid),
|
||||
$getuseroverride($userid)
|
||||
);
|
||||
@ -1460,6 +1475,13 @@ class assign {
|
||||
$update->intro = $formdata->intro;
|
||||
$update->introformat = $formdata->introformat;
|
||||
$update->alwaysshowdescription = !empty($formdata->alwaysshowdescription);
|
||||
if (isset($formdata->activityeditor)) {
|
||||
$update->activity = $this->save_editor_draft_files($formdata);
|
||||
$update->activityformat = $formdata->activityeditor['format'];
|
||||
}
|
||||
if (isset($formdata->submissionattachments)) {
|
||||
$update->submissionattachments = $formdata->submissionattachments;
|
||||
}
|
||||
$update->submissiondrafts = $formdata->submissiondrafts;
|
||||
$update->requiresubmissionstatement = $formdata->requiresubmissionstatement;
|
||||
$update->sendnotifications = $formdata->sendnotifications;
|
||||
@ -1470,6 +1492,9 @@ class assign {
|
||||
}
|
||||
$update->duedate = $formdata->duedate;
|
||||
$update->cutoffdate = $formdata->cutoffdate;
|
||||
if (isset($formdata->timelimit)) {
|
||||
$update->timelimit = $formdata->timelimit;
|
||||
}
|
||||
$update->gradingduedate = $formdata->gradingduedate;
|
||||
$update->allowsubmissionsfromdate = $formdata->allowsubmissionsfromdate;
|
||||
$update->grade = $formdata->grade;
|
||||
@ -1537,7 +1562,7 @@ class assign {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the attachments in the draft areas.
|
||||
* Save the attachments in the intro description.
|
||||
*
|
||||
* @param stdClass $formdata
|
||||
*/
|
||||
@ -1548,6 +1573,25 @@ class assign {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the attachments in the editor description.
|
||||
*
|
||||
* @param stdClass $formdata
|
||||
*/
|
||||
protected function save_editor_draft_files($formdata): string {
|
||||
$text = '';
|
||||
if (isset($formdata->activityeditor)) {
|
||||
$text = $formdata->activityeditor['text'];
|
||||
if (isset($formdata->activityeditor['itemid'])) {
|
||||
$text = file_save_draft_area_files($formdata->activityeditor['itemid'], $this->get_context()->id,
|
||||
'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA,
|
||||
0, array('subdirs' => true), $formdata->activityeditor['text']);
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add elements in grading plugin form.
|
||||
*
|
||||
@ -3038,6 +3082,15 @@ class assign {
|
||||
}
|
||||
|
||||
if ($submission) {
|
||||
if ($create) {
|
||||
$action = optional_param('action', '', PARAM_TEXT);
|
||||
if ($action == 'editsubmission') {
|
||||
if (empty($submission->timestarted) && $this->get_instance()->timelimit) {
|
||||
$submission->timestarted = time();
|
||||
$DB->update_record('assign_submission', $submission);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $submission;
|
||||
}
|
||||
if ($create) {
|
||||
@ -3810,6 +3863,15 @@ class assign {
|
||||
}
|
||||
|
||||
if ($submission) {
|
||||
if ($create) {
|
||||
$action = optional_param('action', '', PARAM_TEXT);
|
||||
if ($action == 'editsubmission') {
|
||||
if (empty($submission->timestarted) && $this->get_instance()->timelimit) {
|
||||
$submission->timestarted = time();
|
||||
$DB->update_record('assign_submission', $submission);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $submission;
|
||||
}
|
||||
if ($create) {
|
||||
@ -4077,7 +4139,8 @@ class assign {
|
||||
$instance->maxattempts,
|
||||
$this->get_grading_status($userid),
|
||||
$instance->preventsubmissionnotingroup,
|
||||
$usergroups);
|
||||
$usergroups,
|
||||
$instance->timelimit);
|
||||
$o .= $this->get_renderer()->render($submissionstatus);
|
||||
}
|
||||
|
||||
@ -4242,7 +4305,6 @@ class assign {
|
||||
$showedit = $this->submissions_open($userid) && ($this->is_any_submission_plugin_enabled());
|
||||
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
||||
$usergroups = $this->get_all_groups($user->id);
|
||||
|
||||
$submissionstatus = new assign_submission_status($instance->allowsubmissionsfromdate,
|
||||
$instance->alwaysshowdescription,
|
||||
$submission,
|
||||
@ -4272,7 +4334,8 @@ class assign {
|
||||
$instance->maxattempts,
|
||||
$this->get_grading_status($userid),
|
||||
$instance->preventsubmissionnotingroup,
|
||||
$usergroups);
|
||||
$usergroups,
|
||||
$instance->timelimit);
|
||||
$o .= $this->get_renderer()->render($submissionstatus);
|
||||
}
|
||||
|
||||
@ -4787,13 +4850,14 @@ class assign {
|
||||
* @return string The page output.
|
||||
*/
|
||||
protected function view_edit_submission_page($mform, $notices) {
|
||||
global $CFG, $USER, $DB;
|
||||
global $CFG, $USER, $DB, $PAGE;
|
||||
|
||||
$o = '';
|
||||
require_once($CFG->dirroot . '/mod/assign/submission_form.php');
|
||||
// Need submit permission to submit an assignment.
|
||||
$userid = optional_param('userid', $USER->id, PARAM_INT);
|
||||
$user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
|
||||
$timelimitenabled = get_config('assign', 'enabletimelimit');
|
||||
|
||||
// This variation on the url will link direct to this student.
|
||||
// The benefit is the url will be the same every time for this student, so Atto autosave drafts can match up.
|
||||
@ -4826,14 +4890,6 @@ class assign {
|
||||
if ($this->has_visible_attachments()) {
|
||||
$postfix = $this->render_area_files('mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0);
|
||||
}
|
||||
$o .= $this->get_renderer()->render(new assign_header($this->get_instance(),
|
||||
$this->get_context(),
|
||||
$this->show_intro(),
|
||||
$this->get_course_module()->id,
|
||||
$title, '', $postfix));
|
||||
|
||||
// Show plagiarism disclosure for any user submitter.
|
||||
$o .= $this->plagiarism_print_disclosure();
|
||||
|
||||
$data = new stdClass();
|
||||
$data->userid = $userid;
|
||||
@ -4841,12 +4897,44 @@ class assign {
|
||||
$mform = new mod_assign_submission_form(null, array($this, $data));
|
||||
}
|
||||
|
||||
if ($this->get_instance()->teamsubmission) {
|
||||
$submission = $this->get_group_submission($userid, 0, false);
|
||||
} else {
|
||||
$submission = $this->get_user_submission($userid, false);
|
||||
}
|
||||
|
||||
if ($timelimitenabled && !empty($submission->timestarted) && $this->get_instance()->timelimit) {
|
||||
$navbc = $this->get_timelimit_panel($submission);
|
||||
$regions = $PAGE->blocks->get_regions();
|
||||
$bc = new \block_contents();
|
||||
$bc->attributes['id'] = 'mod_assign_timelimit_block';
|
||||
$bc->attributes['role'] = 'navigation';
|
||||
$bc->attributes['aria-labelledby'] = 'mod_assign_timelimit_block_title';
|
||||
$bc->title = get_string('assigntimeleft', 'assign');
|
||||
$bc->content = $navbc;
|
||||
$PAGE->blocks->add_fake_block($bc, reset($regions));
|
||||
}
|
||||
$o .= $this->get_renderer()->render(
|
||||
new assign_header($this->get_instance(),
|
||||
$this->get_context(),
|
||||
$this->show_intro(),
|
||||
$this->get_course_module()->id,
|
||||
$title,
|
||||
'',
|
||||
$postfix,
|
||||
null,
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
// Show plagiarism disclosure for any user submitter.
|
||||
$o .= $this->plagiarism_print_disclosure();
|
||||
|
||||
foreach ($notices as $notice) {
|
||||
$o .= $this->get_renderer()->notification($notice);
|
||||
}
|
||||
|
||||
$o .= $this->get_renderer()->render(new assign_form('editsubmissionform', $mform));
|
||||
|
||||
$o .= $this->view_footer();
|
||||
|
||||
\mod_assign\event\submission_form_viewed::create_from_user($this, $user)->trigger();
|
||||
@ -4854,6 +4942,21 @@ class assign {
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time limit panel object for this submission attempt.
|
||||
*
|
||||
* @param stdClass $submission assign submission.
|
||||
* @return string the panel output.
|
||||
*/
|
||||
public function get_timelimit_panel(stdClass $submission): string {
|
||||
global $USER;
|
||||
|
||||
// Apply overrides.
|
||||
$this->update_effective_access($USER->id);
|
||||
$panel = new timelimit_panel($submission, $this->get_instance());
|
||||
return $this->get_renderer()->render($panel);
|
||||
}
|
||||
|
||||
/**
|
||||
* See if this assignment has a grade yet.
|
||||
*
|
||||
@ -5337,7 +5440,8 @@ class assign {
|
||||
$instance->maxattempts,
|
||||
$gradingstatus,
|
||||
$instance->preventsubmissionnotingroup,
|
||||
$usergroups);
|
||||
$usergroups,
|
||||
$instance->timelimit);
|
||||
return $submissionstatus;
|
||||
}
|
||||
|
||||
@ -5713,6 +5817,7 @@ class assign {
|
||||
$this->count_submissions_with_status($submitted, $activitygroup),
|
||||
$instance->cutoffdate,
|
||||
$this->get_duedate($activitygroup),
|
||||
$instance->timelimit,
|
||||
$this->get_course_module()->id,
|
||||
$this->count_submissions_need_grading($activitygroup),
|
||||
$instance->teamsubmission,
|
||||
@ -5733,6 +5838,7 @@ class assign {
|
||||
$this->count_submissions_with_status($submitted, $activitygroup),
|
||||
$instance->cutoffdate,
|
||||
$this->get_duedate($activitygroup),
|
||||
$instance->timelimit,
|
||||
$this->get_course_module()->id,
|
||||
$this->count_submissions_need_grading($activitygroup),
|
||||
$instance->teamsubmission,
|
||||
@ -5784,9 +5890,10 @@ class assign {
|
||||
$o = '';
|
||||
|
||||
$postfix = '';
|
||||
if ($this->has_visible_attachments()) {
|
||||
if ($this->has_visible_attachments() && (!$this->get_instance($USER->id)->submissionattachments)) {
|
||||
$postfix = $this->render_area_files('mod_assign', ASSIGN_INTROATTACHMENT_FILEAREA, 0);
|
||||
}
|
||||
|
||||
$o .= $this->get_renderer()->render(new assign_header($instance,
|
||||
$this->get_context(),
|
||||
$this->show_intro(),
|
||||
@ -9932,3 +10039,14 @@ function reorder_group_overrides($assignid) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the information about the standard assign JavaScript module.
|
||||
* @return array a standard jsmodule structure.
|
||||
*/
|
||||
function assign_get_js_module() {
|
||||
return array(
|
||||
'name' => 'mod_assign',
|
||||
'fullpath' => '/mod/assign/module.js',
|
||||
);
|
||||
}
|
||||
|
@ -58,11 +58,21 @@ class mod_assign_mod_form extends moodleform_mod {
|
||||
|
||||
$this->standard_intro_elements(get_string('description', 'assign'));
|
||||
|
||||
// Activity.
|
||||
$mform->addElement('editor', 'activityeditor',
|
||||
get_string('activityeditor', 'assign'), array('rows' => 10), array('maxfiles' => EDITOR_UNLIMITED_FILES,
|
||||
'noclean' => true, 'context' => $this->context, 'subdirs' => true));
|
||||
$mform->addHelpButton('activityeditor', 'activityeditor', 'assign');
|
||||
$mform->setType('activityeditor', PARAM_RAW);
|
||||
|
||||
$mform->addElement('filemanager', 'introattachments',
|
||||
get_string('introattachments', 'assign'),
|
||||
null, array('subdirs' => 0, 'maxbytes' => $COURSE->maxbytes) );
|
||||
$mform->addHelpButton('introattachments', 'introattachments', 'assign');
|
||||
|
||||
$mform->addElement('advcheckbox', 'submissionattachments', get_string('submissionattachments', 'assign'));
|
||||
$mform->addHelpButton('submissionattachments', 'submissionattachments', 'assign');
|
||||
|
||||
$ctx = null;
|
||||
if ($this->current && $this->current->coursemodule) {
|
||||
$cm = get_coursemodule_from_instance('assign', $this->current->id, 0, false, MUST_EXIST);
|
||||
@ -77,8 +87,6 @@ class mod_assign_mod_form extends moodleform_mod {
|
||||
$assignment->set_course($course);
|
||||
}
|
||||
|
||||
$config = get_config('assign');
|
||||
|
||||
$mform->addElement('header', 'availability', get_string('availability', 'assign'));
|
||||
$mform->setExpanded('availability', true);
|
||||
|
||||
@ -99,6 +107,14 @@ class mod_assign_mod_form extends moodleform_mod {
|
||||
$mform->addElement('date_time_selector', 'gradingduedate', $name, array('optional' => true));
|
||||
$mform->addHelpButton('gradingduedate', 'gradingduedate', 'assign');
|
||||
|
||||
$timelimitenabled = get_config('assign', 'enabletimelimit');
|
||||
// Time limit.
|
||||
if ($timelimitenabled) {
|
||||
$mform->addElement('duration', 'timelimit', get_string('timelimit', 'assign'),
|
||||
array('optional' => true));
|
||||
$mform->addHelpButton('timelimit', 'timelimit', 'assign');
|
||||
}
|
||||
|
||||
$name = get_string('alwaysshowdescription', 'assign');
|
||||
$mform->addElement('checkbox', 'alwaysshowdescription', $name);
|
||||
$mform->addHelpButton('alwaysshowdescription', 'alwaysshowdescription', 'assign');
|
||||
@ -284,6 +300,17 @@ class mod_assign_mod_form extends moodleform_mod {
|
||||
0, array('subdirs' => 0));
|
||||
$defaultvalues['introattachments'] = $draftitemid;
|
||||
|
||||
// Activity editor fields.
|
||||
$activitydraftitemid = file_get_submitted_draft_itemid('activityeditor');
|
||||
if (!empty($defaultvalues['activity'])) {
|
||||
$defaultvalues['activityeditor'] = array(
|
||||
'text' => file_prepare_draft_area($activitydraftitemid, $ctx->id, 'mod_assign', ASSIGN_ACTIVITYATTACHMENT_FILEAREA,
|
||||
0, array('subdirs' => 0), $defaultvalues['activity']),
|
||||
'format' => $defaultvalues['activityformat'],
|
||||
'itemid' => $activitydraftitemid
|
||||
);
|
||||
}
|
||||
|
||||
$assignment->plugin_data_preprocessing($defaultvalues);
|
||||
}
|
||||
|
||||
|
@ -268,6 +268,11 @@ class assign_override_form extends moodleform {
|
||||
userdate($assigninstance->extensionduedate));
|
||||
}
|
||||
|
||||
// Time limit.
|
||||
$mform->addElement('duration', 'timelimit',
|
||||
get_string('timelimit', 'assign'), array('optional' => true));
|
||||
$mform->setDefault('timelimit', $assigninstance->timelimit);
|
||||
|
||||
// Submit buttons.
|
||||
$mform->addElement('submit', 'resetbutton',
|
||||
get_string('reverttodefaults', 'assign'));
|
||||
@ -343,7 +348,7 @@ class assign_override_form extends moodleform {
|
||||
|
||||
// Ensure that at least one assign setting was changed.
|
||||
$changed = false;
|
||||
$keys = array('duedate', 'cutoffdate', 'allowsubmissionsfromdate');
|
||||
$keys = array('duedate', 'cutoffdate', 'allowsubmissionsfromdate', 'timelimit');
|
||||
foreach ($keys as $key) {
|
||||
if ($data[$key] != $assigninstance->{$key}) {
|
||||
$changed = true;
|
||||
|
@ -96,7 +96,7 @@ if ($overrideid) {
|
||||
}
|
||||
|
||||
// Merge assign defaults with data.
|
||||
$keys = array('duedate', 'cutoffdate', 'allowsubmissionsfromdate');
|
||||
$keys = array('duedate', 'cutoffdate', 'allowsubmissionsfromdate', 'timelimit');
|
||||
foreach ($keys as $key) {
|
||||
if (!isset($data->{$key}) || $reset) {
|
||||
$data->{$key} = $assigninstance->{$key};
|
||||
|
@ -199,6 +199,12 @@ foreach ($overrides as $override) {
|
||||
$values[] = $override->cutoffdate > 0 ? userdate($override->cutoffdate) : get_string('noclose', 'assign');
|
||||
}
|
||||
|
||||
// Format timelimit.
|
||||
if (isset($override->timelimit)) {
|
||||
$fields[] = get_string('timelimit', 'assign');
|
||||
$values[] = $override->timelimit > 0 ? format_time($override->timelimit) : get_string('none', 'assign');
|
||||
}
|
||||
|
||||
// Icons.
|
||||
$iconstr = '';
|
||||
|
||||
|
@ -525,6 +525,8 @@ class assign_grading_summary implements renderable {
|
||||
public $duedate = 0;
|
||||
/** @var int cutoffdate - The assignment cut off date (if one is set) */
|
||||
public $cutoffdate = 0;
|
||||
/** @var int timelimit - The assignment time limit (if one is set) */
|
||||
public $timelimit = 0;
|
||||
/** @var int coursemoduleid - The assignment course module id */
|
||||
public $coursemoduleid = 0;
|
||||
/** @var boolean teamsubmission - Are team submissions enabled for this assignment */
|
||||
@ -557,6 +559,7 @@ class assign_grading_summary implements renderable {
|
||||
* @param int $submissionssubmittedcount
|
||||
* @param int $cutoffdate
|
||||
* @param int $duedate
|
||||
* @param int $timelimit
|
||||
* @param int $coursemoduleid
|
||||
* @param int $submissionsneedgradingcount
|
||||
* @param bool $teamsubmission
|
||||
@ -573,6 +576,7 @@ class assign_grading_summary implements renderable {
|
||||
$submissionssubmittedcount,
|
||||
$cutoffdate,
|
||||
$duedate,
|
||||
$timelimit,
|
||||
$coursemoduleid,
|
||||
$submissionsneedgradingcount,
|
||||
$teamsubmission,
|
||||
@ -588,6 +592,7 @@ class assign_grading_summary implements renderable {
|
||||
$this->submissionssubmittedcount = $submissionssubmittedcount;
|
||||
$this->duedate = $duedate;
|
||||
$this->cutoffdate = $cutoffdate;
|
||||
$this->timelimit = $timelimit;
|
||||
$this->coursemoduleid = $coursemoduleid;
|
||||
$this->submissionsneedgradingcount = $submissionsneedgradingcount;
|
||||
$this->teamsubmission = $teamsubmission;
|
||||
|
@ -151,6 +151,14 @@ if ($ADMIN->fulltree) {
|
||||
$setting->set_advanced_flag_options(admin_setting_flag::ENABLED, false);
|
||||
$settings->add($setting);
|
||||
|
||||
$name = new lang_string('enabletimelimit', 'mod_assign');
|
||||
$description = new lang_string('enabletimelimit_help', 'mod_assign');
|
||||
$setting = new admin_setting_configcheckbox('assign/enabletimelimit',
|
||||
$name,
|
||||
$description,
|
||||
0);
|
||||
$settings->add($setting);
|
||||
|
||||
$name = new lang_string('gradingduedate', 'mod_assign');
|
||||
$description = new lang_string('gradingduedate_help', 'mod_assign');
|
||||
$setting = new admin_setting_configduration('assign/gradingduedate',
|
||||
|
@ -1226,3 +1226,10 @@
|
||||
padding: 0;
|
||||
}
|
||||
/** End of YUI tree fix **/
|
||||
|
||||
/* Countdown timer. */
|
||||
div[id*='mod_assign-timer-'] {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
45
mod/assign/templates/timelimit_panel.mustache
Normal file
45
mod/assign/templates/timelimit_panel.mustache
Normal file
@ -0,0 +1,45 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template mod_assign/timelimit_panel
|
||||
|
||||
Show a timer that counts down to 0.
|
||||
|
||||
Classes required for JS:
|
||||
* None
|
||||
|
||||
Data attibutes required for JS:
|
||||
* None
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"timerstartvalue": 1729000
|
||||
}
|
||||
|
||||
}}
|
||||
<div{{!
|
||||
}} id="mod_assign-timer-{{uniqid}}"{{!
|
||||
}} role="timer" aria-atomic="true"{{!
|
||||
}} aria-relevant="text"{{!
|
||||
}} data-starttime="{{timerstartvalue}}"{{!
|
||||
}}>
|
||||
</div>
|
||||
{{#js}}
|
||||
require(['mod_assign/timer'], function(Timer) {
|
||||
Timer.init("mod_assign-timer-{{uniqid}}");
|
||||
});
|
||||
{{/js}}
|
@ -120,6 +120,65 @@ Feature: Set availability dates for an assignment
|
||||
And I should see "Submitted for grading" in the "Student 1" "table_row"
|
||||
And I should see "2 days 5 hours late" in the "Student 1" "table_row"
|
||||
|
||||
@_file_upload
|
||||
Scenario: Student can submit an assignment before the time limit runs out
|
||||
Given I log in as "admin"
|
||||
And I change the window size to "large"
|
||||
And I set the following administration settings values:
|
||||
| Enable timed assignments | 1 |
|
||||
And I log out
|
||||
And I am on the "Assignment name" Activity page logged in as teacher1
|
||||
And I navigate to "Settings" in current page administration
|
||||
And I follow "Expand all"
|
||||
# Set 'Time limit' to 20 seconds.
|
||||
And I set the field "timelimit[enabled]" to "1"
|
||||
And I set the field "timelimit[number]" to "20"
|
||||
And I set the field "timelimit[timeunit]" to "seconds"
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
|
||||
When I am on the "Assignment name" Activity page logged in as student1
|
||||
And I should see "20 secs" in the "Time limit" "table_row"
|
||||
And "Begin assignment" "link" should exist
|
||||
And I follow "Begin assignment"
|
||||
And I wait "1" seconds
|
||||
And "Begin assignment" "button" should exist
|
||||
And I press "Begin assignment"
|
||||
And I upload "lib/tests/fixtures/empty.txt" file to "File submissions" filemanager
|
||||
When I press "Save changes"
|
||||
Then I should see "Submitted for grading" in the "Submission status" "table_row"
|
||||
And I should see "secs under the time limit" in the "Time remaining" "table_row"
|
||||
|
||||
@_file_upload
|
||||
Scenario: Assignment with time limit and due date shows how late assignment is submitted relative to due date
|
||||
Given I log in as "admin"
|
||||
And I change the window size to "large"
|
||||
And I set the following administration settings values:
|
||||
| Enable timed assignments | 1 |
|
||||
And I log out
|
||||
And I am on the "Assignment name" Activity page logged in as teacher1
|
||||
And I navigate to "Settings" in current page administration
|
||||
And I follow "Expand all"
|
||||
# Set 'Time limit' to 5 seconds.
|
||||
And I set the field "timelimit[enabled]" to "1"
|
||||
And I set the field "timelimit[number]" to "5"
|
||||
And I set the field "timelimit[timeunit]" to "seconds"
|
||||
# Set 'Due date' to 2 days 5 hours 30 minutes ago.
|
||||
And I set the field "Due date" to "##2 days 5 hours 30 minutes ago##"
|
||||
And I press "Save and return to course"
|
||||
And I log out
|
||||
|
||||
When I am on the "Assignment name" Activity page logged in as student1
|
||||
And "Begin assignment" "link" should exist
|
||||
And I follow "Begin assignment"
|
||||
And I wait "1" seconds
|
||||
And "Begin assignment" "button" should exist
|
||||
And I press "Begin assignment"
|
||||
And I wait "5" seconds
|
||||
And I upload "lib/tests/fixtures/empty.txt" file to "File submissions" filemanager
|
||||
When I press "Save changes"
|
||||
Then I should see "Assignment was submitted 2 days 5 hours late" in the "Time remaining" "table_row"
|
||||
|
||||
Scenario: Student cannot submit an assignment after the cut-off date
|
||||
Given I am on the "Assignment name" Activity page logged in as teacher1
|
||||
And I navigate to "Settings" in current page administration
|
||||
|
@ -3660,7 +3660,8 @@ Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
|
||||
'sortorder' => 1,
|
||||
'allowsubmissionsfromdate' => 1,
|
||||
'duedate' => 2,
|
||||
'cutoffdate' => 3
|
||||
'cutoffdate' => 3,
|
||||
'timelimit' => null
|
||||
],
|
||||
(object) [
|
||||
// Override for group 2, lower priority (numerically higher sortorder).
|
||||
@ -3670,7 +3671,8 @@ Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
|
||||
'sortorder' => 2,
|
||||
'allowsubmissionsfromdate' => 5,
|
||||
'duedate' => 6,
|
||||
'cutoffdate' => 6
|
||||
'cutoffdate' => 6,
|
||||
'timelimit' => null
|
||||
],
|
||||
(object) [
|
||||
// User override.
|
||||
@ -3680,7 +3682,8 @@ Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
|
||||
'sortorder' => null,
|
||||
'allowsubmissionsfromdate' => 7,
|
||||
'duedate' => 8,
|
||||
'cutoffdate' => 9
|
||||
'cutoffdate' => 9,
|
||||
'timelimit' => null
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -25,5 +25,5 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'mod_assign'; // Full name of the plugin (used for diagnostics).
|
||||
$plugin->version = 2021093000; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2021110900; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2021052500; // Requires this Moodle version.
|
||||
|
Loading…
x
Reference in New Issue
Block a user