diff --git a/grade/amd/build/searchwidget/basewidget.min.js b/grade/amd/build/searchwidget/basewidget.min.js new file mode 100644 index 00000000000..213c859f0af --- /dev/null +++ b/grade/amd/build/searchwidget/basewidget.min.js @@ -0,0 +1,10 @@ +define("core_grades/searchwidget/basewidget",["exports","core/modal_factory","core/modal_events","core/utils","core/templates"],(function(_exports,ModalFactory,ModalEvents,_utils,Templates){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 _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} +/** + * A small modal to search users or grade items within the gradebook. + * + * @module core_grades/searchwidget/basewidget + * @copyright 2022 Mathew May + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.promisesAndResolvers=_exports.init=void 0,ModalFactory=_interopRequireWildcard(ModalFactory),ModalEvents=_interopRequireWildcard(ModalEvents),Templates=_interopRequireWildcard(Templates);_exports.init=function(bodyPromise,data,searchFunc,modalTitle){let unsearchableContent=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;const modal=buildModal(bodyPromise,modalTitle);registerListenerEvents(modal,data,searchFunc,unsearchableContent)};const registerListenerEvents=(modal,data,searchFunc,unsearchableContent)=>{modal.then((modal=>{modal.getRoot().on(ModalEvents.hidden,(()=>{modal.destroy()})),modal.getBodyPromise().then((body=>body[0])).then((body=>{const searchInput=body.querySelector('input[data-action="search"]'),searchResultsContainer=body.querySelector('[data-region="search-results-container-widget"]');if(renderSearchResults(searchResultsContainer,data),unsearchableContent){body.querySelector('[data-region="unsearchable-content-container-widget"]').innerHTML+=unsearchableContent}return searchInput.addEventListener("input",(0,_utils.debounce)((()=>{renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}),300)),body})).catch()})).catch()},buildModal=(bodyPromise,modalTitle)=>ModalFactory.create({type:ModalFactory.types.DEFAULT,title:modalTitle,body:bodyPromise,small:!0,scrollable:!1,templateContext:{classes:"reportdatasearch modal-sm"}}).then((modal=>(modal.show(),modal))),debounceCallee=(searchValue,data,searchFunction)=>searchValue.length>0?searchFunction(data,searchValue):data,renderSearchResults=async(searchResultsContainer,searchResultsData)=>{const templateData={searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/searchresults",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)};_exports.promisesAndResolvers=()=>{let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve}));return{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}}})); + +//# sourceMappingURL=basewidget.min.js.map \ No newline at end of file diff --git a/grade/amd/build/searchwidget/basewidget.min.js.map b/grade/amd/build/searchwidget/basewidget.min.js.map new file mode 100644 index 00000000000..ea796926cb4 --- /dev/null +++ b/grade/amd/build/searchwidget/basewidget.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"basewidget.min.js","sources":["../../src/searchwidget/basewidget.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A small modal to search users or grade items within the gradebook.\n *\n * @module core_grades/searchwidget/basewidget\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as ModalFactory from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {debounce} from 'core/utils';\nimport * as Templates from 'core/templates';\n\n/**\n * Build the base searching widget.\n *\n * @method init\n * @param {Promise} bodyPromise The promise from the callee of the contents to place in the modal body.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n * @param {string} modalTitle The name of the search widget.\n * @param {string|null} unsearchableContent The content rendered in a non-searchable area.\n */\nexport const init = (bodyPromise, data, searchFunc, modalTitle, unsearchableContent = null) => {\n const modal = buildModal(bodyPromise, modalTitle);\n registerListenerEvents(modal, data, searchFunc, unsearchableContent);\n};\n\n/**\n * Register chooser related event listeners.\n *\n * @method registerListenerEvents\n * @param {Promise} modal Our modal that we are working with.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n * @param {string|null} unsearchableContent The content rendered in a non-searchable area.\n */\nconst registerListenerEvents = (modal, data, searchFunc, unsearchableContent) => {\n modal.then(modal => {\n // We want to destroy this when the dialog is closed.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n // Once the body of the modal has been resolved, add more features.\n modal.getBodyPromise()\n // The return value of getBodyPromise is a jquery object containing the body NodeElement.\n .then(body => body[0])\n .then(body => {\n const searchInput = body.querySelector('input[data-action=\"search\"]');\n const searchResultsContainer = body.querySelector('[data-region=\"search-results-container-widget\"]');\n\n renderSearchResults(searchResultsContainer, data);\n\n if (unsearchableContent) {\n const unsearchableContentContainer = body.querySelector(\n '[data-region=\"unsearchable-content-container-widget\"]');\n unsearchableContentContainer.innerHTML += unsearchableContent;\n }\n\n // The search input is triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n renderSearchResults(\n searchResultsContainer,\n debounceCallee(\n searchInput.value,\n data,\n searchFunc()\n )\n );\n }, 300));\n return body;\n }).catch();\n }).catch();\n};\n\n/**\n * Given an object we want to build a modal ready to show.\n *\n * @method buildModal\n * @param {Promise} bodyPromise Body promise that the caller should resolve.\n * @param {string} modalTitle The name of the search widget.\n * @return {Object} The modal ready to display immediately and render body in later.\n */\nconst buildModal = (bodyPromise, modalTitle) => {\n return ModalFactory.create({\n type: ModalFactory.types.DEFAULT,\n // TODO: Make this defined by the interface.\n title: modalTitle,\n body: bodyPromise,\n small: true,\n scrollable: false,\n templateContext: {\n classes: 'reportdatasearch modal-sm'\n }\n }).then(modal => {\n modal.show();\n return modal;\n });\n};\n\n/**\n * We have a small helper that'll call the curried search function allowing callers to filter\n * the data set however we want rather than defining how data must be filtered.\n *\n * @method debounceCallee\n * @param {String} searchValue The input from the user that we'll search against.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunction Partially applied function we need to manage search the passed dataset.\n * @return {Array} The filtered subset of the provided data that we'll then render into the results.\n */\nconst debounceCallee = (searchValue, data, searchFunction) => {\n if (searchValue.length > 0) { // Search query is present.\n return searchFunction(data, searchValue);\n }\n return data;\n};\n\n/**\n * Given the output of the callers' search function, render out the results into the modal.\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The DOM node of the widget where we'll render the provided results.\n * @param {Array} searchResultsData The filtered subset of the provided data that we'll then render into the results.\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n const templateData = {\n 'searchresults': searchResultsData,\n };\n // Build up the html & js ready to place into the help section.\n const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/searchresults', templateData);\n await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * We want to create the basic promises and hooks that the caller will implement, so we can build modals\n * ahead of time and allow the caller to resolve their promises once complete.\n *\n * @method promisesAndResolvers\n * @returns {{bodyPromise: Promise, bodyPromiseResolver}}\n */\nexport const promisesAndResolvers = () => {\n // We want to show the modal instantly but loading whilst waiting for our data.\n let bodyPromiseResolver;\n const bodyPromise = new Promise(resolve => {\n bodyPromiseResolver = resolve;\n });\n\n return {bodyPromiseResolver, bodyPromise};\n};\n"],"names":["bodyPromise","data","searchFunc","modalTitle","unsearchableContent","modal","buildModal","registerListenerEvents","then","getRoot","on","ModalEvents","hidden","destroy","getBodyPromise","body","searchInput","querySelector","searchResultsContainer","renderSearchResults","innerHTML","addEventListener","debounceCallee","value","catch","ModalFactory","create","type","types","DEFAULT","title","small","scrollable","templateContext","classes","show","searchValue","searchFunction","length","async","searchResultsData","templateData","html","js","Templates","renderForPromise","replaceNodeContents","bodyPromiseResolver","Promise","resolve"],"mappings":";;;;;;;+QAqCoB,SAACA,YAAaC,KAAMC,WAAYC,gBAAYC,2EAAsB,WAC5EC,MAAQC,WAAWN,YAAaG,YACtCI,uBAAuBF,MAAOJ,KAAMC,WAAYE,4BAY9CG,uBAAyB,CAACF,MAAOJ,KAAMC,WAAYE,uBACrDC,MAAMG,MAAKH,QAEPA,MAAMI,UAAUC,GAAGC,YAAYC,QAAQ,KACnCP,MAAMQ,aAGVR,MAAMS,iBAEDN,MAAKO,MAAQA,KAAK,KAClBP,MAAKO,aACIC,YAAcD,KAAKE,cAAc,+BACjCC,uBAAyBH,KAAKE,cAAc,sDAElDE,oBAAoBD,uBAAwBjB,MAExCG,oBAAqB,CACgBW,KAAKE,cACtC,yDACyBG,WAAahB,2BAI9CY,YAAYK,iBAAiB,SAAS,oBAAS,KAE3CF,oBACID,uBACAI,eACIN,YAAYO,MACZtB,KACAC,iBAGT,MACIa,QACRS,WACRA,SAWDlB,WAAa,CAACN,YAAaG,aACtBsB,aAAaC,OAAO,CACvBC,KAAMF,aAAaG,MAAMC,QAEzBC,MAAO3B,WACPY,KAAMf,YACN+B,OAAO,EACPC,YAAY,EACZC,gBAAiB,CACbC,QAAS,+BAEd1B,MAAKH,QACJA,MAAM8B,OACC9B,SAcTiB,eAAiB,CAACc,YAAanC,KAAMoC,iBACnCD,YAAYE,OAAS,EACdD,eAAepC,KAAMmC,aAEzBnC,KAULkB,oBAAsBoB,MAAMrB,uBAAwBsB,2BAChDC,aAAe,eACAD,oBAGfE,KAACA,KAADC,GAAOA,UAAYC,UAAUC,iBAAiB,yCAA0CJ,oBACxFG,UAAUE,oBAAoB5B,uBAAwBwB,KAAMC,mCAUlC,SAE5BI,0BACE/C,YAAc,IAAIgD,SAAQC,UAC5BF,oBAAsBE,iBAGnB,CAACF,oBAAAA,oBAAqB/C,YAAAA"} \ No newline at end of file diff --git a/grade/amd/build/searchwidget/repository.min.js b/grade/amd/build/searchwidget/repository.min.js new file mode 100644 index 00000000000..d57f4c6947d --- /dev/null +++ b/grade/amd/build/searchwidget/repository.min.js @@ -0,0 +1,10 @@ +define("core_grades/searchwidget/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj; +/** + * A repo for the search widget. + * + * @module core_grades/searchwidget/repository + * @copyright 2022 Mathew May + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.userFetch=_exports.gradeitemFetch=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.userFetch=(courseid,actionBaseUrl)=>{const request={methodname:"core_grades_get_enrolled_users_for_search_widget",args:{courseid:courseid,actionbaseurl:actionBaseUrl}};return _ajax.default.call([request])[0]};_exports.gradeitemFetch=courseid=>{const request={methodname:"gradereport_singleview_get_grade_items_for_search_widget",args:{courseid:courseid}};return _ajax.default.call([request])[0]}})); + +//# sourceMappingURL=repository.min.js.map \ No newline at end of file diff --git a/grade/amd/build/searchwidget/repository.min.js.map b/grade/amd/build/searchwidget/repository.min.js.map new file mode 100644 index 00000000000..56978025869 --- /dev/null +++ b/grade/amd/build/searchwidget/repository.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"repository.min.js","sources":["../../src/searchwidget/repository.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A repo for the search widget.\n *\n * @module core_grades/searchwidget/repository\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ajax from 'core/ajax';\n\n/**\n * Given a course ID, we want to fetch the enrolled learners, so we may fetch their reports.\n *\n * @method userFetch\n * @param {int} courseid ID of the course to fetch the users of.\n * @param {string} actionBaseUrl The base URL for the user option.\n * @return {object} jQuery promise\n */\nexport const userFetch = (courseid, actionBaseUrl) => {\n const request = {\n methodname: 'core_grades_get_enrolled_users_for_search_widget',\n args: {\n courseid: courseid,\n actionbaseurl: actionBaseUrl,\n },\n };\n return ajax.call([request])[0];\n};\n\n/**\n * Given a course ID, we want to fetch the gradable items, so we may fetch reports based on activity items.\n * Note: This will be worked upon in the single view issue.\n *\n * @method gradeitemFetch\n * @param {int} courseid ID of the course to fetch the users of.\n * @return {object} jQuery promise\n */\nexport const gradeitemFetch = (courseid) => {\n const request = {\n methodname: 'gradereport_singleview_get_grade_items_for_search_widget',\n args: {\n courseid: courseid,\n },\n };\n return ajax.call([request])[0];\n};\n"],"names":["courseid","actionBaseUrl","request","methodname","args","actionbaseurl","ajax","call"],"mappings":";;;;;;;sLAiCyB,CAACA,SAAUC,uBAC1BC,QAAU,CACZC,WAAY,mDACZC,KAAM,CACFJ,SAAUA,SACVK,cAAeJ,uBAGhBK,cAAKC,KAAK,CAACL,UAAU,4BAWDF,iBACrBE,QAAU,CACZC,WAAY,2DACZC,KAAM,CACFJ,SAAUA,kBAGXM,cAAKC,KAAK,CAACL,UAAU"} \ No newline at end of file diff --git a/grade/amd/src/searchwidget/basewidget.js b/grade/amd/src/searchwidget/basewidget.js new file mode 100644 index 00000000000..f4db5e3699d --- /dev/null +++ b/grade/amd/src/searchwidget/basewidget.js @@ -0,0 +1,164 @@ +// 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 . + +/** + * A small modal to search users or grade items within the gradebook. + * + * @module core_grades/searchwidget/basewidget + * @copyright 2022 Mathew May + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +import * as ModalFactory from 'core/modal_factory'; +import * as ModalEvents from 'core/modal_events'; +import {debounce} from 'core/utils'; +import * as Templates from 'core/templates'; + +/** + * Build the base searching widget. + * + * @method init + * @param {Promise} bodyPromise The promise from the callee of the contents to place in the modal body. + * @param {Array} data An array of all the data generated by the callee. + * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset. + * @param {string} modalTitle The name of the search widget. + * @param {string|null} unsearchableContent The content rendered in a non-searchable area. + */ +export const init = (bodyPromise, data, searchFunc, modalTitle, unsearchableContent = null) => { + const modal = buildModal(bodyPromise, modalTitle); + registerListenerEvents(modal, data, searchFunc, unsearchableContent); +}; + +/** + * Register chooser related event listeners. + * + * @method registerListenerEvents + * @param {Promise} modal Our modal that we are working with. + * @param {Array} data An array of all the data generated by the callee. + * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset. + * @param {string|null} unsearchableContent The content rendered in a non-searchable area. + */ +const registerListenerEvents = (modal, data, searchFunc, unsearchableContent) => { + modal.then(modal => { + // We want to destroy this when the dialog is closed. + modal.getRoot().on(ModalEvents.hidden, () => { + modal.destroy(); + }); + // Once the body of the modal has been resolved, add more features. + modal.getBodyPromise() + // The return value of getBodyPromise is a jquery object containing the body NodeElement. + .then(body => body[0]) + .then(body => { + const searchInput = body.querySelector('input[data-action="search"]'); + const searchResultsContainer = body.querySelector('[data-region="search-results-container-widget"]'); + + renderSearchResults(searchResultsContainer, data); + + if (unsearchableContent) { + const unsearchableContentContainer = body.querySelector( + '[data-region="unsearchable-content-container-widget"]'); + unsearchableContentContainer.innerHTML += unsearchableContent; + } + + // The search input is triggered. + searchInput.addEventListener('input', debounce(() => { + // Display the search results. + renderSearchResults( + searchResultsContainer, + debounceCallee( + searchInput.value, + data, + searchFunc() + ) + ); + }, 300)); + return body; + }).catch(); + }).catch(); +}; + +/** + * Given an object we want to build a modal ready to show. + * + * @method buildModal + * @param {Promise} bodyPromise Body promise that the caller should resolve. + * @param {string} modalTitle The name of the search widget. + * @return {Object} The modal ready to display immediately and render body in later. + */ +const buildModal = (bodyPromise, modalTitle) => { + return ModalFactory.create({ + type: ModalFactory.types.DEFAULT, + // TODO: Make this defined by the interface. + title: modalTitle, + body: bodyPromise, + small: true, + scrollable: false, + templateContext: { + classes: 'reportdatasearch modal-sm' + } + }).then(modal => { + modal.show(); + return modal; + }); +}; + +/** + * We have a small helper that'll call the curried search function allowing callers to filter + * the data set however we want rather than defining how data must be filtered. + * + * @method debounceCallee + * @param {String} searchValue The input from the user that we'll search against. + * @param {Array} data An array of all the data generated by the callee. + * @param {Function} searchFunction Partially applied function we need to manage search the passed dataset. + * @return {Array} The filtered subset of the provided data that we'll then render into the results. + */ +const debounceCallee = (searchValue, data, searchFunction) => { + if (searchValue.length > 0) { // Search query is present. + return searchFunction(data, searchValue); + } + return data; +}; + +/** + * Given the output of the callers' search function, render out the results into the modal. + * + * @method renderSearchResults + * @param {HTMLElement} searchResultsContainer The DOM node of the widget where we'll render the provided results. + * @param {Array} searchResultsData The filtered subset of the provided data that we'll then render into the results. + */ +const renderSearchResults = async(searchResultsContainer, searchResultsData) => { + const templateData = { + 'searchresults': searchResultsData, + }; + // Build up the html & js ready to place into the help section. + const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/searchresults', templateData); + await Templates.replaceNodeContents(searchResultsContainer, html, js); +}; + +/** + * We want to create the basic promises and hooks that the caller will implement, so we can build modals + * ahead of time and allow the caller to resolve their promises once complete. + * + * @method promisesAndResolvers + * @returns {{bodyPromise: Promise, bodyPromiseResolver}} + */ +export const promisesAndResolvers = () => { + // We want to show the modal instantly but loading whilst waiting for our data. + let bodyPromiseResolver; + const bodyPromise = new Promise(resolve => { + bodyPromiseResolver = resolve; + }); + + return {bodyPromiseResolver, bodyPromise}; +}; diff --git a/grade/amd/src/searchwidget/repository.js b/grade/amd/src/searchwidget/repository.js new file mode 100644 index 00000000000..74689a82ae2 --- /dev/null +++ b/grade/amd/src/searchwidget/repository.js @@ -0,0 +1,61 @@ +// 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 . + +/** + * A repo for the search widget. + * + * @module core_grades/searchwidget/repository + * @copyright 2022 Mathew May + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import ajax from 'core/ajax'; + +/** + * Given a course ID, we want to fetch the enrolled learners, so we may fetch their reports. + * + * @method userFetch + * @param {int} courseid ID of the course to fetch the users of. + * @param {string} actionBaseUrl The base URL for the user option. + * @return {object} jQuery promise + */ +export const userFetch = (courseid, actionBaseUrl) => { + const request = { + methodname: 'core_grades_get_enrolled_users_for_search_widget', + args: { + courseid: courseid, + actionbaseurl: actionBaseUrl, + }, + }; + return ajax.call([request])[0]; +}; + +/** + * Given a course ID, we want to fetch the gradable items, so we may fetch reports based on activity items. + * Note: This will be worked upon in the single view issue. + * + * @method gradeitemFetch + * @param {int} courseid ID of the course to fetch the users of. + * @return {object} jQuery promise + */ +export const gradeitemFetch = (courseid) => { + const request = { + methodname: 'gradereport_singleview_get_grade_items_for_search_widget', + args: { + courseid: courseid, + }, + }; + return ajax.call([request])[0]; +}; diff --git a/grade/classes/external/get_enrolled_users_for_search_widget.php b/grade/classes/external/get_enrolled_users_for_search_widget.php new file mode 100644 index 00000000000..37bfc2b0a6f --- /dev/null +++ b/grade/classes/external/get_enrolled_users_for_search_widget.php @@ -0,0 +1,157 @@ +. + +namespace core_grades\external; + +use external_api; +use external_function_parameters; +use external_value; +use external_single_structure; +use external_multiple_structure; +use moodle_url; +use core_user; + +defined('MOODLE_INTERNAL') || die; + +require_once($CFG->dirroot.'/grade/lib.php'); + +/** + * Get the enrolled users within and map some fields to the returned array of user objects. + * + * @package core_grades + * @copyright 2022 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 4.1 + */ +class get_enrolled_users_for_search_widget extends external_api { + + /** + * Returns description of method parameters. + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters ( + [ + 'courseid' => new external_value(PARAM_INT, 'Course Id', VALUE_REQUIRED), + 'actionbaseurl' => new external_value(PARAM_URL, 'The base URL for the user option', VALUE_REQUIRED), + 'groupid' => new external_value(PARAM_INT, 'Group Id', VALUE_DEFAULT, 0) + ] + ); + } + + /** + * Given a course ID find the enrolled users within and map some fields to the returned array of user objects. + * + * @param int $courseid + * @param string $actionbaseurl The base URL for the user option. + * @param int|null $groupid + * @return array Users and warnings to pass back to the calling widget. + * @throws coding_exception + * @throws invalid_parameter_exception + * @throws moodle_exception + * @throws restricted_context_exception + */ + public static function execute(int $courseid, string $actionbaseurl, ?int $groupid = 0): array { + global $DB, $PAGE; + + $params = self::validate_parameters( + self::execute_parameters(), + [ + 'courseid' => $courseid, + 'actionbaseurl' => $actionbaseurl, + 'groupid' => $groupid + ] + ); + + $warnings = []; + $coursecontext = \context_course::instance($params['courseid']); + parent::validate_context($coursecontext); + + require_capability('moodle/course:viewparticipants', $coursecontext); + + $course = $DB->get_record('course', ['id' => $params['courseid']]); + // Create a graded_users_iterator because it will properly check the groups etc. + $defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol); + $showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol); + $showonlyactiveenrol = $showonlyactiveenrol || !has_capability('moodle/course:viewsuspendedusers', $coursecontext); + + $gui = new \graded_users_iterator($course, null, $params['groupid']); + $gui->require_active_enrolment($showonlyactiveenrol); + $gui->init(); + + $users = []; + + while ($userdata = $gui->next_user()) { + $guiuser = $userdata->user; + $user = new \stdClass(); + $user->fullname = fullname($guiuser); + $user->id = $guiuser->id; + $user->url = (new moodle_url($actionbaseurl, ['id' => $courseid, 'userid' => $guiuser->id]))->out(false); + $userpicture = new \user_picture($guiuser); + $userpicture->size = 1; + $user->profileimage = $userpicture->get_url($PAGE)->out(false); + $user->email = $guiuser->email; + + $users[] = $user; + } + $gui->close(); + + return [ + 'users' => $users, + 'warnings' => $warnings, + ]; + } + + /** + * Returns description of method result value. + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'users' => new external_multiple_structure(self::user_description()), + 'warnings' => new \external_warnings(), + ]); + } + + /** + * Create user return value description. + * + * @return \external_description + */ + public static function user_description(): \external_description { + $userfields = [ + 'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'), + 'profileimage' => new external_value( + PARAM_URL, + 'The location of the users larger image', + VALUE_OPTIONAL + ), + 'url' => new external_value( + PARAM_URL, + 'The link to the user report', + VALUE_OPTIONAL + ), + 'fullname' => new external_value(PARAM_TEXT, 'The full name of the user', VALUE_OPTIONAL), + 'email' => new external_value( + core_user::get_property_type('email'), + 'An email address - allow email as root@localhost', + VALUE_OPTIONAL), + ]; + return new external_single_structure($userfields); + } +} diff --git a/grade/lib.php b/grade/lib.php index 94f4a12ad1c..ce8fdfe1af6 100644 --- a/grade/lib.php +++ b/grade/lib.php @@ -2959,7 +2959,7 @@ abstract class grade_helper { } $pluginstr = get_string('pluginname', 'gradereport_'.$plugin); - $url = new moodle_url('/grade/report/'.$plugin.'/index.php', array('id' => $courseid)); + $url = new moodle_url('/grade/report/'.$plugin.'/index.php', array('id'=>$courseid)); $gradereports[$plugin] = new grade_plugin_info($plugin, $url, $pluginstr); // Add link to preferences tab if such a page exists diff --git a/grade/report/user/amd/build/user.min.js b/grade/report/user/amd/build/user.min.js new file mode 100644 index 00000000000..b25c8dbdba2 --- /dev/null +++ b/grade/report/user/amd/build/user.min.js @@ -0,0 +1,10 @@ +define("gradereport_user/user",["exports","core/pending","core/templates","core/custom_interaction_events","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","core/str","core/url"],(function(_exports,_pending,Templates,_custom_interaction_events,Repository,WidgetBase,_str,_url){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 _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +/** + * A small modal to search users within the gradebook. + * + * @module gradereport_user/user + * @copyright 2022 Mathew May + * @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),Templates=_interopRequireWildcard(Templates),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(".userwidget");if(trigger){const courseID=trigger.dataset.courseid;e.preventDefault();const actionBaseUrl=_url.default.relativeUrl("/grade/report/user/index.php",{},!1),data=await Repository.userFetch(courseID,actionBaseUrl).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));if(data===[])return;const allUsersOptionName=await(0,_str.get_string)("allusersnum","gradereport_user",data.users.length),allUsersOption=await Templates.render("core_grades/searchwidget/searchitem",{id:0,name:allUsersOptionName,url:_url.default.relativeUrl("/grade/report/user/index.php",{id:courseID,userid:0},!1)});WidgetBase.init(bodyPromise,data.users,searchUsers(),(0,_str.get_string)("selectauser","gradereport_user"),allUsersOption)}}))})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",{displayunsearchablecontent:!0}))},searchUsers=()=>()=>(users,searchTerm)=>{if(""===searchTerm)return users;searchTerm=searchTerm.toLowerCase();const searchResults=[];return users.forEach((user=>{user.fullname.toLowerCase().includes(searchTerm)&&searchResults.push(user)})),searchResults}})); + +//# sourceMappingURL=user.min.js.map \ No newline at end of file diff --git a/grade/report/user/amd/build/user.min.js.map b/grade/report/user/amd/build/user.min.js.map new file mode 100644 index 00000000000..a1f25c6b252 --- /dev/null +++ b/grade/report/user/amd/build/user.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"user.min.js","sources":["../src/user.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A small modal to search users within the gradebook.\n *\n * @module gradereport_user/user\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport {get_string as getString} from 'core/str';\nimport Url from 'core/url';\n\n/**\n * Our entry point into starting to build the search widget.\n * It'll eventually, based upon the listeners, open the search widget and allow filtering.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n};\n\n/**\n * Register user search widget related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n\n // Register events.\n events.forEach((event) => {\n document.addEventListener(event, async(e) => {\n const trigger = e.target.closest('.userwidget');\n if (trigger) {\n const courseID = trigger.dataset.courseid;\n e.preventDefault();\n\n const actionBaseUrl = Url.relativeUrl('/grade/report/user/index.php', {}, false);\n // If an error occurs while fetching the data, display the error within the modal.\n const data = await Repository.userFetch(courseID, actionBaseUrl).catch(async(e) => {\n const errorTemplateData = {\n 'errormessage': e.message\n };\n bodyPromiseResolver(\n await Templates.render('core_grades/searchwidget/error', errorTemplateData)\n );\n });\n\n // Early return if there is no module data.\n if (data === []) {\n return;\n }\n\n // The HTML for the 'All users' option which will be rendered in the non-searchable content are of the widget.\n const allUsersOptionName = await getString('allusersnum', 'gradereport_user', data.users.length);\n const allUsersOption = await Templates.render('core_grades/searchwidget/searchitem', {\n id: 0,\n name: allUsersOptionName,\n url: Url.relativeUrl('/grade/report/user/index.php', {id: courseID, userid: 0}, false),\n });\n\n WidgetBase.init(\n bodyPromise,\n data.users,\n searchUsers(),\n getString('selectauser', 'gradereport_user'),\n allUsersOption\n );\n }\n });\n });\n // Resolvers for passed functions in the modal creation.\n bodyPromiseResolver(Templates.render(\n 'core_grades/searchwidget/user/usersearch_body', {displayunsearchablecontent: true}\n ));\n};\n\n/**\n * Define how we want to search and filter users when the user decides to input a search value.\n *\n * @method registerListenerEvents\n * @returns {function(): function(*, *): (*)}\n */\nconst searchUsers = () => {\n return () => {\n return (users, searchTerm) => {\n if (searchTerm === '') {\n return users;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n users.forEach((user) => {\n const userName = user.fullname.toLowerCase();\n if (userName.includes(searchTerm)) {\n searchResults.push(user);\n }\n });\n return searchResults;\n };\n };\n};\n"],"names":["pendingPromise","Pending","registerListenerEvents","resolve","events","CustomEvents","activate","keyboardActivate","define","document","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","forEach","event","addEventListener","async","trigger","e","target","closest","courseID","dataset","courseid","preventDefault","actionBaseUrl","Url","relativeUrl","data","Repository","userFetch","catch","errorTemplateData","message","Templates","render","allUsersOptionName","users","length","allUsersOption","id","name","url","userid","init","searchUsers","displayunsearchablecontent","searchTerm","toLowerCase","searchResults","user","fullname","includes","push"],"mappings":";;;;;;;qYAqCoB,WACVA,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,iBAQbD,uBAAyB,WACrBE,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAOC,SAAUL,YAE1BM,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,uBAGpDT,OAAOU,SAASC,QACZN,SAASO,iBAAiBD,OAAOE,MAAAA,UACvBC,QAAUC,EAAEC,OAAOC,QAAQ,kBAC7BH,QAAS,OACHI,SAAWJ,QAAQK,QAAQC,SACjCL,EAAEM,uBAEIC,cAAgBC,aAAIC,YAAY,+BAAgC,IAAI,GAEpEC,WAAaC,WAAWC,UAAUT,SAAUI,eAAeM,OAAMf,MAAAA,UAC7DgB,kBAAoB,cACNd,EAAEe,SAEtBxB,0BACUyB,UAAUC,OAAO,iCAAkCH,0BAK7DJ,OAAS,gBAKPQ,yBAA2B,mBAAU,cAAe,mBAAoBR,KAAKS,MAAMC,QACnFC,qBAAuBL,UAAUC,OAAO,sCAAuC,CACjFK,GAAI,EACJC,KAAML,mBACNM,IAAKhB,aAAIC,YAAY,+BAAgC,CAACa,GAAInB,SAAUsB,OAAQ,IAAI,KAGpFhC,WAAWiC,KACPlC,YACAkB,KAAKS,MACLQ,eACA,mBAAU,cAAe,oBACzBN,uBAMhB9B,oBAAoByB,UAAUC,OAC1B,gDAAiD,CAACW,4BAA4B,MAUhFD,YAAc,IACT,IACI,CAACR,MAAOU,iBACQ,KAAfA,kBACOV,MAEXU,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBZ,MAAMxB,SAASqC,OACMA,KAAKC,SAASH,cAClBI,SAASL,aAClBE,cAAcI,KAAKH,SAGpBD"} \ No newline at end of file diff --git a/grade/report/user/amd/src/user.js b/grade/report/user/amd/src/user.js new file mode 100644 index 00000000000..ede80f0574d --- /dev/null +++ b/grade/report/user/amd/src/user.js @@ -0,0 +1,130 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * A small modal to search users within the gradebook. + * + * @module gradereport_user/user + * @copyright 2022 Mathew May + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import Pending from 'core/pending'; +import * as Templates from 'core/templates'; +import CustomEvents from "core/custom_interaction_events"; +import * as Repository from 'core_grades/searchwidget/repository'; +import * as WidgetBase from 'core_grades/searchwidget/basewidget'; +import {get_string as getString} from 'core/str'; +import Url from 'core/url'; + +/** + * Our entry point into starting to build the search widget. + * It'll eventually, based upon the listeners, open the search widget and allow filtering. + * + * @method init + */ +export const init = () => { + const pendingPromise = new Pending(); + registerListenerEvents(); + pendingPromise.resolve(); +}; + +/** + * Register user search widget related event listeners. + * + * @method registerListenerEvents + */ +const registerListenerEvents = () => { + const events = [ + 'click', + CustomEvents.events.activate, + CustomEvents.events.keyboardActivate + ]; + CustomEvents.define(document, events); + + let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers(); + + // Register events. + events.forEach((event) => { + document.addEventListener(event, async(e) => { + const trigger = e.target.closest('.userwidget'); + if (trigger) { + const courseID = trigger.dataset.courseid; + e.preventDefault(); + + const actionBaseUrl = Url.relativeUrl('/grade/report/user/index.php', {}, false); + // If an error occurs while fetching the data, display the error within the modal. + const data = await Repository.userFetch(courseID, actionBaseUrl).catch(async(e) => { + const errorTemplateData = { + 'errormessage': e.message + }; + bodyPromiseResolver( + await Templates.render('core_grades/searchwidget/error', errorTemplateData) + ); + }); + + // Early return if there is no module data. + if (data === []) { + return; + } + + // The HTML for the 'All users' option which will be rendered in the non-searchable content are of the widget. + const allUsersOptionName = await getString('allusersnum', 'gradereport_user', data.users.length); + const allUsersOption = await Templates.render('core_grades/searchwidget/searchitem', { + id: 0, + name: allUsersOptionName, + url: Url.relativeUrl('/grade/report/user/index.php', {id: courseID, userid: 0}, false), + }); + + WidgetBase.init( + bodyPromise, + data.users, + searchUsers(), + getString('selectauser', 'gradereport_user'), + allUsersOption + ); + } + }); + }); + // Resolvers for passed functions in the modal creation. + bodyPromiseResolver(Templates.render( + 'core_grades/searchwidget/user/usersearch_body', {displayunsearchablecontent: true} + )); +}; + +/** + * Define how we want to search and filter users when the user decides to input a search value. + * + * @method registerListenerEvents + * @returns {function(): function(*, *): (*)} + */ +const searchUsers = () => { + return () => { + return (users, searchTerm) => { + if (searchTerm === '') { + return users; + } + searchTerm = searchTerm.toLowerCase(); + const searchResults = []; + users.forEach((user) => { + const userName = user.fullname.toLowerCase(); + if (userName.includes(searchTerm)) { + searchResults.push(user); + } + }); + return searchResults; + }; + }; +}; diff --git a/grade/report/user/classes/external/user.php b/grade/report/user/classes/external/user.php index a533c783c23..0fb8d6dafc3 100644 --- a/grade/report/user/classes/external/user.php +++ b/grade/report/user/classes/external/user.php @@ -35,6 +35,7 @@ use gradereport_user\report\user as user_report; defined('MOODLE_INTERNAL') || die; require_once($CFG->libdir.'/externallib.php'); +require_once($CFG->dirroot.'/grade/lib.php'); /** * External grade report API implementation diff --git a/grade/report/user/classes/report/user.php b/grade/report/user/classes/report/user.php index 30360b84398..4cfda092297 100644 --- a/grade/report/user/classes/report/user.php +++ b/grade/report/user/classes/report/user.php @@ -1220,6 +1220,19 @@ class user extends grade_report { } } + /** + * Build the html for the zero state of the user report. + * @return string HTML to display + */ + public function output_report_zerostate(): string { + global $OUTPUT, $COURSE; + $context = [ + 'courseid' => $COURSE->id, + 'imglink' => new \moodle_url('/pix/f/clip-353 1.png'), + ]; + return $OUTPUT->render_from_template('gradereport_user/zero_state', $context); + } + /** * Trigger the grade_report_viewed event * diff --git a/grade/report/user/db/services.php b/grade/report/user/db/services.php index f0b88f1f23e..1be3aa7e314 100644 --- a/grade/report/user/db/services.php +++ b/grade/report/user/db/services.php @@ -48,5 +48,5 @@ $functions = [ 'type' => 'read', 'capabilities' => 'gradereport/user:view', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], - ] + ], ]; diff --git a/grade/report/user/index.php b/grade/report/user/index.php index 38ac35c6e00..b19d04e0d90 100644 --- a/grade/report/user/index.php +++ b/grade/report/user/index.php @@ -27,11 +27,14 @@ require_once $CFG->libdir.'/gradelib.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->dirroot.'/grade/report/user/lib.php'; +use gradereport_user\report\user as reportbase; + $courseid = required_param('id', PARAM_INT); -$userid = optional_param('userid', $USER->id, PARAM_INT); +$userid = optional_param('userid', null, PARAM_INT); $userview = optional_param('userview', 0, PARAM_INT); $PAGE->set_url(new moodle_url('/grade/report/user/index.php', ['id' => $courseid])); +$PAGE->requires->js_call_amd('gradereport_user/user', 'init'); if ($userview == 0) { $userview = get_user_preferences('gradereport_user_view_user', GRADE_REPORT_USER_VIEW_USER); @@ -49,9 +52,9 @@ $PAGE->set_pagelayout('report'); $context = context_course::instance($course->id); require_capability('gradereport/user:view', $context); -if (empty($userid)) { +if ($userid === 0 || is_null($userid)) { require_capability('moodle/grade:viewall', $context); -} else { +} else if ($userid) { if (!$DB->get_record('user', ['id' => $userid, 'deleted' => 0]) || isguestuser($userid)) { throw new \moodle_exception('invaliduser'); } @@ -61,7 +64,7 @@ $access = false; if (has_capability('moodle/grade:viewall', $context)) { // User can view all course grades. $access = true; -} else if ($userid == $USER->id && has_capability('moodle/grade:view', $context) && $course->showgrades) { +} else if (($userid == $USER->id || is_null($userid)) && has_capability('moodle/grade:view', $context) && $course->showgrades) { // User can view own grades. $access = true; } else if (has_capability('moodle/grade:viewall', context_user::instance($userid)) && $course->showgrades) { @@ -89,7 +92,7 @@ grade_regrade_final_grades_if_required($course); // Teachers will see all student reports. if (has_capability('moodle/grade:viewall', $context)) { // Verify if we are using groups or not. - $groupmode = groups_get_course_groupmode($course); + $groupmode = groups_get_course_groupmode($course); $currentgroup = $gpr->groupid; // To make some other functions work better later. @@ -119,13 +122,25 @@ if (has_capability('moodle/grade:viewall', $context)) { $viewasuser = false; } - if (empty($userid)) { + if (is_null($userid)) { + $report = new reportbase($courseid, $gpr, $context, $USER->id); + + if (isset($report)) { + // Trigger report viewed event. + $report->viewed(); + } + + // Print header. + print_grade_page_head($course->id, 'report', 'user', ' ', false); + + echo $report->output_report_zerostate(); + } else if (empty($userid)) { $gui = new graded_users_iterator($course, null, $currentgroup); $gui->require_active_enrolment($showonlyactiveenrol); $gui->init(); // Add tabs. print_grade_page_head($courseid, 'report', 'user'); - groups_print_course_menu($course, $gpr->get_return_url('index.php?id='.$courseid, ['userid' => 0])); + groups_print_course_menu($course, $gpr->get_return_url('index.php?id=' . $courseid, ['userid' => 0])); if ($user_selector) { echo $renderer->graded_users_selector('user', $course, $userid, $currentgroup, true); @@ -147,7 +162,7 @@ if (has_capability('moodle/grade:viewall', $context)) { echo $OUTPUT->heading($studentnamelink); if ($report->fill_table()) { - echo '
'.$report->print_table(true); + echo '
' . $report->print_table(true); } echo "

"; } @@ -163,10 +178,11 @@ if (has_capability('moodle/grade:viewall', $context)) { ), fullname($report->user) ); - print_grade_page_head($courseid, 'report', 'user', get_string('pluginname', 'gradereport_user') . ' - ' . $studentnamelink, - false, false, true, null, null, $report->user); + print_grade_page_head($courseid, 'report', 'user', + get_string('pluginname', 'gradereport_user') . ' - ' . $studentnamelink, + false, false, true, null, null, $report->user); - groups_print_course_menu($course, $gpr->get_return_url('index.php?id='.$courseid, ['userid' => 0])); + groups_print_course_menu($course, $gpr->get_return_url('index.php?id=' . $courseid, ['userid' => 0])); if ($user_selector) { $showallusersoptions = true; @@ -179,20 +195,21 @@ if (has_capability('moodle/grade:viewall', $context)) { echo $OUTPUT->notification(get_string('groupusernotmember', 'error')); } else { if ($report->fill_table()) { - echo '
'.$report->print_table(true); + echo '
' . $report->print_table(true); } } } } else { // Students will see just their own report. // Create a report instance. - $report = new gradereport_user\report\user($courseid, $gpr, $context, $userid); + $report = new gradereport_user\report\user($courseid, $gpr, $context, $userid ?? $USER->id); // Print the page. - print_grade_page_head($courseid, 'report', 'user', get_string('pluginname', 'gradereport_user'). ' - '.fullname($report->user)); + print_grade_page_head($courseid, 'report', 'user', + get_string('pluginname', 'gradereport_user') . ' - ' . fullname($report->user)); if ($report->fill_table()) { - echo '
'.$report->print_table(true); + echo '
' . $report->print_table(true); } } diff --git a/grade/report/user/lang/en/gradereport_user.php b/grade/report/user/lang/en/gradereport_user.php index 943b8df2eb7..19119d2df7a 100644 --- a/grade/report/user/lang/en/gradereport_user.php +++ b/grade/report/user/lang/en/gradereport_user.php @@ -24,11 +24,16 @@ defined('MOODLE_INTERNAL') || die(); +$string['allusersnum'] = 'All users ({$a})'; $string['eventgradereportviewed'] = 'Grade user report viewed'; $string['pluginname'] = 'User report'; $string['user:view'] = 'View your own grade report'; $string['myself'] = 'Myself'; $string['otheruser'] = 'User'; $string['privacy:metadata:preference:gradereport_user_view_user'] = 'Whether to view report as current user or another user in the gradebook reports'; +$string['selectauser'] = 'Select a user'; +$string['selectuser'] = 'Select a user to view their grades'; +$string['selectuserinstructions'] = 'By selecting a user you can view the grades by activity'; +$string['selectuserlink'] = 'Click to select user'; $string['tablesummary'] = 'The table is arranged as a list of graded items including categories of graded items. When items are in a category they will be indicated as such.'; $string['viewas'] = 'View report as'; diff --git a/grade/report/user/renderer.php b/grade/report/user/renderer.php index 48f7c9709cc..640197040ff 100644 --- a/grade/report/user/renderer.php +++ b/grade/report/user/renderer.php @@ -39,12 +39,12 @@ class gradereport_user_renderer extends plugin_renderer_base { * @param string $report * @param stdClass $course * @param int $userid - * @param int $groupid + * @param null|int $groupid * @param bool $includeall * @return string The raw HTML to render. * @throws coding_exception */ - public function graded_users_selector(string $report, stdClass $course, int $userid, int $groupid, bool $includeall): string { + public function graded_users_selector(string $report, stdClass $course, int $userid, ?int $groupid, bool $includeall): string { $select = grade_get_graded_users_select($report, $course, $userid, $groupid, $includeall); $output = html_writer::tag('div', $this->output->render($select), ['id' => 'graded_users_selector']); diff --git a/grade/report/user/templates/zero_state.mustache b/grade/report/user/templates/zero_state.mustache new file mode 100644 index 00000000000..f7d6b046dc1 --- /dev/null +++ b/grade/report/user/templates/zero_state.mustache @@ -0,0 +1,37 @@ +{{! + 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 . +}} +{{! + @template gradereport_user/zero_state + + The zero state of the user report that contains the image and trigger for the search widget. + + Example context (json): + { + "imglink": "http://foo.bar/gradereport/?userid=25", + "courseid": "2" + } +}} +
+ Temp +

{{#str}}selectuser, gradereport_user{{/str}}

+

{{#str}}selectuserinstructions, gradereport_user{{/str}}

+ {{#str}}selectuserlink, gradereport_user{{/str}} +
diff --git a/grade/report/user/tests/behat/user_view.feature b/grade/report/user/tests/behat/user_view.feature index a6c174bad72..ded04bff316 100644 --- a/grade/report/user/tests/behat/user_view.feature +++ b/grade/report/user/tests/behat/user_view.feature @@ -49,7 +49,7 @@ Feature: View the user report as the student will see it Scenario: View the report as the teacher themselves When I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And I select "Myself" from the "View report as" singleselect Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | @@ -65,7 +65,7 @@ Feature: View the user report as the student will see it Scenario: View the report as the student from both the teachers and students perspective When I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And I select "User" from the "View report as" singleselect Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | @@ -107,7 +107,7 @@ Feature: View the user report as the student will see it And I set the field with xpath "//select[@name='report_user_showtotalsifcontainhidden']" to "Show totals excluding hidden items" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - When I select "Student 1" from the "Select all or one user" singleselect + When I click on "Student 1" in the "user" search widget And I select "User" from the "View report as" singleselect Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | @@ -145,7 +145,7 @@ Feature: View the user report as the student will see it And I set the field with xpath "//select[@name='report_user_showtotalsifcontainhidden']" to "Show totals including hidden items" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - When I select "Student 1" from the "Select all or one user" singleselect + When I click on "Student 1" in the "user" search widget And I select "User" from the "View report as" singleselect Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | @@ -191,7 +191,7 @@ Feature: View the user report as the student will see it And I set the field with xpath "//select[@name='report_user_showtotalsifcontainhidden']" to "Show totals excluding hidden items" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - When I select "Student 1" from the "Select all or one user" singleselect + When I click on "Student 1" in the "user" search widget And I select "User" from the "View report as" singleselect Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | diff --git a/grade/report/user/tests/behat/usersearch.feature b/grade/report/user/tests/behat/usersearch.feature new file mode 100644 index 00000000000..543e34460e9 --- /dev/null +++ b/grade/report/user/tests/behat/usersearch.feature @@ -0,0 +1,33 @@ +@core @core_grades @gradereport_user @javascript +Feature: Within the User report, a teacher can search for users. + Background: + Given the following "courses" exist: + | fullname | shortname | category | groupmode | + | Course 1 | C1 | 0 | 1 | + And the following "users" exist: + | username | firstname | lastname | email | idnumber | + | teacher1 | Teacher | 1 | teacher1@example.com | t1 | + | student1 | Student | 1 | student1@example.com | s1 | + | student2 | Student | 2 | student2@example.com | s2 | + | student32 | Student | 32 | student32@example.com | s32 | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + | student2 | C1 | student | + | student32 | C1 | student | + And I log in as "teacher1" + And I am on "Course 1" course homepage + And I change window size to "large" + + Scenario: A teacher can search for and find a user to view + When I navigate to "View > User report" in the course gradebook + And I click on "Click to select user" "link" + And I confirm "Student 1" in "Select a user" search within the gradebook widget exists + And I confirm "Student 2" in "Select a user" search within the gradebook widget exists + And I confirm "Student 32" in "Select a user" search within the gradebook widget exists + And I set the field "searchinput" to "2" + And I wait "1" seconds + Then I confirm "Student 2" in "Select a user" search within the gradebook widget exists + And I confirm "Student 32" in "Select a user" search within the gradebook widget exists + And I confirm "Student 1" in "Select a user" search within the gradebook widget does not exist diff --git a/grade/report/user/tests/behat/view_usereport.feature b/grade/report/user/tests/behat/view_usereport.feature index 6a3495c1b9f..75ee486a29b 100644 --- a/grade/report/user/tests/behat/view_usereport.feature +++ b/grade/report/user/tests/behat/view_usereport.feature @@ -13,5 +13,5 @@ Feature: We can use the user report Given I log in as "admin" And I am on "Course 1" course homepage And I navigate to "View > User report" in the course gradebook - And I select "All users (0)" from the "Select all or one user" singleselect + And I click on "All users (0)" in the "user" search widget Then I should see "There are no students enrolled in this course." diff --git a/grade/report/user/version.php b/grade/report/user/version.php index 11a52d9f6d5..01abb90baf8 100644 --- a/grade/report/user/version.php +++ b/grade/report/user/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2022041901; // The current plugin version (Date: YYYYMMDDXX). +$plugin->version = 2022041902; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2022041200; // Requires this Moodle version. $plugin->component = 'gradereport_user'; // Full name of the plugin (used for diagnostics) diff --git a/grade/templates/searchwidget/error.mustache b/grade/templates/searchwidget/error.mustache new file mode 100644 index 00000000000..c4dc44fb8a1 --- /dev/null +++ b/grade/templates/searchwidget/error.mustache @@ -0,0 +1,39 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template core_grades/searchwidget/error + + Chooser error template. + + Variables required for this template: + * errormessage - The error message + + Example context (json): + { + "errormessage": "Error" + } +}} +
+ +
diff --git a/grade/templates/searchwidget/searchitem.mustache b/grade/templates/searchwidget/searchitem.mustache new file mode 100644 index 00000000000..de0e4db5450 --- /dev/null +++ b/grade/templates/searchwidget/searchitem.mustache @@ -0,0 +1,48 @@ +{{! + 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 . +}} +{{! + @template core_grades/searchwidget/searchitem + + Search result line items. + + Example context (json): + { + "id": "1", + "name": "Quiz 1", + "url": "http://foo.bar/gradereport/?userid=25", + "fullname": "Cameron Greeve" + } +}} +
+ {{#name}} + + + {{name}} + + + {{/name}} + {{^name}} + + + {{fullname}} + + + {{email}} + + + {{/name}} +
diff --git a/grade/templates/searchwidget/searchresults.mustache b/grade/templates/searchwidget/searchresults.mustache new file mode 100644 index 00000000000..40d40a5c527 --- /dev/null +++ b/grade/templates/searchwidget/searchresults.mustache @@ -0,0 +1,46 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template core_grades/searchwidget/searchresults + + The wrapper in which our search results will be rendered + + Example context (json): + { + "searchresults": [ + { + "id": "1", + "name": "Quiz 1", + "url": "http://foo.bar/gradereport/?userid=25", + "fullname": "Cameron Greeve", + "sendmessage": "http://foo.bar/message/index.php?id=25", + "addcontact": "http://foo.bar/message/index.php?user1=2&user2=14&addcontact=14&sesskey=XXXXX", + "currentuser": "2" + } + ] + } +}} +
+
+ {{#searchresults}} + {{>core_grades/searchwidget/searchitem}} + {{/searchresults}} + {{^searchresults}} +

{{#str}} resultsfound, core, 0 {{/str}}

+ {{/searchresults}} +
+
diff --git a/grade/templates/searchwidget/user/usersearch_body.mustache b/grade/templates/searchwidget/user/usersearch_body.mustache new file mode 100644 index 00000000000..9f5be04520a --- /dev/null +++ b/grade/templates/searchwidget/user/usersearch_body.mustache @@ -0,0 +1,36 @@ +{{! + 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 . +}} +{{! + @template core_grades/searchwidget/user/usersearch_body + + The body of the user widget. + + Example context (json): + { + "displayunsearchablecontent": true + } +}} +{{< core/search_input_auto }} + {{$label}}{{#str}} + searchusers, core_grades + {{/str}}{{/label}} + {{$placeholder}}{{#str}} + searchusers, core_grades + {{/str}}{{/placeholder}} +{{/ core/search_input_auto }} + +
+{{#displayunsearchablecontent}} +
+{{/displayunsearchablecontent}} diff --git a/grade/tests/behat/behat_grade.php b/grade/tests/behat/behat_grade.php index 6ba91e5946d..a3c0971e1a6 100644 --- a/grade/tests/behat/behat_grade.php +++ b/grade/tests/behat/behat_grade.php @@ -421,4 +421,59 @@ class behat_grade extends behat_base { "#{$formid}", 'css_element']); } } + + /** + * Confirm if a value is within the search widget within the gradebook. + * + * Examples: + * - I confirm "User1" in "User" search within the gradebook widget exists + * + * @Given /^I confirm "(?P(?:[^"]|\\")*)" in "(?P(?:[^"]|\\")*)" search within the gradebook widget exists$/ + * @param string $needle The value to search for. + * @param string $haystack The selector to use within the zero state. + */ + public function i_confirm_in_search_within_the_gradebook_widget_exists($needle, $haystack) { + $this->execute("behat_general::wait_until_exists", [$haystack, "dialogue"]); + $this->execute("behat_general::assert_element_contains_text", [$needle, $haystack, "dialogue"]); + } + + /** + * Confirm if a value is not within the search widget within the gradebook. + * + * Examples: + * - I confirm "User1" in "User" search within the gradebook widget does not exist + * + * @Given /^I confirm "(?P(?:[^"]|\\")*)" in "(?P(?:[^"]|\\")*)" search within the gradebook widget does not exist$/ + * @param string $needle The value to search for. + * @param string $haystack The selector to use within the zero state. + */ + public function i_confirm_in_search_within_the_gradebook_widget_does_not_exist($needle, $haystack) { + $this->execute("behat_general::wait_until_exists", [$haystack, "dialogue"]); + $this->execute("behat_general::assert_element_not_contains_text", [$needle, $haystack, "dialogue"]); + } + + /** + * Clicks on an option from the specified search widget in the current gradebook page. + * + * Examples: + * - I click on "Student1" in the "user" search widget + * - I click on "Group1" in the "group" search widget + * + * @Given /^I click on "(?P(?:[^"]|\\")*)" in the "(?P(?:[^"]|\\")*)" search widget$/ + * @param string $needle The value to search for. + * @param string $haystack The type of the search widget. + */ + public function i_click_on_in_search_widget(string $needle, string $haystack) { + $this->execute("behat_general::wait_until_the_page_is_ready"); + if ($haystack === 'user') { + $this->execute("behat_general::i_click_on", ['.userwidget', "css_element"]); + $dialoguetitle = 'Select a user'; + } else { + $this->execute("behat_general::i_click_on", ['.groupwidget', "css_element"]); + $dialoguetitle = 'Select a grade item'; + } + + $this->execute("behat_general::wait_until_exists", [$dialoguetitle, "dialogue"]); + $this->execute('behat_general::i_click_on_in_the', [$needle, "link", $dialoguetitle, 'dialogue']); + } } diff --git a/grade/tests/behat/grade_aggregation.feature b/grade/tests/behat/grade_aggregation.feature index a7649219291..ca396878bed 100644 --- a/grade/tests/behat/grade_aggregation.feature +++ b/grade/tests/behat/grade_aggregation.feature @@ -53,6 +53,7 @@ Feature: We can use calculated grade totals And I give the grade "10.00" to the user "Student 1" for the grade item "Test assignment eight" And I give the grade "5.00" to the user "Student 1" for the grade item "Test assignment nine" And I press "Save changes" + And I change window size to "large" And I set the following settings for grade item "Test assignment two": | Hidden | 1 | And I set the following settings for grade item "Test assignment five": @@ -349,8 +350,8 @@ Feature: We can use calculated grade totals And I set the field "Show weightings" to "Show" And I press "Save changes" And I navigate to "View > User report" in the course gradebook + And I click on "Student 1" in the "user" search widget And I select "Myself" from the "View report as" singleselect - And I select "Student 1" from the "Select all or one user" singleselect And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Contribution to course total | | Test assignment five | 28.57 % | 10.00 (50.00 %) | 0–20 | 1.03 % | @@ -541,8 +542,8 @@ Feature: We can use calculated grade totals And I navigate to "View > Grader report" in the course gradebook Then I should see "75.00 (16.85 %)" in the ".course" "css_element" And I navigate to "View > User report" in the course gradebook + And I click on "Student 1" in the "user" search widget And I select "Myself" from the "View report as" singleselect - And I select "Student 1" from the "Select all or one user" singleselect And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Contribution to course total | | Test assignment five | 57.14 % | 10.00 (50.00 %) | 2.25 % | diff --git a/grade/tests/behat/grade_calculated_grade_items.feature b/grade/tests/behat/grade_calculated_grade_items.feature index 91f4870d922..da234625a12 100644 --- a/grade/tests/behat/grade_calculated_grade_items.feature +++ b/grade/tests/behat/grade_calculated_grade_items.feature @@ -42,7 +42,7 @@ Feature: Calculated grade items can be used in the gradebook And I give the grade "75.00" to the user "Student 1" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | - | 75.00 | 0–100 | 75.00 % | - | @@ -69,7 +69,7 @@ Feature: Calculated grade items can be used in the gradebook And I give the grade "75.00" to the user "Student 1" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | - | 75.00 | 0–100 | 75.00 % | - | @@ -82,7 +82,7 @@ Feature: Calculated grade items can be used in the gradebook And I give the grade "65.00" to the user "Student 2" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - When I select "Student 1" from the "Select all or one user" singleselect + When I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | - | 75.00 | 0–100 | 75.00 % | - | @@ -99,7 +99,7 @@ Feature: Calculated grade items can be used in the gradebook | Min and max grades used in calculation | Initial min and max grades | And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | - | 75.00 | 0–100 | 75.00 % | - | @@ -135,7 +135,7 @@ Feature: Calculated grade items can be used in the gradebook And I give the grade "75.00" to the user "Student 1" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - When I select "Student 1" from the "Select all or one user" singleselect + When I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | 66.67 % | 75.00 | 0–100 | 75.00 % | 50.00 % | @@ -149,7 +149,7 @@ Feature: Calculated grade items can be used in the gradebook And I give the grade "65.00" to the user "Student 2" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | 71.43 % | 75.00 | 0–100 | 75.00 % | 53.57 % | diff --git a/grade/tests/behat/grade_calculated_grade_items_20150627.feature b/grade/tests/behat/grade_calculated_grade_items_20150627.feature index 88558150a30..5fcd2e103d5 100644 --- a/grade/tests/behat/grade_calculated_grade_items_20150627.feature +++ b/grade/tests/behat/grade_calculated_grade_items_20150627.feature @@ -43,7 +43,7 @@ Feature: Gradebook calculations for calculated grade items before the fix 201506 And I give the grade "75.00" to the user "Student 1" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | - | 75.00 | 0–100 | 75.00 % | - | @@ -70,7 +70,7 @@ Feature: Gradebook calculations for calculated grade items before the fix 201506 And I give the grade "75.00" to the user "Student 1" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | - | 75.00 | 0–100 | 75.00 % | - | @@ -83,7 +83,7 @@ Feature: Gradebook calculations for calculated grade items before the fix 201506 And I give the grade "65.00" to the user "Student 2" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - When I select "Student 1" from the "Select all or one user" singleselect + When I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | - | 75.00 | 0–100 | 75.00 % | - | @@ -100,7 +100,7 @@ Feature: Gradebook calculations for calculated grade items before the fix 201506 | Min and max grades used in calculation | Initial min and max grades | And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | - | 75.00 | 0–100 | 75.00 % | - | @@ -136,7 +136,7 @@ Feature: Gradebook calculations for calculated grade items before the fix 201506 And I give the grade "75.00" to the user "Student 1" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - When I select "Student 1" from the "Select all or one user" singleselect + When I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | 50.00 % | 75.00 | 0–100 | 75.00 % | 37.50 % | @@ -150,7 +150,7 @@ Feature: Gradebook calculations for calculated grade items before the fix 201506 And I give the grade "65.00" to the user "Student 2" for the grade item "grade item 1" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | grade item 1 | 50.00 % | 75.00 | 0–100 | 75.00 % | 37.50 % | diff --git a/grade/tests/behat/grade_calculated_weights.feature b/grade/tests/behat/grade_calculated_weights.feature index 72fee663783..18eae783b7c 100644 --- a/grade/tests/behat/grade_calculated_weights.feature +++ b/grade/tests/behat/grade_calculated_weights.feature @@ -62,7 +62,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "Course 1": | Aggregation | Mean of grades | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: @@ -87,7 +87,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "EN Sub category": | Item weight | 1.0 | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: @@ -108,7 +108,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "EN Test assignment three": | Extra credit | 1 | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: @@ -127,7 +127,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "EN Test assignment three": | Extra credit weight | 1.0 | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: @@ -144,7 +144,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "Course 1": | Aggregation | Median of grades | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: @@ -161,7 +161,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "Course 1": | Aggregation | Lowest grade | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: @@ -178,7 +178,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "Course 1": | Aggregation | Highest grade | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: @@ -195,7 +195,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "Course 1": | Aggregation | Mode of grades | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: @@ -216,7 +216,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "EN Test assignment three": | Extra credit | 1 | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: @@ -235,7 +235,7 @@ Feature: We can understand the gradebook user report And I set the following settings for grade item "EN Test assignment three": | Extra credit | 1 | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget # Check the values in the weights column. Then the following should exist in the "user-grade" table: diff --git a/grade/tests/behat/grade_contribution_with_extra_credit.feature b/grade/tests/behat/grade_contribution_with_extra_credit.feature index cb53856f38c..6998bbe42ff 100644 --- a/grade/tests/behat/grade_contribution_with_extra_credit.feature +++ b/grade/tests/behat/grade_contribution_with_extra_credit.feature @@ -64,7 +64,7 @@ Feature: Extra credit contributions are normalised when going out of bounds And I set the following settings for grade item "Manual item 4": | Extra credit | 1 | And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Contribution to course total | | Manual item 1 | | 80.00 | | diff --git a/grade/tests/behat/grade_grade_minmax_change.feature b/grade/tests/behat/grade_grade_minmax_change.feature index 2cc6119ca90..e6b3fd58e50 100644 --- a/grade/tests/behat/grade_grade_minmax_change.feature +++ b/grade/tests/behat/grade_grade_minmax_change.feature @@ -48,7 +48,7 @@ Feature: We can change the maximum and minimum number of points for manual items | Maximum grade | 10 | And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Contribution to course total | | Manual item 1 | 100.00 % | 10.00 | 100.00 % | @@ -64,7 +64,7 @@ Feature: We can change the maximum and minimum number of points for manual items | Maximum grade | 20 | And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Contribution to course total | | Manual item 1 | 100.00 % | 20.00 | 100.00 % | diff --git a/grade/tests/behat/grade_hidden_items.feature b/grade/tests/behat/grade_hidden_items.feature index 911dd5e2a9a..186cbb6c0e2 100644 --- a/grade/tests/behat/grade_hidden_items.feature +++ b/grade/tests/behat/grade_hidden_items.feature @@ -55,8 +55,8 @@ Feature: Student and teacher's view of aggregated grade items is consistent when And I press "Save changes" And I am on "Course 1" course homepage And I navigate to "View > User report" in the course gradebook + And I click on "Student 1" in the "user" search widget And I select "Myself" from the "View report as" singleselect - And I select "Student 1" from the "Select all or one user" singleselect Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 100.00 % | 50.00 | 0–100 | 50.00 % | 25.00 % | diff --git a/grade/tests/behat/grade_hidden_items_locked_category.feature b/grade/tests/behat/grade_hidden_items_locked_category.feature index aaec7a37472..9a4b061c2bd 100644 --- a/grade/tests/behat/grade_hidden_items_locked_category.feature +++ b/grade/tests/behat/grade_hidden_items_locked_category.feature @@ -47,8 +47,8 @@ Feature: Hidden grade items should be hidden when grade category is locked, but Given I log in as "teacher1" And I am on "Course 1" course homepage And I navigate to "View > User report" in the course gradebook + And I click on "Student 1" in the "user" search widget And I select "Myself" from the "View report as" singleselect - When I select "Student 1" from the "Select all or one user" singleselect Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test locked category total | 100.00 % | 50.00 | 0–100 | 50.00 % | - | diff --git a/grade/tests/behat/grade_mingrade.feature b/grade/tests/behat/grade_mingrade.feature index b385b3d27ae..5f3fd1d8330 100644 --- a/grade/tests/behat/grade_mingrade.feature +++ b/grade/tests/behat/grade_mingrade.feature @@ -100,7 +100,7 @@ Feature: We can use a minimum grade different than zero And I give the grade "0.00" to the user "Student 2" for the grade item "Manual item 6" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Contribution to course total | | Manual item 1 | 18.18 % | -25.00 | -4.55 % | diff --git a/grade/tests/behat/grade_minmax.feature b/grade/tests/behat/grade_minmax.feature index 60cc12aa54d..36a520936d9 100644 --- a/grade/tests/behat/grade_minmax.feature +++ b/grade/tests/behat/grade_minmax.feature @@ -76,7 +76,7 @@ Feature: We can choose what min or max grade to use when aggregating grades. And I give the grade "10.00" to the user "Student 2" for the grade item "MI 3" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | MI 1 | 20.00 % | 75.00 | 0–100 | 75.00 % | 15.00 % | @@ -106,7 +106,7 @@ Feature: We can choose what min or max grade to use when aggregating grades. | Maximum grade | 50.00 | | Minimum grade | 5.00 | And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | MI 1 | 12.50 % | 75.00 | 5–50 | 100.00 % | 18.75 % | @@ -131,7 +131,7 @@ Feature: We can choose what min or max grade to use when aggregating grades. | Rescale existing grades | No | | Maximum grade | 200.00 | And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | MI 5 | 40.00 % | 150.00 | 0–200 | 75.00 % | 30.00 % | @@ -145,7 +145,7 @@ Feature: We can choose what min or max grade to use when aggregating grades. When I set the field "Min and max grades used in calculation" to "Initial min and max grades" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | MI 1 | 16.67 % | 75.00 | 0–100 | 75.00 % | 12.50 % | diff --git a/grade/tests/behat/grade_natural_exclude_empty.feature b/grade/tests/behat/grade_natural_exclude_empty.feature index b23be1f4533..0304ca87eac 100644 --- a/grade/tests/behat/grade_natural_exclude_empty.feature +++ b/grade/tests/behat/grade_natural_exclude_empty.feature @@ -42,7 +42,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 28.57 % | 80.00 | 0–100 | 80.00 % | 22.86 % | @@ -63,7 +63,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 66.67 % | 80.00 | 0–100 | 80.00 % | 53.33 % | @@ -82,7 +82,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 0.00 %( Empty ) | - | 0–100 | - | 0.00 % | @@ -109,7 +109,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 0.00 %( Extra credit ) | 80.00 | 0–100 | 80.00 % | 0.00 % | @@ -134,7 +134,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 50.00 % | 80.00 | 0–100 | 80.00 % | 40.00 % | @@ -158,7 +158,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 83.33 % | 80.00 | 0–100 | 80.00 % | 66.67 % | @@ -180,7 +180,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 0.00 %( Empty ) | - | 0–100 | - | 0.00 % | @@ -207,7 +207,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 50.00 % | 80.00 | 0–100 | 80.00 % | 40.00 % | @@ -233,7 +233,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 83.33 % | 80.00 | 0–100 | 80.00 % | 66.67 % | @@ -257,7 +257,7 @@ Feature: Weights in natural aggregation are adjusted if the items are excluded f And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 0.00 %( Empty ) | - | 0–100 | - | 0.00 % | diff --git a/grade/tests/behat/grade_natural_exclude_empty_20150619.feature b/grade/tests/behat/grade_natural_exclude_empty_20150619.feature index 39578ff9a38..039e3951695 100644 --- a/grade/tests/behat/grade_natural_exclude_empty_20150619.feature +++ b/grade/tests/behat/grade_natural_exclude_empty_20150619.feature @@ -43,7 +43,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 28.57 % | 80.00 | 0–100 | 80.00 % | 22.86 % | @@ -64,7 +64,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 66.67 % | 80.00 | 0–100 | 80.00 % | 53.33 % | @@ -83,7 +83,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 0.00 %( Empty ) | - | 0–100 | - | 0.00 % | @@ -110,7 +110,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 0.00 %( Extra credit ) | 80.00 | 0–100 | 80.00 % | 0.00 % | @@ -135,7 +135,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 50.00 % | 80.00 | 0–100 | 80.00 % | 40.00 % | @@ -160,7 +160,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 83.33 % | 80.00 | 0–100 | 80.00 % | 66.67 % | @@ -183,7 +183,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 0.00 %( Empty ) | - | 0–100 | - | 0.00 % | @@ -210,7 +210,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 50.00 % | 80.00 | 0–100 | 80.00 % | 40.00 % | @@ -237,7 +237,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 83.33 % | 80.00 | 0–100 | 80.00 % | 66.67 % | @@ -262,7 +262,7 @@ Feature: Gradebook calculations for extra credit items before the fix 20150619 And I give the grade "8.00" to the user "Student 1" for the grade item "Test assignment five (extra)" And I press "Save changes" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Calculated weight | Grade | Range | Percentage | Contribution to course total | | Test assignment one | 0.00 %( Empty ) | - | 0–100 | - | 0.00 % | diff --git a/grade/tests/behat/grade_scales.feature b/grade/tests/behat/grade_scales.feature index 72a55042146..834370ee921 100644 --- a/grade/tests/behat/grade_scales.feature +++ b/grade/tests/behat/grade_scales.feature @@ -86,7 +86,7 @@ Feature: View gradebook when scales are used | Range | F–A | 0.00–5.00 | 0.00–5.00 | | Overall average | C | 3.00 | 3.00 | And I navigate to "View > User report" in the course gradebook - And I select "Student 3" from the "Select all or one user" singleselect + And I click on "Student 3" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Grade | Range | Percentage | Contribution to course total | | Test assignment one | C | F–A | 50.00 % | 60.00 % | @@ -132,7 +132,7 @@ Feature: View gradebook when scales are used | Range | F–A | 1.00–5.00 | 0.00–100.00 | | Overall average | C | 3.00 | | And I navigate to "View > User report" in the course gradebook - And I select "Student 3" from the "Select all or one user" singleselect + And I click on "Student 3" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Grade | Range | Percentage | Contribution to course total | | Test assignment one | C | F–A | 50.00 % | | diff --git a/grade/tests/behat/grade_scales_aggregation.feature b/grade/tests/behat/grade_scales_aggregation.feature index 7632b5c3797..05c27b186e8 100644 --- a/grade/tests/behat/grade_scales_aggregation.feature +++ b/grade/tests/behat/grade_scales_aggregation.feature @@ -47,7 +47,7 @@ Feature: Control the aggregation of the scales And I set the following settings for grade item "Course 1": | Aggregation | | And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Percentage | Contribution to course total | | Grade me | 10.00 | 10.00 % | | @@ -61,7 +61,7 @@ Feature: Control the aggregation of the scales And I log in as "teacher1" And I am on "Course 1" course homepage And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Grade | Percentage | Contribution to course total | | Grade me | 10.00 | 10.00 % | | diff --git a/grade/tests/behat/grade_single_item_scales.feature b/grade/tests/behat/grade_single_item_scales.feature index 3852ad57083..9cd97bf9c52 100644 --- a/grade/tests/behat/grade_single_item_scales.feature +++ b/grade/tests/behat/grade_single_item_scales.feature @@ -66,7 +66,7 @@ Feature: View gradebook when single item scales are used | Range | Ace!–Ace! | 0.00–1.00 | 0.00–1.00 | | Overall average | Ace! | 1.00 | 1.00 | And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Grade | Range | Contribution to course total | | Test assignment one | Ace! | Ace!–Ace! | 100.00 % | @@ -104,7 +104,7 @@ Feature: View gradebook when single item scales are used | Range | Ace!–Ace! | 0.00–100.0 | 0.00–100.00 | | Overall average | Ace! | | | And I navigate to "View > User report" in the course gradebook - And I select "Student 1" from the "Select all or one user" singleselect + And I click on "Student 1" in the "user" search widget And the following should exist in the "user-grade" table: | Grade item | Grade | Range | Contribution to course total | | Test assignment one | Ace! | Ace!–Ace! | | diff --git a/grade/tests/behat/grade_view.feature b/grade/tests/behat/grade_view.feature index 6a1ac1a4f77..ec74521b2e8 100644 --- a/grade/tests/behat/grade_view.feature +++ b/grade/tests/behat/grade_view.feature @@ -71,7 +71,7 @@ Feature: We can enter in grades and view reports from the gradebook Scenario: Grade a grade item and ensure the results display correctly in the gradebook When I navigate to "View > User report" in the course gradebook And the "Gradebook navigation menu" select menu should contain "Grader report" - And the "Select all or one user" select box should contain "All users (1)" + And I click on "All users (1)" in the "user" search widget And I log out And I log in as "student1" And I follow "Grades" in the user menu diff --git a/lang/en/grades.php b/lang/en/grades.php index fbb78ed66b7..84319617303 100644 --- a/lang/en/grades.php +++ b/lang/en/grades.php @@ -725,6 +725,7 @@ $string['savechanges'] = 'Save changes'; $string['savepreferences'] = 'Save preferences'; $string['scaleconfirmdelete'] = 'Are you sure you wish to delete the scale "{$a}"?'; $string['scaledpct'] = 'Scaled %'; +$string['searchusers'] = 'Search users'; $string['seeallcoursegrades'] = 'See all course grades'; $string['select'] = 'Select {$a}'; $string['selectalloroneuser'] = 'Select all or one user'; diff --git a/lib/db/services.php b/lib/db/services.php index c3ebbffc178..6299fb02747 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -956,6 +956,13 @@ $functions = array( 'type' => 'write', 'capabilities' => 'moodle/grade:manage', ), + 'core_grades_get_enrolled_users_for_search_widget' => array ( + 'classname' => 'core_grades\external\get_enrolled_users_for_search_widget', + 'description' => 'Returns the enrolled users within and map some fields to the returned array of user objects.', + 'type' => 'read', + 'ajax' => true, + 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], + ), 'core_grading_get_definitions' => array( 'classname' => 'core_grading_external', 'methodname' => 'get_definitions', diff --git a/mod/h5pactivity/tests/behat/grading_attempts.feature b/mod/h5pactivity/tests/behat/grading_attempts.feature index 14f9e7a8f7d..ee7b38db7c6 100644 --- a/mod/h5pactivity/tests/behat/grading_attempts.feature +++ b/mod/h5pactivity/tests/behat/grading_attempts.feature @@ -45,7 +45,7 @@ Feature: Change grading options in an H5P activity Scenario: Default grading is max attempt grade Given I am on the "Course 1" course page logged in as teacher1 When I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Percentage | | Awesome H5P package | 100.00 | 100.00 % | @@ -57,7 +57,7 @@ Feature: Change grading options in an H5P activity | Grading method | First attempt | And I click on "Save and return to course" "button" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Percentage | | Awesome H5P package | 0.00 | 0.00 % | @@ -69,7 +69,7 @@ Feature: Change grading options in an H5P activity | Grading method | Last attempt | And I click on "Save and return to course" "button" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Percentage | | Awesome H5P package | 0.00 | 0.00 % | @@ -81,7 +81,7 @@ Feature: Change grading options in an H5P activity | Grading method | Average grade | And I click on "Save and return to course" "button" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Percentage | | Awesome H5P package | 33.33 | 33.33 % | @@ -93,7 +93,7 @@ Feature: Change grading options in an H5P activity | Grading method | Don't calculate a grade | And I click on "Save and return to course" "button" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Percentage | | Awesome H5P package | - | - | @@ -105,7 +105,7 @@ Feature: Change grading options in an H5P activity | Enable attempt tracking | No | And I click on "Save and return to course" "button" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Percentage | | Awesome H5P package | - | - | @@ -118,7 +118,7 @@ Feature: Change grading options in an H5P activity | Grading method | Average grade | And I click on "Save and return to course" "button" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Range | Percentage | | Awesome H5P package | 33.33 | 0–100 | 33.33 % | @@ -130,7 +130,7 @@ Feature: Change grading options in an H5P activity | Maximum grade | 50 | And I click on "Save and return to course" "button" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Range | Percentage | | Awesome H5P package | 16.67 | 0–50 | 33.33 % | @@ -143,7 +143,7 @@ Feature: Change grading options in an H5P activity | Grading method | Average grade | And I click on "Save and return to course" "button" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Range | Percentage | | Awesome H5P package | 33.33 | 0–100 | 33.33 % | @@ -155,7 +155,7 @@ Feature: Change grading options in an H5P activity | Maximum grade | 50 | And I click on "Save and return to course" "button" And I navigate to "View > User report" in the course gradebook - And I set the field "Select all or one user" to "Student 1" + And I click on "Student 1" in the "user" search widget Then the following should exist in the "user-grade" table: | Grade item | Grade | Range | Percentage | | Awesome H5P package | 33.33 | 0–50 | 66.67 % | diff --git a/pix/f/clip-353 1.png b/pix/f/clip-353 1.png new file mode 100644 index 00000000000..3bcaff57f0b Binary files /dev/null and b/pix/f/clip-353 1.png differ diff --git a/theme/boost/scss/moodle/grade.scss b/theme/boost/scss/moodle/grade.scss index 861364b7e88..d4400783a9c 100644 --- a/theme/boost/scss/moodle/grade.scss +++ b/theme/boost/scss/moodle/grade.scss @@ -513,3 +513,22 @@ .path-grade-report-singleview input[name^="finalgrade"] { width: 80px; } + +.searchresultitemscontainer { + max-height: 150px; + overflow: auto; + + a:hover { + background-color: $gray-100; + } + + .focused-cell { + outline-style: dotted; + outline-color: green; + } +} + +.unsearchablecontentcontainer { + border-top: 1px solid #dee2e6; + padding-top: 10px; +} diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 7120d1ba330..23a3e1829a4 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -19696,6 +19696,19 @@ p.arrow_button { .path-grade-report-singleview input[name^="finalgrade"] { width: 80px; } +.searchresultitemscontainer { + max-height: 150px; + overflow: auto; } + .searchresultitemscontainer a:hover { + background-color: #f8f9fa; } + .searchresultitemscontainer .focused-cell { + outline-style: dotted; + outline-color: green; } + +.unsearchablecontentcontainer { + border-top: 1px solid #dee2e6; + padding-top: 10px; } + .columns-autoflow-1to1to1 { column-count: 3; } diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index 85667cd7740..0ead2189a98 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -19696,6 +19696,19 @@ p.arrow_button { .path-grade-report-singleview input[name^="finalgrade"] { width: 80px; } +.searchresultitemscontainer { + max-height: 150px; + overflow: auto; } + .searchresultitemscontainer a:hover { + background-color: #f8f9fa; } + .searchresultitemscontainer .focused-cell { + outline-style: dotted; + outline-color: green; } + +.unsearchablecontentcontainer { + border-top: 1px solid #dee2e6; + padding-top: 10px; } + .columns-autoflow-1to1to1 { column-count: 3; } diff --git a/version.php b/version.php index 74291bc3439..a891d20e0ab 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2022101400.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2022101400.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.1dev+ (Build: 20221014)'; // Human-friendly version name