Merge branch 'MDL-80746-main-update-rebased' of https://github.com/mihailges/moodle
This commit is contained in:
+10
@@ -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
@@ -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>
|
||||
@@ -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"]
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
@@ -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})';
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user