MDL-75455 gradereport_singleview: PR review fixes.

Part of: MDL-75423
This commit is contained in:
Ilya Tregubov 2022-11-03 10:27:34 +03:00 committed by Mihail Geshoski
parent 1022863674
commit a08282a872
22 changed files with 291 additions and 161 deletions

View File

@ -134,7 +134,7 @@ Feature: Allow to mark course as completed without cron for activity completion
Given I log in as "teacher1"
And I am on "Completion course" course homepage
And I navigate to "View > Single view" in the course gradebook
And I click on "User" "link" in the ".singleindex" "css_element"
And I click on "Users" "link" in the ".page-toggler" "css_element"
And I turn editing mode on
And I click on "Student First" in the "user" search widget
And I set the field "Override for Test assignment name" to "1"

View File

@ -1730,7 +1730,8 @@ class grade_structure {
if ($menuitems) {
$menu = new action_menu($menuitems);
$icon = $OUTPUT->pix_icon('i/dropdown', get_string('actions'));
$menu->set_menu_trigger($icon, 'btn btn-icon icon-size-2 bg-secondary d-flex align-items-center justify-content-center');
$extraclasses = 'btn btn-icon icon-size-2 bg-secondary d-flex align-items-center justify-content-center';
$menu->set_menu_trigger($icon, $extraclasses);
$menu->set_menu_left();
return $OUTPUT->render($menu);

View File

@ -1,10 +1,10 @@
define("gradereport_singleview/bulkactions",["exports","core/pending","core/custom_interaction_events","core/modal_factory","core/templates","core/modal_events"],(function(_exports,_pending,_custom_interaction_events,_modal_factory,_templates,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
define("gradereport_singleview/bulkactions",["exports","core/pending","core/custom_interaction_events","core/modal_factory","core/templates","core/modal_events","core/str","core/notification","gradereport_singleview/selectors"],(function(_exports,_pending,_custom_interaction_events,_modal_factory,_templates,_modal_events,Str,_notification,_selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Javascript module for bulk actions.
*
* @module gradereport_singleview/bulkactions
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_modal_factory=_interopRequireDefault(_modal_factory),_templates=_interopRequireDefault(_templates),_modal_events=_interopRequireDefault(_modal_events);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest("[data-role]");if(trigger)if("overrideallgrades"===trigger.dataset.role||"overridenonegrades"===trigger.dataset.role){const overrideAll=document.querySelectorAll("input[type=checkbox][name^=override]");if("overridenonegrades"===trigger.dataset.role){const confirm=new M.core.confirm({title:M.util.get_string("removeoverride","gradereport_singleview"),question:M.util.get_string("overridenoneconfirm","gradereport_singleview"),noLabel:M.util.get_string("cancel","moodle"),yesLabel:M.util.get_string("removeoverridesave","gradereport_singleview")});confirm.on("complete-yes",(function(){confirm.hide(),confirm.destroy(),overrideAll.forEach((function(el){el.checked&&el.click()}))}),self),confirm.show()}else overrideAll.forEach((function(el){el.checked||el.click()}))}else if("excludeallgrades"===trigger.dataset.role||"excludenonegrades"===trigger.dataset.role){const excludeAll=document.querySelectorAll("input[type=checkbox][name^=exclude]"),checked="excludeallgrades"===trigger.dataset.role;excludeAll.forEach((function(el){el.checked=checked}))}else"bulklegend"===trigger.dataset.role&&_modal_factory.default.create({type:_modal_factory.default.types.SAVE_CANCEL,body:_templates.default.render("gradereport_singleview/bulkinsert",{id:"bulkinsertmodal",name:"bulkinsertmodal"}),title:"Bulk insert"}).then((function(modal){return modal.setSaveButtonText("Save"),modal.getFooter().find('[data-action="save"]').attr("disabled",!0),modal.getRoot().on(_modal_events.default.hidden,(function(){modal.getRoot().remove()})),modal.getRoot().on("change",'input[type="checkbox"]',(e=>{if(e.preventDefault(),e.target.checked){modal.getRoot().find(".formdata").removeClass("dimmed_text"),modal.getRoot().find('input[type="radio"]').removeAttr("disabled"),modal.getRoot().find('input[type="text"]').removeAttr("disabled");modal.getRoot().find('input[type="radio"]:checked').val()&&modal.getFooter().find('[data-action="save"]').removeAttr("disabled")}else modal.getRoot().find(".formdata").addClass("dimmed_text"),modal.getRoot().find('input[type="radio"]').attr("disabled",!0),modal.getRoot().find('input[type="text"]').attr("disabled",!0),modal.getFooter().find('[data-action="save"]').attr("disabled",!0)})),modal.getRoot().on("change",'input[type="radio"]',(e=>{e.preventDefault(),modal.getFooter().find('[data-action="save"]').removeAttr("disabled")})),modal.getRoot().on(_modal_events.default.save,(function(){document.querySelector('input[type="checkbox"][name^=bulk]').checked=!0;const formRadioData=modal.getRoot().find('input[type="radio"]:checked').val();document.querySelector("select[name^=bulk]").value=formRadioData;const formData=modal.getRoot().find(".form-control").val();document.querySelector('input[type="text"][name^=bulk]').value=formData,document.querySelector('input[type="submit"]').click()})),modal.show(),modal}))}))}))}}));
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_modal_factory=_interopRequireDefault(_modal_factory),_templates=_interopRequireDefault(_templates),_modal_events=_interopRequireDefault(_modal_events),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str),_notification=_interopRequireDefault(_notification),_selectors=_interopRequireDefault(_selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(_selectors.default.actions.bulkaction);if(trigger)if("overrideallgrades"===trigger.dataset.action||"overridenonegrades"===trigger.dataset.action){const override=document.querySelectorAll(_selectors.default.elements.override);"overridenonegrades"===trigger.dataset.action?Str.get_strings([{key:"removeoverride",component:"gradereport_singleview"},{key:"overridenoneconfirm",component:"gradereport_singleview"},{key:"removeoverridesave",component:"gradereport_singleview"},{key:"cancel",component:"moodle"}]).done((strings=>{_notification.default.confirm(strings[0],strings[1],strings[2],strings[3],(()=>{override.forEach((el=>{el.checked&&el.click()}))}))})).fail(_notification.default.exception):override.forEach((el=>{el.checked||el.click()}))}else if("excludeallgrades"===trigger.dataset.action||"excludenonegrades"===trigger.dataset.action){const exclude=document.querySelectorAll(_selectors.default.elements.exclude),checked="excludeallgrades"===trigger.dataset.action;exclude.forEach((el=>{el.checked=checked}))}else"bulklegend"===trigger.dataset.action&&Str.get_strings([{key:"bulklegend",component:"gradereport_singleview"},{key:"save",component:"moodle"}]).done((strings=>{_modal_factory.default.create({type:_modal_factory.default.types.SAVE_CANCEL,body:_templates.default.render("gradereport_singleview/bulkinsert",{id:"bulkinsertmodal",name:"bulkinsertmodal"}),title:strings[0]}).then((modal=>(modal.setSaveButtonText(strings[1]),modal.getFooter().find(_selectors.default.elements.modalsave).attr("disabled",!0),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.getRoot().remove()})),modal.getRoot().on("change",_selectors.default.elements.warningcheckbox,(e=>{if(e.preventDefault(),e.target.checked){modal.getRoot().find(_selectors.default.elements.modalformdata).removeClass("dimmed_text"),modal.getRoot().find(_selectors.default.elements.modalradio).removeAttr("disabled"),modal.getRoot().find(_selectors.default.elements.modalinput).removeAttr("disabled");modal.getRoot().find(_selectors.default.elements.modalradiochecked).val()&&modal.getFooter().find(_selectors.default.elements.modalsave).removeAttr("disabled")}else modal.getRoot().find(_selectors.default.elements.modalformdata).addClass("dimmed_text"),modal.getRoot().find(_selectors.default.elements.modalradio).attr("disabled",!0),modal.getRoot().find(_selectors.default.elements.modalinput).attr("disabled",!0),modal.getFooter().find(_selectors.default.elements.modalsave).attr("disabled",!0)})),modal.getRoot().on("change",_selectors.default.elements.modalradio,(e=>{e.preventDefault(),modal.getFooter().find(_selectors.default.elements.modalsave).removeAttr("disabled")})),modal.getRoot().on(_modal_events.default.save,(()=>{document.querySelector(_selectors.default.elements.enablebulkinsert).checked=!0;const formRadioData=modal.getRoot().find(_selectors.default.elements.modalradiochecked).val();document.querySelector(_selectors.default.elements.formradio).value=formRadioData;const formData=modal.getRoot().find(_selectors.default.elements.modalgrade).val();document.querySelector(_selectors.default.elements.formgrade).value=formData,document.querySelector(_selectors.default.elements.formsave).click()})),modal.show(),modal))).fail(_notification.default.exception)})).fail(_notification.default.exception)}))}))}}));
//# sourceMappingURL=bulkactions.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
define("gradereport_singleview/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var name,value,_default={actions:{bulkaction:(name="role",value="bulkaction","[data-".concat(name,'="').concat(value,'"]'))},elements:{override:"input[type=checkbox][name^=override]",exclude:"input[type=checkbox][name^=exclude]",modalsave:'[data-action="save"]',warningcheckbox:'input[type="checkbox"]',modalformdata:".formdata",modalradio:'input[type="radio"]',modalinput:'input[type="text"]',modalradiochecked:'input[type="radio"]:checked',enablebulkinsert:'input[type="checkbox"][name^=bulk]',formradio:"select[name^=bulk]",modalgrade:".form-control",formgrade:'input[type="text"][name^=bulk]',formsave:'input[type="submit"]'}};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=selectors.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Define all of the selectors we will be using on the grading interface.\n *\n * @module gradereport_singleview/selectors\n * @copyright 2022 Ilya Tregubov <ilya@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n actions: {\n bulkaction: getDataSelector('role', 'bulkaction'),\n },\n elements: {\n override: 'input[type=checkbox][name^=override]',\n exclude: 'input[type=checkbox][name^=exclude]',\n modalsave: '[data-action=\"save\"]',\n warningcheckbox: 'input[type=\"checkbox\"]',\n modalformdata: '.formdata',\n modalradio: 'input[type=\"radio\"]',\n modalinput: 'input[type=\"text\"]',\n modalradiochecked: 'input[type=\"radio\"]:checked',\n enablebulkinsert: 'input[type=\"checkbox\"][name^=bulk]',\n formradio: 'select[name^=bulk]',\n modalgrade: '.form-control',\n formgrade: 'input[type=\"text\"][name^=bulk]',\n formsave: 'input[type=\"submit\"]',\n },\n};\n"],"names":["name","value","actions","bulkaction","elements","override","exclude","modalsave","warningcheckbox","modalformdata","modalradio","modalinput","modalradiochecked","enablebulkinsert","formradio","modalgrade","formgrade","formsave"],"mappings":"8JA8ByBA,KAAMC,eAIhB,CACXC,QAAS,CACLC,YANiBH,KAMW,OANLC,MAMa,6BALxBD,kBAASC,cAOzBG,SAAU,CACNC,SAAU,uCACVC,QAAS,sCACTC,UAAW,uBACXC,gBAAiB,yBACjBC,cAAe,YACfC,WAAY,sBACZC,WAAY,qBACZC,kBAAmB,8BACnBC,iBAAkB,qCAClBC,UAAW,qBACXC,WAAY,gBACZC,UAAW,iCACXC,SAAU"}

View File

@ -26,6 +26,9 @@ import CustomEvents from "core/custom_interaction_events";
import ModalFactory from 'core/modal_factory';
import Templates from 'core/templates';
import ModalEvents from 'core/modal_events';
import * as Str from 'core/str';
import Notification from 'core/notification';
import selectors from 'gradereport_singleview/selectors';
/**
* Initialize module.
@ -52,102 +55,119 @@ const registerListenerEvents = () => {
// Register events.
events.forEach((event) => {
document.addEventListener(event, async(e) => {
const trigger = e.target.closest('[data-role]');
const trigger = e.target.closest(selectors.actions.bulkaction);
if (trigger) {
if ((trigger.dataset.role === 'overrideallgrades') || (trigger.dataset.role === 'overridenonegrades')) {
const overrideAll = document.querySelectorAll('input[type=checkbox][name^=override]');
if ((trigger.dataset.action === 'overrideallgrades') || (trigger.dataset.action === 'overridenonegrades')) {
const override = document.querySelectorAll(selectors.elements.override);
if (trigger.dataset.role === 'overridenonegrades') {
const confirm = new M.core.confirm({
title: M.util.get_string('removeoverride', 'gradereport_singleview'),
question: M.util.get_string('overridenoneconfirm', 'gradereport_singleview'),
noLabel: M.util.get_string('cancel', 'moodle'),
yesLabel: M.util.get_string('removeoverridesave', 'gradereport_singleview')
});
if (trigger.dataset.action === 'overridenonegrades') {
// Alert for removing all grade overrides on page.
Str.get_strings([
{key: 'removeoverride', component: 'gradereport_singleview'},
{key: 'overridenoneconfirm', component: 'gradereport_singleview'},
{key: 'removeoverridesave', component: 'gradereport_singleview'},
{key: 'cancel', component: 'moodle'},
]).done((strings) => {
Notification.confirm(
strings[0],
strings[1],
strings[2],
strings[3],
() => {
// Uncheck each override checkbox - this will make grade and feedback input fields disabled.
override.forEach((el) => {
if (el.checked) {
el.click();
}
});
});
}).fail(Notification.exception);
confirm.on('complete-yes', function () {
confirm.hide();
confirm.destroy();
overrideAll.forEach(function (el) {
if (el.checked) {
el.click();
}
});
}, self);
confirm.show();
} else {
overrideAll.forEach(function (el) {
// Check each override checkbox - this will make grade and feedback input fields enabled.
override.forEach((el) => {
if (!el.checked) {
el.click();
}
});
}
} else if ((trigger.dataset.role === 'excludeallgrades') || (trigger.dataset.role === 'excludenonegrades')) {
const excludeAll = document.querySelectorAll('input[type=checkbox][name^=exclude]');
const checked = (trigger.dataset.role === 'excludeallgrades');
excludeAll.forEach(function (el) {
} else if ((trigger.dataset.action === 'excludeallgrades') || (trigger.dataset.action === 'excludenonegrades')) {
const exclude = document.querySelectorAll(selectors.elements.exclude);
const checked = (trigger.dataset.action === 'excludeallgrades');
// Uncheck or check each exclude checkbox.
exclude.forEach((el) => {
el.checked = checked;
});
} else if (trigger.dataset.role === 'bulklegend') {
ModalFactory.create({
type: ModalFactory.types.SAVE_CANCEL,
body: Templates.render('gradereport_singleview/bulkinsert', {
id: 'bulkinsertmodal',
name: 'bulkinsertmodal'
}),
title: 'Bulk insert',
})
.then(function (modal) {
modal.setSaveButtonText('Save');
modal.getFooter().find('[data-action="save"]').attr('disabled', true);
} else if (trigger.dataset.action === 'bulklegend') {
// Modal for bulk insert grades.
Str.get_strings([
{key: 'bulklegend', component: 'gradereport_singleview'},
{key: 'save', component: 'moodle'},
]).done((strings) => {
ModalFactory.create({
type: ModalFactory.types.SAVE_CANCEL,
body: Templates.render('gradereport_singleview/bulkinsert', {
id: 'bulkinsertmodal',
name: 'bulkinsertmodal'
}),
title: strings[0],
}).then((modal) => {
modal.setSaveButtonText(strings[1]);
modal.getFooter().find(selectors.elements.modalsave).attr('disabled', true);
modal.getRoot().on(ModalEvents.hidden, function () {
modal.getRoot().on(ModalEvents.hidden, () => {
modal.getRoot().remove();
});
modal.getRoot().on('change', 'input[type="checkbox"]',
// We need to acknowledge that we understand risks of loosing data.
// Only when acknowledge checkbox is checked we allow selecting insert options.
modal.getRoot().on('change', selectors.elements.warningcheckbox,
(e) => {
e.preventDefault();
if (e.target.checked) {
modal.getRoot().find('.formdata').removeClass('dimmed_text');
modal.getRoot().find('input[type="radio"]').removeAttr('disabled');
modal.getRoot().find('input[type="text"]').removeAttr('disabled');
modal.getRoot().find(selectors.elements.modalformdata).removeClass('dimmed_text');
modal.getRoot().find(selectors.elements.modalradio).removeAttr('disabled');
modal.getRoot().find(selectors.elements.modalinput).removeAttr('disabled');
const formRadioData = modal.getRoot().find('input[type="radio"]:checked').val();
const formRadioData = modal.getRoot().find(selectors.elements.modalradiochecked).val();
// We allow saving grades only when all needed data present on form.
if (formRadioData) {
modal.getFooter().find('[data-action="save"]').removeAttr('disabled');
modal.getFooter().find(selectors.elements.modalsave).removeAttr('disabled');
}
} else {
modal.getRoot().find('.formdata').addClass('dimmed_text');
modal.getRoot().find('input[type="radio"]').attr('disabled', true);
modal.getRoot().find('input[type="text"]').attr('disabled', true);
modal.getFooter().find('[data-action="save"]').attr('disabled', true);
modal.getRoot().find(selectors.elements.modalformdata).addClass('dimmed_text');
modal.getRoot().find(selectors.elements.modalradio).attr('disabled', true);
modal.getRoot().find(selectors.elements.modalinput).attr('disabled', true);
modal.getFooter().find(selectors.elements.modalsave).attr('disabled', true);
}
});
modal.getRoot().on('change', 'input[type="radio"]',
// We allow saving grades only when all needed data present on form.
modal.getRoot().on('change', selectors.elements.modalradio,
(e) => {
e.preventDefault();
modal.getFooter().find('[data-action="save"]').removeAttr('disabled');
modal.getFooter().find(selectors.elements.modalsave).removeAttr('disabled');
});
modal.getRoot().on(ModalEvents.save, function () {
document.querySelector('input[type="checkbox"][name^=bulk]').checked = true;
const formRadioData = modal.getRoot().find('input[type="radio"]:checked').val();
const $select = document.querySelector('select[name^=bulk]');
modal.getRoot().on(ModalEvents.save, () => {
// When save button is clicked in modal form we insert data from modal
// into preexisted hidden bulk insert form and Save button for table form.
document.querySelector(selectors.elements.enablebulkinsert).checked = true;
const formRadioData = modal.getRoot().find(selectors.elements.modalradiochecked).val();
const $select = document.querySelector(selectors.elements.formradio);
$select.value = formRadioData;
const formData = modal.getRoot().find('.form-control').val();
document.querySelector('input[type="text"][name^=bulk]').value = formData;
document.querySelector('input[type="submit"]').click();
const formData = modal.getRoot().find(selectors.elements.modalgrade).val();
document.querySelector(selectors.elements.formgrade).value = formData;
document.querySelector(selectors.elements.formsave).click();
});
modal.show();
return modal;
});
}).fail(Notification.exception);
}).fail(Notification.exception);
}
}
});

View File

@ -0,0 +1,54 @@
// 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/>.
/**
* Define all of the selectors we will be using on the grading interface.
*
* @module gradereport_singleview/selectors
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* A small helper function to build queryable data selectors.
* @method getDataSelector
* @param {String} name
* @param {String} value
* @return {string}
*/
const getDataSelector = (name, value) => {
return `[data-${name}="${value}"]`;
};
export default {
actions: {
bulkaction: getDataSelector('role', 'bulkaction'),
},
elements: {
override: 'input[type=checkbox][name^=override]',
exclude: 'input[type=checkbox][name^=exclude]',
modalsave: '[data-action="save"]',
warningcheckbox: 'input[type="checkbox"]',
modalformdata: '.formdata',
modalradio: 'input[type="radio"]',
modalinput: 'input[type="text"]',
modalradiochecked: 'input[type="radio"]:checked',
enablebulkinsert: 'input[type="checkbox"][name^=bulk]',
formradio: 'select[name^=bulk]',
modalgrade: '.form-control',
formgrade: 'input[type="text"][name^=bulk]',
formsave: 'input[type="submit"]',
},
};

View File

@ -97,7 +97,6 @@ class bulk_insert extends element {
['value' => 'blanks', 'name' => get_string('blanks', 'gradereport_singleview'), 'selected' => true],
],
'valuename' => $this->insertname,
'valuelabel' => get_string('bulkinsertvalue', 'gradereport_singleview'),
'valuefield' => $text->html()
];

View File

@ -83,10 +83,10 @@ class action_bar extends \core_grades\output\action_bar {
'displaylabel' => true,
'userselectactive' => $this->itemtype === 'user',
'gradeselectactive' => $this->itemtype === 'grade',
'gradezerolink' => new moodle_url('/grade/report/singleview/index.php',
['id' => $courseid, 'item' => 'grade_select']),
'userzerolink' => new moodle_url('/grade/report/singleview/index.php',
['id' => $courseid, 'item' => 'user_select'])
'gradezerolink' => (new moodle_url('/grade/report/singleview/index.php',
['id' => $courseid, 'item' => 'grade_select']))->out(false),
'userzerolink' => (new moodle_url('/grade/report/singleview/index.php',
['id' => $courseid, 'item' => 'user_select']))->out(false)
];
$data['groupselector'] = $this->report->group_selector;

View File

@ -189,7 +189,7 @@ class singleview extends grade_report {
foreach ($options as $type => $option) {
$action = new \action_menu_link_secondary(new \moodle_url('#'), null, $option,
['data-role' => $type]);
['data-action' => $type, 'data-role' => 'bulkaction']);
$menu->add($action);
}
$menu->attributes['class'] .= ' float-left my-auto';

View File

@ -30,7 +30,6 @@ $string['assessmentname'] = 'Grade item';
$string['blanks'] = 'Empty grades';
$string['bulkappliesto'] = 'For';
$string['bulkinsertvalue'] = 'Insert value';
$string['bulkinsertvaluemodal'] = 'Insert value modal';
$string['bulklegend'] = 'Bulk insert';
$string['bulkchoice'] = 'For which grades do you want to insert?';
$string['bulkperform'] = 'Perform bulk insert';
@ -72,9 +71,8 @@ $string['selectuser'] = 'Select user...';
$string['singleview:view'] = 'View report';
$string['summarygrade'] = 'A table of users, with columns for range, grade, feedback, and whether to override or exclude a particular grade.';
$string['summaryuser'] = 'A table of grade items, with columns for grade category, range, grade, feedback, and whether to override or exclude a particular grade.';
$string['unsavedataalert'] = 'If you have unsave changes in grades table, you might lose some data when chosing "All grades" and click Save.';
$string['unsavedataconfirm'] = 'I understand that my unsaved data might be lost';
$string['users'] = 'Users';
$string['unsavedataalert'] = 'You have unsaved changes in the grades table, you will lose these changes if you proceed with the bulk insert.';
$string['unsavedataconfirm'] = 'I understand that my unsaved changes will be lost.';
$string['userselect'] = 'Select activity';
$string['ariareporttype'] = 'Select a report type to view';
@ -84,6 +82,7 @@ $string['selectagrade'] = 'Select a grade item';
$string['selectuserinstructions'] = 'By selecting a user you can view the grades by activity';
$string['selectgradeinstructions'] = 'Select items to grade...';
$string['selectgradeitemlink'] = 'Click to select a grade item';
$string['unsaveddatawarning'] = 'Unsaved data warning';
$string['whattoview'] = 'What would you like to view';
$string['whattoviewselect'] = 'Select to view by users or grade items';

View File

@ -134,7 +134,14 @@
.path-grade-report-singleview input[type=checkbox] {
width: 22px;
height: 21px;
margin: 11.5px auto 11.5px auto;
margin: 11.5px 11.5px 11.5px auto;
}
#bulkinsertmodal {
width: 181px;
height: 39px;
top: 309px;
border-radius: 4px;
}
.path-grade-report-singleview .singleview_bulk > fieldset {

View File

@ -16,6 +16,59 @@
}}
{{!
@template gradereport_singleview/action_bar
Context variables required for this template:
* generalnavselector - The data object containing the required properties to render the general navigation selector.
* groupselector - (optional) HTML that outputs the group selector
* itemselector - (optional) HTML that outputs the user or grade item selector
* pagetoggler - (optional) HTML that outputs the user/grade item view toggler
* bulkactions - (optional) HTML that outputs the bulk actione menu
Example context (json):
{
"generalnavselector": {
"name": "Gradebook tertiary navigation selector",
"value": "opt2",
"baseid": "select-menu56789",
"selectedoption": "Gradebook setup",
"options": [
{
"selected": false,
"isgroup": {
"name": "View",
"id": "select-menu-group1",
"options": [
{
"name": "Grader report",
"value": "opt1",
"id": "select-menu-option1",
"selected": false
}
]
}
},
{
"selected": false,
"isgroup": {
"name": "Setup",
"id": "select-menu-group2",
"options": [
{
"name": "Gradebook setup",
"value": "opt2",
"id": "select-menu-option2",
"selected": true
}
]
}
}
]
},
"groupselector": "<div class='group-selector'></div>",
"itemselector": "<div class='user-selector'></div>",
"pagetoggler": "<div class='page-toggler'></div>",
"bulkactions": "<div class='action-menu'></div>"
}
}}
<div class="container-fluid tertiary-navigation full-width-bottom-border">
<div class="row">

View File

@ -14,16 +14,17 @@
{{!
@template core_message/send_bulk_message
Template for the bulk insert grades modal.
Context variables required for this template:
None
Example context (json):
{
"name": "bulksinsertmodal",
"id": "bulksinsertmodal"
}
}}
<form>
<div class="alert alert-danger">
<div class="alert alert-warning">
{{#str}}unsavedataalert, gradereport_singleview{{/str}}
<span class="sr-only">{{#str}}unsaveddatawarning, gradereport_singleview{{/str}}</span>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" value="1" id="override">
<label class="form-check-label" for="override">
@ -31,26 +32,22 @@
</label>
</div>
</div>
<div class="formdata dimmed_text">
<p>
{{#str}}bulkchoice, gradereport_singleview{{/str}}
<div class="pt-3 px-3" role="radiogroup">
<label class="form-check">
<input class="form-check-input" type="radio" name="bulkinsert" value="all" disabled/>
{{#str}}all_grades, gradereport_singleview{{/str}}
</label>
<label class="form-check">
<input class="form-check-input" type="radio" name="bulkinsert" value="blanks" disabled/>
{{#str}}blanks, gradereport_singleview{{/str}}
</label>
</div>
</p>
{{#str}}bulkchoice, gradereport_singleview{{/str}}
<div class="pt-3 px-3" role="radiogroup">
<label class="form-check">
<input class="form-check-input" type="radio" name="bulkinsert" value="all" disabled/>
{{#str}}all_grades, gradereport_singleview{{/str}}
</label>
<label class="form-check">
<input class="form-check-input" type="radio" name="bulkinsert" value="blanks" disabled/>
{{#str}}blanks, gradereport_singleview{{/str}}
</label>
</div>
<p class ="font-weight-bold">
{{#str}}bulkinsertvalue, gradereport_singleview{{/str}}
</p>
<label for="{{name}}">{{#str}}bulkinsertvaluemodal, gradereport_singleview{{/str}}</label>
<label for="{{name}}" class="sr-only">{{#str}}bulkinsertvalue, gradereport_singleview{{/str}}</label>
<input type="text" name="{{name}}" id="{{id}}" value="0" class="form-control text-ltr" disabled {{#readonly}}readonly{{/readonly}}>
</div>

View File

@ -25,7 +25,7 @@
{
"courseid": "2",
"selectedoption": {
"text": "Grade item 1",
"text": "Grade item 1"
}
}
}}

View File

@ -33,16 +33,16 @@
<p class="my-auto mr-3 text-uppercase">{{#str}}viewby, gradereport_singleview{{/str}}</p>
{{/displaylabel}}
<div class="btn-group align-items-center" role="group" aria-label="{{#str}}ariareporttype, gradereport_singleview{{/str}}">
<a href="{{{userzerolink}}}"
type="button"
<a href="{{userzerolink}}"
role="button"
class="btn btn-outline-primary {{#userselectactive}}active{{/userselectactive}}"
{{#userselectactive}}aria-pressed="true"{{/userselectactive}}
{{^userselectactive}}aria-pressed="false"{{/userselectactive}}
>
{{#str}}users{{/str}}
</a>
<a href="{{{gradezerolink}}}"
type="button"
<a href="{{gradezerolink}}"
role="button"
class="btn btn-outline-primary {{#gradeselectactive}}active{{/gradeselectactive}}"
{{#gradeselectactive}}aria-pressed="true"{{/gradeselectactive}}
{{^gradeselectactive}}aria-pressed="false"{{/gradeselectactive}}

View File

@ -7,52 +7,52 @@ Feature: We can bulk insert grades for students in a course
Background:
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
| Course 1 | C1 | 0 |
And the following "users" exist:
| username | firstname | lastname | email | idnumber | alternatename |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 | fred |
| student1 | Student | 1 | student1@example.com | s1 | james |
| student2 | Student | 2 | student1@example.com | s2 | holly |
| student3 | Student | 3 | student1@example.com | s3 | anna |
| student4 | Student | 4 | student1@example.com | s4 | zac |
| username | firstname | lastname | email | idnumber | alternatename |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 | fred |
| student1 | Student | 1 | student1@example.com | s1 | james |
| student2 | Student | 2 | student1@example.com | s2 | holly |
| student3 | Student | 3 | student1@example.com | s3 | anna |
| student4 | Student | 4 | student1@example.com | s4 | zac |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
And the following "activities" exist:
| activity | course | idnumber | name | intro |
| assign | C1 | a1 | Test assignment one | Submit something! |
| assign | C1 | a2 | Test assignment two | Submit something! |
| assign | C1 | a3 | Test assignment three | Submit something! |
| assign | C1 | a4 | Test assignment four | Submit nothing! |
| activity | course | idnumber | name | intro |
| assign | C1 | a1 | Test assignment one | Submit something! |
| assign | C1 | a2 | Test assignment two | Submit something! |
| assign | C1 | a3 | Test assignment three | Submit something! |
| assign | C1 | a4 | Test assignment four | Submit nothing! |
And I am on the "Course 1" "Course" page logged in as "teacher1"
And I turn editing mode on
Scenario: I can not save bulk insert until I fill required form elements
Given I am on the "Test assignment one" "assign activity" page logged in as teacher1
And I am on "Course 1" course homepage with editing mode on
And I navigate to "View > Grader report" in the course gradebook
Given I navigate to "View > Grader report" in the course gradebook
And I follow "Single view for Test assignment one"
And I click on "Actions" "link"
When I click on "Bulk insert" "link"
And the "Empty grades" "radio" should be disabled
And the "All grades" "radio" should be disabled
And the "Insert value modal" "field" should be disabled
And the "[name=bulkinsertmodal]" "css_element" should be disabled
And the "[data-action=save]" "css_element" should be disabled
And I click on "I understand that my unsaved data might be lost" "checkbox"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And the "Empty grades" "radio" should be enabled
And the "All grades" "radio" should be enabled
And the "Insert value modal" "field" should be enabled
And the "[name=bulkinsertmodal]" "css_element" should be enabled
And the "[data-action=save]" "css_element" should be disabled
And I click on "Empty grades" "radio"
And the "Empty grades" "radio" should be enabled
And the "All grades" "radio" should be enabled
And the "Insert value modal" "field" should be enabled
And the "[data-action=save]" "css_element" should be enabled
And the "[name=bulkinsertmodal]" "css_element" should be enabled
Then the "[data-action=save]" "css_element" should be enabled
Scenario: I can bulk insert grades and check their override flags for grade view.
Given I am on the "Test assignment one" "assign activity" page logged in as teacher1
Given I am on the "Test assignment one" "assign activity" page
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:
@ -61,13 +61,13 @@ Feature: We can bulk insert grades for students in a course
And I am on "Course 1" course homepage with editing mode on
And I navigate to "View > Grader report" in the course gradebook
And I follow "Single view for Test assignment one"
Then the field "Grade for Student 1" matches value "50.00"
And the field "Grade for Student 1" matches value "50.00"
And the field "Override for Student 1" matches value "0"
And I click on "Actions" "link"
And I click on "Bulk insert" "link"
And I click on "I understand that my unsaved data might be lost" "checkbox"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "Empty grades" "radio"
And I set the field "Insert value modal" to "1.0"
And I set the field "Insert value" to "1.0"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
And the field "Grade for Student 1" matches value "50.00"
And the field "Override for Student 1" matches value "0"
@ -79,10 +79,10 @@ Feature: We can bulk insert grades for students in a course
And the field "Override for Student 4" matches value "1"
And I click on "Actions" "link"
And I click on "Bulk insert" "link"
And I click on "I understand that my unsaved data might be lost" "checkbox"
When I click on "Bulk insert" "link"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "All grades" "radio"
And I set the field "Insert value modal" to "2.0"
And I set the field "Insert value" to "2.0"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
And the field "Grade for Student 1" matches value "2.00"
And the field "Override for Student 1" matches value "1"
@ -91,10 +91,10 @@ Feature: We can bulk insert grades for students in a course
And the field "Grade for Student 3" matches value "2.00"
And the field "Override for Student 3" matches value "1"
And the field "Grade for Student 4" matches value "2.00"
And the field "Override for Student 4" matches value "1"
Then the field "Override for Student 4" matches value "1"
Scenario: I can bulk insert grades and check their override flags for user view.
Given I am on the "Test assignment two" "assign activity" page logged in as teacher1
Given I am on the "Test assignment two" "assign activity" page
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:
@ -102,16 +102,14 @@ Feature: We can bulk insert grades for students in a course
And I press "Save changes"
And I am on "Course 1" course homepage with editing mode on
And I navigate to "View > Grader report" in the course gradebook
# And I click on "input[title='Dock Navigation block']" "css_element"
# And I click on "input[title='Dock Administration block']" "css_element"
And I follow "Single view for Student 1"
Then the field "Grade for Test assignment two" matches value "50.00"
And the field "Grade for Test assignment two" matches value "50.00"
And the field "Override for Test assignment two" matches value "0"
And I click on "Actions" "link"
And I click on "Bulk insert" "link"
And I click on "I understand that my unsaved data might be lost" "checkbox"
When I click on "Bulk insert" "link"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "Empty grades" "radio"
And I set the field "Insert value modal" to "1.0"
And I set the field "Insert value" to "1.0"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
And the field "Grade for Test assignment two" matches value "50.00"
And the field "Override for Test assignment two" matches value "0"
@ -120,21 +118,19 @@ Feature: We can bulk insert grades for students in a course
And the field "Grade for Test assignment three" matches value "1.00"
And the field "Override for Test assignment three" matches value "1"
And the field "Grade for Test assignment four" matches value "1.00"
And the field "Override for Test assignment four" matches value "1"
Then the field "Override for Test assignment four" matches value "1"
Scenario: I can not update grades if the value is out of bounds.
Given I am on the "Course 1" course page logged in as teacher1
And I turn editing mode on
And I navigate to "View > Grader report" in the course gradebook
Given I navigate to "View > Grader report" in the course gradebook
And I follow "Single view for Test assignment one"
And I click on "Actions" "link"
And I click on "Bulk insert" "link"
And I click on "I understand that my unsaved data might be lost" "checkbox"
When I click on "Bulk insert" "link"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "Empty grades" "radio"
And I set the field "Insert value modal" to "-1"
And I set the field "Insert value" to "-1"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
Then I should see "The grade entered for Test assignment one for Student 1 is less than the minimum allowed"
And I should see "The grade entered for Test assignment one for Student 1 is less than the minimum allowed"
And I should see "The grade entered for Test assignment one for Student 2 is less than the minimum allowed"
And I should see "The grade entered for Test assignment one for Student 3 is less than the minimum allowed"
And I should see "The grade entered for Test assignment one for Student 4 is less than the minimum allowed"
And I should see "Grades were set for 0 items"
Then I should see "Grades were set for 0 items"

View File

@ -21,7 +21,7 @@ Feature: Given we have opted to search for a grade item, Lets find and search th
Scenario: A teacher can search for and find a grade item to view
Given I navigate to "View > Single view" in the course gradebook
And I click on "Grade items" "link" in the ".singleindex" "css_element"
And I click on "Grade items" "link" in the ".page-toggler" "css_element"
When I click on ".gradewidget" "css_element"
Then I confirm "Test assignment one" in "Select a grade item" search within the gradebook widget exists
And I confirm "Test assignment two" in "Select a grade item" search within the gradebook widget exists

View File

@ -20,7 +20,7 @@ Feature: Given we land on the index page, select what type of report we wish to
Scenario: I switch between the two report types within singleview
Given I navigate to "View > Single view" in the course gradebook
And I click on "Grade items" "link" in the ".singleindex" "css_element"
And I click on "Grade items" "link" in the ".page-toggler" "css_element"
When I click on ".gradewidget" "css_element"
Then I wait until "Select a grade item" "dialogue" exists
And I click on "Close" "button" in the "Select a grade item" "dialogue"

View File

@ -59,7 +59,7 @@ Feature: We can use Single view
Scenario: I can update grades, add feedback and exclude grades.
Given I navigate to "View > Single view" in the course gradebook
And I click on "User" "link" in the ".singleindex" "css_element"
And I click on "Users" "link"
And I click on "Student" in the "user" search widget
And I turn editing mode on
And I set the field "Override for Test assignment one" to "1"
@ -99,7 +99,7 @@ Feature: We can use Single view
And I log in as "teacher2"
And I am on "Course 1" course homepage
Given I navigate to "View > Single view" in the course gradebook
And I click on "User" "link" in the ".singleindex" "css_element"
And I click on "Users" "link"
And I click on "Student" in the "user" search widget
And I turn editing mode on
And the "Exclude for Test assignment one" "checkbox" should be disabled
@ -118,9 +118,9 @@ Feature: We can use Single view
When I turn editing mode on
And I click on "Actions" "link"
And I click on "Bulk insert" "link"
And I click on "I understand that my unsaved data might be lost" "checkbox"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "All grades" "radio"
And I set the field "Insert value modal" to "1.0"
And I set the field "Insert value" to "1.0"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
Then I should see "Grades were set for 6 items"
@ -133,12 +133,12 @@ Feature: We can use Single view
When I turn editing mode on
And I click on "Actions" "link"
And I click on "Bulk insert" "link"
And I click on "I understand that my unsaved data might be lost" "checkbox"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "All grades" "radio"
And I set the field "Insert value modal" to "1#25"
And I set the field "Insert value" to "1#25"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
Then I should see "Grades were set for 6 items"
# Custome scale, cast to int
# Custom scale, cast to int
And the field "Grade for new grade item 1" matches value "Disappointing"
# Value grade, float with custom decsep.
And the field "Grade for Test assignment one" matches value "1#25"

View File

@ -21,7 +21,7 @@ Feature: Within the singleview report, a teacher can search for users.
Scenario: A teacher can search for and find a user to view
Given I navigate to "View > Single view" in the course gradebook
When I click on "User" "link" in the ".singleindex" "css_element"
When I click on "Users" "link" in the ".page-toggler" "css_element"
And I wait until the page is ready
And I click on ".userwidget" "css_element"
Then I confirm "Student 1" in "Select a user" search within the gradebook widget exists