Merge branch 'MDL-80746-main-update-rebased' of https://github.com/mihailges/moodle

This commit is contained in:
Jake Dallimore
2024-09-25 14:45:26 +08:00
39 changed files with 902 additions and 248 deletions
+10
View File
@@ -0,0 +1,10 @@
define("core_course/actionbar/initials",["exports","core/pending","core/url","core/custom_interaction_events","jquery"],(function(_exports,_pending,Url,_custom_interaction_events,_jquery){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}}
/**
* A small dropdown to filter users.
*
* @module core_course/actionbar/initials
* @copyright 2022 Mathew May <mathew.solutions>
* @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),Url=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}(Url),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_jquery=_interopRequireDefault(_jquery);let registered=!1;const selectors_pageListItem="page-item",selectors_pageClickableItem=".page-link",selectors_activeItem="active",selectors_formDropdown=".initialsdropdownform",selectors_parentDomNode=".initials-selector",selectors_firstInitial="firstinitial",selectors_lastInitial="lastinitial",selectors_initialBars=".initialbar",selectors_targetButton="initialswidget",selectors_formItems={type:"submit",save:"save",cancel:"cancel"};_exports.init=function(callingLink){let firstInitialParam=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"sifirst",lastInitialParam=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"silast",additionalParams=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(callingLink,firstInitialParam,lastInitialParam,additionalParams),(0,_jquery.default)(selectors_parentDomNode).on("shown.bs.dropdown",(()=>{document.querySelector(selectors_pageClickableItem).focus({preventScroll:!0})})),pendingPromise.resolve(),registered=!0};const registerListenerEvents=function(callingLink){let firstInitialParam=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"sifirst",lastInitialParam=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"silast",additionalParams=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];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,(e=>{let{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}=onClickVariables(),itemToReset="";if(e.target.closest(selectors_formDropdown)&&e.preventDefault(),e.target.closest("".concat(selectors_formDropdown," .").concat(selectors_pageListItem))){if(e.target.classList.contains(selectors_pageListItem))return;e.target.closest(selectors_initialBars).classList.contains(selectors_firstInitial)?(sifirst=e.target,itemToReset=firstActive):(silast=e.target,itemToReset=lastActive),swapActiveItems(itemToReset,e)}if(e.target.closest("".concat(selectors_formDropdown))&&e.target.type===selectors_formItems.type){if(e.target.dataset.action===selectors_formItems.save){const params={id:e.target.closest(selectors_formDropdown).dataset.courseid,[firstInitialParam]:sifirst.parentElement.classList.contains("initialbarall")?"":sifirst.value,[lastInitialParam]:silast.parentElement.classList.contains("initialbarall")?"":silast.value};for(const[key,value]of Object.entries(additionalParams))params[key]=value;window.location=Url.relativeUrl(callingLink,params)}e.target.dataset.action===selectors_formItems.cancel&&(0,_jquery.default)(".".concat(selectors_targetButton)).dropdown("toggle")}}))}))},onClickVariables=()=>{const firstItems=[...document.querySelectorAll(".".concat(selectors_firstInitial," li"))],lastItems=[...document.querySelectorAll(".".concat(selectors_lastInitial," li"))],firstActive=firstItems.filter((item=>item.classList.contains(selectors_activeItem)))[0],lastActive=lastItems.filter((item=>item.classList.contains(selectors_activeItem)))[0];let sifirst=firstActive.querySelector(selectors_pageClickableItem),silast=lastActive.querySelector(selectors_pageClickableItem);return{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}},swapActiveItems=(itemToReset,e)=>{itemToReset.classList.remove(selectors_activeItem),itemToReset.querySelector(selectors_pageClickableItem).ariaCurrent=!1;e.target.parentElement.classList.add(selectors_activeItem),e.target.ariaCurrent=!0}}));
//# sourceMappingURL=initials.min.js.map
File diff suppressed because one or more lines are too long
+183
View File
@@ -0,0 +1,183 @@
// 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 small dropdown to filter users.
*
* @module core_course/actionbar/initials
* @copyright 2022 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Pending from 'core/pending';
import * as Url from 'core/url';
import CustomEvents from "core/custom_interaction_events";
import $ from 'jquery';
/**
* Whether the event listener has already been registered for this module.
*
* @type {boolean}
*/
let registered = false;
// Contain our selectors within this file until they could be of use elsewhere.
const selectors = {
pageListItem: 'page-item',
pageClickableItem: '.page-link',
activeItem: 'active',
formDropdown: '.initialsdropdownform',
parentDomNode: '.initials-selector',
firstInitial: 'firstinitial',
lastInitial: 'lastinitial',
initialBars: '.initialbar', // Both first and last name use this class.
targetButton: 'initialswidget',
formItems: {
type: 'submit',
save: 'save',
cancel: 'cancel'
}
};
/**
* Our initial hook into the module which will eventually allow us to handle the dropdown initials bar form.
*
* @param {String} callingLink The link to redirect upon form submission.
* @param {String} firstInitialParam The URL parameter to set for the first name initial.
* @param {String} lastInitialParam The URL parameter to set for the last name initial.
* @param {Array} additionalParams Any additional parameters to set for the URL.
*/
export const init = (callingLink, firstInitialParam = 'sifirst',
lastInitialParam = 'silast', additionalParams = []) => {
if (registered) {
return;
}
const pendingPromise = new Pending();
registerListenerEvents(callingLink, firstInitialParam, lastInitialParam, additionalParams);
// BS events always bubble so, we need to listen for the event higher up the chain.
$(selectors.parentDomNode).on('shown.bs.dropdown', () => {
document.querySelector(selectors.pageClickableItem).focus({preventScroll: true});
});
pendingPromise.resolve();
registered = true;
};
/**
* Register event listeners.
*
* @param {String} callingLink The link to redirect upon form submission.
* @param {String} firstInitialParam The URL parameter to set for the first name initial.
* @param {String} lastInitialParam The URL parameter to set for the last name initial.
* @param {Array} additionalParams Any additional parameters to set for the URL.
*/
const registerListenerEvents = (callingLink, firstInitialParam = 'sifirst',
lastInitialParam = 'silast', additionalParams = []) => {
const events = [
'click',
CustomEvents.events.activate,
CustomEvents.events.keyboardActivate
];
CustomEvents.define(document, events);
// Register events.
events.forEach((event) => {
document.addEventListener(event, (e) => {
// Always fetch the latest information when we click as state is a fickle thing.
let {firstActive, lastActive, sifirst, silast} = onClickVariables();
let itemToReset = '';
// Prevent the usual form behaviour.
if (e.target.closest(selectors.formDropdown)) {
e.preventDefault();
}
// Handle the state of active initials before form submission.
if (e.target.closest(`${selectors.formDropdown} .${selectors.pageListItem}`)) {
// Ensure the li items don't cause weird clicking emptying out the form.
if (e.target.classList.contains(selectors.pageListItem)) {
return;
}
const initialsBar = e.target.closest(selectors.initialBars); // Find out which initial bar we are in.
// We want to find the current active item in the menu area the user selected.
// We also want to fetch the raw item out of the array for instant manipulation.
if (initialsBar.classList.contains(selectors.firstInitial)) {
sifirst = e.target;
itemToReset = firstActive;
} else {
silast = e.target;
itemToReset = lastActive;
}
swapActiveItems(itemToReset, e);
}
// Handle form submissions.
if (e.target.closest(`${selectors.formDropdown}`) && e.target.type === selectors.formItems.type) {
if (e.target.dataset.action === selectors.formItems.save) {
// Ensure we strip out the value (All) as it messes with the PHP side of the initials bar.
// Then we will redirect the user back onto the page with new filters applied.
const params = {
'id': e.target.closest(selectors.formDropdown).dataset.courseid,
[firstInitialParam]: sifirst.parentElement.classList.contains('initialbarall') ? '' : sifirst.value,
[lastInitialParam]: silast.parentElement.classList.contains('initialbarall') ? '' : silast.value,
};
// If additional parameters are passed, add them here (overriding any already set above).
for (const [key, value] of Object.entries(additionalParams)) {
params[key] = value;
}
window.location = Url.relativeUrl(callingLink, params);
}
if (e.target.dataset.action === selectors.formItems.cancel) {
$(`.${selectors.targetButton}`).dropdown('toggle');
}
}
});
});
};
/**
* A small abstracted helper function which allows us to ensure we have up-to-date lists of nodes.
*
* @returns {{firstActive: HTMLElement, lastActive: HTMLElement, sifirst: ?String, silast: ?String}}
*/
const onClickVariables = () => {
// Ensure we have an up-to-date initials bar.
const firstItems = [...document.querySelectorAll(`.${selectors.firstInitial} li`)];
const lastItems = [...document.querySelectorAll(`.${selectors.lastInitial} li`)];
const firstActive = firstItems.filter((item) => item.classList.contains(selectors.activeItem))[0];
const lastActive = lastItems.filter((item) => item.classList.contains(selectors.activeItem))[0];
// Ensure we retain both of the selections from a previous instance.
let sifirst = firstActive.querySelector(selectors.pageClickableItem);
let silast = lastActive.querySelector(selectors.pageClickableItem);
return {firstActive, lastActive, sifirst, silast};
};
/**
* Given we are provided the old li and current click event, swap around the active properties.
*
* @param {HTMLElement} itemToReset
* @param {Event} e
*/
const swapActiveItems = (itemToReset, e) => {
itemToReset.classList.remove(selectors.activeItem);
itemToReset.querySelector(selectors.pageClickableItem).ariaCurrent = false;
// Set the select item as the current item.
const itemToSetActive = e.target.parentElement;
itemToSetActive.classList.add(selectors.activeItem);
e.target.ariaCurrent = true;
};
@@ -28,28 +28,26 @@ use stdClass;
*/
class group_selector extends comboboxsearch {
/**
* @var stdClass The context object.
*/
private stdClass $context;
/** @var int|bool the active group, false if groups not used. */
private int|bool $activegroup;
/**
* The class constructor.
*
* @param null|stdClass $course This parameter has been deprecated since Moodle 4.5 and should not be used anymore.
* @param stdClass $context The context object.
*/
public function __construct(?stdClass $course, stdClass $context) {
if ($course !== null) {
debugging(
'The course argument has been deprecated. Please remove it from your group_selector class instances.',
DEBUG_DEVELOPER,
);
}
$this->context = $context;
public function __construct(private stdClass $context) {
$this->activegroup = $this->get_active_group();
$this->label = $this->get_label();
// The second and third arguments (buttoncontent and dropdowncontent) need to be rendered here, since the comboboxsearch
// template expects HTML in its respective context properties. Ideally, children of comboboxsearch would leverage Mustache's
// blocks pragma, meaning a child template could extend the comboboxsearch, allowing rendering of the child component,
// instead of needing to inject the child's content HTML as part of rendering the comboboxsearch parent, as is the case
// here. Achieving this, however, requires a refactor of comboboxsearch. For now, this must be pre-rendered and injected.
parent::__construct(false, $this->get_button_content(), $this->get_dropdown_content(), 'group-search',
'groupsearchwidget', 'groupsearchdropdown overflow-auto', null, true, $this->get_label(), 'group',
$this->get_active_group());
'groupsearchwidget', 'groupsearchdropdown overflow-auto', null, true, $this->label, 'group',
$this->activegroup);
}
/**
@@ -58,23 +56,10 @@ class group_selector extends comboboxsearch {
* @return string HTML fragment
*/
private function get_button_content(): string {
global $OUTPUT;
global $PAGE;
$groupsselectorbutton = new group_selector_button($this->context, $this->activegroup, $this->label);
$activegroup = $this->get_active_group();
$buttondata = [
'label' => $this->get_label(),
'group' => $activegroup,
];
if ($activegroup) {
$group = groups_get_group($activegroup);
$buttondata['selectedgroup'] = format_string($group->name, true,
['context' => $this->context->get_course_context()]);
} else if ($activegroup === 0) {
$buttondata['selectedgroup'] = get_string('allparticipants');
}
return $OUTPUT->render_from_template('core_group/comboboxsearch/group_selector', $buttondata);
return $PAGE->get_renderer('core', 'course')->render($groupsselectorbutton);
}
/**
@@ -83,13 +68,10 @@ class group_selector extends comboboxsearch {
* @return string HTML fragment
*/
private function get_dropdown_content(): string {
global $OUTPUT;
global $PAGE;
$groupsdropdownform = new group_selector_dropdown_form($this->context);
return $OUTPUT->render_from_template('core_group/comboboxsearch/searchbody', [
'courseid' => $this->context->get_course_context()->instanceid,
'currentvalue' => optional_param('groupsearchvalue', '', PARAM_NOTAGS),
'instance' => rand(),
]);
return $PAGE->get_renderer('core', 'course')->render($groupsdropdownform);
}
/**
@@ -0,0 +1,69 @@
<?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/>.
namespace core_course\output\actionbar;
use context;
use core\output\named_templatable;
use core\output\renderable;
use core\output\renderer_base;
/**
* Renderable class for the group selection button state.
*
* This form is the button state for the group_selector renderable, which itself is an extension of the comboboxsearch component.
* {@see group_selector}.
*
* @package core_course
* @copyright 2024 Jake Dallimore <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_selector_button implements renderable, named_templatable {
/**
* The class constructor.
*
* @param context $context The context instance.
* @param int|bool $activegroup The active group, or false if groups not used.
* @param string $label the label string.
*/
public function __construct(
protected context $context,
protected int|bool $activegroup,
protected string $label
) {
}
public function export_for_template(renderer_base $output) {
$context = [
'label' => $this->label,
'group' => $this->activegroup,
];
if ($this->activegroup) {
$group = groups_get_group($this->activegroup);
$context['selectedgroup'] = format_string($group->name, true, ['context' => $this->context->get_course_context()]);
} else if ($this->activegroup === 0) {
$context['selectedgroup'] = get_string('allparticipants');
}
return $context;
}
public function get_template_name(renderer_base $renderer): string {
return 'core_group/comboboxsearch/group_selector';
}
}
@@ -0,0 +1,56 @@
<?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/>.
namespace core_course\output\actionbar;
use core\output\named_templatable;
use core\output\renderable;
use core\output\renderer_base;
/**
* Renderable class for the group selection dropdown form.
*
* This form is the content for the group_selector renderable, which itself is an extension of the comboboxsearch component.
* {@see group_selector}.
*
* @package core_course
* @copyright 2024 Jake Dallimore <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_selector_dropdown_form implements renderable, named_templatable {
/**
* The class constructor.
*
* @param \context $context The context instance.
*/
public function __construct(
protected \context $context
) {
}
public function export_for_template(renderer_base $output) {
return [
'courseid' => $this->context->get_course_context()->instanceid,
'currentvalue' => optional_param('groupsearchvalue', '', PARAM_NOTAGS),
'instance' => rand(),
];
}
public function get_template_name(renderer_base $renderer): string {
return 'core_group/comboboxsearch/searchbody';
}
}
@@ -0,0 +1,70 @@
<?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/>.
namespace core_course\output\actionbar;
use core\output\renderable;
use core\output\renderer_base;
use stdClass;
use templatable;
/**
* Renderable class for the initial_dropdown_form.
*
* This form is the content for the initials_selector renderable, which itself is an extension of the comboboxsearch component.
* {@see initials_selector}.
*
* @package core_course
* @copyright 2024 Jake Dallimore <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class initials_dropdown_form implements renderable, templatable {
/**
* The class constructor.
*
* @param stdClass $course The course object.
* @param string $targeturl The target URL to send the form to.
* @param string $firstinitial The selected first initial.
* @param string $lastinitial The selected last initial.
* @param string $firstinitialparam The parameter name for the first initial.
* @param string $lastinitialparam The parameter name for the last initial.
* @param array $additionalparams Any additional parameters required for the form submission URL.
*/
public function __construct(
protected stdClass $course,
protected string $targeturl,
protected string $firstinitial = '',
protected string $lastinitial = '',
protected string $firstinitialparam = 'sifirst',
protected string $lastinitialparam = 'silast',
protected array $additionalparams = []
) {
}
public function export_for_template(renderer_base $output) {
global $PAGE;
$PAGE->requires->js_call_amd('core_course/actionbar/initials', 'init',
[$this->targeturl, $this->firstinitialparam, $this->lastinitialparam, $this->additionalparams]);
$renderer = $PAGE->get_renderer('core_user');
return (object) [
'courseid' => $this->course->id,
'initialsbars' => $renderer->partial_user_search($this->targeturl, $this->firstinitial, $this->lastinitial, true),
];
}
}
@@ -0,0 +1,111 @@
<?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/>.
namespace core_course\output\actionbar;
use core\output\comboboxsearch;
use stdClass;
/**
* Renderable class for the initial selector element in the action bar.
*
* @package core_course
* @copyright 2024 Kevin Percy <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class initials_selector extends comboboxsearch {
/**
* The class constructor.
*
* @param stdClass $course The course object.
* @param string $targeturl The target URL to send the form to.
* @param string $firstinitial The selected first initial.
* @param string $lastinitial The selected last initial.
* @param string $firstinitialparam The parameter name for the first initial.
* @param string $lastinitialparam The parameter name for the last initial.
* @param array $additionalparams Any additional parameters required for the form submission URL.
*/
public function __construct(
protected stdClass $course,
protected string $targeturl,
protected string $firstinitial = '',
protected string $lastinitial = '',
protected string $firstinitialparam = 'sifirst',
protected string $lastinitialparam = 'silast',
protected array $additionalparams = []
) {
// The second and third arguments (buttoncontent and dropdowncontent) need to be rendered here, since the comboboxsearch
// template expects HTML in its respective context properties. Ideally, children of comboboxsearch would leverage Mustache's
// blocks pragma, meaning a child template could extend the comboboxsearch, allowing rendering of the child component,
// instead of needing to inject the child's content HTML as part of rendering the comboboxsearch parent, as is the case
// here. Achieving this, however, requires a refactor of comboboxsearch. For now, this must be pre-rendered and injected.
$filterstatestring = $this->get_current_filter_state_string();
parent::__construct(
false,
$filterstatestring !== '' ? $filterstatestring : get_string('filterbyname', 'course'),
$this->render_initials_dropdown_form(),
'initials-selector',
'initialswidget',
'initialsdropdown',
$filterstatestring !== '' ? get_string('name') : null,
true,
get_string('filterbyname', 'course'),
'nameinitials',
json_encode([
'first' => $firstinitial,
'last' => $lastinitial,
])
);
}
/**
* Method to generate the current filter string for the initial selector label.
*
* @return string the HTML string representing the current initials filter state. E.g. "First (A)", or empty if none selected.
*/
private function get_current_filter_state_string(): string {
if ($this->firstinitial !== '' && $this->lastinitial !== '') {
return get_string('filterbothactive', 'course', ['first' => $this->firstinitial, 'last' => $this->lastinitial]);
} else if ($this->firstinitial !== '') {
return get_string('filterfirstactive', 'course', ['first' => $this->firstinitial]);
} else if ($this->lastinitial !== '') {
return get_string('filterlastactive', 'course', ['last' => $this->lastinitial]);
}
return '';
}
/**
* Method to generate the output for the initial selector.
*
* @return string the rendered HTML content.
*/
private function render_initials_dropdown_form(): string {
global $PAGE;
$initialsdropdownform = new initials_dropdown_form(
$this->course,
$this->targeturl,
$this->firstinitial,
$this->lastinitial,
$this->firstinitialparam,
$this->lastinitialparam,
$this->additionalparams
);
return $PAGE->get_renderer('core', 'course')->render($initialsdropdownform);
}
}
@@ -1,49 +0,0 @@
<?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/>.
namespace core_course\output\actionbar;
/**
* Renderer class for the action bar.
*
* @package core_course
* @copyright 2024 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends \plugin_renderer_base {
/**
* Renders the user selector trigger element in the action bar.
*
* @param user_selector $userselector The user selector object.
* @return string The HTML output.
*/
public function render_user_selector(user_selector $userselector): string {
$data = $userselector->export_for_template($this);
return parent::render_from_template($userselector->get_template(), $data);
}
/**
* Renders the group selector trigger element in the action bar.
*
* @param group_selector $groupselector The group selector object.
* @return string The HTML output.
*/
protected function render_group_selector(group_selector $groupselector) {
$data = $groupselector->export_for_template($this);
return parent::render_from_template($groupselector->get_template(), $data);
}
}
@@ -40,57 +40,38 @@ class user_selector extends comboboxsearch {
* @param int|null $instanceid The instance ID.
*/
public function __construct(
stdClass $course,
moodle_url $resetlink,
?int $userid = null,
?int $groupid = null,
string $usersearch = '',
?int $instanceid = null
private stdClass $course,
private moodle_url $resetlink,
private ?int $userid = null,
private ?int $groupid = null,
private string $usersearch = '',
private ?int $instanceid = null
) {
$userselectorontent = $this->user_selector_output($course, $resetlink, $userid, $groupid, $usersearch, $instanceid);
parent::__construct(true, $userselectorontent, null, 'user-search d-flex',
// The second argument (buttoncontent) needs to be rendered here, since the comboboxsearch
// template expects HTML in its respective context properties. Ideally, children of comboboxsearch would leverage Mustache's
// blocks pragma, meaning a child template could extend the comboboxsearch, allowing rendering of the child component,
// instead of needing to inject the child's content HTML as part of rendering the comboboxsearch parent, as is the case
// here. Achieving this, however, requires a refactor of comboboxsearch. For now, this must be pre-rendered and injected.
parent::__construct(true, $this->user_selector_output(), null, 'user-search d-flex',
null, 'usersearchdropdown overflow-auto', null, false);
}
/**
* Method that generates the output for the user selector.
*
* @param stdClass $course The course object.
* @param moodle_url|null $resetlink The reset link.
* @param int|null $userid The user ID.
* @param int|null $groupid The group ID.
* @param string $usersearch The user search query.
* @param int|null $instanceid The instance ID.
* @return string The HTML output.
*/
private function user_selector_output(
stdClass $course,
?moodle_url $resetlink = null,
?int $userid = null,
?int $groupid = null,
string $usersearch = '',
?int $instanceid = null
): string {
global $OUTPUT;
private function user_selector_output(): string {
global $PAGE;
// If the user ID is set, it indicates that a user has been selected. In this case, override the user search
// string with the full name of the selected user.
if ($userid) {
$usersearch = fullname(\core_user::get_user($userid));
}
return $OUTPUT->render_from_template('core_user/comboboxsearch/user_selector', [
'currentvalue' => $usersearch,
'courseid' => $course->id,
'instance' => $instanceid ?? rand(),
'resetlink' => $resetlink->out(false),
'group' => $groupid ?? 0,
'name' => 'usersearch',
'value' => json_encode([
'userid' => $userid,
'search' => $usersearch,
]),
]);
$userselectordropdown = new user_selector_button(
$this->course,
$this->resetlink,
$this->userid,
$this->groupid,
$this->usersearch,
$this->instanceid
);
return $PAGE->get_renderer('core', 'course')->render($userselectordropdown);
}
}
@@ -0,0 +1,73 @@
<?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/>.
namespace core_course\output\actionbar;
use core\output\named_templatable;
use core\output\renderable;
use core\output\renderer_base;
use core\url;
use stdClass;
/**
* Renderable class for the user_selector_button.
*
* This is the button content for the user_selector renderable, which itself is an extension of the comboboxsearch component.
* {@see initials_selector}.
*
* @package core_course
* @copyright 2024 Jake Dallimore <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_selector_button implements renderable, named_templatable {
/**
* The class constructor.
*/
public function __construct(
private stdClass $course,
private url $resetlink,
private ?int $userid = null,
private ?int $groupid = null,
private string $usersearch = '',
private ?int $instanceid = null
) {
// If the user ID is set, it indicates that a user has been selected. In this case, override the user search
// string with the full name of the selected user.
if ($this->userid) {
$this->usersearch = fullname(\core_user::get_user($this->userid));
}
}
public function export_for_template(renderer_base $output) {
return [
'currentvalue' => $this->usersearch,
'courseid' => $this->course->id,
'instance' => $this->instanceid ?? rand(),
'resetlink' => $this->resetlink->out(false),
'group' => $this->groupid ?? 0,
'name' => 'usersearch',
'value' => json_encode([
'userid' => $this->userid,
'search' => $this->usersearch,
]),
];
}
public function get_template_name(renderer_base $renderer): string {
return 'core_user/comboboxsearch/user_selector';
}
}
@@ -0,0 +1,38 @@
{{!
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 core_course/initials_dropdown_form
The form component of our dropdown within the tertiary navigation.
Context variables required for this template:
* courseid - The course ID.
* initialsbars - The rendered mini renderer from the user renderer.
Example context (json):
{
"courseid": "2",
"initialsbars": "Output from renderer method"
}
}}
<form class="initialsdropdownform" action="#" data-courseid="{{courseid}}">
{{{initialsbars}}}
<div class="d-flex flex-row justify-content-end">
<input class="btn btn-outline-secondary pull-right mx-2" data-action="cancel" type="submit" value="{{#str}}closebuttontitle{{/str}}">
<input class="btn btn-primary pull-right" data-action="save" type="submit" value="{{#str}}apply{{/str}}">
</div>
</form>
+4
View File
@@ -59,6 +59,10 @@ class behat_course extends behat_base {
"%core_course/activityChooser%//*[@data-region=%locator%][contains(concat(' ', @class, ' '), ' tab-pane ')]"
]
),
new behat_component_named_selector(
'initials bar',
[".//*[contains(concat(' ', @class, ' '), ' initialbar ')]//span[contains(., %locator%)]/parent::div"]
),
];
}
+2
View File
@@ -5,6 +5,8 @@ define("core_grades/searchwidget/initials",["exports","core/pending","core/url",
* @module core_grades/searchwidget/initials
* @copyright 2022 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @deprecated since Moodle 4.5 - please use core_course/actionbar/initials instead.
* @todo Final deprecation in Moodle 6.0. See MDL-82421.
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Url=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}(Url),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_jquery=_interopRequireDefault(_jquery);let registered=!1;const selectors_pageListItem="page-item",selectors_pageClickableItem=".page-link",selectors_activeItem="active",selectors_formDropdown=".initialsdropdownform",selectors_parentDomNode=".initials-selector",selectors_firstInitial="firstinitial",selectors_lastInitial="lastinitial",selectors_initialBars=".initialbar",selectors_targetButton="initialswidget",selectors_formItems={type:"submit",save:"save",cancel:"cancel"};_exports.init=function(callingLink){let gpr_userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,gpr_search=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(callingLink,gpr_userid,gpr_search),(0,_jquery.default)(selectors_parentDomNode).on("shown.bs.dropdown",(()=>{document.querySelector(selectors_pageClickableItem).focus({preventScroll:!0})})),pendingPromise.resolve(),registered=!0};const registerListenerEvents=function(callingLink){let gpr_userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,gpr_search=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;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,(e=>{let{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}=onClickVariables(),itemToReset="";if(e.target.closest(selectors_formDropdown)&&e.preventDefault(),e.target.closest("".concat(selectors_formDropdown," .").concat(selectors_pageListItem))){if(e.target.classList.contains(selectors_pageListItem))return;e.target.closest(selectors_initialBars).classList.contains(selectors_firstInitial)?(sifirst=e.target,itemToReset=firstActive):(silast=e.target,itemToReset=lastActive),swapActiveItems(itemToReset,e)}if(e.target.closest("".concat(selectors_formDropdown))&&e.target.type===selectors_formItems.type){if(e.target.dataset.action===selectors_formItems.save){const params={id:e.target.closest(selectors_formDropdown).dataset.courseid,gpr_search:null!==gpr_search?gpr_search:"",sifirst:sifirst.parentElement.classList.contains("initialbarall")?"":sifirst.value,silast:silast.parentElement.classList.contains("initialbarall")?"":silast.value};null!==gpr_userid&&(params.gpr_userid=gpr_userid),window.location=Url.relativeUrl(callingLink,params)}e.target.dataset.action===selectors_formItems.cancel&&(0,_jquery.default)(".".concat(selectors_targetButton)).dropdown("toggle")}}))}))},onClickVariables=()=>{const firstItems=[...document.querySelectorAll(".".concat(selectors_firstInitial," li"))],lastItems=[...document.querySelectorAll(".".concat(selectors_lastInitial," li"))],firstActive=firstItems.filter((item=>item.classList.contains(selectors_activeItem)))[0],lastActive=lastItems.filter((item=>item.classList.contains(selectors_activeItem)))[0];let sifirst=firstActive.querySelector(selectors_pageClickableItem),silast=lastActive.querySelector(selectors_pageClickableItem);return{firstActive:firstActive,lastActive:lastActive,sifirst:sifirst,silast:silast}},swapActiveItems=(itemToReset,e)=>{itemToReset.classList.remove(selectors_activeItem),itemToReset.querySelector(selectors_pageClickableItem).ariaCurrent=!1;e.target.parentElement.classList.add(selectors_activeItem),e.target.ariaCurrent=!0}}));
//# sourceMappingURL=initials.min.js.map
File diff suppressed because one or more lines are too long
+2
View File
@@ -19,6 +19,8 @@
* @module core_grades/searchwidget/initials
* @copyright 2022 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @deprecated since Moodle 4.5 - please use core_course/actionbar/initials instead.
* @todo Final deprecation in Moodle 6.0. See MDL-82421.
*/
import Pending from 'core/pending';
+12 -3
View File
@@ -47,13 +47,13 @@ class core_grades_renderer extends plugin_renderer_base {
* @param object $course The course object.
* @param string|null $groupactionbaseurl This parameter has been deprecated since 4.4 and should not be used anymore.
* @return string|null The raw HTML to render.
* @deprecated since 4.5. Use \core_course\output\actionbar\renderer' instead.
* @deprecated since 4.5. See replacement renderable \core_course\output\actionbar\group_selector instead.
* @todo Final deprecation in Moodle 6.0. See MDL-82116.
*/
#[\core\attribute\deprecated(
replacement: null,
since: '4.5',
reason: 'Moved to \core_course\output\actionbar\renderer.'
reason: 'See replacement renderable \core_course\output\actionbar\group_selector.'
)]
public function group_selector(object $course, ?string $groupactionbaseurl = null): ?string {
global $USER;
@@ -112,7 +112,7 @@ class core_grades_renderer extends plugin_renderer_base {
'group',
$activegroup
);
return $this->render_from_template($groupdropdown->get_template(), $groupdropdown->export_for_template($this));
return $this->render($groupdropdown);
}
/**
@@ -124,13 +124,22 @@ class core_grades_renderer extends plugin_renderer_base {
* @param context $context Our current context.
* @param string $slug The slug for the report that called this function.
* @return stdClass The data to output.
* @deprecated since 4.5. See replacement renderable \core_course\output\actionbar\initials_selector instead.
* @todo Final deprecation in Moodle 6.0. See MDL-82421.
*/
#[\core\attribute\deprecated(
replacement: null,
since: '4.5',
reason: 'See replacement renderable \core_course\output\actionbar\initials_selector.'
)]
public function initials_selector(
object $course,
context $context,
string $slug
): stdClass {
global $SESSION, $COURSE;
\core\deprecation::emit_deprecation_if_present([$this, __FUNCTION__]);
// User search.
$searchvalue = optional_param('gpr_search', null, PARAM_NOTAGS);
$userid = optional_param('grp_userid', null, PARAM_INT);
@@ -17,6 +17,9 @@
namespace gradereport_grader\output;
use core\output\comboboxsearch;
use core_course\output\actionbar\group_selector;
use core_course\output\actionbar\initials_selector;
use core_course\output\actionbar\user_selector;
use core_grades\output\general_action_bar;
use moodle_url;
@@ -69,7 +72,7 @@ class action_bar extends \core_grades\output\action_bar {
* @throws \moodle_exception
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE, $OUTPUT, $SESSION, $USER;
global $SESSION, $USER;
// If in the course context, we should display the general navigation selector in gradebook.
$courseid = $this->context->instanceid;
// Get the data used to output the general navigation selector.
@@ -82,50 +85,40 @@ class action_bar extends \core_grades\output\action_bar {
// and the view mode selector (if applicable).
if (has_capability('moodle/grade:viewall', $this->context)) {
$course = get_course($courseid);
$gradesrenderer = $PAGE->get_renderer('core_grades');
$initialscontent = $gradesrenderer->initials_selector(
$course,
$this->context,
'/grade/report/grader/index.php'
);
$firstnameinitial = $SESSION->gradereport["filterfirstname-{$this->context->id}"] ?? '';
$lastnameinitial = $SESSION->gradereport["filtersurname-{$this->context->id}"] ?? '';
$additionalparams = [];
$initialselector = new comboboxsearch(
false,
$initialscontent->buttoncontent,
$initialscontent->dropdowncontent,
'initials-selector',
'initialswidget',
'initialsdropdown',
$initialscontent->buttonheader,
true,
get_string('filterbyname', 'core_grades'),
'nameinitials',
json_encode([
'first' => $firstnameinitial,
'last' => $lastnameinitial,
])
if ($this->userid > 0) {
$additionalparams['gpr_userid'] = $this->userid;
} else if (!empty($this->usersearch)) {
$additionalparams['gpr_search'] = $this->usersearch;
}
$initialselector = new initials_selector(
course: $course,
targeturl: '/grade/report/grader/index.php',
firstinitial: $firstnameinitial,
lastinitial: $lastnameinitial,
additionalparams: $additionalparams,
);
$data['initialselector'] = $initialselector->export_for_template($output);
if ($course->groupmode) {
$actionbarrenderer = $PAGE->get_renderer('core_course', 'actionbar');
$data['groupselector'] = $actionbarrenderer->render(
new \core_course\output\actionbar\group_selector(null, $this->context));
$gs = new group_selector($this->context);
$data['groupselector'] = $gs->export_for_template($output);
}
$resetlink = new moodle_url('/grade/report/grader/index.php', ['id' => $courseid]);
$userselectorrenderer = new \core_course\output\actionbar\user_selector(
$userselector = new user_selector(
course: $course,
resetlink: $resetlink,
userid: $this->userid,
groupid: 0,
usersearch: $this->usersearch
);
$data['searchdropdown'] = $userselectorrenderer->export_for_template($output);
$data['searchdropdown'] = $userselector->export_for_template($output);
// The collapsed column dialog is aligned to the edge of the screen, we need to place it such that it also aligns.
$collapsemenudirection = right_to_left() ? 'dropdown-menu-left' : 'dropdown-menu-right';
@@ -60,7 +60,9 @@
}
]
},
"groupselector": "<div class='group-selector'></div>",
"searchdropdown": {"search dropdown": "context here"},
"groupselector": {"group selector": "context here"},
"initialselector": {"initial selector": "context here"},
"collapsedcolumns": "<div class='collapse-columns'></div>",
"pagereset": "http://moodle.local/grade/report/grader/index.php?id=2&userid=2&reset=1"
}
@@ -81,7 +83,7 @@
{{/searchdropdown}}
{{#groupselector}}
<div class="navitem">
{{{.}}}
{{>core/comboboxsearch}}
</div>
<div class="navitem-divider"></div>
{{/groupselector}}
@@ -31,15 +31,15 @@ Feature: Within the grader report, test that we can open our generic filter drop
Scenario: A teacher can open the filter component
Given I should see "Filter by name"
When I click on "Filter by name" "combobox"
Then I should see "27" node occurrences of type "input" in the "First name" "core_grades > initials bar"
And I should see "27" node occurrences of type "input" in the "Last name" "core_grades > initials bar"
Then I should see "27" node occurrences of type "input" in the "First name" "core_course > initials bar"
And I should see "27" node occurrences of type "input" in the "Last name" "core_course > initials bar"
And "input[data-action=cancel]" "css_element" should exist
And "input[data-action=save]" "css_element" should exist
Scenario: A teacher can filter the grader report to limit users reported
Given I click on "Filter by name" "combobox"
And I wait until "input[data-action=save]" "css_element" exists
When I select "D" in the "First name" "core_grades > initials bar"
When I select "D" in the "First name" "core_course > initials bar"
And I press "Apply"
And I wait to be redirected
# We should only have one user that matches the "D" first name
@@ -57,8 +57,8 @@ Feature: Within the grader report, test that we can open our generic filter drop
# Test filtering on last name
# Business logic: If all is selected, we will not show it i.e. First (D) and NOT First (D) Last (All)
And I click on "First (D)" "combobox"
And I select "All" in the "First name" "core_grades > initials bar"
And I select "M" in the "Last name" "core_grades > initials bar"
And I select "All" in the "First name" "core_course > initials bar"
And I select "M" in the "Last name" "core_course > initials bar"
And I press "Apply"
And I wait to be redirected
# We should only have one user that matches the "T" first name
@@ -75,8 +75,8 @@ Feature: Within the grader report, test that we can open our generic filter drop
# Test filtering on first && last name
And I click on "Last (M)" "combobox"
And I select "U" in the "First name" "core_grades > initials bar"
And I select "T" in the "Last name" "core_grades > initials bar"
And I select "U" in the "First name" "core_course > initials bar"
And I select "T" in the "Last name" "core_course > initials bar"
And I press "Apply"
And I wait to be redirected
# We should only have one user that matches the "T" first name
@@ -96,7 +96,7 @@ Feature: Within the grader report, test that we can open our generic filter drop
Scenario: A teacher can quickly tell that a filter is applied to the current table
Given I click on "Filter by name" "combobox"
And I wait until "input[data-action=save]" "css_element" exists
When I select "T" in the "First name" "core_grades > initials bar"
When I select "T" in the "First name" "core_course > initials bar"
And I press "Apply"
And I wait to be redirected
Then I should see "First (T)"
@@ -104,7 +104,7 @@ Feature: Within the grader report, test that we can open our generic filter drop
# Check if removing the filter, removes the highlight and user notice of applied filters
And I click on "First (T)" "combobox"
And I wait until "input[data-action=save]" "css_element" exists
And I select "All" in the "First name" "core_grades > initials bar"
And I select "All" in the "First name" "core_course > initials bar"
And I press "Apply"
And I wait to be redirected
# Check if the name button indicates if a filter is active
@@ -130,13 +130,13 @@ Feature: Within the grader report, test that we can open our generic filter drop
| core | all | |
And I click on "Filter by name" "combobox"
And "input[data-action=save]" "css_element" should be visible
And I select "T" in the "First name" "core_grades > initials bar"
And I select "T" in the "First name" "core_course > initials bar"
And I press "Apply"
And I wait to be redirected
And I click on "First (T)" "combobox"
And I wait until "input[data-action=save]" "css_element" exists
When I select "" in the "First name" "core_grades > initials bar"
When I select "" in the "First name" "core_course > initials bar"
And I press "Apply"
And I wait to be redirected
Then I should not see "First () Last ()"
@@ -152,7 +152,7 @@ Feature: Within the grader report, test that we can open our generic filter drop
Given I set the field "Search users" to "Student 1"
And I click on "Student 1" in the "Search users" search combo box
And I click on "Filter by name" "combobox"
And I select "S" in the "First name" "core_grades > initials bar"
And I select "S" in the "First name" "core_course > initials bar"
When I press "Apply"
And the field "Search users" matches value "Student 1"
Then the following should exist in the "user-grades" table:
@@ -166,7 +166,7 @@ Feature: Within the grader report, test that we can open our generic filter drop
| User Test | student4@example.com | - |
| Turtle Manatee | student5@example.com | - |
And I click on "First (S)" "combobox"
And I select "M" in the "First name" "core_grades > initials bar"
And I select "M" in the "First name" "core_course > initials bar"
And I press "Apply"
And the following should not exist in the "user-grades" table:
| -1- | -2- | -3- |
@@ -191,7 +191,7 @@ Feature: Within the grader report, test that we can open our generic filter drop
| Teacher 1 | teacher1@example.com | - |
| Turtle Manatee | student5@example.com | - |
When I click on "Filter by name" "combobox"
And I select "E" in the "Last name" "core_grades > initials bar"
And I select "E" in the "Last name" "core_course > initials bar"
And I press "Apply"
Then the following should exist in the "user-grades" table:
| -1- | -2- | -3- |
@@ -212,8 +212,8 @@ Feature: Within the grader report, test that we can open our generic filter drop
And the page should meet accessibility standards with "wcag131, wcag141, wcag412" extra tests
And I click on "Filter by name" "combobox"
And "input[data-action=save]" "css_element" should be visible
And the focused element is "All" "button" in the "First name" "core_grades > initials bar"
And the focused element is "All" "button" in the "First name" "core_course > initials bar"
When I press the tab key
Then the focused element is "input[value=A]" "css_element" in the "First name" "core_grades > initials bar"
Then the focused element is "input[value=A]" "css_element" in the "First name" "core_course > initials bar"
And I press the tab key
And the focused element is "input[value=B]" "css_element" in the "First name" "core_grades > initials bar"
And the focused element is "input[value=B]" "css_element" in the "First name" "core_course > initials bar"
@@ -297,8 +297,8 @@ Feature: Within the grader report, test that we can search for users
Scenario: Once a teacher searches, it'll apply the currently set filters and inform the teacher as such
# Set up a basic filtering case.
Given I click on "Filter by name" "combobox"
And I select "U" in the "First name" "core_grades > initials bar"
And I select "E" in the "Last name" "core_grades > initials bar"
And I select "U" in the "First name" "core_course > initials bar"
And I select "E" in the "Last name" "core_course > initials bar"
And I press "Apply"
And I wait until the page is ready
And the following should exist in the "user-grades" table:
@@ -324,8 +324,8 @@ Feature: Within the grader report, test that we can search for users
| -1- |
| Turtle Manatee |
And I click on "Filter by name" "combobox"
And I select "T" in the "First name" "core_grades > initials bar"
And I select "M" in the "Last name" "core_grades > initials bar"
And I select "T" in the "First name" "core_course > initials bar"
And I select "M" in the "Last name" "core_course > initials bar"
And the following should exist in the "user-grades" table:
| -1- |
| Turtle Manatee |
@@ -146,8 +146,7 @@ class singleview extends grade_report {
protected static function groups_course_menu(stdClass $course) {
global $PAGE;
$renderer = $PAGE->get_renderer('core_course', 'actionbar');
return $renderer->render(new \core_course\output\actionbar\group_selector(null, $PAGE->context));
return $PAGE->get_renderer('core', 'course')->render(new \core_course\output\actionbar\group_selector($PAGE->context));
}
/**
+3 -3
View File
@@ -45,7 +45,7 @@ class gradereport_singleview_renderer extends plugin_renderer_base {
* @return string The raw HTML to render.
*/
public function users_selector(object $course, ?int $userid = null, ?int $groupid = null): string {
$actionbarrenderer = $this->page->get_renderer('core_course', 'actionbar');
$courserenderer = $this->page->get_renderer('core', 'course');
$resetlink = new moodle_url('/grade/report/singleview/index.php', ['id' => $course->id, 'group' => $groupid ?? 0]);
$usersearch = '';
@@ -54,7 +54,7 @@ class gradereport_singleview_renderer extends plugin_renderer_base {
$usersearch = fullname($user);
}
return $actionbarrenderer->render(
return $courserenderer->render(
new \core_course\output\actionbar\user_selector(
course: $course,
resetlink: $resetlink,
@@ -107,7 +107,7 @@ class gradereport_singleview_renderer extends plugin_renderer_base {
'itemid',
$gradeitemid
);
return $this->render_from_template($dropdown->get_template(), $dropdown->export_for_template($this));
return $this->render($dropdown);
}
/**
+31 -10
View File
@@ -91,25 +91,46 @@ class action_bar extends \core_grades\output\action_bar {
// If the user has the capability to view all grades, display the group selector (if applicable), the user selector
// and the view mode selector (if applicable).
if (has_capability('moodle/grade:viewall', $this->context)) {
$userreportrenderer = $PAGE->get_renderer('gradereport_user');
$course = get_course($courseid);
if ($course->groupmode) {
$groupselector = new \core_course\output\actionbar\group_selector(null, $this->context);
$data['groupselector'] = $PAGE->get_renderer('core_course', 'actionbar')->render($groupselector);
$groupselector = new \core_course\output\actionbar\group_selector($this->context);
$data['groupselector'] = $groupselector->export_for_template($output);
}
$resetlink = new moodle_url('/grade/report/user/index.php', ['id' => $courseid, 'group' => 0]);
$baseurl = new moodle_url('/grade/report/user/index.php', ['id' => $courseid]);
$PAGE->requires->js_call_amd('gradereport_user/user', 'init', [$baseurl->out(false)]);
$userselector = new \core_course\output\actionbar\user_selector(
course: $course,
resetlink: $resetlink,
userid: $this->userid,
groupid: $this->currentgroupid,
usersearch: $this->usersearch
);
$data['userselector'] = [
'courseid' => $courseid,
'content' => $userreportrenderer->users_selector(
course: get_course($courseid),
userid: $this->userid,
groupid: $this->currentgroupid,
usersearch: $this->usersearch
),
'content' => $userselector->export_for_template($output),
];
// Do not output the 'view mode' selector when in zero state or when the current user is viewing its own report.
if (!is_null($this->userid) && $USER->id != $this->userid) {
$data['viewasselector'] = $userreportrenderer->view_mode_selector($this->userid, $this->userview, $courseid);
$viewasotheruser = new moodle_url('/grade/report/user/index.php',
['id' => $courseid, 'userid' => $this->userid, 'userview' => GRADE_REPORT_USER_VIEW_USER]);
$viewasmyself = new moodle_url('/grade/report/user/index.php',
['id' => $courseid, 'userid' => $this->userid, 'userview' => GRADE_REPORT_USER_VIEW_SELF]);
$selectoroptions = [
$viewasotheruser->out(false) => get_string('otheruser', 'core_grades'),
$viewasmyself->out(false) => get_string('myself', 'core_grades')
];
$selectoractiveurl = $this->userview === GRADE_REPORT_USER_VIEW_USER ? $viewasotheruser : $viewasmyself;
$viewasselect = new \core\output\select_menu('viewas', $selectoroptions, $selectoractiveurl->out(false));
$viewasselect->set_label(get_string('viewas', 'core_grades'));
$data['viewasselector'] = $viewasselect->export_for_template($output);
}
}
+16 -8
View File
@@ -91,20 +91,25 @@ class gradereport_user_renderer extends plugin_renderer_base {
* @param string $usersearch Search string.
* @return string The raw HTML to render.
* @throws coding_exception
* @deprecated since Moodle 4.5. See user_selector use in \gradereport_user\output\action_bar::export_for_template.
*/
public function users_selector(object $course, ?int $userid = null, ?int $groupid = null, string $usersearch = ''): string {
$actionbarrenderer = $this->page->get_renderer('core_course', 'actionbar');
debugging('users_selector is deprecated.', DEBUG_DEVELOPER);
$courserenderer = $this->page->get_renderer('core', 'course');
$resetlink = new moodle_url('/grade/report/user/index.php', ['id' => $course->id, 'group' => 0]);
$baseurl = new moodle_url('/grade/report/user/index.php', ['id' => $course->id]);
$this->page->requires->js_call_amd('gradereport_user/user', 'init', [$baseurl->out(false)]);
$userselector = new \core_course\output\actionbar\user_selector(
course: $course,
resetlink: $resetlink,
userid: $userid,
groupid: $groupid,
usersearch: $usersearch
return $courserenderer->render(
new \core_course\output\actionbar\user_selector(
course: $course,
resetlink: $resetlink,
userid: $userid,
groupid: $groupid,
usersearch: $usersearch
)
);
return $actionbarrenderer->render($userselector);
}
/**
@@ -167,9 +172,12 @@ class gradereport_user_renderer extends plugin_renderer_base {
* @param int $userview The current view user setting constant
* @param int $courseid The course ID.
* @return string The raw HTML to render.
* @deprecated since Moodle 4.5 See select_menu use in \gradereport_user\output\action_bar::export_for_template.
*/
public function view_mode_selector(int $userid, int $userview, int $courseid): string {
debugging('view_mode_selector is deprecated.', DEBUG_DEVELOPER);
$viewasotheruser = new moodle_url('/grade/report/user/index.php', ['id' => $courseid, 'userid' => $userid,
'userview' => GRADE_REPORT_USER_VIEW_USER]);
$viewasmyself = new moodle_url('/grade/report/user/index.php', ['id' => $courseid, 'userid' => $userid,
@@ -80,7 +80,7 @@
{{/generalnavselector}}
{{#groupselector}}
<div class="navitem">
{{{groupselector}}}
{{>core/comboboxsearch}}
</div>
<div class="navitem-divider"></div>
{{/groupselector}}
@@ -88,7 +88,7 @@
<span class="d-none" data-region="courseid" data-courseid="{{courseid}}" aria-hidden="true"></span>
<div class="navitem flex-column">
{{#content}}
{{{.}}}
{{>core/comboboxsearch}}
{{/content}}
</div>
<div class="navitem-divider"></div>
@@ -97,7 +97,7 @@
{{#viewasselector}}
<div class="navitem-divider"></div>
<div class="navitem">
{{{viewasselector}}}
{{>gradereport_user/view_mode_selector}}
</div>
<div class="navitem-divider"></div>
{{/viewasselector}}
@@ -17,6 +17,9 @@
{{!
@template core_grades/initials_dropdown_form
@deprecated since Moodle 4.5 - please use core_course/initials_dropdown_form instead.
@todo Final deprecation on Moodle 6.0. See MDL-82421.
The form component of our dropdown within the tertiary navigation.
Context variables required for this template:
@@ -34,10 +34,10 @@
<span class="d-none" data-region="groupid" data-groupid="{{group}}"></span>
<div class="align-items-center d-flex">
<div class="d-block pe-3 text-truncate">
<span class="d-block small" aria-hidden="true">
<label class="d-block m-0 small" aria-hidden="true">
{{label}}
</span>
<span class="p-0 font-weight-bold">
</label>
<span class="p-0 font-weight-bold" data-selected-option>
{{selectedgroup}}
</span>
</div>
+4
View File
@@ -96,6 +96,10 @@ The setting has no effect on mobile app content download for offline usage.';
$string['enabledownloadcoursecontent'] = 'Enable download course content';
$string['errorendbeforestart'] = 'The end date ({$a}) is before the course start date.';
$string['favourite'] = 'Starred course';
$string['filterbothactive'] = 'First ({$a->first}) Last ({$a->last})';
$string['filterbyname'] = 'Filter by name';
$string['filterfirstactive'] = 'First ({$a->first})';
$string['filterlastactive'] = 'Last ({$a->last})';
$string['gradetopassnotset'] = 'This course does not have a grade to pass set. It may be set in the grade item of the course (Gradebook setup).';
$string['hideendedcoursestask'] = 'Hide courses on end date';
$string['informationformodule'] = 'Information about the {$a} activity';
+4
View File
@@ -130,3 +130,7 @@ cannotsetpassword,core_error
registerwithmoodleorginfoapp,core_hub
registration_help,core_admin
coursemanagementoptions,core_my
filterbothactive,core_grades
filterbyname,core_grades
filterfirstactive,core_grades
filterlastactive,core_grades
+6 -4
View File
@@ -241,10 +241,6 @@ $string['feedbacks'] = 'Feedbacks';
$string['feedbacksaved'] = 'Feedback saved';
$string['feedbackview'] = 'View feedback';
$string['finalgrade'] = 'Final grade';
$string['filterbothactive'] = 'First ({$a->first}) Last ({$a->last})';
$string['filterbyname'] = 'Filter by name';
$string['filterfirstactive'] = 'First ({$a->first})';
$string['filterlastactive'] = 'Last ({$a->last})';
$string['finalgrade_help'] = 'If the overridden checkbox is ticked, a grade may be added or amended.';
$string['fixedstudents'] = 'Static students column';
$string['fixedstudents_help'] = 'Allows grades to scroll horizontally without losing sight of the students column, by making it static.';
@@ -917,3 +913,9 @@ $string['gradeitemadvanced'] = 'Advanced grade item options';
$string['gradeitemadvanced_help'] = 'Select all elements that should be displayed as advanced when editing grade items.';
$string['aria-toggledropdown'] = 'Toggle the following dropdown';
$string['aria:dropdowngrades'] = 'Grade items found';
// Deprecated since Moodle 4.5.
$string['filterbothactive'] = 'First ({$a->first}) Last ({$a->last})';
$string['filterbyname'] = 'Filter by name';
$string['filterfirstactive'] = 'First ({$a->first})';
$string['filterlastactive'] = 'Last ({$a->last})';
+8 -1
View File
@@ -25,7 +25,7 @@ use core\exception\moodle_exception;
* @copyright 2022 Mathew May <Mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class comboboxsearch implements renderable, templatable {
class comboboxsearch implements renderable, named_templatable {
/** @var bool $renderlater Should the dropdown render straightaway? We sometimes need to output the component without all of the
* data and leave the rendering of any defaults and actual data to the caller. We will give you a basic placeholder that can
* then be easily replaced.*/
@@ -154,9 +154,16 @@ class comboboxsearch implements renderable, templatable {
/**
* Returns the standard template for the dropdown.
*
* @deprecated since Moodle 4.5. {@see named_templatable::get_template_name() instead}
* @return string
*/
public function get_template(): string {
debugging('get_template is deprecated. Please use get_template_name instead');
return 'core/comboboxsearch';
}
public function get_template_name(renderer_base $renderer): string {
return 'core/comboboxsearch';
}
}
+20 -8
View File
@@ -64,12 +64,14 @@ Feature: Initials bar
| student23 | C1 | student |
| student24 | C1 | student |
@javascript
Scenario: Filter users on assignment submission page
Given the following "activities" exist:
| activity | course | idnumber | name | intro | assignsubmission_onlinetext_enabled | assignsubmission_file_enabled |
| assign | C1 | assign1 | TestAssignment | Test assignment description | 0 | 0 |
And I am on the "assign1" "Activity" page logged in as "teacher"
When I navigate to "Submissions" in current page administration
And I click on "Filter by name" "combobox"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.firstinitial" "css_element"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.lastinitial" "css_element"
And ".page-item.active.B" "css_element" should not exist in the ".initialbar.firstinitial" "css_element"
@@ -77,15 +79,19 @@ Feature: Initials bar
And I should see "Astudent Astudent"
And I should see "Bstudent Astudent"
And I should see "Cstudent Cstudent"
And I click on "A" "link" in the ".initialbar.lastinitial .page-item.A" "css_element"
And I select "A" in the "Last name" "core_course > initials bar"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.firstinitial" "css_element"
And ".initialbarall.page-item.active" "css_element" should not exist in the ".initialbar.lastinitial" "css_element"
And ".page-item.active.B" "css_element" should not exist in the ".initialbar.firstinitial" "css_element"
And ".page-item.active.A" "css_element" should exist in the ".initialbar.lastinitial" "css_element"
And I press "Apply"
And I should see "Astudent Astudent"
And I should see "Bstudent Astudent"
And I should not see "Cstudent Cstudent"
And I click on "B" "link" in the ".initialbar.firstinitial .page-item.B" "css_element"
And I click on "Last (A)" "combobox"
And I select "B" in the "First name" "core_course > initials bar"
And I press "Apply"
And I wait until the page is ready
And ".initialbarall.page-item.active" "css_element" should not exist in the ".initialbar.firstinitial" "css_element"
And ".initialbarall.page-item.active" "css_element" should not exist in the ".initialbar.lastinitial" "css_element"
And ".page-item.active.B" "css_element" should exist in the ".initialbar.firstinitial" "css_element"
@@ -102,19 +108,25 @@ Feature: Initials bar
And I should not see "Astudent Astudent"
And I should see "Bstudent Astudent"
And I should not see "Cstudent Cstudent"
And I click on "All" "link" in the ".initialbar.firstinitial" "css_element"
And I click on "First (B) Last (A)" "combobox"
And I select "All" in the "First name" "core_course > initials bar"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.firstinitial" "css_element"
And ".initialbarall.page-item.active" "css_element" should not exist in the ".initialbar.lastinitial" "css_element"
And ".page-item.active.B" "css_element" should not exist in the ".initialbar.firstinitial" "css_element"
And ".page-item.active.A" "css_element" should exist in the ".initialbar.lastinitial" "css_element"
And I press "Apply"
And I wait until the page is ready
And I should see "Astudent Astudent"
And I should see "Bstudent Astudent"
And I should not see "Cstudent Cstudent"
And I click on "All" "link" in the ".initialbar.lastinitial" "css_element"
And I click on "Last (A)" "combobox"
And I select "All" in the "Last name" "core_course > initials bar"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.firstinitial" "css_element"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.lastinitial" "css_element"
And ".page-item.active.B" "css_element" should not exist in the ".initialbar.firstinitial" "css_element"
And ".page-item.active.A" "css_element" should not exist in the ".initialbar.lastinitial" "css_element"
And I press "Apply"
And I wait until the page is ready
And I should see "Astudent Astudent"
And I should see "Bstudent Astudent"
And I should see "Cstudent Cstudent"
@@ -135,7 +147,7 @@ Feature: Initials bar
And I should see "Astudent Astudent"
And I should see "Bstudent Astudent"
And I should see "Cstudent Cstudent"
And I select "A" in the "Last name" "core_grades > initials bar"
And I select "A" in the "Last name" "core_course > initials bar"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.firstinitial" "css_element"
And ".initialbarall.page-item.active" "css_element" should not exist in the ".initialbar.lastinitial" "css_element"
And ".page-item.active.B" "css_element" should not exist in the ".initialbar.firstinitial" "css_element"
@@ -145,7 +157,7 @@ Feature: Initials bar
And I should see "Bstudent Astudent"
And I should not see "Cstudent Cstudent"
And I click on "Last (A)" "combobox"
And I select "B" in the "First name" "core_grades > initials bar"
And I select "B" in the "First name" "core_course > initials bar"
And I press "Apply"
And I wait until the page is ready
And ".initialbarall.page-item.active" "css_element" should not exist in the ".initialbar.firstinitial" "css_element"
@@ -166,7 +178,7 @@ Feature: Initials bar
And I should see "Bstudent Astudent"
And I should not see "Cstudent Cstudent"
And I click on "First (B) Last (A)" "combobox"
And I select "All" in the "First name" "core_grades > initials bar"
And I select "All" in the "First name" "core_course > initials bar"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.firstinitial" "css_element"
And ".initialbarall.page-item.active" "css_element" should not exist in the ".initialbar.lastinitial" "css_element"
And ".page-item.active.B" "css_element" should not exist in the ".initialbar.firstinitial" "css_element"
@@ -177,7 +189,7 @@ Feature: Initials bar
And I should see "Bstudent Astudent"
And I should not see "Cstudent Cstudent"
And I click on "Last (A)" "combobox"
And I select "All" in the "Last name" "core_grades > initials bar"
And I select "All" in the "Last name" "core_course > initials bar"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.firstinitial" "css_element"
And ".initialbarall.page-item.active" "css_element" should exist in the ".initialbar.lastinitial" "css_element"
And ".page-item.active.B" "css_element" should not exist in the ".initialbar.firstinitial" "css_element"
@@ -26,6 +26,8 @@ namespace mod_assign\output;
use assign;
use context_module;
use core_course\output\actionbar\group_selector;
use core_course\output\actionbar\user_selector;
use templatable;
use renderable;
use moodle_url;
@@ -48,9 +50,13 @@ class grading_actionmenu implements templatable, renderable {
protected int $submissioncount;
/** @var assign The assign instance. */
protected assign $assign;
/** @var bool If there are submissions to download. */
protected bool $showdownload;
/** @var array Applied user initials filters, containing 'firstname' and 'lastname'. **/
protected array $userinitials;
/**
* Constructor for this object.
*
@@ -58,12 +64,14 @@ class grading_actionmenu implements templatable, renderable {
* @param null|bool $submissionpluginenabled This parameter has been deprecated since 4.5 and should not be used anymore.
* @param null|int $submissioncount This parameter has been deprecated since 4.5 and should not be used anymore.
* @param assign|null $assign The assign instance. If not provided, it will be loaded based on the cmid.
* @param array $userinitials The user initials to filter the table by.
*/
public function __construct(
int $cmid,
?bool $submissionpluginenabled = null,
?int $submissioncount = null,
?assign $assign = null
?assign $assign = null,
array $userinitials = []
) {
$this->cmid = $cmid;
if (!$assign) {
@@ -72,6 +80,7 @@ class grading_actionmenu implements templatable, renderable {
}
$this->assign = $assign;
$this->showdownload = $this->assign->is_any_submission_plugin_enabled() && $this->assign->count_submissions();
$this->userinitials = $userinitials;
}
/**
@@ -81,12 +90,10 @@ class grading_actionmenu implements templatable, renderable {
* @return array Data to render.
*/
public function export_for_template(\renderer_base $output): array {
global $PAGE, $OUTPUT;
global $PAGE;
$course = $this->assign->get_course();
$cm = get_coursemodule_from_id('assign', $this->cmid);
$actionbarrenderer = $PAGE->get_renderer('core_course', 'actionbar');
$data = [];
$userid = optional_param('userid', null, PARAM_INT);
@@ -96,7 +103,7 @@ class grading_actionmenu implements templatable, renderable {
$resetlink = new moodle_url('/mod/assign/view.php', ['id' => $this->cmid, 'action' => 'grading']);
$groupid = groups_get_course_group($course, true);
$userselector = new \core_course\output\actionbar\user_selector(
$userselector = new user_selector(
course: $course,
resetlink: $resetlink,
userid: $userid,
@@ -104,22 +111,43 @@ class grading_actionmenu implements templatable, renderable {
usersearch: $usersearch,
instanceid: $this->assign->get_instance()->id
);
$data['userselector'] = $actionbarrenderer->render($userselector);
$data['userselector'] = $userselector->export_for_template($output);
$hasinitials = !empty($this->userinitials['firstname']) || !empty($this->userinitials['lastname']);
$additionalparams = ['action' => 'grading', 'id' => $this->cmid];
if (!empty($userid)) {
$additionalparams['userid'] = $userid;
} else if (!empty($usersearch)) {
$additionalparams['search'] = $usersearch;
}
$initialselector = new \core_course\output\actionbar\initials_selector(
course: $course,
targeturl: 'mod/assign/view.php',
firstinitial: $this->userinitials['firstname'] ?? '',
lastinitial: $this->userinitials['lastname'] ?? '',
firstinitialparam: 'tifirst',
lastinitialparam: 'tilast',
additionalparams: $additionalparams
);
$data['initialselector'] = $initialselector->export_for_template($output);
if (groups_get_activity_groupmode($cm, $course)) {
$data['groupselector'] = $actionbarrenderer->render(
new \core_course\output\actionbar\group_selector(null, $PAGE->context));
$gs = new group_selector($PAGE->context);
$data['groupselector'] = $gs->export_for_template($output);
}
if ($extrafiltersdropdown = $this->get_extra_filters_dropdown()) {
$PAGE->requires->js_call_amd('mod_assign/actionbar/grading/extra_filters_dropdown', 'init', []);
$data['extrafiltersdropdown'] = $OUTPUT->render($extrafiltersdropdown);
$data['extrafiltersdropdown'] = $extrafiltersdropdown->export_for_template($output);
}
$activitygroup = groups_get_activity_group($cm);
$hasuserfilter = get_user_preferences('assign_filter');
$hasextrafilters = $this->get_applied_extra_filters_count() > 0;
if ($activitygroup || $hasuserfilter || $hasextrafilters) {
if ($activitygroup || $hasuserfilter || $hasextrafilters || $hasinitials) {
$url = new moodle_url('/mod/assign/view.php', [
'id' => $this->cmid,
'action' => 'grading',
@@ -128,6 +156,8 @@ class grading_actionmenu implements templatable, renderable {
'workflowfilter' => '',
'markingallocationfilter' => '',
'suspendedparticipantsfilter' => 0,
'tifirst' => '',
'tilast' => '',
]);
$data['pagereset'] = $url->out(false);
}
+1 -1
View File
@@ -1131,7 +1131,7 @@ class renderer extends \plugin_renderer_base {
$o .= $this->output->box_start('boxaligncenter gradingtable position-relative');
$this->page->requires->js_init_call('M.mod_assign.init_grading_table', array());
$o .= $this->flexible_table($table, $table->get_rows_per_page(), true);
$o .= $this->flexible_table($table, $table->get_rows_per_page(), false);
$o .= $this->output->box_end();
return $o;
+16 -6
View File
@@ -4545,7 +4545,21 @@ class assign {
$markingworkflow = $this->get_instance()->markingworkflow;
$buttons = new \mod_assign\output\grading_actionmenu(cmid: $this->get_course_module()->id, assign: $this);
// Load the table of submissions, to be printed further down.
$usequickgrading = $showquickgrading && $quickgrading;
$gradingtable = new assign_grading_table($this, $perpage, $filter, 0, $usequickgrading);
$gradingtable->responsive = false;
$table = $this->get_renderer()->render($gradingtable);
// This initialises the selected first/last initials for the action menu.
$gradingtable->initialbars(true);
$buttons = new \mod_assign\output\grading_actionmenu(
cmid: $this->get_course_module()->id,
assign: $this,
userinitials: [
'firstname' => $gradingtable->get_initial_first(),
'lastname' => $gradingtable->get_initial_last(),
]
);
$actionformtext = $this->get_renderer()->render($buttons);
$currenturl = new moodle_url('/mod/assign/view.php', ['id' => $this->get_course_module()->id, 'action' => 'grading']);
$PAGE->activityheader->set_attrs(['hidecompletion' => true]);
@@ -4573,11 +4587,7 @@ class assign {
$o .= $this->get_renderer()->notification(get_string('blindmarkingenabledwarning', 'assign'), 'notifymessage');
}
// Load and print the table of submissions.
$usequickgrading = $showquickgrading && $quickgrading;
$gradingtable = new assign_grading_table($this, $perpage, $filter, 0, $usequickgrading);
$gradingtable->responsive = false;
$table = $this->get_renderer()->render($gradingtable);
// Print the table of submissions.
$footerdata = [
'perpage' => $gradingtable->get_paging_selector(),
'pagingbar' => $gradingtable->get_paging_bar(),
+8
View File
@@ -1236,3 +1236,11 @@ body.tox-fullscreen.path-mod-assign [data-region="grade-panel"] {
body.path-mod-assign [data-region="grade-panel"] .mform .fitem.has-popout .felement[data-fieldtype="editor"] {
overflow: unset;
}
.path-mod-assign .initialsdropdown {
width: 460px;
}
.path-mod-assign .initialsdropdown::after {
content: '';
}
@@ -35,8 +35,9 @@
Example context (json):
{
"userselector": "<div class='user-search'></div>",
"groupselector": "<div class='group-selector'></div>",
"userselector": {"user selector": "context here"},
"groupselector": {"group selector": "context here"},
"initialselector": {"initial selector": "context here"},
"statusselector": {
"name": "filter",
"value": "submitted",
@@ -74,16 +75,22 @@
<div class="navitem-divider d-none d-sm-flex"></div>
{{#userselector}}
<div class="navitem">
{{{.}}}
{{>core/comboboxsearch}}
</div>
<div class="navitem-divider d-none d-sm-flex"></div>
{{/userselector}}
{{#groupselector}}
<div class="navitem">
{{{.}}}
{{>core/comboboxsearch}}
</div>
<div class="navitem-divider d-none d-sm-flex"></div>
{{/groupselector}}
{{#initialselector}}
<div class="navitem d-flex flex-column align-self-center">
{{>core/comboboxsearch}}
</div>
<div class="navitem-divider"></div>
{{/initialselector}}
{{#statusselector}}
<div class="navitem">
{{>core/select_menu}}
@@ -97,7 +104,7 @@
{{/statusselector}}
{{#extrafiltersdropdown}}
<div class="navitem">
{{{.}}}
{{>core/local/dropdown/dialog}}
</div>
<div class="navitem-divider d-none d-sm-flex"></div>
{{/extrafiltersdropdown}}
@@ -47,7 +47,9 @@ Feature: In an assignment, teachers can use table preferences.
When I log in as "teacher1"
And I am on the "Test assignment" Activity page
And I navigate to "Submissions" in current page administration
And I click on "T" "link" in the ".lastinitial" "css_element"
And I click on "Filter by name" "combobox"
And I select "T" in the "Last name" "core_course > initials bar"
And I press "Apply"
And I change window size to "large"
And I click on "Grade actions" "actionmenu" in the "Student Two" "table_row"
And I choose "Grade" in the open action menu