mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 16:36:37 +02:00
MDL-52954 assign: Rebuild the assignment single grade page.
This commit is contained in:
parent
2a3647bae5
commit
bb690849c9
86 changed files with 4593 additions and 279 deletions
|
@ -651,7 +651,7 @@ class gradingform_guide_renderer extends plugin_renderer_base {
|
|||
'name' => 'showmarkerdesc',
|
||||
'value' => "true")+$checked1);
|
||||
$radio1 = html_writer::tag('label', $radio1);
|
||||
$radio2 .= html_writer::tag('input', get_string('hidemarkerdesc', 'gradingform_guide'), array('type' => 'radio',
|
||||
$radio2 = html_writer::tag('input', get_string('hidemarkerdesc', 'gradingform_guide'), array('type' => 'radio',
|
||||
'name' => 'showmarkerdesc',
|
||||
'value' => "false")+$checked2);
|
||||
$radio2 = html_writer::tag('label', $radio2);
|
||||
|
|
2
lib/amd/build/form-autocomplete.min.js
vendored
2
lib/amd/build/form-autocomplete.min.js
vendored
File diff suppressed because one or more lines are too long
1
lib/amd/build/tooltip.min.js
vendored
Normal file
1
lib/amd/build/tooltip.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery"],function(a){var b=function(b){this._regionSelector=b,a(this._regionSelector).each(function(b,c){var d=a(c).attr("aria-describedby");if(d){var e=document.getElementById(d);if(e){var f="tooltip"==a(e).attr("role");f&&(a(e).hide(),a(c).attr("tabindex","0")),a(c).on("focus",this._handleFocus.bind(this)),a(c).on("mouseover",this._handleMouseOver.bind(this)),a(c).on("mouseout",this._handleMouseOut.bind(this)),a(c).on("blur",this._handleBlur.bind(this)),a(c).on("keydown",this._handleKeyDown.bind(this))}}}.bind(this))};return b.prototype._regionSelector=null,b.prototype._showTooltip=function(b){var c=a(b.target),d=c.attr("aria-describedby");if(d){var e=a(document.getElementById(d));if(e.show(),e.attr("aria-hidden","false"),!e.is(".tooltip")){var f=a('<div class="tooltip-inner"></div>');f.append(e.contents()),e.append(f),e.addClass("tooltip"),e.addClass("bottom"),e.append('<div class="tooltip-arrow"></div>')}var g=c.offset();g.top+=c.height()+10,a(e).offset(g)}},b.prototype._hideTooltip=function(b){var c=a(b.target),d=c.attr("aria-describedby");if(d){var e=document.getElementById(d);a(e).hide(),a(e).attr("aria-hidden","true")}},b.prototype._handleFocus=function(a){this._showTooltip(a)},b.prototype._handleKeyDown=function(a){27==a.which&&this._hideTooltip(a)},b.prototype._handleMouseOver=function(a){this._showTooltip(a)},b.prototype._handleMouseOut=function(b){var c=a(b.target);c.is(":focus")||this._hideTooltip(b)},b.prototype._handleBlur=function(a){this._hideTooltip(a)},b});
|
|
@ -775,8 +775,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
|||
throttleTimeout = window.setTimeout(handler.bind(this, e), 300);
|
||||
};
|
||||
// Trigger an ajax update after the text field value changes.
|
||||
inputElement.on("input keypress", throttledHandler);
|
||||
|
||||
inputElement.on("input", throttledHandler);
|
||||
var arrowElement = $(document.getElementById(state.downArrowId));
|
||||
arrowElement.on("click", handler);
|
||||
});
|
||||
|
|
133
lib/amd/src/tooltip.js
Normal file
133
lib/amd/src/tooltip.js
Normal file
|
@ -0,0 +1,133 @@
|
|||
define(['jquery'], function($) {
|
||||
|
||||
/**
|
||||
* Tooltip class.
|
||||
*
|
||||
* @param {String} selector The css selector for the node(s) to enhance with tooltips.
|
||||
*/
|
||||
var Tooltip = function(selector) {
|
||||
// Tooltip code matches: http://www.w3.org/WAI/PF/aria-practices/#tooltip
|
||||
this._regionSelector = selector;
|
||||
|
||||
// For each node matching the selector - find an aria-describedby attribute pointing to an role="tooltip" element.
|
||||
|
||||
$(this._regionSelector).each(function(index, element) {
|
||||
var tooltipId = $(element).attr('aria-describedby');
|
||||
if (tooltipId) {
|
||||
var tooltipele = document.getElementById(tooltipId);
|
||||
if (tooltipele) {
|
||||
var correctRole = $(tooltipele).attr('role') == 'tooltip';
|
||||
|
||||
if (correctRole) {
|
||||
$(tooltipele).hide();
|
||||
// Ensure the trigger for the tooltip is keyboard focusable.
|
||||
$(element).attr('tabindex', '0');
|
||||
}
|
||||
|
||||
// Attach listeners.
|
||||
$(element).on('focus', this._handleFocus.bind(this));
|
||||
$(element).on('mouseover', this._handleMouseOver.bind(this));
|
||||
$(element).on('mouseout', this._handleMouseOut.bind(this));
|
||||
$(element).on('blur', this._handleBlur.bind(this));
|
||||
$(element).on('keydown', this._handleKeyDown.bind(this));
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/** @type {String} Selector for the page region containing the user navigation. */
|
||||
Tooltip.prototype._regionSelector = null;
|
||||
|
||||
/**
|
||||
* Find the tooltip referred to by this element and show it.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
Tooltip.prototype._showTooltip = function(e) {
|
||||
var triggerElement = $(e.target);
|
||||
var tooltipId = triggerElement.attr('aria-describedby');
|
||||
if (tooltipId) {
|
||||
var tooltipele = $(document.getElementById(tooltipId));
|
||||
|
||||
tooltipele.show();
|
||||
tooltipele.attr('aria-hidden', 'false');
|
||||
|
||||
if (!tooltipele.is('.tooltip')) {
|
||||
// Change the markup to a bootstrap tooltip.
|
||||
var inner = $('<div class="tooltip-inner"></div>');
|
||||
inner.append(tooltipele.contents());
|
||||
tooltipele.append(inner);
|
||||
tooltipele.addClass('tooltip');
|
||||
tooltipele.addClass('bottom');
|
||||
tooltipele.append('<div class="tooltip-arrow"></div>');
|
||||
}
|
||||
var pos = triggerElement.offset();
|
||||
pos.top += triggerElement.height() + 10;
|
||||
$(tooltipele).offset(pos);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the tooltip referred to by this element and hide it.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
Tooltip.prototype._hideTooltip = function(e) {
|
||||
var triggerElement = $(e.target);
|
||||
var tooltipId = triggerElement.attr('aria-describedby');
|
||||
if (tooltipId) {
|
||||
var tooltipele = document.getElementById(tooltipId);
|
||||
|
||||
$(tooltipele).hide();
|
||||
$(tooltipele).attr('aria-hidden', 'true');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener for focus events.
|
||||
* @param {Event} e
|
||||
*/
|
||||
Tooltip.prototype._handleFocus = function(e) {
|
||||
this._showTooltip(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener for keydown events.
|
||||
* @param {Event} e
|
||||
*/
|
||||
Tooltip.prototype._handleKeyDown = function(e) {
|
||||
if (e.which == 27) {
|
||||
this._hideTooltip(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener for mouseover events.
|
||||
* @param {Event} e
|
||||
*/
|
||||
Tooltip.prototype._handleMouseOver = function(e) {
|
||||
this._showTooltip(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener for mouseout events.
|
||||
* @param {Event} e
|
||||
*/
|
||||
Tooltip.prototype._handleMouseOut = function(e) {
|
||||
var triggerElement = $(e.target);
|
||||
|
||||
if (!triggerElement.is(":focus")) {
|
||||
this._hideTooltip(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener for blur events.
|
||||
* @param {Event} e
|
||||
*/
|
||||
Tooltip.prototype._handleBlur = function(e) {
|
||||
this._hideTooltip(e);
|
||||
};
|
||||
|
||||
return Tooltip;
|
||||
});
|
|
@ -428,6 +428,7 @@ $functions = array(
|
|||
'classpath' => 'user/externallib.php',
|
||||
'description' => 'Retrieve users information for a specified unique field - If you want to do a user search, use core_user_get_users()',
|
||||
'type' => 'read',
|
||||
'ajax' => true,
|
||||
'capabilities'=> 'moodle/user:viewdetails, moodle/user:viewhiddendetails, moodle/course:useremail, moodle/user:update',
|
||||
),
|
||||
|
||||
|
|
|
@ -53,6 +53,9 @@ class file_storage {
|
|||
private $dirpermissions;
|
||||
/** @var int Permissions for new files */
|
||||
private $filepermissions;
|
||||
/** @var array List of formats supported by unoconv */
|
||||
private $unoconvformats;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor - do not use directly use {@link get_file_storage()} call instead.
|
||||
|
@ -206,7 +209,7 @@ class file_storage {
|
|||
$this->unoconvformats = array_unique($this->unoconvformats);
|
||||
}
|
||||
|
||||
$sanitized = trim(strtolower($format));
|
||||
$sanitized = trim(core_text::strtolower($format));
|
||||
return in_array($sanitized, $this->unoconvformats);
|
||||
}
|
||||
|
||||
|
@ -226,7 +229,7 @@ class file_storage {
|
|||
return false;
|
||||
}
|
||||
|
||||
$fileextension = strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
|
||||
$fileextension = core_text::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
|
||||
if (!self::is_format_supported_by_unoconv($fileextension)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -260,7 +263,6 @@ class file_storage {
|
|||
$output = null;
|
||||
$currentdir = getcwd();
|
||||
chdir($tmp);
|
||||
$result = exec('env 1>&2', $output);
|
||||
$result = exec($cmd, $output);
|
||||
chdir($currentdir);
|
||||
if (!file_exists($newtmpfile)) {
|
||||
|
|
|
@ -278,7 +278,7 @@ abstract class moodleform {
|
|||
*/
|
||||
function _process_submission($method) {
|
||||
$submission = array();
|
||||
if (!empty($this->_ajaxformdata) && defined('AJAX_SCRIPT')) {
|
||||
if (!empty($this->_ajaxformdata)) {
|
||||
$submission = $this->_ajaxformdata;
|
||||
} else if ($method == 'post') {
|
||||
if (!empty($_POST)) {
|
||||
|
|
|
@ -42,7 +42,10 @@ information provided here is intended especially for developers.
|
|||
- upgrade_course_modules_sequences()
|
||||
- upgrade_grade_item_fix_sortorder()
|
||||
- upgrade_availability_item()
|
||||
|
||||
* A new parameter $ajaxformdata was added to the constructor for moodleform. When building a
|
||||
moodleform in a webservice or ajax script (for example using the new fragments API) we
|
||||
cannot allow the moodleform to parse it's own data from _GET and _POST - we must pass it as
|
||||
an array.
|
||||
* Plugins can extend the navigation for user by declaring the following callback:
|
||||
<frankenstyle>_extend_navigation_user(navigation_node $parentnode, stdClass $user,
|
||||
context_user $context, stdClass $course,
|
||||
|
|
1
mod/assign/amd/build/grading_actions.min.js
vendored
Normal file
1
mod/assign/amd/build/grading_actions.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery"],function(a){var b=function(b){this._regionSelector=b,this._region=a(b),a(document).on("user-changed",this._showActionsForm.bind(this)),this._region.find('[name="savechanges"]').on("click",this._trigger.bind(this,"save-changes")),this._region.find('[name="resetbutton"]').on("click",this._trigger.bind(this,"reset")),this._region.find("form").on("submit",function(a){a.preventDefault()})};return b.prototype._regionSelector=null,b.prototype._lastUserId=0,b.prototype._region=null,b.prototype._showActionsForm=function(a,b){var c=this._region.find("[data-region=grading-actions-form]");b!=this._lastUserId&&b>0&&(this._lastUserId=b),b>0?c.removeClass("hide"):c.addClass("hide")},b.prototype._trigger=function(b){a(document).trigger(b)},b});
|
1
mod/assign/amd/build/grading_form_change_checker.min.js
vendored
Normal file
1
mod/assign/amd/build/grading_form_change_checker.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery"],function(a){return{saveFormState:function(b){a(b).trigger("save-form-state");var c=a(b).serialize();a(b).data("saved-form-state",c)},checkFormForChanges:function(b){a(b).trigger("save-form-state");var c=a(b).serialize(),d=a(b).data("saved-form-state");return"undefined"==typeof d?!1:d!=c}}});
|
1
mod/assign/amd/build/grading_navigation.min.js
vendored
Normal file
1
mod/assign/amd/build/grading_navigation.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
mod/assign/amd/build/grading_navigation_user_info.min.js
vendored
Normal file
1
mod/assign/amd/build/grading_navigation_user_info.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery","core/notification","core/ajax","core/templates"],function(a,b,c,d){var e=function(b){this._regionSelector=b,this._region=a(b),this._userCache=[],a(document).on("user-changed",this._refreshUserInfo.bind(this))};return e.prototype._regionSelector=null,e.prototype._userCache=null,e.prototype._region=null,e.prototype._lastUserId=0,e.prototype._refreshUserInfo=function(e,f){var g=a.Deferred();this._lastUserId!=f&&(this._lastUserId=f,d.render("mod_assign/loading",{}).done(function(e,h){if(this._region.fadeOut("fast",function(){d.replaceNodeContents(this._region,e,h),this._region.fadeIn("fast")}.bind(this)),0>f)return void d.render("mod_assign/grading_navigation_no_users",{}).done(function(a,b){this._region.fadeOut("fast",function(){d.replaceNodeContents(this._region,a,b),this._region.fadeIn("fast")}.bind(this))}.bind(this)).fail(b.exception);if("undefined"!=typeof this._userCache[f])g.resolve(this._userCache[f]);else{var i=c.call([{methodname:"core_user_get_users_by_field",args:{field:"id",values:[f]}}]);i[0].done(function(b){b.length<1?g.reject("No users"):(a.each(b,function(a,b){this._userCache[b.id]=b}.bind(this)),g.resolve(this._userCache[f]))}.bind(this)).fail(b.exception)}g.done(function(c){var e=a("[data-showuseridentity]").data("showuseridentity").split(","),f=[];c.courseid=a('[data-region="grading-navigation-panel"]').attr("data-courseid"),a.each(e,function(a,b){"undefined"!=typeof c[b]&&""!==c[b]&&(c.hasidentity=!0,f.push(c[b]))}),c.identity=f.join(", "),d.render("mod_assign/grading_navigation_user_summary",c).done(function(a,b){this._region.fadeOut("fast",function(){d.replaceNodeContents(this._region,a,b),this._region.fadeIn("fast")}.bind(this))}.bind(this)).fail(b.exception)}.bind(this)).fail(function(){d.render("mod_assign/grading_navigation_no_users",{}).done(function(a,b){this._region.fadeOut("fast",function(){d.replaceNodeContents(this._region,a,b),this._region.fadeIn("fast")}.bind(this))}.bind(this)).fail(b.exception)})}.bind(this)).fail(b.exception))},e});
|
1
mod/assign/amd/build/grading_panel.min.js
vendored
Normal file
1
mod/assign/amd/build/grading_panel.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery","core/notification","core/templates","core/fragment","core/ajax","core/str","mod_assign/grading_form_change_checker"],function(a,b,c,d,e,f,g){var h=function(b){this._regionSelector=b,this._region=a(b),this._userCache=[],a(document).on("user-changed",this._refreshGradingPanel.bind(this)),a(document).on("save-changes",this._submitForm.bind(this)),a(document).on("reset",this._resetForm.bind(this)),a(document).on("save-form-state",this._saveFormState.bind(this))};return h.prototype._regionSelector=null,h.prototype._lastUserId=0,h.prototype._lastAttemptNumber=-1,h.prototype._region=null,h.prototype._niceReplaceNodeContents=function(b,d,e){var f=a.Deferred();return b.fadeOut("fast",function(){c.replaceNodeContents(b,d,e),b.fadeIn("fast",function(){f.resolve()})}),f.promise()},h.prototype._saveFormState=function(){"undefined"!=typeof window.tinyMCE&&window.tinyMCE.triggerSave();var b=a('[data-region="grading-actions-form"] [name="sendstudentnotifications"]').val();a('.gradeform [name="sendstudentnotifications"]').val(b)},h.prototype._submitForm=function(c,d){var f=a(this._region.find("form.gradeform"));a('[data-region="overlay"]').show(),f.trigger("save-form-state");var g=f.serialize(),h=this._region.attr("data-assignmentid");e.call([{methodname:"mod_assign_submit_grading_form",args:{assignmentid:h,userid:this._lastUserId,jsonformdata:JSON.stringify(g)},done:this._handleFormSubmissionResponse.bind(this,g,d),fail:b.exception}])},h.prototype._handleFormSubmissionResponse=function(c,d,e){"undefined"==typeof d&&(d=this._lastUserId),e.length?a(document).trigger("reset",[this._lastUserId,c]):(f.get_strings([{key:"changessaved",component:"core"},{key:"gradechangessaveddetail",component:"mod_assign"}]).done(function(a){b.alert(a[0],a[1])}).fail(b.exception),d==this._lastUserId?a(document).trigger("reset",d):a(document).trigger("user-changed",d)),a('[data-region="overlay"]').hide()},h.prototype._resetForm=function(b,c,d){var e=a.Event("custom");"undefined"==typeof c&&(c=this._lastUserId),this._lastUserId=0,this._refreshGradingPanel(e,c,d)},h.prototype._chooseAttempt=function(c){var d=a(c.target),e=d.data("submissions"),g=a(document.getElementById(e)),h=g.clone(),i=h.wrap(a("<form/>")).html();f.get_strings([{key:"viewadifferentattempt",component:"mod_assign"},{key:"view",component:"core"},{key:"cancel",component:"core"}]).done(function(c){b.confirm(c[0],i,c[1],c[2],function(){var b=a("input:radio[name='select-attemptnumber']:checked").val();this._refreshGradingPanel(null,this._lastUserId,"",b)}.bind(this))}.bind(this)).fail(b.exception)},h.prototype._addPopoutButtons=function(d){var e=a(d);c.render("mod_assign/popout_button",{}).done(function(a){e.find(".fitem_ffilemanager .fitemtitle").append(a),e.find(".fitem_feditor .fitemtitle").append(a),e.find(".fitem_f .fitemtitle").append(a),e.on("click",'[data-region="popout-button"]',this._togglePopout.bind(this))}.bind(this)).fail(b.exception)},h.prototype._togglePopout=function(b){b.preventDefault();var c=a(b.target).closest(".fitem");c.hasClass("popout")?a(".popout").removeClass("popout"):(a(".popout").removeClass("popout"),c.addClass("popout"),c.addClass("moodle-has-zindex"))},h.prototype._refreshGradingPanel=function(e,f,h,i){var j=this._region.attr("data-contextid");"undefined"==typeof h&&(h=""),"undefined"==typeof i&&(i=-1),(this._lastUserId!=f||this._lastAttemptNumber!=i||""!==h)&&(this._lastUserId=f,this._lastAttemptNumber=i,a(document).trigger("start-loading-user"),window.M.util.js_pending("mod-assign-loading-user"),c.render("mod_assign/loading",{}).done(function(c,e){this._niceReplaceNodeContents(this._region,c,e).done(function(){if(f>0){this._region.show();var c={userid:f,attemptnumber:i,jsonformdata:JSON.stringify(h)};d.loadFragment("mod_assign","gradingpanel",j,c).done(function(c,d){this._niceReplaceNodeContents(this._region,c,d).done(function(){g.saveFormState('[data-region="grade-panel"] .gradeform'),a('[data-region="attempt-chooser"]').on("click",this._chooseAttempt.bind(this)),this._addPopoutButtons('[data-region="grade-panel"] .gradeform'),a(document).trigger("finish-loading-user"),window.M.util.js_complete("mod-assign-loading-user")}.bind(this)).fail(b.exception)}.bind(this)).fail(b.exception)}else{this._region.hide();var e=a('[data-region="review-panel"]');e.length&&this._niceReplaceNodeContents(e,"",""),a(document).trigger("finish-loading-user"),window.M.util.js_complete("mod-assign-loading-user")}}.bind(this))}.bind(this)).fail(b.exception))},h});
|
1
mod/assign/amd/build/grading_review_panel.min.js
vendored
Normal file
1
mod/assign/amd/build/grading_review_panel.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["jquery"],function(a){var b=function(){this._region=a('[data-region="review-panel"]')};return b.prototype._region=null,b.prototype.getReviewPanel=function(a){var b=this._region.data("panel-owner");return"undefined"==typeof b&&this._region.data("review-panel-plugin",a),this._region.data("review-panel-plugin")==a?this._region[0]:!1},b});
|
1
mod/assign/amd/build/participant_selector.min.js
vendored
Normal file
1
mod/assign/amd/build/participant_selector.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
define(["core/ajax","jquery","core/templates"],function(a,b,c){return{processResults:function(a,b){var c=[],d=0;for(d=0;d<b.length;d++)c[d]={value:b[d].id,label:b[d].label};return c},transport:function(d,e,f,g){var h=b(d).attr("data-assignmentid"),i=b('[data-region="configure-filters"] input[type="checkbox"]'),j=[];i.each(function(a,c){j[b(c).attr("name")]=b(c).prop("checked")});var k=a.call([{methodname:"mod_assign_list_participants",args:{assignid:h,groupid:0,filter:e,limit:30}}]);k[0].then(function(a){var d=[],e=b("[data-showuseridentity]").data("showuseridentity").split(",");b.each(a,function(a,f){var g=f,h=[],i=!0;j.filter_submitted&&!f.submitted&&(i=!1),j.filter_notsubmitted&&f.submitted&&(i=!1),j.filter_requiregrading&&!f.requiregrading&&(i=!1),i&&(b.each(e,function(a,b){"undefined"!=typeof f[b]&&""!==f[b]&&(g.hasidentity=!0,h.push(f[b]))}),g.identity=h.join(", "),d.push(c.render("mod_assign/list_participant_user_summary",g)))}),b.when.apply(b.when,d).then(function(){var c=arguments,d=0;b.each(a,function(a,b){b.label=c[d],d++}),f(a)})},g)}}});
|
89
mod/assign/amd/src/grading_actions.js
Normal file
89
mod/assign/amd/src/grading_actions.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* Javascript controller for the "Actions" panel at the bottom of the page.
|
||||
*
|
||||
* @module mod_assign/grading_actions
|
||||
* @package mod_assign
|
||||
* @class GradingActions
|
||||
* @copyright 2016 Damyon Wiese <damyon@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.1
|
||||
*/
|
||||
define(['jquery'], function($) {
|
||||
|
||||
/**
|
||||
* GradingActions class.
|
||||
*
|
||||
* @class GradingActions
|
||||
* @param {String} selector The selector for the page region containing the actions panel.
|
||||
*/
|
||||
var GradingActions = function(selector) {
|
||||
this._regionSelector = selector;
|
||||
this._region = $(selector);
|
||||
|
||||
$(document).on('user-changed', this._showActionsForm.bind(this));
|
||||
|
||||
this._region.find('[name="savechanges"]').on('click', this._trigger.bind(this, 'save-changes'));
|
||||
this._region.find('[name="resetbutton"]').on('click', this._trigger.bind(this, 'reset'));
|
||||
this._region.find('form').on('submit', function(e) { e.preventDefault(); });
|
||||
};
|
||||
|
||||
/** @type {String} Selector for the page region containing the user navigation. */
|
||||
GradingActions.prototype._regionSelector = null;
|
||||
|
||||
/** @type {Integer} Remember the last user id to prevent unnessecary reloads. */
|
||||
GradingActions.prototype._lastUserId = 0;
|
||||
|
||||
/** @type {JQuery} JQuery node for the page region containing the user navigation. */
|
||||
GradingActions.prototype._region = null;
|
||||
|
||||
/**
|
||||
* Show the actions if there is valid user.
|
||||
*
|
||||
* @method _showActionsForm
|
||||
* @private
|
||||
* @param {Event} event
|
||||
* @param {Integer} userid
|
||||
* @return {Deferred} promise resolved when the animations are complete.
|
||||
*/
|
||||
GradingActions.prototype._showActionsForm = function(event, userid) {
|
||||
var form = this._region.find('[data-region=grading-actions-form]');
|
||||
|
||||
if (userid != this._lastUserId && userid > 0) {
|
||||
this._lastUserId = userid;
|
||||
}
|
||||
if (userid > 0) {
|
||||
form.removeClass('hide');
|
||||
} else {
|
||||
form.addClass('hide');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger the named action.
|
||||
*
|
||||
* @method _trigger
|
||||
* @private
|
||||
* @param {String} action
|
||||
*/
|
||||
GradingActions.prototype._trigger = function(action) {
|
||||
$(document).trigger(action);
|
||||
};
|
||||
|
||||
return GradingActions;
|
||||
});
|
60
mod/assign/amd/src/grading_form_change_checker.js
Normal file
60
mod/assign/amd/src/grading_form_change_checker.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* Simple method to check for changes to a form between two points in time.
|
||||
*
|
||||
* @module mod_assign/grading_form_change_checker
|
||||
* @package mod_assign
|
||||
* @copyright 2016 Damyon Wiese <damyon@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.1
|
||||
*/
|
||||
define(['jquery'], function($) {
|
||||
|
||||
return /** @alias module:mod_assign/grading_form_change_checker */ {
|
||||
/**
|
||||
* Save the values in the form to a data attribute so they can be compared later for changes.
|
||||
*
|
||||
* @method saveFormState
|
||||
* @param {String} selector The selector for the form element.
|
||||
*/
|
||||
saveFormState: function(selector) {
|
||||
$(selector).trigger('save-form-state');
|
||||
var data = $(selector).serialize();
|
||||
$(selector).data('saved-form-state', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Compare the current values in the form to the previously saved state.
|
||||
*
|
||||
* @method checkFormForChanges
|
||||
* @param {String} selector The selector for the form element.
|
||||
* @return {Boolean} True if there are changes to the form data.
|
||||
*/
|
||||
checkFormForChanges: function(selector) {
|
||||
|
||||
$(selector).trigger('save-form-state');
|
||||
|
||||
var data = $(selector).serialize(),
|
||||
previousdata = $(selector).data('saved-form-state');
|
||||
|
||||
if (typeof previousdata === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
return (previousdata != data);
|
||||
}
|
||||
};
|
||||
});
|
470
mod/assign/amd/src/grading_navigation.js
Normal file
470
mod/assign/amd/src/grading_navigation.js
Normal file
|
@ -0,0 +1,470 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* Javascript to handle changing users via the user selector in the header.
|
||||
*
|
||||
* @module mod_assign/grading_navigation
|
||||
* @package mod_assign
|
||||
* @copyright 2016 Damyon Wiese <damyon@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.1
|
||||
*/
|
||||
define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
|
||||
'core/ajax', 'mod_assign/grading_form_change_checker'],
|
||||
function($, notification, str, autocomplete, ajax, checker) {
|
||||
|
||||
/**
|
||||
* GradingNavigation class.
|
||||
*
|
||||
* @class GradingNavigation
|
||||
* @param {String} selector The selector for the page region containing the user navigation.
|
||||
*/
|
||||
var GradingNavigation = function(selector) {
|
||||
this._regionSelector = selector;
|
||||
this._region = $(selector);
|
||||
this._filters = [];
|
||||
this._users = [];
|
||||
this._filteredUsers = [];
|
||||
|
||||
// Get the current user list from a webservice.
|
||||
this._loadAllUsers();
|
||||
|
||||
// Attach listeners to the select and arrow buttons.
|
||||
|
||||
this._region.find('[data-action="previous-user"]').on('click', this._handlePreviousUser.bind(this));
|
||||
this._region.find('[data-action="next-user"]').on('click', this._handleNextUser.bind(this));
|
||||
this._region.find('[data-action="change-user"]').on('change', this._handleChangeUser.bind(this));
|
||||
this._region.find('[data-region="user-filters"]').on('click', this._toggleExpandFilters.bind(this));
|
||||
|
||||
$(document).on('user-changed', this._refreshSelector.bind(this));
|
||||
|
||||
// Position the configure filters panel under the link that expands it.
|
||||
var toggleLink = this._region.find('[data-region="user-filters"]');
|
||||
var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));
|
||||
|
||||
configPanel.on('change', '[type="checkbox"]', this._filterChanged.bind(this));
|
||||
|
||||
var userid = $('[data-region="grading-navigation-panel"]').data('first-userid');
|
||||
if (userid) {
|
||||
this._selectUserById(userid);
|
||||
}
|
||||
|
||||
str.get_string('changeuser', 'mod_assign').done(function(s) {
|
||||
autocomplete.enhance('[data-action=change-user]', false, 'mod_assign/participant_selector', s);
|
||||
}.bind(this)
|
||||
).fail(notification.exception);
|
||||
|
||||
// We do not allow navigation while ajax requests are pending.
|
||||
|
||||
$(document).bind("start-loading-user", function(){
|
||||
this._isLoading = true;
|
||||
}.bind(this));
|
||||
$(document).bind("finish-loading-user", function(){
|
||||
this._isLoading = false;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/** @type {Boolean} Boolean tracking active ajax requests. */
|
||||
GradingNavigation.prototype._isLoading = false;
|
||||
|
||||
/** @type {String} Selector for the page region containing the user navigation. */
|
||||
GradingNavigation.prototype._regionSelector = null;
|
||||
|
||||
/** @type {Array} The list of active filter keys */
|
||||
GradingNavigation.prototype._filters = null;
|
||||
|
||||
/** @type {Array} The list of users */
|
||||
GradingNavigation.prototype._users = null;
|
||||
|
||||
/** @type {JQuery} JQuery node for the page region containing the user navigation. */
|
||||
GradingNavigation.prototype._region = null;
|
||||
|
||||
/**
|
||||
* Load the list of all users for this assignment.
|
||||
*
|
||||
* @private
|
||||
* @method _loadAllUsers
|
||||
*/
|
||||
GradingNavigation.prototype._loadAllUsers = function() {
|
||||
var select = this._region.find('[data-action=change-user]');
|
||||
var assignmentid = select.attr('data-assignmentid');
|
||||
var groupid = select.attr('data-groupid');
|
||||
|
||||
ajax.call([{
|
||||
methodname: 'mod_assign_list_participants',
|
||||
args: { assignid: assignmentid, groupid: groupid, filter: '', onlyids: true },
|
||||
done: this._usersLoaded.bind(this),
|
||||
fail: notification.exception
|
||||
}]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Call back to rebuild the user selector and x of y info when the user list is updated.
|
||||
*
|
||||
* @private
|
||||
* @method _usersLoaded
|
||||
* @param {Array} users
|
||||
*/
|
||||
GradingNavigation.prototype._usersLoaded = function(users) {
|
||||
this._filteredUsers = this._users = users;
|
||||
if (this._users.length) {
|
||||
// Position the configure filters panel under the link that expands it.
|
||||
var toggleLink = this._region.find('[data-region="user-filters"]');
|
||||
var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));
|
||||
|
||||
configPanel.find('[type="checkbox"]').trigger('change');
|
||||
} else {
|
||||
this._selectNoUser();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the configure filters panel if a click is detected outside of it.
|
||||
*
|
||||
* @private
|
||||
* @method _checkClickOutsideConfigureFilters
|
||||
* @param {Event}
|
||||
*/
|
||||
GradingNavigation.prototype._checkClickOutsideConfigureFilters = function(event) {
|
||||
var configPanel = this._region.find('[data-region="configure-filters"]');
|
||||
|
||||
if (!configPanel.is(event.target) && configPanel.has(event.target).length === 0) {
|
||||
var toggleLink = this._region.find('[data-region="user-filters"]');
|
||||
|
||||
configPanel.hide();
|
||||
configPanel.attr('aria-hidden', 'true');
|
||||
toggleLink.attr('aria-expanded', 'false');
|
||||
$(document).unbind('click.mod_assign_grading_navigation');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Turn a filter on or off.
|
||||
*
|
||||
* @private
|
||||
* @method _filterChanged
|
||||
* @param {Event}
|
||||
*/
|
||||
GradingNavigation.prototype._filterChanged = function(event) {
|
||||
var name = $(event.target).attr('name');
|
||||
var key = name.split('_').pop();
|
||||
var enabled = $(event.target).prop('checked');
|
||||
|
||||
if (enabled) {
|
||||
if (this._filters.indexOf(key) == -1) {
|
||||
this._filters[this._filters.length] = key;
|
||||
}
|
||||
} else {
|
||||
var index = this._filters.indexOf(key);
|
||||
if (index != -1) {
|
||||
this._filters.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the active filter string.
|
||||
var filterlist = [];
|
||||
this._region.find('[data-region="configure-filters"]').find('[type="checkbox"]').each(function(idx, ele) {
|
||||
if ($(ele).prop('checked')) {
|
||||
filterlist[filterlist.length] = $(ele).closest('label').text();
|
||||
}
|
||||
}.bind(this));
|
||||
if (filterlist.length) {
|
||||
this._region.find('[data-region="user-filters"] span').text(filterlist.join(', '));
|
||||
} else {
|
||||
str.get_string('nofilters', 'mod_assign').done(function(s) {
|
||||
this._region.find('[data-region="user-filters"] span').text(s);
|
||||
}.bind(this)).fail(notification.exception);
|
||||
}
|
||||
|
||||
// Filter the options in the select box that do not match the current filters.
|
||||
|
||||
var select = this._region.find('[data-action=change-user]');
|
||||
var userid = select.attr('data-selected');
|
||||
var foundIndex = 0;
|
||||
|
||||
this._filteredUsers = [];
|
||||
|
||||
$.each(this._users, function(index, user) {
|
||||
var show = true;
|
||||
$.each(this._filters, function(filterindex, filter) {
|
||||
if (filter == "submitted") {
|
||||
if (user.submitted == "0") {
|
||||
show = false;
|
||||
}
|
||||
} else if (filter == "notsubmitted") {
|
||||
if (user.submitted == "1") {
|
||||
show = false;
|
||||
}
|
||||
} else if (filter == "requiregrading") {
|
||||
if (user.requiregrading == "0") {
|
||||
show = false;
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
if (show) {
|
||||
this._filteredUsers[this._filteredUsers.length] = user;
|
||||
if (userid == user.id) {
|
||||
foundIndex = index;
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
if (this._filteredUsers.length) {
|
||||
this._selectUserById(this._filteredUsers[foundIndex].id);
|
||||
} else {
|
||||
this._selectNoUser();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select no users, because no users match the filters.
|
||||
*
|
||||
* @private
|
||||
* @method _selectNoUser
|
||||
*/
|
||||
GradingNavigation.prototype._selectNoUser = function() {
|
||||
// Detect unsaved changes, and offer to save them - otherwise change user right now.
|
||||
if (this._isLoading) {
|
||||
return;
|
||||
}
|
||||
if (checker.checkFormForChanges('[data-region="grade-panel"] .gradeform')) {
|
||||
// Form has changes, so we need to confirm before switching users.
|
||||
str.get_strings([
|
||||
{ key: 'unsavedchanges', component: 'mod_assign' },
|
||||
{ key: 'unsavedchangesquestion', component: 'mod_assign' },
|
||||
{ key: 'saveandcontinue', component: 'mod_assign' },
|
||||
{ key: 'cancel', component: 'core' },
|
||||
]).done(function(strs) {
|
||||
notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {
|
||||
$(document).trigger('save-changes', -1);
|
||||
});
|
||||
}.bind(this));
|
||||
} else {
|
||||
$(document).trigger('user-changed', -1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the specified user by id.
|
||||
*
|
||||
* @private
|
||||
* @method _selectUserById
|
||||
* @param {Number} userid
|
||||
*/
|
||||
GradingNavigation.prototype._selectUserById = function(userid) {
|
||||
var select = this._region.find('[data-action=change-user]');
|
||||
var useridnumber = parseInt(userid, 10);
|
||||
|
||||
// Detect unsaved changes, and offer to save them - otherwise change user right now.
|
||||
if (this._isLoading) {
|
||||
return;
|
||||
}
|
||||
if (checker.checkFormForChanges('[data-region="grade-panel"] .gradeform')) {
|
||||
// Form has changes, so we need to confirm before switching users.
|
||||
str.get_strings([
|
||||
{ key: 'unsavedchanges', component: 'mod_assign' },
|
||||
{ key: 'unsavedchangesquestion', component: 'mod_assign' },
|
||||
{ key: 'saveandcontinue', component: 'mod_assign' },
|
||||
{ key: 'cancel', component: 'core' },
|
||||
]).done(function(strs) {
|
||||
notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {
|
||||
$(document).trigger('save-changes', useridnumber);
|
||||
});
|
||||
}.bind(this));
|
||||
} else {
|
||||
select.attr('data-selected', userid);
|
||||
|
||||
if (!isNaN(useridnumber) && useridnumber > 0) {
|
||||
$(document).trigger('user-changed', userid);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Expand or collapse the filter config panel.
|
||||
*
|
||||
* @private
|
||||
* @method _toggleExpandFilters
|
||||
* @param {Event}
|
||||
*/
|
||||
GradingNavigation.prototype._toggleExpandFilters = function(event) {
|
||||
event.preventDefault();
|
||||
var toggleLink = $(event.target).closest('[data-region="user-filters"]');
|
||||
var expanded = toggleLink.attr('aria-expanded') == 'true';
|
||||
var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));
|
||||
|
||||
if (expanded) {
|
||||
configPanel.hide();
|
||||
configPanel.attr('aria-hidden', 'true');
|
||||
toggleLink.attr('aria-expanded', 'false');
|
||||
$(document).unbind('click.mod_assign_grading_navigation');
|
||||
} else {
|
||||
configPanel.css('display', 'inline-block');
|
||||
configPanel.attr('aria-hidden', 'false');
|
||||
toggleLink.attr('aria-expanded', 'true');
|
||||
event.stopPropagation();
|
||||
$(document).on('click.mod_assign_grading_navigation', this._checkClickOutsideConfigureFilters.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change to the previous user in the grading list.
|
||||
*
|
||||
* @private
|
||||
* @method _handlePreviousUser
|
||||
* @param {Event} e
|
||||
*/
|
||||
GradingNavigation.prototype._handlePreviousUser = function(e) {
|
||||
e.preventDefault();
|
||||
var select = this._region.find('[data-action=change-user]');
|
||||
var currentUserId = select.attr('data-selected');
|
||||
var i = 0, currentIndex = 0;
|
||||
|
||||
for (i = 0; i < this._filteredUsers.length; i++) {
|
||||
if (this._filteredUsers[i].id == currentUserId) {
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var count = this._filteredUsers.length;
|
||||
var newIndex = (currentIndex - 1);
|
||||
if (newIndex < 0) {
|
||||
newIndex = count - 1;
|
||||
}
|
||||
|
||||
if (count) {
|
||||
this._selectUserById(this._filteredUsers[newIndex].id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change to the next user in the grading list.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
GradingNavigation.prototype._handleNextUser = function(e) {
|
||||
e.preventDefault();
|
||||
var select = this._region.find('[data-action=change-user]');
|
||||
var currentUserId = select.attr('data-selected');
|
||||
var i = 0, currentIndex = 0;
|
||||
|
||||
for (i = 0; i < this._filteredUsers.length; i++) {
|
||||
if (this._filteredUsers[i].id == currentUserId) {
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var count = this._filteredUsers.length;
|
||||
var newIndex = (currentIndex + 1) % count;
|
||||
|
||||
if (count) {
|
||||
this._selectUserById(this._filteredUsers[newIndex].id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Rebuild the x of y string.
|
||||
*
|
||||
* @private
|
||||
* @method _refreshCount
|
||||
*/
|
||||
GradingNavigation.prototype._refreshCount = function() {
|
||||
var select = this._region.find('[data-action=change-user]');
|
||||
var userid = select.attr('data-selected');
|
||||
var i = 0;
|
||||
var currentIndex = 0;
|
||||
|
||||
if (isNaN(userid) || userid <= 0) {
|
||||
this._region.find('[data-region="user-count"]').hide();
|
||||
} else {
|
||||
this._region.find('[data-region="user-count"]').show();
|
||||
|
||||
for (i = 0; i < this._filteredUsers.length; i++) {
|
||||
if (this._filteredUsers[i].id == userid) {
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var count = this._filteredUsers.length;
|
||||
if (count) {
|
||||
currentIndex += 1;
|
||||
}
|
||||
var param = { x: currentIndex, y: count };
|
||||
|
||||
str.get_string('xofy', 'mod_assign', param).done(function(s) {
|
||||
this._region.find('[data-region="user-count-summary"]').text(s);
|
||||
}.bind(this)).fail(notification.exception);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond to a user-changed event by updating the selector.
|
||||
*
|
||||
* @private
|
||||
* @method _refreshSelector
|
||||
* @param {Event} event
|
||||
* @param {String} userid
|
||||
*/
|
||||
GradingNavigation.prototype._refreshSelector = function(event, userid) {
|
||||
var select = this._region.find('[data-action=change-user]');
|
||||
userid = parseInt(userid, 10);
|
||||
|
||||
if (!isNaN(userid) && userid > 0) {
|
||||
select.attr('data-selected', userid);
|
||||
}
|
||||
this._refreshCount();
|
||||
};
|
||||
|
||||
/**
|
||||
* Change to a different user in the grading list.
|
||||
*
|
||||
* @private
|
||||
* @method _handleChangeUser
|
||||
* @param {Event}
|
||||
*/
|
||||
GradingNavigation.prototype._handleChangeUser = function() {
|
||||
var select = this._region.find('[data-action=change-user]');
|
||||
var userid = parseInt(select.val(), 10);
|
||||
|
||||
if (this._isLoading) {
|
||||
return;
|
||||
}
|
||||
if (checker.checkFormForChanges('[data-region="grade-panel"] .gradeform')) {
|
||||
// Form has changes, so we need to confirm before switching users.
|
||||
str.get_strings([
|
||||
{ key: 'unsavedchanges', component: 'mod_assign' },
|
||||
{ key: 'unsavedchangesquestion', component: 'mod_assign' },
|
||||
{ key: 'saveandcontinue', component: 'mod_assign' },
|
||||
{ key: 'cancel', component: 'core' },
|
||||
]).done(function(strs) {
|
||||
notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {
|
||||
$(document).trigger('save-changes', userid);
|
||||
});
|
||||
}.bind(this));
|
||||
} else {
|
||||
if (!isNaN(userid) && userid > 0) {
|
||||
select.attr('data-selected', userid);
|
||||
|
||||
$(document).trigger('user-changed', userid);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return GradingNavigation;
|
||||
});
|
147
mod/assign/amd/src/grading_navigation_user_info.js
Normal file
147
mod/assign/amd/src/grading_navigation_user_info.js
Normal file
|
@ -0,0 +1,147 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* Javascript controller for the "User summary" panel at the top of the page.
|
||||
*
|
||||
* @module mod_assign/grading_navigation_user_info
|
||||
* @package mod_assign
|
||||
* @class UserInfo
|
||||
* @copyright 2016 Damyon Wiese <damyon@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.1
|
||||
*/
|
||||
define(['jquery', 'core/notification', 'core/ajax', 'core/templates'], function($, notification, ajax, templates) {
|
||||
|
||||
/**
|
||||
* UserInfo class.
|
||||
*
|
||||
* @class UserInfo
|
||||
* @param {String} selector The selector for the page region containing the user navigation.
|
||||
*/
|
||||
var UserInfo = function(selector) {
|
||||
this._regionSelector = selector;
|
||||
this._region = $(selector);
|
||||
this._userCache = [];
|
||||
|
||||
$(document).on('user-changed', this._refreshUserInfo.bind(this));
|
||||
};
|
||||
|
||||
/** @type {String} Selector for the page region containing the user navigation. */
|
||||
UserInfo.prototype._regionSelector = null;
|
||||
|
||||
/** @type {Array} Cache of user info contexts. */
|
||||
UserInfo.prototype._userCache = null;
|
||||
|
||||
/** @type {JQuery} JQuery node for the page region containing the user navigation. */
|
||||
UserInfo.prototype._region = null;
|
||||
|
||||
/** @type {Integer} Remember the last user id to prevent unnessecary reloads. */
|
||||
UserInfo.prototype._lastUserId = 0;
|
||||
|
||||
/**
|
||||
* Get the user context - re-render the template in the page.
|
||||
*
|
||||
* @private
|
||||
* @method _refreshUserInfo
|
||||
* @param {Event} event
|
||||
* @param {Number} userid
|
||||
*/
|
||||
UserInfo.prototype._refreshUserInfo = function(event, userid) {
|
||||
var promise = $.Deferred();
|
||||
|
||||
// Skip reloading if it is the same user.
|
||||
if (this._lastUserId == userid) {
|
||||
return;
|
||||
}
|
||||
this._lastUserId = userid;
|
||||
|
||||
// First insert the loading template.
|
||||
templates.render('mod_assign/loading', {}).done(function(html, js) {
|
||||
// Update the page.
|
||||
this._region.fadeOut("fast", function() {
|
||||
templates.replaceNodeContents(this._region, html, js);
|
||||
this._region.fadeIn("fast");
|
||||
}.bind(this));
|
||||
|
||||
if (userid < 0) {
|
||||
// Render the template.
|
||||
templates.render('mod_assign/grading_navigation_no_users', {}).done(function(html, js) {
|
||||
// Update the page.
|
||||
this._region.fadeOut("fast", function() {
|
||||
templates.replaceNodeContents(this._region, html, js);
|
||||
this._region.fadeIn("fast");
|
||||
}.bind(this));
|
||||
}.bind(this)).fail(notification.exception);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this._userCache[userid] !== "undefined") {
|
||||
promise.resolve(this._userCache[userid]);
|
||||
} else {
|
||||
// Load context from ajax.
|
||||
var requests = ajax.call([{
|
||||
methodname: 'core_user_get_users_by_field',
|
||||
args: { field: 'id', values: [ userid ] }
|
||||
}]);
|
||||
|
||||
requests[0].done(function(result) {
|
||||
if (result.length < 1) {
|
||||
promise.reject('No users');
|
||||
} else {
|
||||
$.each(result, function(index, user) {
|
||||
this._userCache[user.id] = user;
|
||||
}.bind(this));
|
||||
promise.resolve(this._userCache[userid]);
|
||||
}
|
||||
}.bind(this)).fail(notification.exception);
|
||||
}
|
||||
|
||||
promise.done(function(context) {
|
||||
var identityfields = $('[data-showuseridentity]').data('showuseridentity').split(','),
|
||||
identity = [];
|
||||
// Render the template.
|
||||
context.courseid = $('[data-region="grading-navigation-panel"]').attr('data-courseid');
|
||||
// Build a string for the visible identity fields listed in showuseridentity config setting.
|
||||
$.each(identityfields, function(i, k) {
|
||||
if (typeof context[k] !== 'undefined' && context[k] !== '') {
|
||||
context.hasidentity = true;
|
||||
identity.push(context[k]);
|
||||
}
|
||||
});
|
||||
context.identity = identity.join(', ');
|
||||
|
||||
templates.render('mod_assign/grading_navigation_user_summary', context).done(function(html, js) {
|
||||
// Update the page.
|
||||
this._region.fadeOut("fast", function() {
|
||||
templates.replaceNodeContents(this._region, html, js);
|
||||
this._region.fadeIn("fast");
|
||||
}.bind(this));
|
||||
}.bind(this)).fail(notification.exception);
|
||||
}.bind(this)).fail(function() {
|
||||
// Render the template.
|
||||
templates.render('mod_assign/grading_navigation_no_users', {}).done(function(html, js) {
|
||||
// Update the page.
|
||||
this._region.fadeOut("fast", function() {
|
||||
templates.replaceNodeContents(this._region, html, js);
|
||||
this._region.fadeIn("fast");
|
||||
}.bind(this));
|
||||
}.bind(this)).fail(notification.exception);
|
||||
});
|
||||
}.bind(this)).fail(notification.exception);
|
||||
};
|
||||
|
||||
return UserInfo;
|
||||
});
|
307
mod/assign/amd/src/grading_panel.js
Normal file
307
mod/assign/amd/src/grading_panel.js
Normal file
|
@ -0,0 +1,307 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* Javascript controller for the "Grading" panel at the right of the page.
|
||||
*
|
||||
* @module mod_assign/grading_panel
|
||||
* @package mod_assign
|
||||
* @class GradingPanel
|
||||
* @copyright 2016 Damyon Wiese <damyon@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.1
|
||||
*/
|
||||
define(['jquery', 'core/notification', 'core/templates', 'core/fragment',
|
||||
'core/ajax', 'core/str', 'mod_assign/grading_form_change_checker'],
|
||||
function($, notification, templates, fragment, ajax, str, checker) {
|
||||
|
||||
/**
|
||||
* GradingPanel class.
|
||||
*
|
||||
* @class GradingPanel
|
||||
* @param {String} selector The selector for the page region containing the user navigation.
|
||||
*/
|
||||
var GradingPanel = function(selector) {
|
||||
this._regionSelector = selector;
|
||||
this._region = $(selector);
|
||||
this._userCache = [];
|
||||
|
||||
$(document).on('user-changed', this._refreshGradingPanel.bind(this));
|
||||
$(document).on('save-changes', this._submitForm.bind(this));
|
||||
$(document).on('reset', this._resetForm.bind(this));
|
||||
|
||||
$(document).on('save-form-state', this._saveFormState.bind(this));
|
||||
};
|
||||
|
||||
/** @type {String} Selector for the page region containing the user navigation. */
|
||||
GradingPanel.prototype._regionSelector = null;
|
||||
|
||||
/** @type {Integer} Remember the last user id to prevent unnessecary reloads. */
|
||||
GradingPanel.prototype._lastUserId = 0;
|
||||
|
||||
/** @type {Integer} Remember the last attempt number to prevent unnessecary reloads. */
|
||||
GradingPanel.prototype._lastAttemptNumber = -1;
|
||||
|
||||
/** @type {JQuery} JQuery node for the page region containing the user navigation. */
|
||||
GradingPanel.prototype._region = null;
|
||||
|
||||
/**
|
||||
* Fade the dom node out, update it, and fade it back.
|
||||
*
|
||||
* @private
|
||||
* @method _niceReplaceNodeContents
|
||||
* @param {JQuery} node
|
||||
* @param {String} html
|
||||
* @param {String} js
|
||||
* @return {Deferred} promise resolved when the animations are complete.
|
||||
*/
|
||||
GradingPanel.prototype._niceReplaceNodeContents = function(node, html, js) {
|
||||
var promise = $.Deferred();
|
||||
|
||||
node.fadeOut("fast", function() {
|
||||
templates.replaceNodeContents(node, html, js);
|
||||
node.fadeIn("fast", function() {
|
||||
promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
return promise.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Make sure all form fields have the latest saved state.
|
||||
* @private
|
||||
* @method _saveFormState
|
||||
*/
|
||||
GradingPanel.prototype._saveFormState = function() {
|
||||
// Grrrrr! TinyMCE you know what you did.
|
||||
if (typeof window.tinyMCE !== 'undefined') {
|
||||
window.tinyMCE.triggerSave();
|
||||
}
|
||||
|
||||
// Copy data from notify students checkbox which was moved out of the form.
|
||||
var checked = $('[data-region="grading-actions-form"] [name="sendstudentnotifications"]').val();
|
||||
$('.gradeform [name="sendstudentnotifications"]').val(checked);
|
||||
};
|
||||
|
||||
/**
|
||||
* Make form submit via ajax.
|
||||
*
|
||||
* @private
|
||||
* @method _submitForm
|
||||
*/
|
||||
GradingPanel.prototype._submitForm = function(event, nextUserId) {
|
||||
// The form was submitted - send it via ajax instead.
|
||||
var form = $(this._region.find('form.gradeform'));
|
||||
|
||||
$('[data-region="overlay"]').show();
|
||||
|
||||
// We call this, so other modules can update the form with the latest state.
|
||||
form.trigger('save-form-state');
|
||||
|
||||
// Now we get all the current values from the form.
|
||||
var data = form.serialize();
|
||||
var assignmentid = this._region.attr('data-assignmentid');
|
||||
|
||||
// Now we can continue...
|
||||
ajax.call([{
|
||||
methodname: 'mod_assign_submit_grading_form',
|
||||
args: {assignmentid: assignmentid, userid: this._lastUserId, jsonformdata: JSON.stringify(data)},
|
||||
done: this._handleFormSubmissionResponse.bind(this, data, nextUserId),
|
||||
fail: notification.exception
|
||||
}]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle form submission response.
|
||||
*
|
||||
* @private
|
||||
* @method _handleFormSubmissionResponse
|
||||
* @param {Array} formdata - submitted values
|
||||
* @param {Integer} nextUserId - optional. The id of the user to load after the form is saved.
|
||||
* @param {Array} response List of errors.
|
||||
*/
|
||||
GradingPanel.prototype._handleFormSubmissionResponse = function(formdata, nextUserId, response) {
|
||||
if (typeof nextUserId === "undefined") {
|
||||
nextUserId = this._lastUserId;
|
||||
}
|
||||
if (response.length) {
|
||||
// There was an error saving the grade. Re-render the form using the submitted data so we can show
|
||||
// validation errors.
|
||||
$(document).trigger('reset', [this._lastUserId, formdata]);
|
||||
} else {
|
||||
str.get_strings([
|
||||
{ key: 'changessaved', component: 'core' },
|
||||
{ key: 'gradechangessaveddetail', component: 'mod_assign' },
|
||||
]).done(function(strs) {
|
||||
notification.alert(strs[0], strs[1]);
|
||||
}).fail(notification.exception);
|
||||
if (nextUserId == this._lastUserId) {
|
||||
$(document).trigger('reset', nextUserId);
|
||||
} else {
|
||||
$(document).trigger('user-changed', nextUserId);
|
||||
}
|
||||
}
|
||||
$('[data-region="overlay"]').hide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh form with default values.
|
||||
*
|
||||
* @private
|
||||
* @method _resetForm
|
||||
* @param {Event} e
|
||||
* @param {Integer} userid
|
||||
* @param {Array} formdata
|
||||
*/
|
||||
GradingPanel.prototype._resetForm = function(e, userid, formdata) {
|
||||
// The form was cancelled - refresh with default values.
|
||||
var event = $.Event("custom");
|
||||
if (typeof userid == "undefined") {
|
||||
userid = this._lastUserId;
|
||||
}
|
||||
this._lastUserId = 0;
|
||||
this._refreshGradingPanel(event, userid, formdata);
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a picker to choose an older attempt.
|
||||
*
|
||||
* @private
|
||||
* @method _chooseAttempt
|
||||
*/
|
||||
GradingPanel.prototype._chooseAttempt = function(e) {
|
||||
// Show a dialog.
|
||||
|
||||
// The form is in the element pointed to by data-submissions.
|
||||
var link = $(e.target);
|
||||
var submissionsId = link.data('submissions');
|
||||
var submissionsform = $(document.getElementById(submissionsId));
|
||||
var formcopy = submissionsform.clone();
|
||||
var formhtml = formcopy.wrap($('<form/>')).html();
|
||||
|
||||
str.get_strings([
|
||||
{ key: 'viewadifferentattempt', component: 'mod_assign' },
|
||||
{ key: 'view', component: 'core' },
|
||||
{ key: 'cancel', component: 'core' },
|
||||
]).done(function(strs) {
|
||||
notification.confirm(strs[0], formhtml, strs[1], strs[2], function() {
|
||||
var attemptnumber = $("input:radio[name='select-attemptnumber']:checked").val();
|
||||
|
||||
this._refreshGradingPanel(null, this._lastUserId, '', attemptnumber);
|
||||
}.bind(this));
|
||||
}.bind(this)).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add popout buttons
|
||||
*
|
||||
* @private
|
||||
* @method _addPopoutButtons
|
||||
* @param {JQuery} region The region to add popout buttons to.
|
||||
*/
|
||||
GradingPanel.prototype._addPopoutButtons = function(selector) {
|
||||
var region = $(selector);
|
||||
|
||||
templates.render('mod_assign/popout_button', {}).done(function(html) {
|
||||
region.find('.fitem_ffilemanager .fitemtitle').append(html);
|
||||
region.find('.fitem_feditor .fitemtitle').append(html);
|
||||
region.find('.fitem_f .fitemtitle').append(html);
|
||||
|
||||
region.on('click', '[data-region="popout-button"]', this._togglePopout.bind(this));
|
||||
}.bind(this)).fail(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a div "popout" or "popback".
|
||||
*
|
||||
* @private
|
||||
* @method _togglePopout
|
||||
* @param {Event} event
|
||||
*/
|
||||
GradingPanel.prototype._togglePopout = function(event) {
|
||||
event.preventDefault();
|
||||
var container = $(event.target).closest('.fitem');
|
||||
if (container.hasClass('popout')) {
|
||||
$('.popout').removeClass('popout');
|
||||
} else {
|
||||
$('.popout').removeClass('popout');
|
||||
container.addClass('popout');
|
||||
container.addClass('moodle-has-zindex');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the user context - re-render the template in the page.
|
||||
*
|
||||
* @private
|
||||
* @method _refreshGradingPanel
|
||||
* @param {Event} event
|
||||
* @param {Number} userid
|
||||
* @param {String} serialised submission data.
|
||||
*/
|
||||
GradingPanel.prototype._refreshGradingPanel = function(event, userid, submissiondata, attemptnumber) {
|
||||
var contextid = this._region.attr('data-contextid');
|
||||
if (typeof submissiondata === 'undefined') {
|
||||
submissiondata = '';
|
||||
}
|
||||
if (typeof attemptnumber === 'undefined') {
|
||||
attemptnumber = -1;
|
||||
}
|
||||
// Skip reloading if it is the same user.
|
||||
if (this._lastUserId == userid && this._lastAttemptNumber == attemptnumber && submissiondata === '') {
|
||||
return;
|
||||
}
|
||||
this._lastUserId = userid;
|
||||
this._lastAttemptNumber = attemptnumber;
|
||||
$(document).trigger('start-loading-user');
|
||||
// Tell behat to back off too.
|
||||
window.M.util.js_pending('mod-assign-loading-user');
|
||||
// First insert the loading template.
|
||||
templates.render('mod_assign/loading', {}).done(function(html, js) {
|
||||
// Update the page.
|
||||
this._niceReplaceNodeContents(this._region, html, js).done(function() {
|
||||
if (userid > 0) {
|
||||
this._region.show();
|
||||
// Reload the grading form "fragment" for this user.
|
||||
var params = { userid: userid, attemptnumber: attemptnumber, jsonformdata: JSON.stringify(submissiondata) };
|
||||
fragment.loadFragment('mod_assign', 'gradingpanel', contextid, params).done(function(html, js) {
|
||||
this._niceReplaceNodeContents(this._region, html, js)
|
||||
.done(function() {
|
||||
checker.saveFormState('[data-region="grade-panel"] .gradeform');
|
||||
$('[data-region="attempt-chooser"]').on('click', this._chooseAttempt.bind(this));
|
||||
this._addPopoutButtons('[data-region="grade-panel"] .gradeform');
|
||||
$(document).trigger('finish-loading-user');
|
||||
// Tell behat we are friends again.
|
||||
window.M.util.js_complete('mod-assign-loading-user');
|
||||
}.bind(this))
|
||||
.fail(notification.exception);
|
||||
}.bind(this)).fail(notification.exception);
|
||||
} else {
|
||||
this._region.hide();
|
||||
var reviewPanel = $('[data-region="review-panel"]');
|
||||
if (reviewPanel.length) {
|
||||
this._niceReplaceNodeContents(reviewPanel, '', '');
|
||||
}
|
||||
$(document).trigger('finish-loading-user');
|
||||
// Tell behat we are friends again.
|
||||
window.M.util.js_complete('mod-assign-loading-user');
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this)).fail(notification.exception);
|
||||
};
|
||||
|
||||
return GradingPanel;
|
||||
});
|
62
mod/assign/amd/src/grading_review_panel.js
Normal file
62
mod/assign/amd/src/grading_review_panel.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* Javascript controller for the "Review" panel at the left of the page.
|
||||
*
|
||||
* @module mod_assign/grading_review_panel
|
||||
* @package mod_assign
|
||||
* @class GradingReviewPanel
|
||||
* @copyright 2016 Damyon Wiese <damyon@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 3.1
|
||||
*/
|
||||
define(['jquery'], function($) {
|
||||
|
||||
/**
|
||||
* GradingReviewPanel class.
|
||||
*
|
||||
* @class GradingReviewPanel
|
||||
* @param {String} selector The selector for the page region containing the user navigation.
|
||||
*/
|
||||
var GradingReviewPanel = function() {
|
||||
this._region = $('[data-region="review-panel"]');
|
||||
};
|
||||
|
||||
/** @type {JQuery} JQuery node for the page region containing the user navigation. */
|
||||
GradingReviewPanel.prototype._region = null;
|
||||
|
||||
/**
|
||||
* It is first come first served to get ownership of the grading review panel.
|
||||
* There can be only one.
|
||||
*
|
||||
* @public
|
||||
* @method getReviewPanel
|
||||
* @param {String} pluginname - the first plugin to ask for the panel gets it.
|
||||
* @return {DOMNode} or false
|
||||
*/
|
||||
GradingReviewPanel.prototype.getReviewPanel = function(pluginname) {
|
||||
var owner = this._region.data('panel-owner');
|
||||
if (typeof owner == "undefined") {
|
||||
this._region.data('review-panel-plugin', pluginname);
|
||||
}
|
||||
if (this._region.data('review-panel-plugin') == pluginname) {
|
||||
return this._region[0];
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return GradingReviewPanel;
|
||||
});
|
113
mod/assign/amd/src/participant_selector.js
Normal file
113
mod/assign/amd/src/participant_selector.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
// 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/>.
|
||||
|
||||
/**
|
||||
* Custom auto-complete adapter to load users from the assignment list_participants webservice.
|
||||
*
|
||||
* @module mod_assign/participants_selector
|
||||
* @copyright 2015 Damyon Wiese <damyon@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['core/ajax', 'jquery', 'core/templates'], function(ajax, $, templates) {
|
||||
|
||||
|
||||
return /** @alias module:mod_assign/participants_selector */ {
|
||||
|
||||
// Public variables and functions.
|
||||
/**
|
||||
* Process the results returned from transport (convert to value + label)
|
||||
*
|
||||
* @method processResults
|
||||
* @param {String} selector
|
||||
* @param {Array} data
|
||||
* @return {Array}
|
||||
*/
|
||||
processResults: function(selector, data) {
|
||||
var results = [], i = 0;
|
||||
for (i = 0; i < data.length; i++) {
|
||||
results[i] = { value: data[i].id, label: data[i].label };
|
||||
}
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch results based on the current query. This also renders each result from a template before returning them.
|
||||
*
|
||||
* @method transport
|
||||
* @param {String} selector Selector for the original select element
|
||||
* @param {String} query Current search string
|
||||
* @param {Function} success Success handler
|
||||
* @param {Function} failure Failure handler
|
||||
*/
|
||||
transport: function(selector, query, success, failure) {
|
||||
var assignmentid = $(selector).attr('data-assignmentid');
|
||||
var filters = $('[data-region="configure-filters"] input[type="checkbox"]');
|
||||
var filterstrings = [];
|
||||
|
||||
filters.each(function(index, element) {
|
||||
filterstrings[$(element).attr('name')] = $(element).prop('checked');
|
||||
});
|
||||
|
||||
var promise = ajax.call([{
|
||||
methodname: 'mod_assign_list_participants', args: { assignid: assignmentid, groupid: 0, filter: query, limit: 30 }
|
||||
}]);
|
||||
|
||||
promise[0].then(function(results) {
|
||||
var promises = [];
|
||||
var identityfields = $('[data-showuseridentity]').data('showuseridentity').split(',');
|
||||
|
||||
// We got the results, now we loop over them and render each one from a template.
|
||||
$.each(results, function(index, user) {
|
||||
var ctx = user,
|
||||
identity = [],
|
||||
show = true;
|
||||
|
||||
if (filterstrings.filter_submitted && !user.submitted) {
|
||||
show = false;
|
||||
}
|
||||
if (filterstrings.filter_notsubmitted && user.submitted) {
|
||||
show = false;
|
||||
}
|
||||
if (filterstrings.filter_requiregrading && !user.requiregrading) {
|
||||
show = false;
|
||||
}
|
||||
if (show) {
|
||||
$.each(identityfields, function(i, k) {
|
||||
if (typeof user[k] !== 'undefined' && user[k] !== '') {
|
||||
ctx.hasidentity = true;
|
||||
identity.push(user[k]);
|
||||
}
|
||||
});
|
||||
ctx.identity = identity.join(', ');
|
||||
promises.push(templates.render('mod_assign/list_participant_user_summary', ctx));
|
||||
}
|
||||
});
|
||||
|
||||
// When all the templates have been rendered, call the success handler.
|
||||
$.when.apply($.when, promises).then(function() {
|
||||
var args = arguments,
|
||||
i = 0;
|
||||
|
||||
$.each(results, function(index, user) {
|
||||
user.label = args[i];
|
||||
i++;
|
||||
});
|
||||
|
||||
success(results);
|
||||
});
|
||||
}, failure);
|
||||
}
|
||||
};
|
||||
});
|
163
mod/assign/classes/output/grading_app.php
Normal file
163
mod/assign/classes/output/grading_app.php
Normal file
|
@ -0,0 +1,163 @@
|
|||
<?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/>.
|
||||
|
||||
/**
|
||||
* Renderable that initialises the grading "app".
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace mod_assign\output;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderer_base;
|
||||
use renderable;
|
||||
use templatable;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Grading app renderable.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @since Moodle 3.1
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class grading_app implements templatable, renderable {
|
||||
|
||||
/**
|
||||
* The initial user id.
|
||||
*/
|
||||
public $userid = 0;
|
||||
|
||||
/**
|
||||
* The initial group id.
|
||||
*/
|
||||
public $groupid = 0;
|
||||
|
||||
/**
|
||||
* The assignment instance.
|
||||
*/
|
||||
public $assignment = null;
|
||||
|
||||
/**
|
||||
* Constructor for this renderable.
|
||||
*
|
||||
* @param int $userid The user we will open the grading app too.
|
||||
* @param int $groupid If groups are enabled this is the current course group.
|
||||
* @param assign $assignment The assignment class
|
||||
*/
|
||||
public function __construct($userid, $groupid, $assignment) {
|
||||
$this->userid = $userid;
|
||||
$this->groupid = $groupid;
|
||||
$this->assignment = $assignment;
|
||||
$this->participants = $assignment->list_participants_with_filter_status_and_group($groupid);
|
||||
if (!$this->userid && count($this->participants)) {
|
||||
$this->userid = reset($this->participants)->id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this class data as a flat list for rendering in a template.
|
||||
*
|
||||
* @param renderer_base $output The current page renderer.
|
||||
* @return stdClass - Flat list of exported data.
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
global $CFG;
|
||||
|
||||
$export = new stdClass();
|
||||
$export->userid = $this->userid;
|
||||
$export->assignmentid = $this->assignment->get_instance()->id;
|
||||
$export->cmid = $this->assignment->get_course_module()->id;
|
||||
$export->contextid = $this->assignment->get_context()->id;
|
||||
$export->groupid = $this->groupid;
|
||||
$export->name = $this->assignment->get_instance()->name;
|
||||
$export->courseid = $this->assignment->get_course()->id;
|
||||
$export->participants = array();
|
||||
$num = 1;
|
||||
foreach ($this->participants as $idx => $record) {
|
||||
$user = new stdClass();
|
||||
$user->id = $record->id;
|
||||
$user->fullname = fullname($record);
|
||||
$user->requiregrading = $record->requiregrading;
|
||||
$user->submitted = $record->submitted;
|
||||
if (!empty($record->groupid)) {
|
||||
$user->groupid = $record->groupid;
|
||||
$user->groupname = $record->groupname;
|
||||
}
|
||||
if ($record->id == $this->userid) {
|
||||
$export->index = $num;
|
||||
$user->current = true;
|
||||
}
|
||||
$export->participants[] = $user;
|
||||
$num++;
|
||||
}
|
||||
|
||||
$feedbackplugins = $this->assignment->get_feedback_plugins();
|
||||
$showreview = false;
|
||||
foreach ($feedbackplugins as $plugin) {
|
||||
if ($plugin->is_enabled() && $plugin->is_visible()) {
|
||||
if ($plugin->supports_review_panel()) {
|
||||
$showreview = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$export->showreview = $showreview;
|
||||
|
||||
$time = time();
|
||||
$export->count = count($export->participants);
|
||||
$export->coursename = $this->assignment->get_course_context()->get_context_name();
|
||||
$export->caneditsettings = has_capability('mod/assign:addinstance', $this->assignment->get_context());
|
||||
$export->duedate = $this->assignment->get_instance()->duedate;
|
||||
$export->duedatestr = userdate($this->assignment->get_instance()->duedate);
|
||||
|
||||
// Time remaining.
|
||||
$due = '';
|
||||
if ($export->duedate - $time <= 0) {
|
||||
$due = get_string('assignmentisdue', 'assign');
|
||||
} else {
|
||||
$due = get_string('timeremainingcolon', 'assign', format_time($export->duedate - $time));
|
||||
}
|
||||
$export->timeremainingstr = $due;
|
||||
|
||||
if ($export->duedate < $time) {
|
||||
$export->cutoffdate = $this->assignment->get_instance()->cutoffdate;
|
||||
$cutoffdate = $export->cutoffdate;
|
||||
if ($cutoffdate) {
|
||||
if ($cutoffdate > $time) {
|
||||
$late = get_string('latesubmissionsaccepted', 'assign', userdate($export->cutoffdate));
|
||||
} else {
|
||||
$late = get_string('nomoresubmissionsaccepted', 'assign');
|
||||
}
|
||||
$export->cutoffdatestr = $late;
|
||||
}
|
||||
}
|
||||
|
||||
$export->defaultsendnotifications = $this->assignment->get_instance()->sendstudentnotifications;
|
||||
$export->rarrow = $output->rarrow();
|
||||
$export->larrow = $output->larrow();
|
||||
// List of identity fields to display (the user info will not contain any fields the user cannot view anyway).
|
||||
$export->showuseridentity = $CFG->showuseridentity;
|
||||
|
||||
return $export;
|
||||
}
|
||||
|
||||
}
|
|
@ -190,4 +190,24 @@ $functions = array(
|
|||
'capabilities' => 'mod/assign:view',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||
),
|
||||
|
||||
'mod_assign_list_participants' => array(
|
||||
'classname' => 'mod_assign_external',
|
||||
'methodname' => 'list_participants',
|
||||
'classpath' => 'mod/assign/externallib.php',
|
||||
'description' => 'List the participants for a single assignment, with some summary info about their submissions.',
|
||||
'type' => 'read',
|
||||
'ajax' => true,
|
||||
'capabilities' => 'mod/assign:view, mod/assign:viewgrades'
|
||||
),
|
||||
|
||||
'mod_assign_submit_grading_form' => array(
|
||||
'classname' => 'mod_assign_external',
|
||||
'methodname' => 'submit_grading_form',
|
||||
'classpath' => 'mod/assign/externallib.php',
|
||||
'description' => 'Submit the grading form data via ajax',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
'capabilities' => 'mod/assign:grade'
|
||||
),
|
||||
);
|
||||
|
|
|
@ -2266,6 +2266,7 @@ class mod_assign_external extends external_api {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the parameters for view_submission_status.
|
||||
*
|
||||
|
@ -2567,4 +2568,194 @@ class mod_assign_external extends external_api {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function list_participants_parameters() {
|
||||
return new external_function_parameters(
|
||||
array(
|
||||
'assignid' => new external_value(PARAM_INT, 'assign instance id'),
|
||||
'groupid' => new external_value(PARAM_INT, 'group id'),
|
||||
'filter' => new external_value(PARAM_RAW, 'search string to filter the results'),
|
||||
'skip' => new external_value(PARAM_INT, 'number of records to skip', VALUE_DEFAULT, 0),
|
||||
'limit' => new external_value(PARAM_INT, 'maximum number of records to return', VALUE_DEFAULT, 0),
|
||||
'onlyids' => new external_value(PARAM_BOOL, 'Do not return all user fields - only the id and flags (requiresgrading etc)', VALUE_DEFAULT, false),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger the grading_table_viewed event.
|
||||
*
|
||||
* @param int $assignid the assign instance id
|
||||
* @param int $groupid the current group id
|
||||
* @param string $filter search string to filter the results.
|
||||
* @param int $skip Number of records to skip
|
||||
* @param int $limit Maximum number of records to return
|
||||
* @return array of warnings and status result
|
||||
* @since Moodle 3.0
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function list_participants($assignid, $groupid, $filter, $skip, $limit) {
|
||||
global $DB, $CFG;
|
||||
require_once($CFG->dirroot . "/mod/assign/locallib.php");
|
||||
require_once($CFG->dirroot . "/user/lib.php");
|
||||
|
||||
$params = self::validate_parameters(self::list_participants_parameters(),
|
||||
array(
|
||||
'assignid' => $assignid,
|
||||
'groupid' => $groupid,
|
||||
'filter' => $filter,
|
||||
'skip' => $skip,
|
||||
'limit' => $limit
|
||||
));
|
||||
$warnings = array();
|
||||
|
||||
// Request and permission validation.
|
||||
$assign = $DB->get_record('assign', array('id' => $params['assignid']), 'id', MUST_EXIST);
|
||||
list($course, $cm) = get_course_and_cm_from_instance($assign, 'assign');
|
||||
|
||||
$context = context_module::instance($cm->id);
|
||||
self::validate_context($context);
|
||||
|
||||
require_capability('mod/assign:view', $context);
|
||||
|
||||
$assign = new assign($context, null, null);
|
||||
$assign->require_view_grades();
|
||||
|
||||
$participants = $assign->list_participants_with_filter_status_and_group($params['groupid']);
|
||||
|
||||
$result = array();
|
||||
$index = 0;
|
||||
foreach ($participants as $record) {
|
||||
// Preserve the fullname set by the assignment.
|
||||
$fullname = $record->fullname;
|
||||
$searchable = $fullname;
|
||||
$match = false;
|
||||
if (empty($filter)) {
|
||||
$match = true;
|
||||
} else {
|
||||
$filter = core_text::strtolower($filter);
|
||||
$value = core_text::strtolower($searchable);
|
||||
if (is_string($value) && (core_text::strpos($value, $filter) !== false)) {
|
||||
$match = true;
|
||||
}
|
||||
}
|
||||
if ($match) {
|
||||
$index++;
|
||||
if ($index <= $params['skip']) {
|
||||
continue;
|
||||
}
|
||||
if (($params['limit'] > 0) && (($index - $params['skip']) > $params['limit'])) {
|
||||
break;
|
||||
}
|
||||
// Now we do the expensive lookup of user details because we completed the filtering.
|
||||
if (!$assign->is_blind_marking() && !$params['onlyids']) {
|
||||
$userdetails = user_get_user_details($record, $course);
|
||||
} else {
|
||||
$userdetails = array('id' => $record->id);
|
||||
}
|
||||
$userdetails['fullname'] = $fullname;
|
||||
$userdetails['submitted'] = $record->submitted;
|
||||
$userdetails['requiregrading'] = $record->requiregrading;
|
||||
if (!empty($record->groupid)) {
|
||||
$userdetails['groupid'] = $record->groupid;
|
||||
}
|
||||
if (!empty($record->groupname)) {
|
||||
$userdetails['groupname'] = $record->groupname;
|
||||
}
|
||||
|
||||
$result[] = $userdetails;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method result value
|
||||
*
|
||||
* @return external_description
|
||||
* @since Moodle 3.0
|
||||
*/
|
||||
public static function list_participants_returns() {
|
||||
return new external_multiple_structure(
|
||||
new external_single_structure(array(
|
||||
'id' => new external_value(PARAM_INT, 'ID of the user'),
|
||||
'username' => new external_value(PARAM_RAW, 'Username', VALUE_OPTIONAL),
|
||||
'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
|
||||
'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
|
||||
'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
|
||||
'idnumber' => new external_value(PARAM_NOTAGS, 'The idnumber of the user', VALUE_OPTIONAL),
|
||||
'email' => new external_value(PARAM_TEXT, 'Email address', VALUE_OPTIONAL),
|
||||
'address' => new external_value(PARAM_MULTILANG, 'Postal address', VALUE_OPTIONAL),
|
||||
'phone1' => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
|
||||
'phone2' => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
|
||||
'icq' => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
|
||||
'skype' => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
|
||||
'yahoo' => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
|
||||
'aim' => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
|
||||
'msn' => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
|
||||
'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
|
||||
'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
|
||||
'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
|
||||
'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
|
||||
'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
|
||||
'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
|
||||
'descriptionformat' => new external_value(PARAM_INT, 'User profile description format', VALUE_OPTIONAL),
|
||||
'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
|
||||
'url' => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
|
||||
'country' => new external_value(PARAM_ALPHA, 'Country code of the user, such as AU or CZ', VALUE_OPTIONAL),
|
||||
'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small', VALUE_OPTIONAL),
|
||||
'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big', VALUE_OPTIONAL),
|
||||
'customfields' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field'),
|
||||
'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
|
||||
'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
|
||||
'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field'),
|
||||
)
|
||||
), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
|
||||
'groups' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'id' => new external_value(PARAM_INT, 'group id'),
|
||||
'name' => new external_value(PARAM_RAW, 'group name'),
|
||||
'description' => new external_value(PARAM_RAW, 'group description'),
|
||||
)
|
||||
), 'user groups', VALUE_OPTIONAL),
|
||||
'roles' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'roleid' => new external_value(PARAM_INT, 'role id'),
|
||||
'name' => new external_value(PARAM_RAW, 'role name'),
|
||||
'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
|
||||
'sortorder' => new external_value(PARAM_INT, 'role sortorder')
|
||||
)
|
||||
), 'user roles', VALUE_OPTIONAL),
|
||||
'preferences' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'name' => new external_value(PARAM_ALPHANUMEXT, 'The name of the preferences'),
|
||||
'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
|
||||
)
|
||||
), 'User preferences', VALUE_OPTIONAL),
|
||||
'enrolledcourses' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'id' => new external_value(PARAM_INT, 'Id of the course'),
|
||||
'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
|
||||
'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
|
||||
)
|
||||
), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL),
|
||||
'submitted' => new external_value(PARAM_BOOL, 'have they submitted their assignment'),
|
||||
'requiregrading' => new external_value(PARAM_BOOL, 'is their submission waiting for grading'),
|
||||
'groupid' => new external_value(PARAM_INT, 'for group assignments this is the group id', VALUE_OPTIONAL),
|
||||
'groupname' => new external_value(PARAM_NOTAGS, 'for group assignments this is the group name', VALUE_OPTIONAL),
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
66
mod/assign/feedback/editpdf/classes/event/observer.php
Normal file
66
mod/assign/feedback/editpdf/classes/event/observer.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?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/>.
|
||||
|
||||
/**
|
||||
* An event observer.
|
||||
*
|
||||
* @package assignfeedback_editpdf
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace assignfeedback_editpdf\event;
|
||||
|
||||
/**
|
||||
* Simple task to run the grade cron.
|
||||
*/
|
||||
class observer {
|
||||
|
||||
/**
|
||||
* Listen to events and queue the submission for processing.
|
||||
* @param \mod_assign\event\submission_created $event
|
||||
*/
|
||||
public static function submission_created(\mod_assign\event\submission_created $event) {
|
||||
global $DB;
|
||||
|
||||
$submissionid = $event->other['submissionid'];
|
||||
$submissionattempt = $event->other['submissionattempt'];
|
||||
$fields = array( 'submissionid' => $submissionid, 'submissionattempt' => $submissionattempt);
|
||||
$record = (object) $fields;
|
||||
|
||||
$exists = $DB->get_records('assignfeedback_editpdf_queue', $fields);
|
||||
if (!$exists) {
|
||||
$DB->insert_record('assignfeedback_editpdf_queue', $record);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to events and queue the submission for processing.
|
||||
* @param \mod_assign\event\submission_updated $event
|
||||
*/
|
||||
public static function submission_updated(\mod_assign\event\submission_updated $event) {
|
||||
global $DB;
|
||||
|
||||
$submissionid = $event->other['submissionid'];
|
||||
$submissionattempt = $event->other['submissionattempt'];
|
||||
$fields = array( 'submissionid' => $submissionid, 'submissionattempt' => $submissionattempt);
|
||||
$record = (object) $fields;
|
||||
|
||||
$exists = $DB->get_records('assignfeedback_editpdf_queue', $fields);
|
||||
if (!$exists) {
|
||||
$DB->insert_record('assignfeedback_editpdf_queue', $record);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -118,10 +118,7 @@ class assignfeedback_editpdf_renderer extends plugin_renderer_base {
|
|||
array('id'=>$linkid, 'class'=>'btn', 'href'=>'#'));
|
||||
}
|
||||
$links = $launcheditorlink;
|
||||
|
||||
$links .= html_writer::tag('div',
|
||||
get_string('unsavedchanges', 'assignfeedback_editpdf'),
|
||||
array('class'=>'assignfeedback_editpdf_unsavedchanges warning'));
|
||||
$html .= '<input type="hidden" name="assignfeedback_editpdf_haschanges" value="false"/>';
|
||||
|
||||
$html .= html_writer::div($links, 'visibleifjs');
|
||||
$header = get_string('pluginname', 'assignfeedback_editpdf');
|
||||
|
@ -129,6 +126,7 @@ class assignfeedback_editpdf_renderer extends plugin_renderer_base {
|
|||
// Create the page navigation.
|
||||
$navigation1 = '';
|
||||
$navigation2 = '';
|
||||
$navigation3 = '';
|
||||
|
||||
// Pick the correct arrow icons for right to left mode.
|
||||
if (right_to_left()) {
|
||||
|
@ -156,6 +154,7 @@ class assignfeedback_editpdf_renderer extends plugin_renderer_base {
|
|||
$navigation2 .= $this->render_toolbar_button('comment_search', 'searchcomments', $this->get_shortcut('searchcomments'));
|
||||
$navigation2 = html_writer::div($navigation2, 'navigation-search', array('role'=>'navigation'));
|
||||
|
||||
|
||||
$toolbar1 = '';
|
||||
$toolbar2 = '';
|
||||
$toolbar3 = '';
|
||||
|
@ -210,7 +209,14 @@ class assignfeedback_editpdf_renderer extends plugin_renderer_base {
|
|||
|
||||
$canvas = html_writer::div($loading, 'drawingcanvas');
|
||||
$canvas = html_writer::div($canvas, 'drawingregion');
|
||||
$body .= html_writer::div($canvas, 'hideoverflow');
|
||||
$changesmessage = html_writer::tag('div',
|
||||
get_string('draftchangessaved', 'assignfeedback_editpdf'),
|
||||
array('class'=>'assignfeedback_editpdf_unsavedchanges warning label label-info'));
|
||||
|
||||
$changesmessage = html_writer::div($changesmessage, 'unsaved-changes');
|
||||
$canvas .= $changesmessage;
|
||||
|
||||
$body .= $canvas;
|
||||
|
||||
$footer = '';
|
||||
|
||||
|
|
107
mod/assign/feedback/editpdf/classes/task/convert_submissions.php
Normal file
107
mod/assign/feedback/editpdf/classes/task/convert_submissions.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?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/>.
|
||||
|
||||
/**
|
||||
* A scheduled task.
|
||||
*
|
||||
* @package assignfeedback_editpdf
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace assignfeedback_editpdf\task;
|
||||
|
||||
use core\task\scheduled_task;
|
||||
use assignfeedback_editpdf\document_services;
|
||||
use context_module;
|
||||
use assign;
|
||||
|
||||
/**
|
||||
* Simple task to run the grade cron.
|
||||
*/
|
||||
class convert_submissions extends scheduled_task {
|
||||
|
||||
/**
|
||||
* Get a descriptive name for this task (shown to admins).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return get_string('preparesubmissionsforannotation', 'assignfeedback_editpdf');
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the job.
|
||||
* Throw exceptions on errors (the job will be retried).
|
||||
*/
|
||||
public function execute() {
|
||||
global $CFG, $DB;
|
||||
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
|
||||
$records = $DB->get_records('assignfeedback_editpdf_queue');
|
||||
|
||||
$assignmentcache = array();
|
||||
|
||||
foreach ($records as $record) {
|
||||
$submissionid = $record->submissionid;
|
||||
$submission = $DB->get_record('assign_submission', array('id' => $submissionid), '*', IGNORE_MISSING);
|
||||
if (!$submission) {
|
||||
// Submission no longer exists.
|
||||
$DB->delete_records('assignfeedback_editpdf_queue', array('id' => $record->id));
|
||||
continue;
|
||||
}
|
||||
|
||||
$assignmentid = $submission->assignment;
|
||||
$attemptnumber = $record->submissionattempt;
|
||||
|
||||
if (empty($assignmentcache[$assignmentid])) {
|
||||
$cm = get_coursemodule_from_instance('assign', $assignmentid, 0, false, MUST_EXIST);
|
||||
$context = context_module::instance($cm->id);
|
||||
|
||||
$assignment = new assign($context, null, null);
|
||||
$assignmentcache[$assignmentid] = $assignment;
|
||||
} else {
|
||||
$assignment = $assignmentcache[$assignmentid];
|
||||
}
|
||||
|
||||
$users = array();
|
||||
if ($submission->userid) {
|
||||
array_push($users, $submission->userid);
|
||||
} else {
|
||||
$members = $assignment->get_submission_group_members($submission->groupid, true);
|
||||
|
||||
foreach ($members as $memberid) {
|
||||
array_push($users, $memberid);
|
||||
}
|
||||
}
|
||||
|
||||
mtrace('Convert ' . count($users) . ' submission attempt(s) for assignment ' . $assignmentid);
|
||||
foreach ($users as $userid) {
|
||||
document_services::get_page_images_for_attempt($assignment,
|
||||
$userid,
|
||||
$attemptnumber,
|
||||
true);
|
||||
document_services::get_page_images_for_attempt($assignment,
|
||||
$userid,
|
||||
$attemptnumber,
|
||||
false);
|
||||
}
|
||||
|
||||
$DB->delete_records('assignfeedback_editpdf_queue', array('id' => $record->id));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
36
mod/assign/feedback/editpdf/db/events.php
Normal file
36
mod/assign/feedback/editpdf/db/events.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?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/>.
|
||||
|
||||
/**
|
||||
* EditPDF event handler definition.
|
||||
*
|
||||
* @package assignfeedback_editpdf
|
||||
* @category event
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// List of observers.
|
||||
$observers = array(
|
||||
array(
|
||||
'eventname' => '\mod_assign\event\submission_created',
|
||||
'callback' => '\assignfeedback_editpdf\event\observer::submission_created',
|
||||
),
|
||||
array(
|
||||
'eventname' => '\mod_assign\event\submission_updated',
|
||||
'callback' => '\assignfeedback_editpdf\event\observer::submission_updated',
|
||||
),
|
||||
);
|
14
mod/assign/feedback/editpdf/db/install.xml
Normal file → Executable file
14
mod/assign/feedback/editpdf/db/install.xml
Normal file → Executable file
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="mod/assign/feedback/editpdf/db" VERSION="20130926" COMMENT="XMLDB file for Moodle mod/assign/feedback/editpdf"
|
||||
<XMLDB PATH="mod/assign/feedback/editpdf/db" VERSION="20160216" COMMENT="XMLDB file for Moodle mod/assign/feedback/editpdf"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
|
@ -59,5 +59,15 @@
|
|||
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="assignfeedback_editpdf_queue" COMMENT="Queue for processing.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="submissionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="submissionattempt" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
</TABLES>
|
||||
</XMLDB>
|
||||
</XMLDB>
|
44
mod/assign/feedback/editpdf/db/tasks.php
Normal file
44
mod/assign/feedback/editpdf/db/tasks.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?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/>.
|
||||
|
||||
/**
|
||||
* Definition of core scheduled tasks.
|
||||
*
|
||||
* The handlers defined on this file are processed and registered into
|
||||
* the Moodle DB after any install or upgrade operation. All plugins
|
||||
* support this.
|
||||
*
|
||||
* @package core
|
||||
* @category task
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/* List of handlers */
|
||||
|
||||
$tasks = array(
|
||||
array(
|
||||
'classname' => 'assignfeedback_editpdf\task\convert_submissions',
|
||||
'blocking' => 0,
|
||||
'minute' => '*/15',
|
||||
'hour' => '*',
|
||||
'day' => '*',
|
||||
'dayofweek' => '*',
|
||||
'month' => '*'
|
||||
),
|
||||
);
|
|
@ -30,7 +30,9 @@ defined('MOODLE_INTERNAL') || die();
|
|||
* @return bool
|
||||
*/
|
||||
function xmldb_assignfeedback_editpdf_upgrade($oldversion) {
|
||||
global $CFG;
|
||||
global $CFG, $DB;
|
||||
|
||||
$dbman = $DB->get_manager();
|
||||
|
||||
// Moodle v2.8.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
@ -41,5 +43,29 @@ function xmldb_assignfeedback_editpdf_upgrade($oldversion) {
|
|||
// Moodle v3.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2016021600) {
|
||||
|
||||
// Define table assignfeedback_editpdf_queue to be created.
|
||||
$table = new xmldb_table('assignfeedback_editpdf_queue');
|
||||
|
||||
// Adding fields to table assignfeedback_editpdf_queue.
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
$table->add_field('submissionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
|
||||
$table->add_field('submissionattempt', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
|
||||
|
||||
// Adding keys to table assignfeedback_editpdf_queue.
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
|
||||
// Conditionally launch create table for assignfeedback_editpdf_queue.
|
||||
if (!$dbman->table_exists($table)) {
|
||||
$dbman->create_table($table);
|
||||
}
|
||||
|
||||
// Editpdf savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2016021600, 'assignfeedback', 'editpdf');
|
||||
}
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -85,7 +85,8 @@ $string['test_notexecutable'] = 'The ghostscript points to a file that is not ex
|
|||
$string['test_ok'] = 'The ghostscript path appears to be OK - please check you can see the message in the image below';
|
||||
$string['toolbarbutton'] = '{$a->tool} {$a->shortcut}';
|
||||
$string['tool'] = 'Tool';
|
||||
$string['unsavedchanges'] = 'Unsaved changes';
|
||||
$string['viewfeedbackonline'] = 'View annotated PDF...';
|
||||
$string['white'] = 'White';
|
||||
$string['yellow'] = 'Yellow';
|
||||
$string['draftchangessaved'] = 'Draft annotations saved';
|
||||
$string['preparesubmissionsforannotation'] = 'Prepare submissions for annotation';
|
||||
|
|
|
@ -168,21 +168,16 @@ class assign_feedback_editpdf extends assign_feedback_plugin {
|
|||
$attempt = $grade->attemptnumber;
|
||||
}
|
||||
|
||||
$files = document_services::list_compatible_submission_files_for_attempt($this->assignment, $userid, $attempt);
|
||||
// Only show the editor if there was a compatible file submitted.
|
||||
if (count($files)) {
|
||||
$renderer = $PAGE->get_renderer('assignfeedback_editpdf');
|
||||
|
||||
$renderer = $PAGE->get_renderer('assignfeedback_editpdf');
|
||||
$widget = $this->get_widget($userid, $grade, false);
|
||||
|
||||
$widget = $this->get_widget($userid, $grade, false);
|
||||
|
||||
$html = $renderer->render($widget);
|
||||
$mform->addElement('static', 'editpdf', get_string('editpdf', 'assignfeedback_editpdf'), $html);
|
||||
$mform->addHelpButton('editpdf', 'editpdf', 'assignfeedback_editpdf');
|
||||
$mform->addElement('hidden', 'editpdf_source_userid', $userid);
|
||||
$mform->setType('editpdf_source_userid', PARAM_INT);
|
||||
$mform->setConstant('editpdf_source_userid', $userid);
|
||||
}
|
||||
$html = $renderer->render($widget);
|
||||
$mform->addElement('static', 'editpdf', get_string('editpdf', 'assignfeedback_editpdf'), $html);
|
||||
$mform->addHelpButton('editpdf', 'editpdf', 'assignfeedback_editpdf');
|
||||
$mform->addElement('hidden', 'editpdf_source_userid', $userid);
|
||||
$mform->setType('editpdf_source_userid', PARAM_INT);
|
||||
$mform->setConstant('editpdf_source_userid', $userid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -364,4 +359,12 @@ class assign_feedback_editpdf extends assign_feedback_plugin {
|
|||
public function get_file_areas() {
|
||||
return array(document_services::FINAL_PDF_FILEAREA => $this->get_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* This plugin will inject content into the review panel with javascript.
|
||||
* @return bool true
|
||||
*/
|
||||
public function supports_review_panel() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,20 @@
|
|||
cursor: crosshair;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #ccc;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.assignfeedback_editpdf_widget .moodle-dialogue-bd .drawingregion {
|
||||
position: inherit;
|
||||
}
|
||||
.assignfeedback_editpdf_widget .drawingregion {
|
||||
border: 1px solid #ccc;
|
||||
left: 1em;
|
||||
right: 1em;
|
||||
top: 52px;
|
||||
bottom: 0px;
|
||||
position: absolute;
|
||||
overflow: auto;
|
||||
width: 842px; /* A4 portrait should not need a horizontal scrollbar */
|
||||
}
|
||||
|
||||
.assignfeedback_editpdf_widget {
|
||||
|
@ -32,18 +42,25 @@
|
|||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
min-height: 50px;
|
||||
height: 52px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.moodle-dialogue-base .moodle-dialogue.assignfeedback_editpdf_widget .moodle-dialogue-bd {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.assignfeedback_editpdf_unsavedchanges.haschanges{
|
||||
display: block;
|
||||
.assignfeedback_editpdf_widget .assignfeedback_editpdf_unsavedchanges.haschanges{
|
||||
display: inline-block;
|
||||
}
|
||||
.assignfeedback_editpdf_unsavedchanges {
|
||||
.assignfeedback_editpdf_widget .assignfeedback_editpdf_unsavedchanges {
|
||||
display: none;
|
||||
margin-top: 1em;
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 60px;
|
||||
}
|
||||
.dir-rtl .assignfeedback_editpdf_widget .assignfeedback_editpdf_unsavedchanges {
|
||||
float: right;
|
||||
}
|
||||
.yui3-colourpicker-hidden,
|
||||
.yui3-commentsearch-hidden,
|
||||
|
@ -290,3 +307,31 @@ ul.assignfeedback_editpdf_menu {
|
|||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
@media (max-width: 899px) {
|
||||
.assignfeedback_editpdf_widget .pageheader {
|
||||
height: 104px;
|
||||
}
|
||||
.assignfeedback_editpdf_widget .drawingregion {
|
||||
top: 104px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.assignfeedback_editpdf_widget .drawingregion {
|
||||
position: inherit;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.assignfeedback_editpdf_widget .pageheader {
|
||||
height: 52px;
|
||||
}
|
||||
.assignfeedback_editpdf_widget .drawingregion {
|
||||
top: 52px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 683px) {
|
||||
.assignfeedback_editpdf_widget .pageheader {
|
||||
height: 104px;
|
||||
}
|
||||
.assignfeedback_editpdf_widget .drawingregion {
|
||||
top: 104px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,11 +50,9 @@ Feature: In an assignment, teacher can annotate PDF files during grading
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Edit" "link" in the "Submitted for grading" "table_row"
|
||||
And I click on "Grade" "link" in the "Submitted for grading" "table_row"
|
||||
And I follow "Launch PDF editor..."
|
||||
And I change window size to "large"
|
||||
And I should see "Page 1 of 3"
|
||||
And I click on ".navigate-next-button" "css_element"
|
||||
And I should see "Page 2 of 3"
|
||||
|
@ -62,11 +60,10 @@ Feature: In an assignment, teacher can annotate PDF files during grading
|
|||
And I click on ".linebutton" "css_element"
|
||||
And I click on ".commentcolourbutton" "css_element"
|
||||
And I click on "//img[@alt=\"Blue\"]/parent::button" "xpath_element"
|
||||
And I change window size to "medium"
|
||||
And I wait until the page is ready
|
||||
And I click on "Close" "button"
|
||||
And I press "Save changes"
|
||||
And I should see "The grade changes were saved"
|
||||
And I wait until the page is ready
|
||||
And I should see "The changes to the grade and feedback were saved"
|
||||
|
||||
@javascript
|
||||
Scenario: Submit a PDF file as a student in a team and annotate the PDF as a teacher
|
||||
|
@ -129,17 +126,15 @@ Feature: In an assignment, teacher can annotate PDF files during grading
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Edit" "link" in the "Student 2" "table_row"
|
||||
And I click on "Grade" "link" in the "Student 2" "table_row"
|
||||
And I follow "Launch PDF editor..."
|
||||
And I change window size to "large"
|
||||
And I click on ".stampbutton" "css_element"
|
||||
And I click on ".drawingcanvas" "css_element"
|
||||
And I change window size to "medium"
|
||||
And I wait until the page is ready
|
||||
And I click on "Close" "button"
|
||||
And I click on ".linebutton" "css_element"
|
||||
And I draw on the pdf
|
||||
And I press "Save changes"
|
||||
And I should see "The grade changes were saved"
|
||||
And I press "Continue"
|
||||
And I should see "The changes to the grade and feedback were saved"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "View annotated PDF..." in the "student2@example.com" "table_row"
|
||||
|
|
|
@ -49,4 +49,33 @@ class behat_assignfeedback_editpdf extends behat_base {
|
|||
throw new \Moodle\BehatExtension\Exception\SkippedException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw on the pdf.
|
||||
*
|
||||
* @When /^I draw on the pdf$/
|
||||
*/
|
||||
public function i_draw_on_the_pdf() {
|
||||
$js = ' (function() {
|
||||
var instance = M.assignfeedback_editpdf.instance;
|
||||
var event = { clientX: 100, clientY: 250, preventDefault: function() {} };
|
||||
instance.edit_start(event);
|
||||
}()); ';
|
||||
$this->getSession()->executeScript($js);
|
||||
sleep(1);
|
||||
$js = ' (function() {
|
||||
var instance = M.assignfeedback_editpdf.instance;
|
||||
var event = { clientX: 150, clientY: 275, preventDefault: function() {} };
|
||||
instance.edit_move(event);
|
||||
}()); ';
|
||||
$this->getSession()->executeScript($js);
|
||||
sleep(1);
|
||||
$js = ' (function() {
|
||||
var instance = M.assignfeedback_editpdf.instance;
|
||||
var event = { clientX: 200, clientY: 300, preventDefault: function() {} };
|
||||
instance.edit_end(event);
|
||||
}()); ';
|
||||
$this->getSession()->executeScript($js);
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,26 +50,22 @@ Feature: In a group assignment, teacher can annotate PDF files for all users
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Submitted for grading" "table_row"
|
||||
And I follow "Launch PDF editor..."
|
||||
And I change window size to "large"
|
||||
And I click on ".navigate-next-button" "css_element"
|
||||
And I click on ".stampbutton" "css_element"
|
||||
And I click on ".drawingcanvas" "css_element"
|
||||
And I change window size to "medium"
|
||||
And I draw on the pdf
|
||||
And I wait until the page is ready
|
||||
And I click on "Close" "button"
|
||||
And I press "Save changes"
|
||||
And I should see "The grade changes were saved"
|
||||
And I should see "The changes to the grade and feedback were saved"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
When I follow "View annotated PDF..."
|
||||
And I change window size to "large"
|
||||
Then I should see "Annotate PDF"
|
||||
And I change window size to "medium"
|
||||
And I wait until the page is ready
|
||||
And I click on "Close" "button"
|
||||
And I log out
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2015111600;
|
||||
$plugin->version = 2016021601;
|
||||
$plugin->requires = 2015111000;
|
||||
$plugin->component = 'assignfeedback_editpdf';
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ var AJAXBASE = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax.php',
|
|||
ANNOTATIONCOLOURBUTTON : '.annotationcolourbutton',
|
||||
DELETEANNOTATIONBUTTON : '.deleteannotationbutton',
|
||||
UNSAVEDCHANGESDIV : '.assignfeedback_editpdf_unsavedchanges',
|
||||
UNSAVEDCHANGESINPUT : 'input[name="assignfeedback_editpdf_haschanges"]',
|
||||
STAMPSBUTTON : '.currentstampbutton',
|
||||
DIALOGUE : '.' + CSS.DIALOGUE
|
||||
},
|
||||
|
@ -3097,6 +3098,15 @@ EDITOR.prototype = {
|
|||
*/
|
||||
dialogue : null,
|
||||
|
||||
/**
|
||||
* The panel used for all action menu displays.
|
||||
*
|
||||
* @property type
|
||||
* @type Y.Node
|
||||
* @protected
|
||||
*/
|
||||
panel : null,
|
||||
|
||||
/**
|
||||
* The number of pages in the pdf.
|
||||
*
|
||||
|
@ -3258,11 +3268,24 @@ EDITOR.prototype = {
|
|||
link.on('click', this.link_handler, this);
|
||||
link.on('key', this.link_handler, 'down:13', this);
|
||||
|
||||
this.currentedit.start = false;
|
||||
this.currentedit.end = false;
|
||||
if (!this.get('readonly')) {
|
||||
this.quicklist = new M.assignfeedback_editpdf.quickcommentlist(this);
|
||||
}
|
||||
// We call the amd module to see if we can take control of the review panel.
|
||||
require(['mod_assign/grading_review_panel'], function(ReviewPanelManager) {
|
||||
var panelManager = new ReviewPanelManager();
|
||||
|
||||
var panel = panelManager.getReviewPanel('assignfeedback_editpdf');
|
||||
if (panel) {
|
||||
panel = Y.one(panel);
|
||||
panel.empty();
|
||||
link.ancestor('.fitem').hide();
|
||||
this.open_in_panel(panel);
|
||||
}
|
||||
this.currentedit.start = false;
|
||||
this.currentedit.end = false;
|
||||
if (!this.get('readonly')) {
|
||||
this.quicklist = new M.assignfeedback_editpdf.quickcommentlist(this);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3341,6 +3364,36 @@ EDITOR.prototype = {
|
|||
return newpoint;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the edit-pdf editor in the panel in the page instead of a popup.
|
||||
* @method open_in_panel
|
||||
*/
|
||||
open_in_panel : function(panel) {
|
||||
var drawingcanvas, drawingregion;
|
||||
|
||||
this.panel = panel;
|
||||
panel.append(this.get('body'));
|
||||
panel.addClass(CSS.DIALOGUE);
|
||||
|
||||
this.loadingicon = this.get_dialogue_element(SELECTOR.LOADINGICON);
|
||||
|
||||
drawingcanvas = this.get_dialogue_element(SELECTOR.DRAWINGCANVAS);
|
||||
this.graphic = new Y.Graphic({render : drawingcanvas});
|
||||
|
||||
drawingregion = this.get_dialogue_element(SELECTOR.DRAWINGREGION);
|
||||
drawingregion.on('scroll', this.move_canvas, this);
|
||||
|
||||
if (!this.get('readonly')) {
|
||||
drawingcanvas.on('gesturemovestart', this.edit_start, null, this);
|
||||
drawingcanvas.on('gesturemove', this.edit_move, null, this);
|
||||
drawingcanvas.on('gesturemoveend', this.edit_end, null, this);
|
||||
|
||||
this.refresh_button_state();
|
||||
}
|
||||
|
||||
this.load_all_pages();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to open the pdf editing dialogue.
|
||||
* @method link_handler
|
||||
|
@ -3496,14 +3549,18 @@ EDITOR.prototype = {
|
|||
try {
|
||||
data = Y.JSON.parse(responsetext);
|
||||
if (data.error || !data.pagecount) {
|
||||
this.dialogue.hide();
|
||||
if (this.dialogue) {
|
||||
this.dialogue.hide();
|
||||
}
|
||||
// Display alert dialogue.
|
||||
error = new M.core.alert({ message: M.util.get_string('cannotopenpdf', 'assignfeedback_editpdf') });
|
||||
error.show();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.dialogue.hide();
|
||||
if (this.dialogue) {
|
||||
this.dialogue.hide();
|
||||
}
|
||||
// Display alert dialogue.
|
||||
error = new M.core.alert({ title: M.util.get_string('cannotopenpdf', 'assignfeedback_editpdf')});
|
||||
error.show();
|
||||
|
@ -3734,7 +3791,11 @@ EDITOR.prototype = {
|
|||
* @method get_dialogue_element
|
||||
*/
|
||||
get_dialogue_element : function(selector) {
|
||||
return this.dialogue.get('boundingBox').one(selector);
|
||||
if (this.panel) {
|
||||
return this.panel.one(selector);
|
||||
} else {
|
||||
return this.dialogue.get('boundingBox').one(selector);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -3915,10 +3976,12 @@ EDITOR.prototype = {
|
|||
resize : function() {
|
||||
var drawingregion, drawregionheight;
|
||||
|
||||
if (!this.dialogue.get('visible')) {
|
||||
return;
|
||||
if (this.dialogue) {
|
||||
if (!this.dialogue.get('visible')) {
|
||||
return;
|
||||
}
|
||||
this.dialogue.centerDialogue();
|
||||
}
|
||||
this.dialogue.centerDialogue();
|
||||
|
||||
// Make sure the dialogue box is not bigger than the max height of the viewport.
|
||||
drawregionheight = Y.one('body').get('winHeight') - 120; // Space for toolbar + titlebar.
|
||||
|
@ -3926,7 +3989,9 @@ EDITOR.prototype = {
|
|||
drawregionheight = 100;
|
||||
}
|
||||
drawingregion = this.get_dialogue_element(SELECTOR.DRAWINGREGION);
|
||||
drawingregion.setStyle('maxHeight', drawregionheight +'px');
|
||||
if (this.dialogue) {
|
||||
drawingregion.setStyle('maxHeight', drawregionheight +'px');
|
||||
}
|
||||
this.redraw();
|
||||
return true;
|
||||
},
|
||||
|
@ -3985,8 +4050,16 @@ EDITOR.prototype = {
|
|||
if (jsondata.error) {
|
||||
return new M.core.ajaxException(jsondata);
|
||||
}
|
||||
Y.one('#' + this.get('linkid')).siblings(SELECTOR.UNSAVEDCHANGESDIV)
|
||||
.item(0).addClass('haschanges');
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESINPUT).set('value', 'true');
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).setStyle('opacity', 1);
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).setStyle('display', 'inline-block');
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).transition({
|
||||
duration: 1,
|
||||
delay: 2,
|
||||
opacity: 0
|
||||
}, function() {
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).setStyle('display', 'none');
|
||||
});
|
||||
} catch (e) {
|
||||
return new M.core.exception(e);
|
||||
}
|
||||
|
@ -4229,7 +4302,8 @@ M.assignfeedback_editpdf.editor = M.assignfeedback_editpdf.editor || {};
|
|||
* @param {Object} params
|
||||
*/
|
||||
M.assignfeedback_editpdf.editor.init = M.assignfeedback_editpdf.editor.init || function(params) {
|
||||
return new EDITOR(params);
|
||||
M.assignfeedback_editpdf.instance = new EDITOR(params);
|
||||
return M.assignfeedback_editpdf.instance;
|
||||
};
|
||||
|
||||
|
||||
|
@ -4243,6 +4317,7 @@ M.assignfeedback_editpdf.editor.init = M.assignfeedback_editpdf.editor.init || f
|
|||
"json",
|
||||
"event-move",
|
||||
"event-resize",
|
||||
"transition",
|
||||
"querystring-stringify-simple",
|
||||
"moodle-core-notification-dialog",
|
||||
"moodle-core-notification-exception",
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -42,6 +42,7 @@ var AJAXBASE = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax.php',
|
|||
ANNOTATIONCOLOURBUTTON : '.annotationcolourbutton',
|
||||
DELETEANNOTATIONBUTTON : '.deleteannotationbutton',
|
||||
UNSAVEDCHANGESDIV : '.assignfeedback_editpdf_unsavedchanges',
|
||||
UNSAVEDCHANGESINPUT : 'input[name="assignfeedback_editpdf_haschanges"]',
|
||||
STAMPSBUTTON : '.currentstampbutton',
|
||||
DIALOGUE : '.' + CSS.DIALOGUE
|
||||
},
|
||||
|
@ -3097,6 +3098,15 @@ EDITOR.prototype = {
|
|||
*/
|
||||
dialogue : null,
|
||||
|
||||
/**
|
||||
* The panel used for all action menu displays.
|
||||
*
|
||||
* @property type
|
||||
* @type Y.Node
|
||||
* @protected
|
||||
*/
|
||||
panel : null,
|
||||
|
||||
/**
|
||||
* The number of pages in the pdf.
|
||||
*
|
||||
|
@ -3258,11 +3268,24 @@ EDITOR.prototype = {
|
|||
link.on('click', this.link_handler, this);
|
||||
link.on('key', this.link_handler, 'down:13', this);
|
||||
|
||||
this.currentedit.start = false;
|
||||
this.currentedit.end = false;
|
||||
if (!this.get('readonly')) {
|
||||
this.quicklist = new M.assignfeedback_editpdf.quickcommentlist(this);
|
||||
}
|
||||
// We call the amd module to see if we can take control of the review panel.
|
||||
require(['mod_assign/grading_review_panel'], function(ReviewPanelManager) {
|
||||
var panelManager = new ReviewPanelManager();
|
||||
|
||||
var panel = panelManager.getReviewPanel('assignfeedback_editpdf');
|
||||
if (panel) {
|
||||
panel = Y.one(panel);
|
||||
panel.empty();
|
||||
link.ancestor('.fitem').hide();
|
||||
this.open_in_panel(panel);
|
||||
}
|
||||
this.currentedit.start = false;
|
||||
this.currentedit.end = false;
|
||||
if (!this.get('readonly')) {
|
||||
this.quicklist = new M.assignfeedback_editpdf.quickcommentlist(this);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3341,6 +3364,36 @@ EDITOR.prototype = {
|
|||
return newpoint;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the edit-pdf editor in the panel in the page instead of a popup.
|
||||
* @method open_in_panel
|
||||
*/
|
||||
open_in_panel : function(panel) {
|
||||
var drawingcanvas, drawingregion;
|
||||
|
||||
this.panel = panel;
|
||||
panel.append(this.get('body'));
|
||||
panel.addClass(CSS.DIALOGUE);
|
||||
|
||||
this.loadingicon = this.get_dialogue_element(SELECTOR.LOADINGICON);
|
||||
|
||||
drawingcanvas = this.get_dialogue_element(SELECTOR.DRAWINGCANVAS);
|
||||
this.graphic = new Y.Graphic({render : drawingcanvas});
|
||||
|
||||
drawingregion = this.get_dialogue_element(SELECTOR.DRAWINGREGION);
|
||||
drawingregion.on('scroll', this.move_canvas, this);
|
||||
|
||||
if (!this.get('readonly')) {
|
||||
drawingcanvas.on('gesturemovestart', this.edit_start, null, this);
|
||||
drawingcanvas.on('gesturemove', this.edit_move, null, this);
|
||||
drawingcanvas.on('gesturemoveend', this.edit_end, null, this);
|
||||
|
||||
this.refresh_button_state();
|
||||
}
|
||||
|
||||
this.load_all_pages();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to open the pdf editing dialogue.
|
||||
* @method link_handler
|
||||
|
@ -3496,14 +3549,18 @@ EDITOR.prototype = {
|
|||
try {
|
||||
data = Y.JSON.parse(responsetext);
|
||||
if (data.error || !data.pagecount) {
|
||||
this.dialogue.hide();
|
||||
if (this.dialogue) {
|
||||
this.dialogue.hide();
|
||||
}
|
||||
// Display alert dialogue.
|
||||
error = new M.core.alert({ message: M.util.get_string('cannotopenpdf', 'assignfeedback_editpdf') });
|
||||
error.show();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.dialogue.hide();
|
||||
if (this.dialogue) {
|
||||
this.dialogue.hide();
|
||||
}
|
||||
// Display alert dialogue.
|
||||
error = new M.core.alert({ title: M.util.get_string('cannotopenpdf', 'assignfeedback_editpdf')});
|
||||
error.show();
|
||||
|
@ -3734,7 +3791,11 @@ EDITOR.prototype = {
|
|||
* @method get_dialogue_element
|
||||
*/
|
||||
get_dialogue_element : function(selector) {
|
||||
return this.dialogue.get('boundingBox').one(selector);
|
||||
if (this.panel) {
|
||||
return this.panel.one(selector);
|
||||
} else {
|
||||
return this.dialogue.get('boundingBox').one(selector);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -3915,10 +3976,12 @@ EDITOR.prototype = {
|
|||
resize : function() {
|
||||
var drawingregion, drawregionheight;
|
||||
|
||||
if (!this.dialogue.get('visible')) {
|
||||
return;
|
||||
if (this.dialogue) {
|
||||
if (!this.dialogue.get('visible')) {
|
||||
return;
|
||||
}
|
||||
this.dialogue.centerDialogue();
|
||||
}
|
||||
this.dialogue.centerDialogue();
|
||||
|
||||
// Make sure the dialogue box is not bigger than the max height of the viewport.
|
||||
drawregionheight = Y.one('body').get('winHeight') - 120; // Space for toolbar + titlebar.
|
||||
|
@ -3926,7 +3989,9 @@ EDITOR.prototype = {
|
|||
drawregionheight = 100;
|
||||
}
|
||||
drawingregion = this.get_dialogue_element(SELECTOR.DRAWINGREGION);
|
||||
drawingregion.setStyle('maxHeight', drawregionheight +'px');
|
||||
if (this.dialogue) {
|
||||
drawingregion.setStyle('maxHeight', drawregionheight +'px');
|
||||
}
|
||||
this.redraw();
|
||||
return true;
|
||||
},
|
||||
|
@ -3985,8 +4050,16 @@ EDITOR.prototype = {
|
|||
if (jsondata.error) {
|
||||
return new M.core.ajaxException(jsondata);
|
||||
}
|
||||
Y.one('#' + this.get('linkid')).siblings(SELECTOR.UNSAVEDCHANGESDIV)
|
||||
.item(0).addClass('haschanges');
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESINPUT).set('value', 'true');
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).setStyle('opacity', 1);
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).setStyle('display', 'inline-block');
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).transition({
|
||||
duration: 1,
|
||||
delay: 2,
|
||||
opacity: 0
|
||||
}, function() {
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).setStyle('display', 'none');
|
||||
});
|
||||
} catch (e) {
|
||||
return new M.core.exception(e);
|
||||
}
|
||||
|
@ -4229,7 +4302,8 @@ M.assignfeedback_editpdf.editor = M.assignfeedback_editpdf.editor || {};
|
|||
* @param {Object} params
|
||||
*/
|
||||
M.assignfeedback_editpdf.editor.init = M.assignfeedback_editpdf.editor.init || function(params) {
|
||||
return new EDITOR(params);
|
||||
M.assignfeedback_editpdf.instance = new EDITOR(params);
|
||||
return M.assignfeedback_editpdf.instance;
|
||||
};
|
||||
|
||||
|
||||
|
@ -4243,6 +4317,7 @@ M.assignfeedback_editpdf.editor.init = M.assignfeedback_editpdf.editor.init || f
|
|||
"json",
|
||||
"event-move",
|
||||
"event-resize",
|
||||
"transition",
|
||||
"querystring-stringify-simple",
|
||||
"moodle-core-notification-dialog",
|
||||
"moodle-core-notification-exception",
|
||||
|
|
|
@ -42,6 +42,15 @@ EDITOR.prototype = {
|
|||
*/
|
||||
dialogue : null,
|
||||
|
||||
/**
|
||||
* The panel used for all action menu displays.
|
||||
*
|
||||
* @property type
|
||||
* @type Y.Node
|
||||
* @protected
|
||||
*/
|
||||
panel : null,
|
||||
|
||||
/**
|
||||
* The number of pages in the pdf.
|
||||
*
|
||||
|
@ -203,11 +212,24 @@ EDITOR.prototype = {
|
|||
link.on('click', this.link_handler, this);
|
||||
link.on('key', this.link_handler, 'down:13', this);
|
||||
|
||||
this.currentedit.start = false;
|
||||
this.currentedit.end = false;
|
||||
if (!this.get('readonly')) {
|
||||
this.quicklist = new M.assignfeedback_editpdf.quickcommentlist(this);
|
||||
}
|
||||
// We call the amd module to see if we can take control of the review panel.
|
||||
require(['mod_assign/grading_review_panel'], function(ReviewPanelManager) {
|
||||
var panelManager = new ReviewPanelManager();
|
||||
|
||||
var panel = panelManager.getReviewPanel('assignfeedback_editpdf');
|
||||
if (panel) {
|
||||
panel = Y.one(panel);
|
||||
panel.empty();
|
||||
link.ancestor('.fitem').hide();
|
||||
this.open_in_panel(panel);
|
||||
}
|
||||
this.currentedit.start = false;
|
||||
this.currentedit.end = false;
|
||||
if (!this.get('readonly')) {
|
||||
this.quicklist = new M.assignfeedback_editpdf.quickcommentlist(this);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -286,6 +308,36 @@ EDITOR.prototype = {
|
|||
return newpoint;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the edit-pdf editor in the panel in the page instead of a popup.
|
||||
* @method open_in_panel
|
||||
*/
|
||||
open_in_panel : function(panel) {
|
||||
var drawingcanvas, drawingregion;
|
||||
|
||||
this.panel = panel;
|
||||
panel.append(this.get('body'));
|
||||
panel.addClass(CSS.DIALOGUE);
|
||||
|
||||
this.loadingicon = this.get_dialogue_element(SELECTOR.LOADINGICON);
|
||||
|
||||
drawingcanvas = this.get_dialogue_element(SELECTOR.DRAWINGCANVAS);
|
||||
this.graphic = new Y.Graphic({render : drawingcanvas});
|
||||
|
||||
drawingregion = this.get_dialogue_element(SELECTOR.DRAWINGREGION);
|
||||
drawingregion.on('scroll', this.move_canvas, this);
|
||||
|
||||
if (!this.get('readonly')) {
|
||||
drawingcanvas.on('gesturemovestart', this.edit_start, null, this);
|
||||
drawingcanvas.on('gesturemove', this.edit_move, null, this);
|
||||
drawingcanvas.on('gesturemoveend', this.edit_end, null, this);
|
||||
|
||||
this.refresh_button_state();
|
||||
}
|
||||
|
||||
this.load_all_pages();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to open the pdf editing dialogue.
|
||||
* @method link_handler
|
||||
|
@ -441,14 +493,18 @@ EDITOR.prototype = {
|
|||
try {
|
||||
data = Y.JSON.parse(responsetext);
|
||||
if (data.error || !data.pagecount) {
|
||||
this.dialogue.hide();
|
||||
if (this.dialogue) {
|
||||
this.dialogue.hide();
|
||||
}
|
||||
// Display alert dialogue.
|
||||
error = new M.core.alert({ message: M.util.get_string('cannotopenpdf', 'assignfeedback_editpdf') });
|
||||
error.show();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.dialogue.hide();
|
||||
if (this.dialogue) {
|
||||
this.dialogue.hide();
|
||||
}
|
||||
// Display alert dialogue.
|
||||
error = new M.core.alert({ title: M.util.get_string('cannotopenpdf', 'assignfeedback_editpdf')});
|
||||
error.show();
|
||||
|
@ -679,7 +735,11 @@ EDITOR.prototype = {
|
|||
* @method get_dialogue_element
|
||||
*/
|
||||
get_dialogue_element : function(selector) {
|
||||
return this.dialogue.get('boundingBox').one(selector);
|
||||
if (this.panel) {
|
||||
return this.panel.one(selector);
|
||||
} else {
|
||||
return this.dialogue.get('boundingBox').one(selector);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -860,10 +920,12 @@ EDITOR.prototype = {
|
|||
resize : function() {
|
||||
var drawingregion, drawregionheight;
|
||||
|
||||
if (!this.dialogue.get('visible')) {
|
||||
return;
|
||||
if (this.dialogue) {
|
||||
if (!this.dialogue.get('visible')) {
|
||||
return;
|
||||
}
|
||||
this.dialogue.centerDialogue();
|
||||
}
|
||||
this.dialogue.centerDialogue();
|
||||
|
||||
// Make sure the dialogue box is not bigger than the max height of the viewport.
|
||||
drawregionheight = Y.one('body').get('winHeight') - 120; // Space for toolbar + titlebar.
|
||||
|
@ -871,7 +933,9 @@ EDITOR.prototype = {
|
|||
drawregionheight = 100;
|
||||
}
|
||||
drawingregion = this.get_dialogue_element(SELECTOR.DRAWINGREGION);
|
||||
drawingregion.setStyle('maxHeight', drawregionheight +'px');
|
||||
if (this.dialogue) {
|
||||
drawingregion.setStyle('maxHeight', drawregionheight +'px');
|
||||
}
|
||||
this.redraw();
|
||||
return true;
|
||||
},
|
||||
|
@ -930,8 +994,16 @@ EDITOR.prototype = {
|
|||
if (jsondata.error) {
|
||||
return new M.core.ajaxException(jsondata);
|
||||
}
|
||||
Y.one('#' + this.get('linkid')).siblings(SELECTOR.UNSAVEDCHANGESDIV)
|
||||
.item(0).addClass('haschanges');
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESINPUT).set('value', 'true');
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).setStyle('opacity', 1);
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).setStyle('display', 'inline-block');
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).transition({
|
||||
duration: 1,
|
||||
delay: 2,
|
||||
opacity: 0
|
||||
}, function() {
|
||||
Y.one(SELECTOR.UNSAVEDCHANGESDIV).setStyle('display', 'none');
|
||||
});
|
||||
} catch (e) {
|
||||
return new M.core.exception(e);
|
||||
}
|
||||
|
@ -1174,5 +1246,6 @@ M.assignfeedback_editpdf.editor = M.assignfeedback_editpdf.editor || {};
|
|||
* @param {Object} params
|
||||
*/
|
||||
M.assignfeedback_editpdf.editor.init = M.assignfeedback_editpdf.editor.init || function(params) {
|
||||
return new EDITOR(params);
|
||||
M.assignfeedback_editpdf.instance = new EDITOR(params);
|
||||
return M.assignfeedback_editpdf.instance;
|
||||
};
|
||||
|
|
|
@ -40,6 +40,7 @@ var AJAXBASE = M.cfg.wwwroot + '/mod/assign/feedback/editpdf/ajax.php',
|
|||
ANNOTATIONCOLOURBUTTON : '.annotationcolourbutton',
|
||||
DELETEANNOTATIONBUTTON : '.deleteannotationbutton',
|
||||
UNSAVEDCHANGESDIV : '.assignfeedback_editpdf_unsavedchanges',
|
||||
UNSAVEDCHANGESINPUT : 'input[name="assignfeedback_editpdf_haschanges"]',
|
||||
STAMPSBUTTON : '.currentstampbutton',
|
||||
DIALOGUE : '.' + CSS.DIALOGUE
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"json",
|
||||
"event-move",
|
||||
"event-resize",
|
||||
"transition",
|
||||
"querystring-stringify-simple",
|
||||
"moodle-core-notification-dialog",
|
||||
"moodle-core-notification-exception",
|
||||
|
|
|
@ -167,6 +167,15 @@ abstract class assign_feedback_plugin extends assign_plugin {
|
|||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports injecting content into the review panel of the grading app.
|
||||
*
|
||||
* @return bool True if this plugin will add content to the review panel of the grading app.
|
||||
*/
|
||||
public function supports_review_panel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a batch operations form
|
||||
*
|
||||
|
|
|
@ -833,16 +833,12 @@ class assign_grading_table extends table_sql implements renderable {
|
|||
$gradingdisabled = $this->assignment->grading_disabled($row->id);
|
||||
|
||||
if (!$this->is_downloading() && $this->hasgrade) {
|
||||
$name = $this->assignment->fullname($row);
|
||||
$icon = $this->output->pix_icon('gradefeedback',
|
||||
get_string('gradeuser', 'assign', $name),
|
||||
'mod_assign');
|
||||
$urlparams = array('id' => $this->assignment->get_course_module()->id,
|
||||
'rownum'=>$this->rownum,
|
||||
'action' => 'grade',
|
||||
'useridlistid' => $this->assignment->get_useridlist_key_id());
|
||||
'rownum'=> 0,
|
||||
'action' => 'grader',
|
||||
'userid' => $row->userid);
|
||||
$url = new moodle_url('/mod/assign/view.php', $urlparams);
|
||||
$link = $this->output->action_link($url, $icon);
|
||||
$link = '<a href="' . $url . '" class="btn btn-primary">' . get_string('grade') . '</a>';
|
||||
$grade .= $link . $separator;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,6 +101,8 @@ $string['batchsetmarkingworkflowstateforusers'] = 'Set marking workflow state fo
|
|||
$string['blindmarking'] = 'Blind marking';
|
||||
$string['blindmarkingenabledwarning'] = 'Blind marking is enabled for this activity.';
|
||||
$string['blindmarking_help'] = 'Blind marking hides the identity of students from markers. Blind marking settings will be locked once a submission or grade has been made in relation to this assignment.';
|
||||
$string['changeuser'] = 'Change user';
|
||||
$string['changefilters'] = 'Change filters';
|
||||
$string['changegradewarning'] = 'This assignment has graded submissions and changing the grade will not automatically re-calculate existing submission grades. You must re-grade all existing submissions, if you wish to change the grade.';
|
||||
$string['choosegradingaction'] = 'Grading action';
|
||||
$string['choosemarker'] = 'Choose...';
|
||||
|
@ -120,6 +122,7 @@ $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['cutoffdatecolon'] = 'Cut-off date: {$a}';
|
||||
$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.';
|
||||
$string['cutoffdatefromdatevalidation'] = 'Cut-off date must be after the allow submissions from date.';
|
||||
|
@ -131,6 +134,7 @@ $string['description'] = 'Description';
|
|||
$string['downloadall'] = 'Download all submissions';
|
||||
$string['download all submissions'] = 'Download all submissions in a zip file.';
|
||||
$string['duedate'] = 'Due date';
|
||||
$string['duedatecolon'] = 'Due date: {$a}';
|
||||
$string['duedate_help'] = 'This is when the assignment is due. Submissions will still be allowed after this date but any assignments submitted after this date are marked as late. To prevent submissions after a certain date - set the assignment cut off date.';
|
||||
$string['duedateno'] = 'No due date';
|
||||
$string['submissionempty'] = 'Nothing was submitted';
|
||||
|
@ -185,6 +189,7 @@ $string['gradersubmissionupdatedsmall'] = '{$a->username} has updated their subm
|
|||
$string['gradeuser'] = 'Grade {$a}';
|
||||
$string['grantextension'] = 'Grant extension';
|
||||
$string['grantextensionforusers'] = 'Grant extension for {$a} students';
|
||||
$string['groupsubmissionsettings'] = 'Group submission settings';
|
||||
$string['enabled'] = 'Enabled';
|
||||
$string['errornosubmissions'] = 'There are no submissions to download';
|
||||
$string['errorquickgradingvsadvancedgrading'] = 'The grades were not saved because this assignment is currently using advanced grading';
|
||||
|
@ -205,6 +210,7 @@ $string['feedbackpluginforgradebook'] = 'Feedback plugin that will push comments
|
|||
$string['feedbackpluginforgradebook_help'] = 'Only one assignment feedback plugin can push feedback into the gradebook.';
|
||||
$string['feedbackplugin'] = 'Feedback plugin';
|
||||
$string['feedbacksettings'] = 'Feedback settings';
|
||||
$string['feedbacktypes'] = 'Feedback types';
|
||||
$string['filesubmissions'] = 'File submissions';
|
||||
$string['filter'] = 'Filter';
|
||||
$string['filternone'] = 'No filter';
|
||||
|
@ -223,6 +229,7 @@ $string['gradeoutofhelp_help'] = 'Enter the grade for the student\'s submission
|
|||
$string['gradestudent'] = 'Grade student: (id={$a->id}, fullname={$a->fullname}). ';
|
||||
$string['grading'] = 'Grading';
|
||||
$string['gradingchangessaved'] = 'The grade changes were saved';
|
||||
$string['gradechangessaveddetail'] = 'The changes to the grade and feedback were saved';
|
||||
$string['gradingmethodpreview'] = 'Grading criteria';
|
||||
$string['gradingoptions'] = 'Options';
|
||||
$string['gradingstatus'] = 'Grading status';
|
||||
|
@ -239,6 +246,7 @@ $string['lastmodifiedsubmission'] = 'Last modified (submission)';
|
|||
$string['lastmodifiedgrade'] = 'Last modified (grade)';
|
||||
$string['latesubmissions'] = 'Late submissions';
|
||||
$string['latesubmissionsaccepted'] = 'Allowed until {$a}';
|
||||
$string['loading'] = 'Loading...';
|
||||
$string['locksubmissionforstudent'] = 'Prevent any more submissions for student: (id={$a->id}, fullname={$a->fullname}).';
|
||||
$string['locksubmissions'] = 'Lock submissions';
|
||||
$string['manageassignfeedbackplugins'] = 'Manage assignment feedback plugins';
|
||||
|
@ -284,6 +292,7 @@ $string['multipleteamsgrader'] = 'Member of more than one group, so unable to ma
|
|||
$string['mysubmission'] = 'My submission: ';
|
||||
$string['newsubmissions'] = 'Assignments submitted';
|
||||
$string['noattempt'] = 'No attempt';
|
||||
$string['nofilters'] = 'No filters';
|
||||
$string['nofiles'] = 'No files. ';
|
||||
$string['nograde'] = 'No grade. ';
|
||||
$string['nolatesubmissions'] = 'No late submissions accepted. ';
|
||||
|
@ -299,6 +308,7 @@ $string['notgradedyet'] = 'Not graded yet';
|
|||
$string['notsubmittedyet'] = 'Not submitted yet';
|
||||
$string['notifications'] = 'Notifications';
|
||||
$string['nousersselected'] = 'No users selected';
|
||||
$string['nousers'] = 'No users';
|
||||
$string['numberofdraftsubmissions'] = 'Drafts';
|
||||
$string['numberofparticipants'] = 'Participants';
|
||||
$string['numberofsubmittedassignments'] = 'Submitted';
|
||||
|
@ -311,6 +321,7 @@ $string['overdue'] = '<font color="red">Assignment is overdue by: {$a}</font>';
|
|||
$string['outlinegrade'] = 'Grade: {$a}';
|
||||
$string['page-mod-assign-x'] = 'Any assignment module page';
|
||||
$string['page-mod-assign-view'] = 'Assignment module main and submission page';
|
||||
$string['paramtimeremaining'] = '{$a} remaining';
|
||||
$string['participant'] = 'Participant';
|
||||
$string['pluginadministration'] = 'Assignment administration';
|
||||
$string['pluginname'] = 'Assignment';
|
||||
|
@ -335,10 +346,12 @@ $string['reverttodraftforstudent'] = 'Revert submission to draft for student: (i
|
|||
$string['reverttodraft'] = 'Revert the submission to draft status.';
|
||||
$string['reverttodraftshort'] = 'Revert the submission to draft';
|
||||
$string['reviewed'] = 'Reviewed';
|
||||
$string['saveallquickgradingchanges'] = 'Save all quick grading changes';
|
||||
$string['saveandcontinue'] = 'Save and continue';
|
||||
$string['savechanges'] = 'Save changes';
|
||||
$string['savegradingresult'] = 'Grade';
|
||||
$string['saveallquickgradingchanges'] = 'Save all quick grading changes';
|
||||
$string['savenext'] = 'Save and show next';
|
||||
$string['savingchanges'] = 'Saving changes...';
|
||||
$string['scale'] = 'Scale';
|
||||
$string['search:activity'] = 'Assignment activities';
|
||||
$string['sendstudentnotificationsdefault'] = 'Default setting for "Notify students"';
|
||||
|
@ -375,6 +388,7 @@ $string['submissioncopiedsmall'] = 'You have copied your previous assignment sub
|
|||
$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['submissionlog'] = 'Student: {$a->fullname}, Status: {$a->status}';
|
||||
$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:';
|
||||
|
@ -419,6 +433,7 @@ $string['submissionstatus'] = 'Submission status';
|
|||
$string['submissionstatus_submitted'] = 'Submitted for grading';
|
||||
$string['submissionsummary'] = '{$a->status}. Last modified on {$a->timemodified}';
|
||||
$string['submissionteam'] = 'Group';
|
||||
$string['submissiontypes'] = 'Submission types';
|
||||
$string['submission'] = 'Submission';
|
||||
$string['submitaction'] = 'Submit';
|
||||
$string['submitforgrading'] = 'Submit for grading';
|
||||
|
@ -439,11 +454,15 @@ $string['teamsubmissiongroupingid_help'] = 'This is the grouping that the assign
|
|||
$string['textinstructions'] = 'Assignment instructions';
|
||||
$string['timemodified'] = 'Last modified';
|
||||
$string['timeremaining'] = 'Time remaining';
|
||||
$string['timeremainingcolon'] = 'Time remaining: {$a}';
|
||||
$string['togglezoom'] = 'Zoom in/out of region';
|
||||
$string['ungroupedusers'] = 'The setting \'Require group to make submission\' is enabled and some users are either not a member of any group, or are a member of more than one group, so are unable to make submissions.';
|
||||
$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['unsavedchanges'] = 'Unsaved changes';
|
||||
$string['unsavedchangesquestion'] = 'There are unsaved changes to grades or feedback. Do you want to save the changes and continue?';
|
||||
$string['updategrade'] = 'Update grade';
|
||||
$string['updatetable'] = 'Save and update table';
|
||||
$string['upgradenotimplemented'] = 'Upgrade not implemented in plugin ({$a->type} {$a->subtype})';
|
||||
|
@ -452,6 +471,7 @@ $string['useridlistnotcached'] = 'The grade changes were NOT saved, as it was no
|
|||
$string['userswhoneedtosubmit'] = 'Users who need to submit: {$a}';
|
||||
$string['usergrade'] = 'User grade';
|
||||
$string['validmarkingworkflowstates'] = 'Valid marking workflow states';
|
||||
$string['viewadifferentattempt'] = 'View a different attempt';
|
||||
$string['viewbatchsetmarkingworkflowstate'] = 'View batch set marking workflow state page.';
|
||||
$string['viewbatchmarkingallocation'] = 'View batch set marking allocation page.';
|
||||
$string['viewfeedback'] = 'View feedback';
|
||||
|
@ -459,7 +479,7 @@ $string['viewfeedbackforuser'] = 'View feedback for user: {$a}';
|
|||
$string['viewfullgradingpage'] = 'Open the full grading page to provide feedback';
|
||||
$string['viewgradebook'] = 'View gradebook';
|
||||
$string['viewgradingformforstudent'] = 'View grading page for student: (id={$a->id}, fullname={$a->fullname}).';
|
||||
$string['viewgrading'] = 'View/grade all submissions';
|
||||
$string['viewgrading'] = 'View all submissions';
|
||||
$string['viewownsubmissionform'] = 'View own submit assignment page.';
|
||||
$string['viewownsubmissionstatus'] = 'View own submission status page.';
|
||||
$string['viewsubmissionforuser'] = 'View submission for user: {$a}';
|
||||
|
@ -469,7 +489,4 @@ $string['viewsummary'] = 'View summary';
|
|||
$string['viewsubmissiongradingtable'] = 'View submission grading table.';
|
||||
$string['viewrevealidentitiesconfirm'] = 'View reveal student identities confirmation page.';
|
||||
$string['workflowfilter'] = 'Workflow filter';
|
||||
$string['submissiontypes'] = 'Submission types';
|
||||
$string['feedbacktypes'] = 'Feedback types';
|
||||
$string['groupsubmissionsettings'] = 'Group submission settings';
|
||||
$string['submissionlog'] = 'Student: {$a->fullname}, Status: {$a->status}';
|
||||
$string['xofy'] = '{$a->x} of {$a->y}';
|
||||
|
|
|
@ -1474,3 +1474,36 @@ function assign_pluginfile($course,
|
|||
}
|
||||
send_stored_file($file, 0, 0, $forcedownload, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve the grading panel as a fragment.
|
||||
*
|
||||
* @param array $args List of named arguments for the fragment loader.
|
||||
* @return string
|
||||
*/
|
||||
function mod_assign_output_fragment_gradingpanel($args) {
|
||||
global $CFG;
|
||||
|
||||
$context = $args['context'];
|
||||
|
||||
if ($context->contextlevel != CONTEXT_MODULE) {
|
||||
return null;
|
||||
}
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
$assign = new assign($context, null, null);
|
||||
|
||||
$userid = clean_param($args['userid'], PARAM_INT);
|
||||
$attemptnumber = clean_param($args['attemptnumber'], PARAM_INT);
|
||||
$formdata = array();
|
||||
if (!empty($args['jsonformdata'])) {
|
||||
$serialiseddata = json_decode($args['jsonformdata']);
|
||||
parse_str($serialiseddata, $formdata);
|
||||
}
|
||||
$viewargs = array(
|
||||
'userid' => $userid,
|
||||
'attemptnumber' => $attemptnumber,
|
||||
'formdata' => $formdata
|
||||
);
|
||||
|
||||
return $assign->view('gradingpanel', $viewargs);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,8 @@ require_once($CFG->dirroot . '/mod/assign/gradingtable.php');
|
|||
require_once($CFG->libdir . '/eventslib.php');
|
||||
require_once($CFG->libdir . '/portfolio/caller.php');
|
||||
|
||||
use \mod_assign\output\grading_app;
|
||||
|
||||
/**
|
||||
* Standard base class for mod_assign (assignment types).
|
||||
*
|
||||
|
@ -203,7 +205,7 @@ class assign {
|
|||
public function register_return_link($action, $params) {
|
||||
global $PAGE;
|
||||
$params['action'] = $action;
|
||||
$currenturl = $PAGE->url;
|
||||
$currenturl = new moodle_url('/mod/assign/view.php');
|
||||
|
||||
$currenturl->params($params);
|
||||
$PAGE->set_url($currenturl);
|
||||
|
@ -406,9 +408,10 @@ class assign {
|
|||
* the settings for the assignment and the status of the assignment.
|
||||
*
|
||||
* @param string $action The current action if any.
|
||||
* @param array $args Optional arguments to pass to the view (instead of getting them from GET and POST).
|
||||
* @return string - The page output.
|
||||
*/
|
||||
public function view($action='') {
|
||||
public function view($action='', $args = array()) {
|
||||
global $PAGE;
|
||||
|
||||
$o = '';
|
||||
|
@ -548,6 +551,8 @@ class assign {
|
|||
} else if ($action == 'quickgradingresult') {
|
||||
$mform = null;
|
||||
$o .= $this->view_quickgrading_result($message);
|
||||
} else if ($action == 'gradingpanel') {
|
||||
$o .= $this->view_single_grading_panel($args);
|
||||
} else if ($action == 'grade') {
|
||||
$o .= $this->view_single_grade_page($mform);
|
||||
} else if ($action == 'viewpluginassignfeedback') {
|
||||
|
@ -556,6 +561,8 @@ class assign {
|
|||
$o .= $this->view_plugin_content('assignsubmission');
|
||||
} else if ($action == 'editsubmission') {
|
||||
$o .= $this->view_edit_submission_page($mform, $notices);
|
||||
} else if ($action == 'grader') {
|
||||
$o .= $this->view_grader();
|
||||
} else if ($action == 'grading') {
|
||||
$o .= $this->view_grading_page();
|
||||
} else if ($action == 'downloadall') {
|
||||
|
@ -1413,6 +1420,85 @@ class assign {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submission status/grading status for all submissions in this assignment.
|
||||
* These statuses match the available filters (requiregrading, submitted, notsubmitted).
|
||||
* If this is a group assignment, group info is also returned.
|
||||
*
|
||||
* @param int $currentgroup
|
||||
* @param bool $idsonly
|
||||
* @return array List of user records with extra fields 'submitted', 'notsubmitted', 'requiregrading', 'groupid', 'groupname'
|
||||
*/
|
||||
public function list_participants_with_filter_status_and_group($currentgroup) {
|
||||
global $DB;
|
||||
|
||||
$participants = $this->list_participants($currentgroup, false);
|
||||
|
||||
if (empty($participants)) {
|
||||
return $participants;
|
||||
}
|
||||
|
||||
list($insql, $params) = $DB->get_in_or_equal(array_keys($participants), SQL_PARAMS_NAMED);
|
||||
|
||||
$assignid = $this->get_instance()->id;
|
||||
$params['assignmentid1'] = $assignid;
|
||||
$params['assignmentid2'] = $assignid;
|
||||
|
||||
$sql = 'SELECT u.id, s.status, s.timemodified AS stime, g.timemodified AS gtime, g.grade FROM {user} u
|
||||
LEFT JOIN {assign_submission} s
|
||||
ON u.id = s.userid
|
||||
AND s.assignment = :assignmentid1
|
||||
AND s.latest = 1
|
||||
LEFT JOIN {assign_grades} g
|
||||
ON u.id = g.userid
|
||||
AND g.assignment = :assignmentid2
|
||||
AND g.attemptnumber = s.attemptnumber
|
||||
WHERE u.id ' . $insql;
|
||||
|
||||
$records = $DB->get_records_sql($sql, $params);
|
||||
|
||||
if ($this->get_instance()->teamsubmission) {
|
||||
// Get all groups.
|
||||
$allgroups = groups_get_all_groups($this->get_course()->id,
|
||||
array_keys($participants),
|
||||
$this->get_instance()->teamsubmissiongroupingid,
|
||||
'DISTINCT g.id, g.name');
|
||||
|
||||
}
|
||||
foreach ($participants as $userid => $participant) {
|
||||
$participants[$userid]->fullname = $this->fullname($participant);
|
||||
$participants[$userid]->submitted = false;
|
||||
$participants[$userid]->requiregrading = false;
|
||||
}
|
||||
|
||||
foreach ($records as $userid => $submissioninfo) {
|
||||
// These filters are 100% the same as the ones in the grading table SQL.
|
||||
$submitted = false;
|
||||
$requiregrading = false;
|
||||
|
||||
if (!empty($submissioninfo->stime) && $submissioninfo->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED) {
|
||||
$submitted = true;
|
||||
}
|
||||
|
||||
if ($submitted && ($submissioninfo->stime >= $submissioninfo->gtime ||
|
||||
empty($submissioninfo->gtime) ||
|
||||
$submissioninfo->grade === null)) {
|
||||
$requiregrading = true;
|
||||
}
|
||||
|
||||
$participants[$userid]->submitted = $submitted;
|
||||
$participants[$userid]->requiregrading = $requiregrading;
|
||||
if ($this->get_instance()->teamsubmission) {
|
||||
$group = $this->get_submission_group($userid);
|
||||
if ($group) {
|
||||
$participants[$userid]->groupid = $group->id;
|
||||
$participants[$userid]->groupname = $group->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $participants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of users enrolled in the current course with the specified permission and group.
|
||||
* 0 for no group.
|
||||
|
@ -1429,7 +1515,11 @@ class assign {
|
|||
|
||||
$key = $this->context->id . '-' . $currentgroup . '-' . $this->show_only_active_users();
|
||||
if (!isset($this->participants[$key])) {
|
||||
$users = get_enrolled_users($this->context, 'mod/assign:submit', $currentgroup, 'u.*', null, null, null,
|
||||
$order = 'u.lastname, u.firstname, u.id';
|
||||
if ($this->is_blind_marking()) {
|
||||
$order = 'u.id';
|
||||
}
|
||||
$users = get_enrolled_users($this->context, 'mod/assign:submit', $currentgroup, 'u.*', $order, null, null,
|
||||
$this->show_only_active_users());
|
||||
|
||||
$cm = $this->get_course_module();
|
||||
|
@ -2836,7 +2926,7 @@ class assign {
|
|||
if ($this->output) {
|
||||
return $this->output;
|
||||
}
|
||||
$this->output = $PAGE->get_renderer('mod_assign');
|
||||
$this->output = $PAGE->get_renderer('mod_assign', null, RENDERER_TARGET_GENERAL);
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
|
@ -3047,6 +3137,166 @@ class assign {
|
|||
return $DB->get_record('assign_grades', $params, '*', MUST_EXIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the grading page for a single user submission.
|
||||
*
|
||||
* @param array $args Optional args array (better than pulling args from _GET and _POST)
|
||||
* @return string
|
||||
*/
|
||||
protected function view_single_grading_panel($args) {
|
||||
global $DB, $CFG, $SESSION, $PAGE;
|
||||
|
||||
$o = '';
|
||||
$instance = $this->get_instance();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/assign/gradeform.php');
|
||||
|
||||
// Need submit permission to submit an assignment.
|
||||
require_capability('mod/assign:grade', $this->context);
|
||||
|
||||
// If userid is passed - we are only grading a single student.
|
||||
$userid = $args['userid'];
|
||||
$attemptnumber = $args['attemptnumber'];
|
||||
|
||||
$rownum = 0;
|
||||
$useridlist = array($userid);
|
||||
|
||||
$last = true;
|
||||
// This variation on the url will link direct to this student, with no next/previous links.
|
||||
// The benefit is the url will be the same every time for this student, so Atto autosave drafts can match up.
|
||||
$returnparams = array('userid' => $userid, 'rownum' => 0, 'useridlistid' => 0);
|
||||
$this->register_return_link('grade', $returnparams);
|
||||
|
||||
$user = $DB->get_record('user', array('id' => $userid));
|
||||
$submission = $this->get_user_submission($userid, false, $attemptnumber);
|
||||
$submissiongroup = null;
|
||||
$teamsubmission = null;
|
||||
$notsubmitted = array();
|
||||
if ($instance->teamsubmission) {
|
||||
$teamsubmission = $this->get_group_submission($userid, 0, false, $attemptnumber);
|
||||
$submissiongroup = $this->get_submission_group($userid);
|
||||
$groupid = 0;
|
||||
if ($submissiongroup) {
|
||||
$groupid = $submissiongroup->id;
|
||||
}
|
||||
$notsubmitted = $this->get_submission_group_members_who_have_not_submitted($groupid, false);
|
||||
|
||||
}
|
||||
|
||||
// Get the requested grade.
|
||||
$grade = $this->get_user_grade($userid, false, $attemptnumber);
|
||||
$flags = $this->get_user_flags($userid, false);
|
||||
if ($this->can_view_submission($userid)) {
|
||||
$gradelocked = ($flags && $flags->locked) || $this->grading_disabled($userid);
|
||||
$extensionduedate = null;
|
||||
if ($flags) {
|
||||
$extensionduedate = $flags->extensionduedate;
|
||||
}
|
||||
$showedit = $this->submissions_open($userid) && ($this->is_any_submission_plugin_enabled());
|
||||
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_course_context());
|
||||
$usergroups = $this->get_all_groups($user->id);
|
||||
|
||||
$submissionstatus = new assign_submission_status_compact($instance->allowsubmissionsfromdate,
|
||||
$instance->alwaysshowdescription,
|
||||
$submission,
|
||||
$instance->teamsubmission,
|
||||
$teamsubmission,
|
||||
$submissiongroup,
|
||||
$notsubmitted,
|
||||
$this->is_any_submission_plugin_enabled(),
|
||||
$gradelocked,
|
||||
$this->is_graded($userid),
|
||||
$instance->duedate,
|
||||
$instance->cutoffdate,
|
||||
$this->get_submission_plugins(),
|
||||
$this->get_return_action(),
|
||||
$this->get_return_params(),
|
||||
$this->get_course_module()->id,
|
||||
$this->get_course()->id,
|
||||
assign_submission_status::GRADER_VIEW,
|
||||
$showedit,
|
||||
false,
|
||||
$viewfullnames,
|
||||
$extensionduedate,
|
||||
$this->get_context(),
|
||||
$this->is_blind_marking(),
|
||||
'',
|
||||
$instance->attemptreopenmethod,
|
||||
$instance->maxattempts,
|
||||
$this->get_grading_status($userid),
|
||||
$instance->preventsubmissionnotingroup,
|
||||
$usergroups);
|
||||
$o .= $this->get_renderer()->render($submissionstatus);
|
||||
}
|
||||
|
||||
if ($grade) {
|
||||
$data = new stdClass();
|
||||
if ($grade->grade !== null && $grade->grade >= 0) {
|
||||
$data->grade = format_float($grade->grade, 2);
|
||||
}
|
||||
} else {
|
||||
$data = new stdClass();
|
||||
$data->grade = '';
|
||||
}
|
||||
|
||||
if (!empty($flags->workflowstate)) {
|
||||
$data->workflowstate = $flags->workflowstate;
|
||||
}
|
||||
if (!empty($flags->allocatedmarker)) {
|
||||
$data->allocatedmarker = $flags->allocatedmarker;
|
||||
}
|
||||
|
||||
// Warning if required.
|
||||
$allsubmissions = $this->get_all_submissions($userid);
|
||||
|
||||
if ($attemptnumber != -1 && ($attemptnumber + 1) != count($allsubmissions)) {
|
||||
$params = array('attemptnumber'=>$attemptnumber + 1,
|
||||
'totalattempts'=>count($allsubmissions));
|
||||
$message = get_string('editingpreviousfeedbackwarning', 'assign', $params);
|
||||
$o .= $this->get_renderer()->notification($message);
|
||||
}
|
||||
|
||||
$pagination = array('rownum'=>$rownum,
|
||||
'useridlistid'=>0,
|
||||
'last'=>$last,
|
||||
'userid'=>$userid,
|
||||
'attemptnumber'=>$attemptnumber,
|
||||
'gradingpanel' => true);
|
||||
|
||||
if (!empty($args['formdata'])) {
|
||||
$data = (array) $data;
|
||||
$data = (object) array_merge($data, $args['formdata']);
|
||||
}
|
||||
$formparams = array($this, $data, $pagination);
|
||||
$mform = new mod_assign_grade_form(null,
|
||||
$formparams,
|
||||
'post',
|
||||
'',
|
||||
array('class'=>'gradeform'));
|
||||
|
||||
if (!empty($args['formdata'])) {
|
||||
// If we were passed form data - we want the form to check the data
|
||||
// and show errors.
|
||||
$mform->is_validated();
|
||||
}
|
||||
$o .= $this->get_renderer()->heading(get_string('grade'), 3);
|
||||
$o .= $this->get_renderer()->render(new assign_form('gradingform', $mform));
|
||||
|
||||
if (count($allsubmissions) > 1) {
|
||||
$allgrades = $this->get_all_grades($userid);
|
||||
$history = new assign_attempt_history_chooser($allsubmissions,
|
||||
$allgrades,
|
||||
$this->get_course_module()->id,
|
||||
$userid);
|
||||
|
||||
$o .= $this->get_renderer()->render($history);
|
||||
}
|
||||
|
||||
\mod_assign\event\grading_form_viewed::create_from_user($this, $user)->trigger();
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the grading page for a single user submission.
|
||||
*
|
||||
|
@ -3195,7 +3445,7 @@ class assign {
|
|||
// Warning if required.
|
||||
$allsubmissions = $this->get_all_submissions($userid);
|
||||
|
||||
if ($attemptnumber != -1) {
|
||||
if ($attemptnumber != -1 && ($attemptnumber + 1) != count($allsubmissions)) {
|
||||
$params = array('attemptnumber'=>$attemptnumber + 1,
|
||||
'totalattempts'=>count($allsubmissions));
|
||||
$message = get_string('editingpreviousfeedbackwarning', 'assign', $params);
|
||||
|
@ -3485,6 +3735,37 @@ class assign {
|
|||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* View entire grader app.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function view_grader() {
|
||||
global $USER, $PAGE;
|
||||
|
||||
$o = '';
|
||||
// Need submit permission to submit an assignment.
|
||||
$this->require_view_grades();
|
||||
|
||||
$PAGE->set_pagelayout('embedded');
|
||||
|
||||
$PAGE->set_title($this->get_context()->get_context_name());
|
||||
|
||||
$o .= $this->get_renderer()->header();
|
||||
|
||||
$userid = optional_param('userid', 0, PARAM_INT);
|
||||
|
||||
$currentgroup = groups_get_activity_group($this->get_course_module(), true);
|
||||
$framegrader = new grading_app($userid, $currentgroup, $this);
|
||||
|
||||
$o .= $this->get_renderer()->render($framegrader);
|
||||
|
||||
$o .= $this->view_footer();
|
||||
|
||||
\mod_assign\event\grading_table_viewed::create_from_assign($this)->trigger();
|
||||
|
||||
return $o;
|
||||
}
|
||||
/**
|
||||
* View entire grading page.
|
||||
*
|
||||
|
@ -3566,10 +3847,10 @@ class assign {
|
|||
public function fullname($user) {
|
||||
if ($this->is_blind_marking()) {
|
||||
$hasviewblind = has_capability('mod/assign:viewblinddetails', $this->get_context());
|
||||
$uniqueid = $this->get_uniqueid_for_user($user->id);
|
||||
if ($hasviewblind) {
|
||||
return fullname($user);
|
||||
return get_string('participant', 'assign') . ' ' . $uniqueid . ' (' . fullname($user) . ')';
|
||||
} else {
|
||||
$uniqueid = $this->get_uniqueid_for_user($user->id);
|
||||
return get_string('participant', 'assign') . ' ' . $uniqueid;
|
||||
}
|
||||
} else {
|
||||
|
@ -3666,15 +3947,10 @@ class assign {
|
|||
public function can_view_group_submission($groupid) {
|
||||
global $USER;
|
||||
|
||||
if (has_capability('mod/assign:grade', $this->context)) {
|
||||
return true;
|
||||
}
|
||||
if (!is_enrolled($this->get_course_context(), $USER->id)) {
|
||||
return false;
|
||||
}
|
||||
$members = $this->get_submission_group_members($groupid, true);
|
||||
foreach ($members as $member) {
|
||||
if ($member->id == $USER->id) {
|
||||
// If we can view any members submission, we can view the submission for the group.
|
||||
if ($this->can_view_submission($member->id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -3693,12 +3969,12 @@ class assign {
|
|||
if (!$this->is_active_user($userid) && !has_capability('moodle/course:viewsuspendedusers', $this->context)) {
|
||||
return false;
|
||||
}
|
||||
if (has_any_capability(array('mod/assign:viewgrades', 'mod/assign:grade'), $this->context)) {
|
||||
return true;
|
||||
}
|
||||
if (!is_enrolled($this->get_course_context(), $userid)) {
|
||||
return false;
|
||||
}
|
||||
if (has_any_capability(array('mod/assign:viewgrades', 'mod/assign:grade'), $this->context)) {
|
||||
return true;
|
||||
}
|
||||
if ($userid == $USER->id && has_capability('mod/assign:submit', $this->context)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -6226,11 +6502,12 @@ class assign {
|
|||
global $USER, $CFG, $SESSION;
|
||||
$settings = $this->get_instance();
|
||||
|
||||
$rownum = $params['rownum'];
|
||||
$last = $params['last'];
|
||||
$useridlistid = $params['useridlistid'];
|
||||
$userid = $params['userid'];
|
||||
$attemptnumber = $params['attemptnumber'];
|
||||
$rownum = isset($params['rownum']) ? $params['rownum'] : 0;
|
||||
$last = isset($params['last']) ? $params['last'] : true;
|
||||
$useridlistid = isset($params['useridlistid']) ? $params['useridlistid'] : 0;
|
||||
$userid = isset($params['userid']) ? $params['userid'] : 0;
|
||||
$attemptnumber = isset($params['attemptnumber']) ? $params['attemptnumber'] : 0;
|
||||
$gradingpanel = !empty($params['gradingpanel']);
|
||||
if (!$userid) {
|
||||
$useridlistkey = $this->get_useridlist_key($useridlistid);
|
||||
if (empty($SESSION->mod_assign_useridlist[$useridlistkey])) {
|
||||
|
@ -6373,7 +6650,7 @@ class assign {
|
|||
$mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE);
|
||||
$mform->disabledIf('allocatedmarker', 'workflowstate', 'eq', ASSIGN_MARKING_WORKFLOW_STATE_RELEASED);
|
||||
}
|
||||
|
||||
$gradestring = '<span class="currentgrade">' . $gradestring . '</span>';
|
||||
$mform->addElement('static', 'currentgrade', get_string('currentgrade', 'assign'), $gradestring);
|
||||
|
||||
if (count($useridlist) > 1) {
|
||||
|
@ -6431,7 +6708,12 @@ class assign {
|
|||
$mform->setDefault('addattempt', 0);
|
||||
}
|
||||
}
|
||||
$mform->addElement('selectyesno', 'sendstudentnotifications', get_string('sendstudentnotifications', 'assign'));
|
||||
if (!$gradingpanel) {
|
||||
$mform->addElement('selectyesno', 'sendstudentnotifications', get_string('sendstudentnotifications', 'assign'));
|
||||
} else {
|
||||
$mform->addElement('hidden', 'sendstudentnotifications', get_string('sendstudentnotifications', 'assign'));
|
||||
$mform->setType('sendstudentnotifications', PARAM_BOOL);
|
||||
}
|
||||
// Get assignment visibility information for student.
|
||||
$modinfo = get_fast_modinfo($settings->course, $userid);
|
||||
$cm = $modinfo->get_cm($this->get_course_module()->id);
|
||||
|
@ -6451,29 +6733,32 @@ class assign {
|
|||
$mform->addElement('hidden', 'action', 'submitgrade');
|
||||
$mform->setType('action', PARAM_ALPHA);
|
||||
|
||||
$buttonarray=array();
|
||||
$name = get_string('savechanges', 'assign');
|
||||
$buttonarray[] = $mform->createElement('submit', 'savegrade', $name);
|
||||
if (!$last) {
|
||||
$name = get_string('savenext', 'assign');
|
||||
$buttonarray[] = $mform->createElement('submit', 'saveandshownext', $name);
|
||||
}
|
||||
$buttonarray[] = $mform->createElement('cancel', 'cancelbutton', get_string('cancel'));
|
||||
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
|
||||
$mform->closeHeaderBefore('buttonar');
|
||||
$buttonarray=array();
|
||||
if (!$gradingpanel) {
|
||||
|
||||
if ($rownum > 0) {
|
||||
$name = get_string('previous', 'assign');
|
||||
$buttonarray[] = $mform->createElement('submit', 'nosaveandprevious', $name);
|
||||
}
|
||||
$buttonarray=array();
|
||||
$name = get_string('savechanges', 'assign');
|
||||
$buttonarray[] = $mform->createElement('submit', 'savegrade', $name);
|
||||
if (!$last) {
|
||||
$name = get_string('savenext', 'assign');
|
||||
$buttonarray[] = $mform->createElement('submit', 'saveandshownext', $name);
|
||||
}
|
||||
$buttonarray[] = $mform->createElement('cancel', 'cancelbutton', get_string('cancel'));
|
||||
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
|
||||
$mform->closeHeaderBefore('buttonar');
|
||||
$buttonarray=array();
|
||||
|
||||
if (!$last) {
|
||||
$name = get_string('nosavebutnext', 'assign');
|
||||
$buttonarray[] = $mform->createElement('submit', 'nosaveandnext', $name);
|
||||
}
|
||||
if (!empty($buttonarray)) {
|
||||
$mform->addGroup($buttonarray, 'navar', '', array(' '), false);
|
||||
if ($rownum > 0) {
|
||||
$name = get_string('previous', 'assign');
|
||||
$buttonarray[] = $mform->createElement('submit', 'nosaveandprevious', $name);
|
||||
}
|
||||
|
||||
if (!$last) {
|
||||
$name = get_string('nosavebutnext', 'assign');
|
||||
$buttonarray[] = $mform->createElement('submit', 'nosaveandnext', $name);
|
||||
}
|
||||
if (!empty($buttonarray)) {
|
||||
$mform->addGroup($buttonarray, 'navar', '', array(' '), false);
|
||||
}
|
||||
}
|
||||
// The grading form does not work well with shortforms.
|
||||
$mform->setDisableShortforms();
|
||||
|
@ -7057,7 +7342,7 @@ class assign {
|
|||
} else {
|
||||
$submission = $this->get_user_submission($userid, false, $data->attemptnumber);
|
||||
}
|
||||
if ($instance->teamsubmission && $data->applytoall) {
|
||||
if ($instance->teamsubmission && !empty($data->applytoall)) {
|
||||
$groupid = 0;
|
||||
if ($this->get_submission_group($userid)) {
|
||||
$group = $this->get_submission_group($userid);
|
||||
|
|
|
@ -488,6 +488,15 @@ class assign_submission_status implements renderable {
|
|||
$this->usergroups = $usergroups;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Renderable submission status
|
||||
* @package mod_assign
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class assign_submission_status_compact extends assign_submission_status implements renderable {
|
||||
// Compact view of the submission status. Not in a table etc.
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to output the attempt history for a particular assignment.
|
||||
|
@ -556,6 +565,86 @@ class assign_attempt_history implements renderable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to output the attempt history chooser for a particular assignment.
|
||||
*
|
||||
* @package mod_assign
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class assign_attempt_history_chooser implements renderable, templatable {
|
||||
|
||||
/** @var array submissions - The list of previous attempts */
|
||||
public $submissions = array();
|
||||
/** @var array grades - The grades for the previous attempts */
|
||||
public $grades = array();
|
||||
/** @var int coursemoduleid - The cmid for the assignment */
|
||||
public $coursemoduleid = 0;
|
||||
/** @var int userid - The current userid */
|
||||
public $userid = 0;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $submissions
|
||||
* @param array $grades
|
||||
* @param int $coursemoduleid
|
||||
* @param int $userid
|
||||
*/
|
||||
public function __construct($submissions,
|
||||
$grades,
|
||||
$coursemoduleid,
|
||||
$userid) {
|
||||
$this->submissions = $submissions;
|
||||
$this->grades = $grades;
|
||||
$this->coursemoduleid = $coursemoduleid;
|
||||
$this->userid = $userid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to export the renderer data in a format that is suitable for a
|
||||
* mustache template.
|
||||
*
|
||||
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
|
||||
* @return stdClass|array
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
// Show newest to oldest.
|
||||
$export = (object) $this;
|
||||
$export->submissions = array_reverse($export->submissions);
|
||||
$export->submissioncount = count($export->submissions);
|
||||
|
||||
foreach ($export->submissions as $i => $submission) {
|
||||
$grade = null;
|
||||
foreach ($export->grades as $onegrade) {
|
||||
if ($onegrade->attemptnumber == $submission->attemptnumber) {
|
||||
$submission->grade = $onegrade;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$submission) {
|
||||
$submission = new stdClass();
|
||||
}
|
||||
|
||||
$editbtn = '';
|
||||
|
||||
if ($submission->timemodified) {
|
||||
$submissionsummary = userdate($submission->timemodified);
|
||||
} else {
|
||||
$submissionsummary = get_string('nosubmission', 'assign');
|
||||
}
|
||||
|
||||
$attemptsummaryparams = array('attemptnumber'=>$submission->attemptnumber+1,
|
||||
'submissionsummary'=>$submissionsummary);
|
||||
$submission->attemptsummary = get_string('attemptheading', 'assign', $attemptsummaryparams);
|
||||
$submission->statussummary = get_string('submissionstatus_' . $submission->status, 'assign');
|
||||
|
||||
}
|
||||
|
||||
return $export;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderable header
|
||||
* @package mod_assign
|
||||
|
|
|
@ -26,6 +26,8 @@ defined('MOODLE_INTERNAL') || die();
|
|||
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
|
||||
use \mod_assign\output\grading_app;
|
||||
|
||||
/**
|
||||
* A custom renderer class that extends the plugin_renderer_base and is used by the assign module.
|
||||
*
|
||||
|
@ -333,14 +335,19 @@ class mod_assign_renderer extends plugin_renderer_base {
|
|||
$o .= $this->output->box_end();
|
||||
|
||||
// Link to the grading page.
|
||||
$o .= $this->output->container_start('submissionlinks');
|
||||
$o .= '<center>';
|
||||
$o .= $this->output->container_start('submissionlinks btn-group');
|
||||
$urlparams = array('id' => $summary->coursemoduleid, 'action'=>'grading');
|
||||
$url = new moodle_url('/mod/assign/view.php', $urlparams);
|
||||
$o .= $this->output->action_link($url, get_string('viewgrading', 'assign'));
|
||||
$o .= '<a href="' . $url . '" class="btn">' . get_string('viewgrading', 'mod_assign') . '</a>';
|
||||
$urlparams = array('id' => $summary->coursemoduleid, 'action'=>'grader');
|
||||
$url = new moodle_url('/mod/assign/view.php', $urlparams);
|
||||
$o .= '<a href="' . $url . '" class="btn btn-primary">' . get_string('grade') . '</a>';
|
||||
$o .= $this->output->container_end();
|
||||
|
||||
// Close the container and insert a spacer.
|
||||
$o .= $this->output->container_end();
|
||||
$o .= '</center>';
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
@ -417,6 +424,189 @@ class mod_assign_renderer extends plugin_renderer_base {
|
|||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a compact view of the current status of the submission.
|
||||
*
|
||||
* @param assign_submission_status_compact $status
|
||||
* @return string
|
||||
*/
|
||||
public function render_assign_submission_status_compact(assign_submission_status_compact $status) {
|
||||
$o = '';
|
||||
$o .= $this->output->container_start('submissionstatustable');
|
||||
$o .= $this->output->heading(get_string('submission', 'assign'), 3);
|
||||
$time = time();
|
||||
|
||||
if ($status->teamsubmissionenabled) {
|
||||
$group = $status->submissiongroup;
|
||||
if ($group) {
|
||||
$team = format_string($group->name, false, $status->context);
|
||||
} else if ($status->preventsubmissionnotingroup) {
|
||||
if (count($status->usergroups) == 0) {
|
||||
$team = '<span class="alert alert-error">' . get_string('noteam', 'assign') . '</span>';
|
||||
} else if (count($status->usergroups) > 1) {
|
||||
$team = '<span class="alert alert-error">' . get_string('multipleteams', 'assign') . '</span>';
|
||||
}
|
||||
} else {
|
||||
$team = get_string('defaultteam', 'assign');
|
||||
}
|
||||
$o .= $this->output->container(get_string('teamname', 'assign', $team), 'teamname');
|
||||
}
|
||||
|
||||
if (!$status->teamsubmissionenabled) {
|
||||
if ($status->submission && $status->submission->status != ASSIGN_SUBMISSION_STATUS_NEW) {
|
||||
$statusstr = get_string('submissionstatus_' . $status->submission->status, 'assign');
|
||||
$o .= $this->output->container($statusstr, 'submissionstatus' . $status->submission->status);
|
||||
} else {
|
||||
if (!$status->submissionsenabled) {
|
||||
$o .= $this->output->container(get_string('noonlinesubmissions', 'assign'), 'submissionstatus');
|
||||
} else {
|
||||
$o .= $this->output->container(get_string('noattempt', 'assign'), 'submissionstatus');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$group = $status->submissiongroup;
|
||||
if (!$group && $status->preventsubmissionnotingroup) {
|
||||
$o .= $this->output->container(get_string('nosubmission', 'assign'), 'submissionstatus');
|
||||
} else if ($status->teamsubmission && $status->teamsubmission->status != ASSIGN_SUBMISSION_STATUS_NEW) {
|
||||
$teamstatus = $status->teamsubmission->status;
|
||||
$submissionsummary = get_string('submissionstatus_' . $teamstatus, 'assign');
|
||||
$groupid = 0;
|
||||
if ($status->submissiongroup) {
|
||||
$groupid = $status->submissiongroup->id;
|
||||
}
|
||||
|
||||
$members = $status->submissiongroupmemberswhoneedtosubmit;
|
||||
$userslist = array();
|
||||
foreach ($members as $member) {
|
||||
$urlparams = array('id' => $member->id, 'course'=>$status->courseid);
|
||||
$url = new moodle_url('/user/view.php', $urlparams);
|
||||
if ($status->view == assign_submission_status::GRADER_VIEW && $status->blindmarking) {
|
||||
$userslist[] = $member->alias;
|
||||
} else {
|
||||
$fullname = fullname($member, $status->canviewfullnames);
|
||||
$userslist[] = $this->output->action_link($url, $fullname);
|
||||
}
|
||||
}
|
||||
if (count($userslist) > 0) {
|
||||
$userstr = join(', ', $userslist);
|
||||
$formatteduserstr = get_string('userswhoneedtosubmit', 'assign', $userstr);
|
||||
$submissionsummary .= $this->output->container($formatteduserstr);
|
||||
}
|
||||
$o .= $this->output->container($submissionsummary, 'submissionstatus' . $status->teamsubmission->status);
|
||||
} else {
|
||||
if (!$status->submissionsenabled) {
|
||||
$o .= $this->output->container(get_string('noonlinesubmissions', 'assign'), 'submissionstatus');
|
||||
} else {
|
||||
$o .= $this->output->container(get_string('nosubmission', 'assign'), 'submissionstatus');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is locked?
|
||||
if ($status->locked) {
|
||||
$o .= $this->output->container(get_string('submissionslocked', 'assign'), 'submissionlocked');
|
||||
}
|
||||
|
||||
// Grading status.
|
||||
$statusstr = '';
|
||||
$classname = 'gradingstatus';
|
||||
if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED ||
|
||||
$status->gradingstatus == ASSIGN_GRADING_STATUS_NOT_GRADED) {
|
||||
$statusstr = get_string($status->gradingstatus, 'assign');
|
||||
} else {
|
||||
$gradingstatus = 'markingworkflowstate' . $status->gradingstatus;
|
||||
$statusstr = get_string($gradingstatus, 'assign');
|
||||
}
|
||||
if ($status->gradingstatus == ASSIGN_GRADING_STATUS_GRADED ||
|
||||
$status->gradingstatus == ASSIGN_MARKING_WORKFLOW_STATE_RELEASED) {
|
||||
$classname = 'submissiongraded';
|
||||
} else {
|
||||
$classname = 'submissionnotgraded';
|
||||
}
|
||||
$o .= $this->output->container($statusstr, $classname);
|
||||
|
||||
$submission = $status->teamsubmission ? $status->teamsubmission : $status->submission;
|
||||
$duedate = $status->duedate;
|
||||
if ($duedate > 0) {
|
||||
|
||||
if ($status->extensionduedate) {
|
||||
// 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));
|
||||
}
|
||||
$o .= $this->output->container($remaining, $classname);
|
||||
}
|
||||
|
||||
// Show graders whether this submission is editable by students.
|
||||
if ($status->view == assign_submission_status::GRADER_VIEW) {
|
||||
if ($status->canedit) {
|
||||
$o .= $this->output->container(get_string('submissioneditable', 'assign'), 'submissioneditable');
|
||||
} else {
|
||||
$o .= $this->output->container(get_string('submissionnoteditable', 'assign'), 'submissionnoteditable');
|
||||
}
|
||||
}
|
||||
|
||||
// Grading criteria preview.
|
||||
if (!empty($status->gradingcontrollerpreview)) {
|
||||
$o .= $this->output->container($status->gradingcontrollerpreview, 'gradingmethodpreview');
|
||||
}
|
||||
|
||||
if ($submission) {
|
||||
|
||||
if (!$status->teamsubmission || $status->submissiongroup != false || !$status->preventsubmissionnotingroup) {
|
||||
foreach ($status->submissionplugins as $plugin) {
|
||||
$pluginshowsummary = !$plugin->is_empty($submission) || !$plugin->allow_submissions();
|
||||
if ($plugin->is_enabled() &&
|
||||
$plugin->is_visible() &&
|
||||
$plugin->has_user_summary() &&
|
||||
$pluginshowsummary
|
||||
) {
|
||||
|
||||
$displaymode = assign_submission_plugin_submission::SUMMARY;
|
||||
$pluginsubmission = new assign_submission_plugin_submission($plugin,
|
||||
$submission,
|
||||
$displaymode,
|
||||
$status->coursemoduleid,
|
||||
$status->returnaction,
|
||||
$status->returnparams);
|
||||
$plugincomponent = $plugin->get_subtype() . '_' . $plugin->get_type();
|
||||
$o .= $this->output->container($this->render($pluginsubmission), 'assignsubmission ' . $plugincomponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$o .= $this->output->container_end();
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a table containing the current status of the submission.
|
||||
*
|
||||
|
@ -772,6 +962,21 @@ class mod_assign_renderer extends plugin_renderer_base {
|
|||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the attempt history chooser for this assignment
|
||||
*
|
||||
* @param assign_attempt_history_chooser $history
|
||||
* @return string
|
||||
*/
|
||||
public function render_assign_attempt_history_chooser(assign_attempt_history_chooser $history) {
|
||||
$o = '';
|
||||
|
||||
$context = $history->export_for_template($this);
|
||||
$o .= $this->render_from_template('mod_assign/attempt_history_chooser', $context);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the attempt history for this assignment
|
||||
*
|
||||
|
@ -797,7 +1002,7 @@ class mod_assign_renderer extends plugin_renderer_base {
|
|||
}
|
||||
|
||||
$containerid = 'attempthistory' . uniqid();
|
||||
$o .= $this->heading(get_string('attempthistory', 'assign'), 3);
|
||||
$o .= $this->output->heading(get_string('attempthistory', 'assign'), 3);
|
||||
$o .= $this->box_start('attempthistory', $containerid);
|
||||
|
||||
foreach ($history->submissions as $i => $submission) {
|
||||
|
@ -1248,5 +1453,14 @@ class mod_assign_renderer extends plugin_renderer_base {
|
|||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defer to template..
|
||||
*
|
||||
* @param grading_app - All the data to render the grading app.
|
||||
*/
|
||||
public function render_grading_app(grading_app $app) {
|
||||
$context = $app->export_for_template($this);
|
||||
return $this->render_from_template('mod_assign/grading_app', $context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -307,4 +307,698 @@
|
|||
}
|
||||
.path-mod-assign .editsubmissionform label[for="id_submissionstatement"] {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.path-mod-assign.layout-option-nonavbar {
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-selector"] select {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-selector"] .alignment {
|
||||
float: right;
|
||||
width: 320px;
|
||||
text-align: center;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-selector"] [data-action="previous-user"],
|
||||
.path-mod-assign [data-region="user-selector"] [data-action="next-user"] {
|
||||
font-size: 26px;
|
||||
}
|
||||
.path-mod-assign [data-region="user-selector"] [data-action="next-user"] {
|
||||
margin-left: -10px;
|
||||
}
|
||||
.dir-rtl.path-mod-assign [data-region="user-selector"] [data-action="next-user"] {
|
||||
margin-right: -10px;
|
||||
}
|
||||
|
||||
.dir-rtl.path-mod-assign [data-region="user-selector"] .alignment {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-selector"] .alignment input {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-selector"] .alignment .form-autocomplete-downarrow {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-selector"] .form-autocomplete-selection {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-selector"] .form-autocomplete-suggestions {
|
||||
text-align: left;
|
||||
}
|
||||
.path-mod-assign [data-region="user-selector"] .form-autocomplete-suggestions {
|
||||
margin-left: 48px;
|
||||
}
|
||||
.dir-rtl.path-mod-assign [data-region="user-selector"] .form-autocomplete-suggestions {
|
||||
margin-right: 64px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-filters"] {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="configure-filters"] {
|
||||
display: none;
|
||||
text-align: left;
|
||||
width: auto;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 6px;
|
||||
position: absolute;
|
||||
margin-top: 28px;
|
||||
margin-left: -140px;
|
||||
padding: 10px 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="configure-filters"]::before,
|
||||
.path-mod-assign [data-region="configure-filters"]::after {
|
||||
position: absolute;
|
||||
left: auto;
|
||||
display: inline-block;
|
||||
content: '';
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="configure-filters"]::before {
|
||||
top: -7px;
|
||||
right: 12px;
|
||||
border-width: 7px;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="configure-filters"]::after {
|
||||
top: -6px;
|
||||
right: 13px;
|
||||
border-width: 6px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
.path-mod-assign.dir-rtl [data-region="configure-filters"] {
|
||||
text-align: right;
|
||||
margin-left: 0;
|
||||
margin-right: -140px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="configure-filters"] label {
|
||||
padding: 3px 20px;
|
||||
}
|
||||
|
||||
.path-mod-assign .alignment [data-region="configure-filters"] input {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grading-navigation-panel"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 6em;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grading-navigation"] {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-info"] {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-info"] a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-info"] .img-rounded {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-top: -3px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.dir-rtl.path-mod-assign [data-region="user-info"] .img-rounded {
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="user-info"] em {
|
||||
display: block;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grading-actions-form"] label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.path-mod-assign.pagelayout-embedded {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="review-panel"] {
|
||||
position: absolute;
|
||||
top: 85px;
|
||||
bottom: 60px;
|
||||
left: 0;
|
||||
width: 70%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="review-panel"] .pageheader {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="review-panel"] .drawingregion {
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-color: #ddd;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"].fullwidth {
|
||||
position: absolute;
|
||||
top: 7em;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 99%;
|
||||
overflow: auto;
|
||||
bottom: 7em;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"].fullwidth [data-region="grade"] {
|
||||
max-width: 800px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] {
|
||||
position: absolute;
|
||||
top: 85px;
|
||||
bottom: 60px;
|
||||
right: 0;
|
||||
width: 30%;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
background-color: #f5f5f5;
|
||||
padding: 15px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/***** Start submission status *****/
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] div.submissionstatustable {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .submissionsummarytable {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .submissionsummarytable table.generaltable td {
|
||||
padding: 8px 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .submissionsummarytable .generaltable tbody > tr:nth-child(2n+1) > td,
|
||||
.path-mod-assign [data-region="grade-panel"] .submissionsummarytable .generaltable tbody tr:hover > td {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] div.submissionsummarytable table tbody tr td.c0 {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] div.submissionsummarytable table tbody tr.lastrow td.c0,
|
||||
.path-mod-assign [data-region="grade-panel"] div.submissionsummarytable table tbody tr.lastrow td.c1 {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] td.submissionnotgraded,
|
||||
.path-mod-assign [data-region="grade-panel"] div.submissionnotgraded {
|
||||
color: red;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/***** End submission status *****/
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] #id_gradeheader {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] #id_gradeheader > legend {
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .comment-area textarea[cols] {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem.fitem_ftext,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem.fitem_f,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem.fitem_feditor,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem.fitem_ffilemanager {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem.fitem_ftext .fitemtitle,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem.fitem_f .fitemtitle,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem.fitem_feditor .fitemtitle,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem.fitem_ffilemanager .fitemtitle {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fcontainer .fitem.fitem_ftext .felement,
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fcontainer .fitem.fitem_f .felement,
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fcontainer .fitem.fitem_feditor .felement,
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fcontainer .fitem.fitem_ffilemanager .felement {
|
||||
padding: 6px 10px 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fitem.fitem_ftext .fitemtitle,
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fitem.fitem_f .fitemtitle,
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fitem.fitem_feditor .fitemtitle,
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fitem.fitem_ffilemanager .fitemtitle {
|
||||
border-bottom: 1px solid #ddd;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.05);
|
||||
padding: 6px 10px 3px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] [data-region="popout-button"] img {
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .popout [data-region="popout-button"] img {
|
||||
margin-left: -6px;
|
||||
margin-right: -6px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .fitem .fstaticlabel,
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fitem .fitemtitle label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/***** Start grade *****/
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .mform #fitem_id_grade.fitem {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #fitem_id_grade.fitem .fitemtitle {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
border-bottom: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #fitem_id_grade.fitem .felement {
|
||||
width: auto;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content .mform:not(.unresponsive) #fitem_id_grade.fitem .felement input {
|
||||
width: 80px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/***** End grade *****/
|
||||
|
||||
/***** Start rubric *****/
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_rubric {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_rubric .criterion .description {
|
||||
font-weight: 500;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_rubric .criterion .levels {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_rubric .criterion,
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_rubric .criterion.even {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_rubric.evaluate .criterion .levels .level:hover {
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_rubric .criterion .levels .level.checked {
|
||||
background-color: #dff0d8;
|
||||
border: none;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_rubric .criterion .levels .level .score {
|
||||
color: #468847;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_rubric .criterion .remark textarea {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/***** End rubric *****/
|
||||
|
||||
/***** Start marking guide *****/
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .descriptionreadonly,
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .remark,
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .score {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .descriptionreadonly {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .criteriondescription {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .criteriondescriptionmarkers {
|
||||
width: auto;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .markingguideremark {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .remark .commentchooser {
|
||||
float: right;
|
||||
margin-top: 2px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .score {
|
||||
float: left;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .score input,
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .score div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .criterion,
|
||||
.path-mod-assign [data-region="grade-panel"] .gradingform_guide .criterion.even {
|
||||
background-color: transparent;
|
||||
border-width: 0 0 1px 0;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .showmarkerdesc,
|
||||
.path-mod-assign [data-region="grade-panel"] .showstudentdesc {
|
||||
background-color: #f5f5f5;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/***** End marking guide *****/
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .fitem.fitem_ffilemanager {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/***** Start popout dialogue *****/
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .fitem.popout {
|
||||
position: fixed;
|
||||
left: 20%;
|
||||
right: 20%;
|
||||
top: 20%;
|
||||
z-index: 1000;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fcontainer .fitem.popout .fitemtitle {
|
||||
text-align: center;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fcontainer .fitem.popout .fitemtitle label {
|
||||
font-size: 16px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] [data-region="popout-button"] {
|
||||
float: right;
|
||||
}
|
||||
.dir-rtl.path-mod-assign #page-content [data-region="grade-panel"] [data-region="popout-button"] {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fitem.popout .fitemtitle [data-region="popout-button"] img {
|
||||
margin-top: -10px;
|
||||
margin-right: -7px;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fcontainer .fitem.popout .felement {
|
||||
padding: 10px 15px 15px;
|
||||
}
|
||||
|
||||
/***** End popout dialogue *****/
|
||||
|
||||
/***** Start attempt settings *****/
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] #id_attemptsettings > legend {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
border-bottom: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] #id_attemptsettings .fcontainer {
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .mform #id_attemptsettings .fitem {
|
||||
display: table-row;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem .fitemtitle,
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem .felement {
|
||||
display: table-cell;
|
||||
float: none;
|
||||
border-top: 1px solid #ddd;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem:last-of-type .fitemtitle,
|
||||
.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem:last-of-type .felement {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] #id_attemptsettings .fitem .fstaticlabel,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem .fitemtitle label {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem .felement select {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] [data-region="attempt-chooser"] {
|
||||
margin-bottom: 10px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
/***** End attempt settings *****/
|
||||
|
||||
.path-mod-assign [data-region="grade-actions-panel"] {
|
||||
border-top: 1px solid #ddd;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.path-mod-assign [data-region="grade-actions"] {
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
.path-mod-assign [data-region="submissions-list"] {
|
||||
text-align: initial;
|
||||
}
|
||||
.path-mod-assign [data-region="submissions-list"] label.radio input {
|
||||
margin-top: 4px;
|
||||
min-width: inherit;
|
||||
}
|
||||
.path-mod-assign [data-region="overlay"] {
|
||||
display: none;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
top: 0em;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
bottom: 0em;
|
||||
background-color: #ddd;
|
||||
opacity: 0.4;
|
||||
padding-top: 4em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.path-mod-assign.pagelayout-embedded {
|
||||
overflow: auto;
|
||||
}
|
||||
.path-mod-assign [data-region="assignment-info"] {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.path-mod-assign .page-context-header .page-header-headings {
|
||||
margin-top: 13px;
|
||||
}
|
||||
.path-mod-assign [data-region="grading-navigation-panel"],
|
||||
.path-mod-assign [data-region="review-panel"],
|
||||
.path-mod-assign [data-region="grade-panel"],
|
||||
.path-mod-assign [data-region="grade-actions-panel"] {
|
||||
position: inherit;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: none;
|
||||
height: auto;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.path-mod-assign [data-region="grading-navigation"] {
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] [data-region="popout-button"] {
|
||||
display: none;
|
||||
}
|
||||
.path-mod-assign [data-region="review-panel"] .pageheader {
|
||||
border-right: none;
|
||||
}
|
||||
.path-mod-assign.pagelayout-popup {
|
||||
overflow: inherit;
|
||||
}
|
||||
.path-mod-assign [data-region="grading-navigation"] [data-region="user-info"] {
|
||||
text-align: left;
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.path-mod-assign [data-region="user-selector"] .alignment {
|
||||
float: none;
|
||||
margin: 0 auto 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/** Start of CSS to make forms vertical in the grading panel (taken from theme/bootstrapbase/less/moodle/forms.less). */
|
||||
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem .fitemtitle {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem .felement {
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
float: left;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem .fstatic:empty {
|
||||
display: none;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem .fcheckbox > span,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem .fradio > span,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem .fgroup > span,
|
||||
margin-top: 4px;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .femptylabel .fitemtitle {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .femptylabel .felement {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
padding-top: 5px;
|
||||
width: auto;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem_fcheckbox .fitemtitle,
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem_fcheckbox .felement {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
.path-mod-assign [data-region="grade-panel"] .mform .fitem_fcheckbox .felement {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.dir-rtl.path-mod-assign [data-region="grade-panel"] .mform .femptylabel .fitemtitle {
|
||||
margin-right: 0px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.dir-rtl.path-mod-assign [data-region="grade-panel"] .mform .fitem .fitemtitle {
|
||||
text-align: right;
|
||||
}
|
||||
.dir-rtl.path-mod-assign [data-region="grade-panel"] .mform .fitem .felement {
|
||||
margin-right: 0;
|
||||
float: right;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
.dir-rtl.path-mod-assign [data-region="grade-panel"] .mform .fitem_checkbox .felement {
|
||||
float: right;
|
||||
}
|
||||
/** End of CSS to make forms vertical in the grading panel (taken from theme/bootstrapbase/less/moodle/forms.less). */
|
||||
|
||||
/** Styles to fix base theme **/
|
||||
.path-mod-assign #page,
|
||||
.path-mod-assign #page-content {
|
||||
position: initial;
|
||||
}
|
||||
/** End of base fixes **/
|
||||
|
|
16
mod/assign/templates/attempt_history_chooser.mustache
Normal file
16
mod/assign/templates/attempt_history_chooser.mustache
Normal file
|
@ -0,0 +1,16 @@
|
|||
{{#submissioncount}}
|
||||
<div>
|
||||
<a class="btn" href="#" data-region="attempt-chooser" data-submissions="submissions-list-{{uniqid}}">{{#str}}viewadifferentattempt, mod_assign{{/str}}</a>
|
||||
<div class="hide" id="submissions-list-{{uniqid}}">
|
||||
<div data-region="submissions-list">
|
||||
{{#submissions}}
|
||||
<label class="radio">
|
||||
<input type="radio" name="select-attemptnumber" value="{{attemptnumber}}"/>
|
||||
{{attemptsummary}}
|
||||
<br><em>{{statussummary}}</em>
|
||||
</label>
|
||||
{{/submissions}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/submissioncount}}
|
46
mod/assign/templates/grading_actions.mustache
Normal file
46
mod/assign/templates/grading_actions.mustache
Normal file
|
@ -0,0 +1,46 @@
|
|||
{{!
|
||||
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/grading_actions
|
||||
|
||||
Actions panel at the bottom of the assignment grading UI.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-region
|
||||
|
||||
Context variables required for this template:
|
||||
* see mod/assign/classes/output/grading_app.php
|
||||
|
||||
Example context (json):
|
||||
This template is initially hidden, and is only displayed after the current user info has been loaded.
|
||||
}}
|
||||
<form data-region="grading-actions-form" class="hide">
|
||||
<label>{{#str}}sendstudentnotifications, mod_assign{{/str}}
|
||||
<input type="checkbox" name="sendstudentnotifications"
|
||||
{{#defaultsendnotifications}}checked="checked"{{/defaultsendnotifications}} />
|
||||
</label>
|
||||
<button type="submit" class="btn btn-primary" name="savechanges">{{#str}}savechanges{{/str}}</button>
|
||||
<button type="submit" class="btn" name="resetbutton">{{#str}}reset{{/str}}</button>
|
||||
</form>
|
||||
{{#js}}
|
||||
require(['mod_assign/grading_actions'], function(GradingActions) {
|
||||
new GradingActions('[data-region="grade-actions"]');
|
||||
});
|
||||
{{/js}}
|
56
mod/assign/templates/grading_app.mustache
Normal file
56
mod/assign/templates/grading_app.mustache
Normal file
|
@ -0,0 +1,56 @@
|
|||
{{!
|
||||
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/grading_app
|
||||
|
||||
Actions panel at the bottom of the assignment grading UI.
|
||||
|
||||
Classes required for JS:
|
||||
* Uses some bootstrap classes, but for visual appeal only.
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-region, data-first-userid, data-courseid, data-contextid, data-assignmentid
|
||||
|
||||
Context variables required for this template:
|
||||
* see mod/assign/classes/output/grading_app.php
|
||||
|
||||
Example context (json):
|
||||
This template includes ajax functionality, so it cannot be shown in the template library.
|
||||
}}
|
||||
<div data-region="grading-navigation-panel" data-first-userid="{{userid}}" data-courseid="{{courseid}}" data-showuseridentity="{{showuseridentity}}">
|
||||
{{> mod_assign/grading_navigation }}
|
||||
</div>
|
||||
<div data-region="grade-panel" {{^showreview}}class="fullwidth"{{/showreview}}>
|
||||
<div data-region="grade" data-contextid="{{contextid}}" data-assignmentid="{{assignmentid}}">
|
||||
{{> mod_assign/grading_panel }}
|
||||
</div>
|
||||
</div>
|
||||
{{#showreview}}
|
||||
<div data-region="review-panel">
|
||||
<div data-region="review">
|
||||
{{> mod_assign/review_panel }}
|
||||
</div>
|
||||
</div>
|
||||
{{/showreview}}
|
||||
<div data-region="grade-actions-panel">
|
||||
<div data-region="grade-actions">
|
||||
{{> mod_assign/grading_actions }}
|
||||
</div>
|
||||
</div>
|
||||
<div data-region="overlay" class="moodle-has-zindex">
|
||||
{{> mod_assign/grading_save_in_progress }}
|
||||
</div>
|
73
mod/assign/templates/grading_navigation.mustache
Normal file
73
mod/assign/templates/grading_navigation.mustache
Normal file
|
@ -0,0 +1,73 @@
|
|||
{{!
|
||||
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/grading_navigation
|
||||
|
||||
Actions panel at the bottom of the assignment grading UI.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-region, data-assignmentid, data-groupid
|
||||
|
||||
Context variables required for this template:
|
||||
* see mod/assign/classes/output/grading_app.php
|
||||
|
||||
Example context (json):
|
||||
This template includes ajax functionality, so it cannot be shown in the template library.
|
||||
}}
|
||||
<div data-region="grading-navigation" class="row-fluid">
|
||||
<div data-region="assignment-info" class="span4">
|
||||
<a href="{{config.wwwroot}}/course/view.php?id={{courseid}}">{{coursename}}</a><br/>
|
||||
<a href="{{config.wwwroot}}/mod/assign/view.php?id={{cmid}}">{{name}}</a>
|
||||
{{#caneditsettings}}
|
||||
<a href="{{config.wwwroot}}/course/modedit.php?update={{cmid}}&return=1">{{#pix}}t/edit, core,{{#str}}editsettings{{/str}}{{/pix}}</a>
|
||||
{{/caneditsettings}}
|
||||
<div role="tooltip" id="tooltip-{{uniqid}}" class="accesshide">
|
||||
{{#duedate}}
|
||||
{{#str}}duedatecolon, mod_assign, {{duedatestr}}{{/str}}
|
||||
{{/duedate}}
|
||||
|
||||
{{#cutoffdate}}
|
||||
<br>{{cutoffdatestr}}
|
||||
{{/cutoffdate}}
|
||||
|
||||
{{#duedate}}
|
||||
<br>{{timeremainingstr}}
|
||||
{{/duedate}}
|
||||
</div>
|
||||
{{#duedate}}
|
||||
<br/><small data-region="assignment-tooltip" aria-describedby="tooltip-{{uniqid}}">{{#str}}duedatecolon, mod_assign, {{duedatestr}}{{/str}}</small>
|
||||
{{/duedate}}
|
||||
</span>
|
||||
</div>
|
||||
<div data-region="user-info" class="span4" data-assignmentid="{{assignmentid}}" data-groupid="{{groupid}}">
|
||||
{{> mod_assign/grading_navigation_user_info }}
|
||||
</div>
|
||||
<div data-region="user-selector" class="span4">
|
||||
<div class="alignment">
|
||||
{{> mod_assign/grading_navigation_user_selector }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#js}}
|
||||
require(['mod_assign/grading_navigation', 'core/tooltip'], function(GradingNavigation, ToolTip) {
|
||||
var nav = new GradingNavigation('[data-region="user-selector"]');
|
||||
var tooltip = new ToolTip('[data-region="assignment-tooltip"]');
|
||||
});
|
||||
{{/js}}
|
34
mod/assign/templates/grading_navigation_no_users.mustache
Normal file
34
mod/assign/templates/grading_navigation_no_users.mustache
Normal file
|
@ -0,0 +1,34 @@
|
|||
{{!
|
||||
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/grading_navigation_no_users
|
||||
|
||||
Notification when no users are available (given the current filters).
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* None
|
||||
|
||||
Context variables required for this template:
|
||||
* None
|
||||
|
||||
Example context (json):
|
||||
{ }
|
||||
}}
|
||||
<h3>{{#str}}nousersselected, mod_assign{{/str}}</h3>
|
40
mod/assign/templates/grading_navigation_user_info.mustache
Normal file
40
mod/assign/templates/grading_navigation_user_info.mustache
Normal file
|
@ -0,0 +1,40 @@
|
|||
{{!
|
||||
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/grading_navigation_user_info
|
||||
|
||||
Initial template for the user info panel - until a user is selected
|
||||
a loading spinner will be displayed.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* None
|
||||
|
||||
Context variables required for this template:
|
||||
* None
|
||||
|
||||
Example context (json):
|
||||
{ }
|
||||
}}
|
||||
{{> mod_assign/loading }}
|
||||
{{#js}}
|
||||
require(['mod_assign/grading_navigation_user_info'], function(UserInfo) {
|
||||
new UserInfo('[data-region="user-info"]');
|
||||
});
|
||||
{{/js}}
|
|
@ -0,0 +1,67 @@
|
|||
{{!
|
||||
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/grading_navigation_user_selector
|
||||
|
||||
The template HTML for the user selector in the top right corner.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-action, data-assignmentid, data-groupid, data-region
|
||||
|
||||
Context variables required for this template:
|
||||
* see mod/assign/classes/output/grading_app.php
|
||||
|
||||
Example context (json):
|
||||
This template uses ajax functionality, so it cannot be shown in the template library.
|
||||
}}
|
||||
<a href="#previous" data-action="previous-user">{{larrow}}</a>
|
||||
<span>
|
||||
<select data-action="change-user" data-assignmentid="{{assignmentid}}" data-groupid="{{groupid}}">
|
||||
</select>
|
||||
</span>
|
||||
<a href="#next" data-action="next-user">{{rarrow}}</a>
|
||||
|
||||
<br>
|
||||
|
||||
<span data-region="user-count">
|
||||
<small>
|
||||
<span data-region="user-count-summary">{{#str}}xofy, mod_assign, { "x": "{{index}}", "y": "{{count}}" }{{/str}}</span>
|
||||
</small>
|
||||
</span>
|
||||
|
||||
<span data-region="configure-filters" id="filter-configuration-{{uniqid}}" class="well well-small">
|
||||
<form>
|
||||
<label><input type="checkbox" name="filter_submitted">{{#str}}filtersubmitted, mod_assign{{/str}}</label>
|
||||
<label><input type="checkbox" name="filter_notsubmitted">{{#str}}filternotsubmitted, mod_assign{{/str}}</label>
|
||||
<label><input type="checkbox" name="filter_requiregrading">{{#str}}filterrequiregrading, mod_assign{{/str}}</label>
|
||||
</form>
|
||||
</span>
|
||||
|
||||
<a href="#" data-region="user-filters" title="{{#str}}changefilters, mod_assign{{/str}}" aria-expanded="false" aria-controls="filter-configuration-{{uniqid}}">
|
||||
<span class="accesshide">
|
||||
{{#filters}}
|
||||
{{filtername}}
|
||||
{{/filters}}
|
||||
{{^filters}}
|
||||
{{#str}}nofilters, mod_assign{{/str}}
|
||||
{{/filters}}
|
||||
</span>
|
||||
{{#pix}}i/filter{{/pix}}
|
||||
</a>
|
|
@ -0,0 +1,40 @@
|
|||
{{!
|
||||
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/grading_navigation_user_summary
|
||||
|
||||
Shown when the real data for a user has been loaded. Display in the top left corner.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-region, data-assignmentid, data-groupid
|
||||
|
||||
Context variables required for this template:
|
||||
* id, courseid, all fields from mod_assign_list_participants
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"id": "5",
|
||||
"fullname": "Mr T",
|
||||
"hasidentity": true,
|
||||
"identity": "t@example.org, T"
|
||||
"profileimageurl": "https://moodle.org/pix/u/f3.png"
|
||||
}
|
||||
}}
|
||||
<h4><a href="{{globals.config.wwwroot}}/user/view.php?id={{id}}&course={{courseid}}">{{#profileimageurl}}<img src="{{profileimageurl}}" class="img-rounded" height="40" role="presentation"/>{{/profileimageurl}} {{fullname}}{{#hasidentity}}<em><small> {{identity}} </small></em>{{/hasidentity}}</a></h4>
|
39
mod/assign/templates/grading_panel.mustache
Normal file
39
mod/assign/templates/grading_panel.mustache
Normal file
|
@ -0,0 +1,39 @@
|
|||
{{!
|
||||
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/grading_panel
|
||||
|
||||
Shown a place holder until the real content is fetched via ajax.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* None
|
||||
|
||||
Context variables required for this template:
|
||||
* None
|
||||
|
||||
Example context (json):
|
||||
This template loads it's content via ajax, so it cannot be shown in the template library.
|
||||
}}
|
||||
{{> mod_assign/loading }}
|
||||
{{#js}}
|
||||
require(['mod_assign/grading_panel'], function(GradingPanel) {
|
||||
new GradingPanel('[data-region="grade"]');
|
||||
});
|
||||
{{/js}}
|
35
mod/assign/templates/grading_save_in_progress.mustache
Normal file
35
mod/assign/templates/grading_save_in_progress.mustache
Normal file
|
@ -0,0 +1,35 @@
|
|||
{{!
|
||||
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/grading_save_in_progress
|
||||
|
||||
Shown in an overlay when the grading form has been submitted, and we are
|
||||
waiting for a response.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* None
|
||||
|
||||
Context variables required for this template:
|
||||
* None
|
||||
|
||||
Example context (json):
|
||||
{ "id": "4" }
|
||||
}}
|
||||
<h3>{{#str}}savingchanges, mod_assign{{/str}} {{> mod_assign/loading }}</h3>
|
41
mod/assign/templates/list_participant_user_summary.mustache
Normal file
41
mod/assign/templates/list_participant_user_summary.mustache
Normal file
|
@ -0,0 +1,41 @@
|
|||
{{!
|
||||
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/list_participant_user_summary
|
||||
|
||||
Shown as the options for the list of users in the user picker on the grading page.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* None
|
||||
|
||||
Context variables required for this template:
|
||||
* fullname
|
||||
* requiregrading
|
||||
* identity - list of only the visible identity fields
|
||||
* list of fields from mod_assign_list_participants webservice
|
||||
|
||||
Example context (json):
|
||||
{ "id": "4",
|
||||
"fullname": "Mr T",
|
||||
"requiregrading": "true",
|
||||
"identity": "email, phone, etc"
|
||||
}
|
||||
}}
|
||||
<span>{{fullname}} <small>{{identity}}</small> {{#requiregrading}}<abbr title="{{#str}}filterrequiregrading, mod_assign{{/str}}">*</abbr>{{/requiregrading}}</span>
|
34
mod/assign/templates/loading.mustache
Normal file
34
mod/assign/templates/loading.mustache
Normal file
|
@ -0,0 +1,34 @@
|
|||
{{!
|
||||
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/loading
|
||||
|
||||
Loading spinner shown while waiting for an ajax request
|
||||
|
||||
Classes required for JS:
|
||||
* None
|
||||
|
||||
Data attibutes required for JS:
|
||||
* None
|
||||
|
||||
Context variables required for this template:
|
||||
* None
|
||||
|
||||
Example context (json):
|
||||
{ }
|
||||
}}
|
||||
{{#pix}}y/loading, core, {{#str}}loading, mod_assign{{/str}}{{/pix}}
|
1
mod/assign/templates/popout_button.mustache
Normal file
1
mod/assign/templates/popout_button.mustache
Normal file
|
@ -0,0 +1 @@
|
|||
<a href="#" data-region="popout-button">{{#pix}}e/fullscreen,core,{{#str}}togglezoom, mod_assign{{/str}}{{/pix}}</a>
|
34
mod/assign/templates/review_panel.mustache
Normal file
34
mod/assign/templates/review_panel.mustache
Normal file
|
@ -0,0 +1,34 @@
|
|||
{{!
|
||||
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/review_panel
|
||||
|
||||
Show a placeholder in the review panel until something injects some content.
|
||||
|
||||
Classes required for JS:
|
||||
* None
|
||||
|
||||
Data attibutes required for JS:
|
||||
* None
|
||||
|
||||
Context variables required for this template:
|
||||
* None
|
||||
|
||||
Example context (json):
|
||||
{ }
|
||||
}}
|
||||
{{> mod_assign/loading }}
|
|
@ -310,11 +310,15 @@ class testable_assign extends assign {
|
|||
}
|
||||
|
||||
public function testable_view_batch_set_workflow_state($selectedusers) {
|
||||
global $PAGE;
|
||||
$PAGE->set_url('/mod/assign/view.php');
|
||||
$mform = $this->testable_grading_batch_operations_form('setmarkingworkflowstate', $selectedusers);
|
||||
return parent::view_batch_set_workflow_state($mform);
|
||||
}
|
||||
|
||||
public function testable_view_batch_markingallocation($selectedusers) {
|
||||
global $PAGE;
|
||||
$PAGE->set_url('/mod/assign/view.php');
|
||||
$mform = $this->testable_grading_batch_operations_form('setmarkingallocation', $selectedusers);
|
||||
return parent::view_batch_markingallocation($mform);
|
||||
}
|
||||
|
|
|
@ -38,11 +38,13 @@ Feature: In an assignment, students start a new attempt based on their previous
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Allow another attempt | 1 |
|
||||
And I press "Save changes"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
|
@ -53,8 +55,8 @@ Feature: In an assignment, students start a new attempt based on their previous
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I should see "I'm the student first submission"
|
||||
|
||||
@javascript @_alert
|
||||
|
@ -110,7 +112,7 @@ Feature: In an assignment, students start a new attempt based on their previous
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
When I follow "View/grade all submissions"
|
||||
When I follow "View all submissions"
|
||||
Then "Student 1" row "Status" column of "generaltable" table should contain "Submitted for grading"
|
||||
And "Student 2" row "Status" column of "generaltable" table should contain "Submitted for grading"
|
||||
And "Student 3" row "Status" column of "generaltable" table should contain "No submission"
|
||||
|
@ -140,12 +142,12 @@ Feature: In an assignment, students start a new attempt based on their previous
|
|||
# And I log in as "teacher1"
|
||||
# And I follow "Course 1"
|
||||
# And I follow "Test assignment name"
|
||||
# And I follow "View/grade all submissions"
|
||||
# And I follow "View all submissions"
|
||||
# And "Student 1" row "Status" column of "generaltable" table should contain "Reopened"
|
||||
# And "Student 2" row "Status" column of "generaltable" table should contain "Reopened"
|
||||
# And "Student 3" row "Status" column of "generaltable" table should contain "Submitted for grading"
|
||||
# And "Student 4" row "Status" column of "generaltable" table should contain "Submitted for grading"
|
||||
# And I click on "Grade Student 3" "link" in the "Student 3" "table_row"
|
||||
# And I click on "Grade " "link" in the "Student 3" "table_row"
|
||||
# And I set the following fields to these values:
|
||||
# | Allow another attempt | 1 |
|
||||
# And I press "Save changes"
|
||||
|
@ -162,6 +164,6 @@ Feature: In an assignment, students start a new attempt based on their previous
|
|||
# And I log in as "teacher1"
|
||||
# And I follow "Course 1"
|
||||
# And I follow "Test assignment name"
|
||||
# And I follow "View/grade all submissions"
|
||||
# And I click on "Grade Student 4" "link" in the "Student 1" "table_row"
|
||||
# And I follow "View all submissions"
|
||||
# And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
#And I should see "This is attempt 2 (3 attempts allowed)"
|
||||
|
|
|
@ -40,14 +40,17 @@ Feature: In an assignment, teachers can edit a students submission inline
|
|||
When I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Grade out of 100 | 50 |
|
||||
| Feedback comments | I'm the teacher feedback |
|
||||
And I upload "lib/tests/fixtures/empty.txt" file to "Feedback files" filemanager
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
Then I should see "50.00" in the "Student 1" "table_row"
|
||||
And I should see "Submitted for grading" in the "Student 1" "table_row"
|
||||
And I should see "Graded" in the "Student 1" "table_row"
|
||||
|
|
|
@ -28,8 +28,8 @@ Feature: Check that the assignment grade can not be input in a wrong format.
|
|||
| Description | Test assignment description |
|
||||
| Use marking workflow | Yes |
|
||||
When I follow "Test assignment name"
|
||||
Then I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
Then I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "50,,6"
|
||||
And I press "Save changes"
|
||||
And I should see "The grade provided could not be understood: 50,,6"
|
||||
|
@ -58,8 +58,8 @@ Feature: Check that the assignment grade can not be input in a wrong format.
|
|||
| Description | Test assignment description |
|
||||
| Use marking workflow | Yes |
|
||||
When I follow "Test assignment name"
|
||||
Then I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
Then I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "50..6"
|
||||
And I press "Save changes"
|
||||
And I should see "The grade provided could not be understood: 50..6"
|
||||
|
|
|
@ -28,11 +28,14 @@ Feature: Check that the assignment grade can be updated correctly
|
|||
| Description | Test assignment description |
|
||||
| Use marking workflow | Yes |
|
||||
When I follow "Test assignment name"
|
||||
Then I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
Then I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "50"
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And "Student 1" row "Grade" column of "generaltable" table should contain "50.00"
|
||||
|
||||
@javascript
|
||||
|
@ -61,9 +64,12 @@ Feature: Check that the assignment grade can be updated correctly
|
|||
| Students submit in groups | Yes |
|
||||
| Group mode | No groups |
|
||||
When I follow "Test assignment name"
|
||||
Then I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
Then I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "50"
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And "Student 1" row "Grade" column of "generaltable" table should contain "50.00"
|
||||
|
|
|
@ -40,13 +40,15 @@ Feature: In an assignment, teachers can edit feedback for a students previous su
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 2" "link" in the "Student 2" "table_row"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 2" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Grade | 49 |
|
||||
| Feedback comments | I'm the teacher first feedback |
|
||||
| Allow another attempt | Yes |
|
||||
And I press "Save changes"
|
||||
And I click on "Ok" "button"
|
||||
And I click on "Edit settings" "link"
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I follow "Course 1"
|
||||
|
@ -56,13 +58,17 @@ Feature: In an assignment, teachers can edit feedback for a students previous su
|
|||
When I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 2" "link" in the "Student 2" "table_row"
|
||||
And I click on ".mod-assign-history-link" "css_element"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 2" "table_row"
|
||||
And I click on "View a different attempt" "link"
|
||||
And I click on "//div[contains(concat(' ', normalize-space(@class), ' '), ' confirmation-dialogue ')]//input[@value='0']" "xpath_element"
|
||||
And I click on "View" "button"
|
||||
And I set the following fields to these values:
|
||||
| Grade | 50 |
|
||||
| Feedback comments | I'm the teacher second feedback |
|
||||
And I press "Save changes"
|
||||
And I click on "Ok" "button"
|
||||
And I click on "Edit settings" "link"
|
||||
And I log out
|
||||
Then I log in as "student2"
|
||||
And I follow "Course 1"
|
||||
|
|
|
@ -32,15 +32,17 @@ Feature: In an assignment, teachers can filter displayed submissions by assigned
|
|||
| Use marking workflow | Yes |
|
||||
| Use marking allocation | Yes |
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "allocatedmarker" to "Marker 1"
|
||||
And I click on "Save changes" "button"
|
||||
And I press "Save changes"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I log out
|
||||
When I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I set the field "markerfilter" to "Marker 1"
|
||||
Then I should see "Student 1"
|
||||
And I should not see "Student 2"
|
||||
|
|
|
@ -50,14 +50,17 @@ Feature: View the grading status of an assignment
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Not marked" in the "Student 1" "table_row"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "50"
|
||||
And I set the field "Marking workflow state" to "In review"
|
||||
And I set the field "Feedback comments" to "Great job! Lol, not really."
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "In review" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
# View the grading status as a student.
|
||||
|
@ -74,12 +77,15 @@ Feature: View the grading status of an assignment
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I should see "In review" in the "Student 1" "table_row"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Marking workflow state" to "Released"
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Released" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
# View the grading status as a student.
|
||||
|
@ -96,12 +102,15 @@ Feature: View the grading status of an assignment
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Released" in the "Student 1" "table_row"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Marking workflow state" to "In marking"
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "In marking" in the "Student 1" "table_row"
|
||||
# The grade should also remain displayed as it's stored in the assign DB tables, but the final grade should be empty.
|
||||
And "Student 1" row "Grade" column of "generaltable" table should contain "50.00"
|
||||
|
@ -137,13 +146,16 @@ Feature: View the grading status of an assignment
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I should not see "Graded" in the "Student 1" "table_row"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Grade out of 100" to "50"
|
||||
And I set the field "Feedback comments" to "Great job! Lol, not really."
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Graded" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
# View the grading status as a student.
|
||||
|
|
|
@ -35,7 +35,7 @@ Feature: Grant an extension to an offline student
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
When I follow "View/grade all submissions"
|
||||
When I follow "View all submissions"
|
||||
And I click on "Edit" "link" in the "Student 1" "table_row"
|
||||
And I follow "Grant extension"
|
||||
And I should see "Student 1 (student1@example.com)"
|
||||
|
@ -56,7 +56,7 @@ Feature: Grant an extension to an offline student
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
When I follow "View/grade all submissions"
|
||||
When I follow "View all submissions"
|
||||
And I set the field "selectall" to "1"
|
||||
And I set the field "operation" to "Grant extension"
|
||||
And I click on "Go" "button" confirming the dialogue
|
||||
|
|
|
@ -35,7 +35,7 @@ Feature: Group assignment submissions
|
|||
| Students submit in groups | Yes |
|
||||
| Group mode | No groups |
|
||||
And I follow "Test assignment name"
|
||||
When I follow "View/grade all submissions"
|
||||
When I follow "View all submissions"
|
||||
Then "//tr[contains(., 'Student 0')][contains(., 'Default group')]" "xpath_element" should exist
|
||||
And "//tr[contains(., 'Student 1')][contains(., 'Default group')]" "xpath_element" should exist
|
||||
And "//tr[contains(., 'Student 2')][contains(., 'Default group')]" "xpath_element" should exist
|
||||
|
@ -54,7 +54,7 @@ Feature: Group assignment submissions
|
|||
And I add "Student 1 (student1@example.com)" user to "Group 1" group members
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And "//tr[contains(., 'Student 0')][contains(., 'Group 1')]" "xpath_element" should exist
|
||||
And "//tr[contains(., 'Student 1')][contains(., 'Group 1')]" "xpath_element" should exist
|
||||
And I should not see "Student 2"
|
||||
|
@ -113,7 +113,7 @@ Feature: Group assignment submissions
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
When I follow "View/grade all submissions"
|
||||
When I follow "View all submissions"
|
||||
Then "Student 1" row "Status" column of "generaltable" table should contain "Submitted for grading"
|
||||
And "Student 2" row "Status" column of "generaltable" table should contain "Submitted for grading"
|
||||
And "Student 3" row "Status" column of "generaltable" table should not contain "Submitted for grading"
|
||||
|
@ -130,7 +130,7 @@ Feature: Group assignment submissions
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And "Student 1" row "Status" column of "generaltable" table should contain "Submitted for grading"
|
||||
And "Student 2" row "Status" column of "generaltable" table should contain "Submitted for grading"
|
||||
And "Student 3" row "Status" column of "generaltable" table should contain "Submitted for grading"
|
||||
|
|
|
@ -62,12 +62,15 @@ Feature: Outcome grading
|
|||
When I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "img[alt='Grade Student 0']" "css_element"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 0" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Outcome Test: | Excellent |
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
Then I should see "Outcome Test: Excellent" in the "Student 0" "table_row"
|
||||
And I should not see "Outcome Test: Excellent" in the "Student 1" "table_row"
|
||||
|
||||
|
@ -109,22 +112,28 @@ Feature: Outcome grading
|
|||
When I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "img[alt='Grade Student 0']" "css_element"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 0" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Outcome Test: | Excellent |
|
||||
| Apply grades and feedback to entire group | Yes |
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
Then I should see "Outcome Test: Excellent" in the "Student 0" "table_row"
|
||||
And I should see "Outcome Test: Excellent" in the "Student 1" "table_row"
|
||||
And I should not see "Outcome Test: Excellent" in the "Student 2" "table_row"
|
||||
And I click on "img[alt='Grade Student 1']" "css_element"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Outcome Test: | Disappointing |
|
||||
| Apply grades and feedback to entire group | No |
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Outcome Test: Excellent" in the "Student 0" "table_row"
|
||||
And I should see "Outcome Test: Disappointing" in the "Student 1" "table_row"
|
||||
And I should not see "Outcome Test: Disappointing" in the "Student 0" "table_row"
|
||||
|
|
|
@ -45,7 +45,7 @@ Feature: Prevent or allow assignment submission changes
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
When I follow "View/grade all submissions"
|
||||
When I follow "View all submissions"
|
||||
And I click on "Edit" "link" in the "Student 1" "table_row"
|
||||
And I follow "Prevent submission changes"
|
||||
Then I should see "Submission changes not allowed"
|
||||
|
@ -59,7 +59,7 @@ Feature: Prevent or allow assignment submission changes
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Edit" "link" in the "Student 1" "table_row"
|
||||
And I follow "Allow submission changes"
|
||||
And I should not see "Submission changes not allowed"
|
||||
|
@ -98,7 +98,7 @@ Feature: Prevent or allow assignment submission changes
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
When I follow "View/grade all submissions"
|
||||
When I follow "View all submissions"
|
||||
And I set the field "selectall" to "1"
|
||||
And I click on "Go" "button" confirming the dialogue
|
||||
Then I should see "Submission changes not allowed" in the "Student 1" "table_row"
|
||||
|
@ -112,7 +112,7 @@ Feature: Prevent or allow assignment submission changes
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I set the field "selectall" to "1"
|
||||
And I set the field "id_operation" to "Unlock submissions"
|
||||
And I click on "Go" "button" confirming the dialogue
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@mod @mod_assign
|
||||
@mod @mod_assign @javascript
|
||||
Feature: In an assignment, teachers grade multiple students on one page
|
||||
In order to quickly give students grades and feedback
|
||||
As a teacher
|
||||
|
@ -38,14 +38,14 @@ Feature: In an assignment, teachers grade multiple students on one page
|
|||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
Then I should see "1" in the "Needs grading" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: Grade multiple students on one page
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category | groupmode |
|
||||
|
@ -109,14 +109,17 @@ Feature: In an assignment, teachers grade multiple students on one page
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Grade out of 100 | 50.0 |
|
||||
| M8d skillZ! | 1337 |
|
||||
| Feedback comments | I'm the teacher first feedback |
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
Then I click on "Quick grading" "checkbox"
|
||||
And I set the field "User grade" to "60.0"
|
||||
And I press "Save all quick grading changes"
|
||||
|
@ -144,7 +147,7 @@ Feature: In an assignment, teachers grade multiple students on one page
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Hide User picture" "link"
|
||||
And I click on "Hide Full name" "link"
|
||||
And I click on "Hide Email address" "link"
|
||||
|
|
|
@ -41,7 +41,7 @@ Feature: Submissions are unlocked when a new attempt is given
|
|||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Edit" "link" in the "Student 1" "table_row"
|
||||
And I follow "Prevent submission changes"
|
||||
And I should see "Submission changes not allowed"
|
||||
|
@ -76,11 +76,11 @@ Feature: Submissions are unlocked when a new attempt is given
|
|||
And I am on site homepage
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Edit" "link" in the "Student 1" "table_row"
|
||||
And I follow "Prevent submission changes"
|
||||
And I should see "Submission changes not allowed"
|
||||
And I click on "Edit" "link" in the "Student 1" "table_row"
|
||||
And I follow "Allow another attempt"
|
||||
Then I should see "Reopened"
|
||||
And I should not see "Submission changes not allowed"
|
||||
And I should not see "Submission changes not allowed"
|
||||
|
|
|
@ -44,27 +44,36 @@ Feature: Assignments correctly add feedback to the grade report when workflow an
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Not marked" in the "I'm the student's first submission" "table_row"
|
||||
And I click on "Grade Participant " "link" in the "I'm the student's first submission" "table_row"
|
||||
And I click on "Grade" "link" in the "I'm the student's first submission" "table_row"
|
||||
And I set the field "Grade out of 100" to "50"
|
||||
And I set the field "Marking workflow state" to "In review"
|
||||
And I set the field "Feedback comments" to "Great job! Lol, not really."
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "In review" in the "I'm the student's first submission" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: Student identities are revealed after releasing the grades.
|
||||
When I click on "Grade Participant " "link" in the "I'm the student's first submission" "table_row"
|
||||
When I click on "Grade" "link" in the "I'm the student's first submission" "table_row"
|
||||
And I set the field "Marking workflow state" to "Ready for release"
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Ready for release" in the "I'm the student's first submission" "table_row"
|
||||
And I click on "Grade Participant " "link" in the "I'm the student's first submission" "table_row"
|
||||
And I click on "Grade" "link" in the "I'm the student's first submission" "table_row"
|
||||
And I set the field "Marking workflow state" to "Released"
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Released" in the "I'm the student's first submission" "table_row"
|
||||
And I set the field "Grading action" to "Reveal student identities"
|
||||
And I press "Continue"
|
||||
|
@ -78,17 +87,23 @@ Feature: Assignments correctly add feedback to the grade report when workflow an
|
|||
|
||||
@javascript
|
||||
Scenario: Student identities are revealed before releasing the grades.
|
||||
When I click on "Grade Participant " "link" in the "I'm the student's first submission" "table_row"
|
||||
When I click on "Grade" "link" in the "I'm the student's first submission" "table_row"
|
||||
And I set the field "Marking workflow state" to "Ready for release"
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Ready for release" in the "I'm the student's first submission" "table_row"
|
||||
And I set the field "Grading action" to "Reveal student identities"
|
||||
And I press "Continue"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the field "Marking workflow state" to "Released"
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Released" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@mod @mod_assign
|
||||
@mod @mod_assign @javascript
|
||||
Feature: In an assignment, students can comment in their submissions
|
||||
In order to refine assignment submissions
|
||||
As a student
|
||||
|
@ -17,7 +17,6 @@ Feature: In an assignment, students can comment in their submissions
|
|||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
|
||||
@javascript
|
||||
Scenario: Student comments an assignment submission
|
||||
Given the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled |
|
||||
|
@ -46,7 +45,6 @@ Feature: In an assignment, students can comment in their submissions
|
|||
And I should see "Second student comment"
|
||||
And I should not see "First student comment"
|
||||
|
||||
@javascript
|
||||
Scenario: Teacher can comment on an offline assignment
|
||||
Given the following "activities" exist:
|
||||
| activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled | assignmentsubmission_file_enabled | assignfeedback_comments_enabled |
|
||||
|
@ -54,13 +52,16 @@ Feature: In an assignment, students can comment in their submissions
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
When I set the following fields to these values:
|
||||
| Grade out of 100 | 50 |
|
||||
| Feedback comments | I'm the teacher feedback |
|
||||
And I press "Save changes"
|
||||
And I press "Continue"
|
||||
And I press "Ok"
|
||||
And I click on "Edit settings" "link"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View all submissions"
|
||||
Then I should see "50.00" in the "Student 1" "table_row"
|
||||
And I should see "I'm the teacher feedback" in the "Student 1" "table_row"
|
||||
|
||||
|
@ -71,15 +72,14 @@ Feature: In an assignment, students can comment in their submissions
|
|||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment name"
|
||||
And I follow "View/grade all submissions"
|
||||
And I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I follow "View all submissions"
|
||||
And I click on "Grade" "link" in the "Student 1" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Grade out of 100 | 0 |
|
||||
And I press "Save changes"
|
||||
And I should see "The grade changes were saved"
|
||||
And I press "Continue"
|
||||
When I click on "Grade Student 1" "link" in the "Student 1" "table_row"
|
||||
And I should see "The changes to the grade and feedback were saved"
|
||||
And I press "Ok"
|
||||
And I set the following fields to these values:
|
||||
| Feedback comments | I'm the teacher feedback |
|
||||
And I press "Save changes"
|
||||
Then I should see "The grade changes were saved"
|
||||
Then I should see "The changes to the grade and feedback were saved"
|
||||
|
|
|
@ -90,7 +90,7 @@ Feature: Submit assignment without group
|
|||
And I follow "Allow default group"
|
||||
And I should see "1" in the "Groups" "table_row"
|
||||
And I should not see "The setting 'Require group to make submission\' is enabled and some users are either not a member of any group, or are a member of more than one group, so are unable to make submissions."
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Default group" in the "Student 1" "table_row"
|
||||
And I should see "Default group" in the "Student 2" "table_row"
|
||||
And I should see "Submitted for grading" in the "Student 1" "table_row"
|
||||
|
@ -100,7 +100,7 @@ Feature: Submit assignment without group
|
|||
And I follow "Require group membership"
|
||||
And I should see "0" in the "Groups" "table_row"
|
||||
And I should see "The setting 'Require group to make submission' is enabled and some users are either not a member of any group, or are a member of more than one group, so are unable to make submissions."
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Not a member of any group, so unable to make submissions." in the "Student 1" "table_row"
|
||||
And I should see "Not a member of any group, so unable to make submissions." in the "Student 2" "table_row"
|
||||
And I should not see "Submitted for grading" in the "Student 1" "table_row"
|
||||
|
@ -110,7 +110,7 @@ Feature: Submit assignment without group
|
|||
And I follow "Require group membership"
|
||||
And I should see "1" in the "Groups" "table_row"
|
||||
And I should not see "The setting 'Require group to make submission' is enabled and some users are either not a member of any group, or are a member of more than one group, so are unable to make submissions."
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Group 1" in the "Student 1" "table_row"
|
||||
And I should see "Group 1" in the "Student 2" "table_row"
|
||||
And I should see "Submitted for grading" in the "Student 1" "table_row"
|
||||
|
@ -128,5 +128,5 @@ Feature: Submit assignment without group
|
|||
And I follow "Course 3"
|
||||
And I follow "Require group membership"
|
||||
And I should see "The setting 'Require group to make submission' is enabled and some users are either not a member of any group, or are a member of more than one group, so are unable to make submissions."
|
||||
And I follow "View/grade all submissions"
|
||||
And I follow "View all submissions"
|
||||
And I should see "Member of more than one group, so unable to make submissions." in the "Student 3" "table_row"
|
||||
|
|
|
@ -481,6 +481,8 @@ fieldset.coursesearchbox label {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Make forms vertical when the screen is less than 1474px AND both side-pre and side-post contain blocks.
|
||||
* This is an extra special media rule.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue