From 02471ef1cd7968c28eeb30572f202175d5025e23 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Fri, 3 Feb 2023 02:45:02 +1100 Subject: [PATCH] MDL-76246 core_grades: Remove url param from ws --- grade/amd/build/searchwidget/group.min.js | 2 +- grade/amd/build/searchwidget/group.min.js.map | 2 +- .../amd/build/searchwidget/repository.min.js | 2 +- .../build/searchwidget/repository.min.js.map | 2 +- grade/amd/src/searchwidget/group.js | 3 +- grade/amd/src/searchwidget/repository.js | 12 +- .../get_enrolled_users_for_search_widget.php | 14 +- .../get_enrolled_users_for_selector.php | 150 ++++++++++++++++++ .../external/get_groups_for_search_widget.php | 14 +- .../external/get_groups_for_selector.php | 140 ++++++++++++++++ grade/report/singleview/amd/build/user.min.js | 2 +- .../singleview/amd/build/user.min.js.map | 2 +- grade/report/singleview/amd/src/user.js | 3 +- .../classes/external/singleview.php | 17 +- grade/report/user/amd/build/user.min.js | 2 +- grade/report/user/amd/build/user.min.js.map | 2 +- grade/report/user/amd/src/user.js | 3 +- .../searchwidget/searchitem.mustache | 3 +- lib/db/services.php | 24 ++- lib/upgrade.txt | 4 + version.php | 2 +- 21 files changed, 359 insertions(+), 46 deletions(-) create mode 100644 grade/classes/external/get_enrolled_users_for_selector.php create mode 100644 grade/classes/external/get_groups_for_selector.php diff --git a/grade/amd/build/searchwidget/group.min.js b/grade/amd/build/searchwidget/group.min.js index e653f7edb7c..7b34629fa84 100644 --- a/grade/amd/build/searchwidget/group.min.js +++ b/grade/amd/build/searchwidget/group.min.js @@ -5,6 +5,6 @@ define("core_grades/searchwidget/group",["exports","core/local/aria/focuslock"," * @module core_grades/searchwidget/group * @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,FocusLockManager=_interopRequireWildcard(FocusLockManager),_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);let initialised=!1;_exports.init=()=>{if(!initialised&&document.querySelector(Selectors.elements.getSearchWidgetSelector("group"))){const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()}initialised=!0};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("group")),menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("group")),inputElement=menuContainer.querySelector('input[name="group"]');(0,_jquery.default)(menuContainer).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,actionBaseUrl=e.relatedTarget.dataset.actionBaseUrl;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.groupFetch(courseID,actionBaseUrl).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&(await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.groups,searchGroups(),null,afterSelect),FocusLockManager.trapFocus(dropdownMenuContainer))})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/group/groupsearch_body",[])),(0,_jquery.default)(menuContainer).on("hide.bs.dropdown",(()=>{FocusLockManager.untrapFocus()})),inputElement.addEventListener("change",(e=>{const toggle=menuContainer.querySelector(".dropdown-toggle"),courseId=toggle.dataset.courseid,actionUrl=toggle.dataset.actionBaseUrl?new URL(toggle.dataset.actionBaseUrl.replace(/&/g,"&")):new URL(location.href);actionUrl.searchParams.set("id",courseId),actionUrl.searchParams.set("group",e.target.value),location.href=actionUrl.href,e.stopPropagation()}))},searchGroups=()=>()=>(groups,searchTerm)=>{if(""===searchTerm)return groups;searchTerm=searchTerm.toLowerCase();const searchResults=[];return groups.forEach((group=>{group.name.toLowerCase().includes(searchTerm)&&searchResults.push(group)})),searchResults},afterSelect=selected=>{const menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("group")),inputElement=menuContainer.querySelector('input[name="group"]');(0,_jquery.default)(menuContainer).dropdown("hide"),inputElement.value!=selected&&(inputElement.value=selected,inputElement.dispatchEvent(new Event("change",{bubbles:!0})))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,FocusLockManager=_interopRequireWildcard(FocusLockManager),_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);let initialised=!1;_exports.init=()=>{if(!initialised&&document.querySelector(Selectors.elements.getSearchWidgetSelector("group"))){const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()}initialised=!0};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("group")),menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("group")),inputElement=menuContainer.querySelector('input[name="group"]');(0,_jquery.default)(menuContainer).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.groupFetch(courseID).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&(await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.groups,searchGroups(),null,afterSelect),FocusLockManager.trapFocus(dropdownMenuContainer))})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/group/groupsearch_body",[])),(0,_jquery.default)(menuContainer).on("hide.bs.dropdown",(()=>{FocusLockManager.untrapFocus()})),inputElement.addEventListener("change",(e=>{const toggle=menuContainer.querySelector(".dropdown-toggle"),courseId=toggle.dataset.courseid,actionUrl=toggle.dataset.actionBaseUrl?new URL(toggle.dataset.actionBaseUrl.replace(/&/g,"&")):new URL(location.href);actionUrl.searchParams.set("id",courseId),actionUrl.searchParams.set("group",e.target.value),location.href=actionUrl.href,e.stopPropagation()}))},searchGroups=()=>()=>(groups,searchTerm)=>{if(""===searchTerm)return groups;searchTerm=searchTerm.toLowerCase();const searchResults=[];return groups.forEach((group=>{group.name.toLowerCase().includes(searchTerm)&&searchResults.push(group)})),searchResults},afterSelect=selected=>{const menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("group")),inputElement=menuContainer.querySelector('input[name="group"]');(0,_jquery.default)(menuContainer).dropdown("hide"),inputElement.value!=selected&&(inputElement.value=selected,inputElement.dispatchEvent(new Event("change",{bubbles:!0})))}})); //# sourceMappingURL=group.min.js.map \ No newline at end of file diff --git a/grade/amd/build/searchwidget/group.min.js.map b/grade/amd/build/searchwidget/group.min.js.map index 96897c74364..f6dc31a0520 100644 --- a/grade/amd/build/searchwidget/group.min.js.map +++ b/grade/amd/build/searchwidget/group.min.js.map @@ -1 +1 @@ -{"version":3,"file":"group.min.js","sources":["../../src/searchwidget/group.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 widget to search groups within the gradebook.\n *\n * @module core_grades/searchwidget/group\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as FocusLockManager from 'core/local/aria/focuslock';\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport $ from 'jquery';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\n\n/**\n * Whether this module is already initialised.\n *\n * @type {boolean}\n */\nlet initialised = false;\n\n/**\n * Our entry point into starting to build the group search widget.\n *\n * It'll eventually, based upon the listeners, open the search widget and allow filtering.\n *\n * @method init\n */\nexport const init = () => {\n if (!initialised && document.querySelector(Selectors.elements.getSearchWidgetSelector('group'))) {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n }\n initialised = true;\n};\n\n/**\n * Register event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('group'));\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('group'));\n const inputElement = menuContainer.querySelector('input[name=\"group\"]');\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n $(menuContainer).on('show.bs.dropdown', async(e) => {\n const courseID = e.relatedTarget.dataset.courseid;\n const actionBaseUrl = e.relatedTarget.dataset.actionBaseUrl;\n // Display a loading icon in the dropdown menu container until the body promise is resolved.\n await WidgetBase.showLoader(dropdownMenuContainer);\n\n // If an error occurs while fetching the data, display the error within the dropdown menu.\n const data = await Repository.groupFetch(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 // Early return if there is no module data.\n if (data === []) {\n return;\n }\n await WidgetBase.init(\n dropdownMenuContainer,\n bodyPromise,\n data.groups,\n searchGroups(),\n null,\n afterSelect\n );\n\n // Lock tab control. It has to be locked because the dropdown's role is dialog.\n FocusLockManager.trapFocus(dropdownMenuContainer);\n });\n\n // Resolvers for passed functions in the dropdown creation.\n bodyPromiseResolver(Templates.render(\n 'core_grades/searchwidget/group/groupsearch_body',\n []\n ));\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n $(menuContainer).on('hide.bs.dropdown', () => {\n FocusLockManager.untrapFocus();\n });\n\n inputElement.addEventListener('change', e => {\n const toggle = menuContainer.querySelector('.dropdown-toggle');\n const courseId = toggle.dataset.courseid;\n const actionUrl = toggle.dataset.actionBaseUrl ?\n new URL(toggle.dataset.actionBaseUrl.replace(/&/g, \"&\")) :\n new URL(location.href);\n actionUrl.searchParams.set('id', courseId);\n actionUrl.searchParams.set('group', e.target.value);\n\n location.href = actionUrl.href;\n\n e.stopPropagation();\n });\n};\n\n/**\n * Define how we want to search and filter groups when the user decides to input a search value.\n *\n * @method searchGroups\n * @returns {function(): function(*, *): (*)}\n */\nconst searchGroups = () => {\n return () => {\n return (groups, searchTerm) => {\n if (searchTerm === '') {\n return groups;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n groups.forEach((group) => {\n const groupName = group.name.toLowerCase();\n if (groupName.includes(searchTerm)) {\n searchResults.push(group);\n }\n });\n return searchResults;\n };\n };\n};\n\n/**\n * Define the action to be performed when an item is selected by the search widget.\n *\n * @param {String} selected The selected item's value.\n */\nconst afterSelect = (selected) => {\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('group'));\n const inputElement = menuContainer.querySelector('input[name=\"group\"]');\n\n $(menuContainer).dropdown('hide'); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n if (inputElement.value != selected) {\n inputElement.value = selected;\n inputElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n};\n"],"names":["initialised","document","querySelector","Selectors","elements","getSearchWidgetSelector","pendingPromise","Pending","registerListenerEvents","resolve","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","dropdownMenuContainer","getSearchWidgetDropdownSelector","menuContainer","inputElement","on","async","courseID","e","relatedTarget","dataset","courseid","actionBaseUrl","showLoader","data","Repository","groupFetch","catch","errorTemplateData","message","Templates","render","init","groups","searchGroups","afterSelect","FocusLockManager","trapFocus","untrapFocus","addEventListener","toggle","courseId","actionUrl","URL","replace","location","href","searchParams","set","target","value","stopPropagation","searchTerm","toLowerCase","searchResults","forEach","group","name","includes","push","selected","dropdown","dispatchEvent","Event","bubbles"],"mappings":";;;;;;;2ZAoCIA,aAAc,gBASE,SACXA,aAAeC,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,UAAW,OACvFC,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,UAEnBT,aAAc,SAQZQ,uBAAyB,SACvBE,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,6BAC9CC,sBAAwBb,SAASC,cAAcC,UAAUC,SAASW,gCAAgC,UAClGC,cAAgBf,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,UAClFY,aAAeD,cAAcd,cAAc,2CAG/Cc,eAAeE,GAAG,oBAAoBC,MAAAA,UAC9BC,SAAWC,EAAEC,cAAcC,QAAQC,SACnCC,cAAgBJ,EAAEC,cAAcC,QAAQE,oBAExCb,WAAWc,WAAWZ,6BAGtBa,WAAaC,WAAWC,WAAWT,SAAUK,eAAeK,OAAMX,MAAAA,UAC9DY,kBAAoB,cACNV,EAAEW,SAEtBtB,0BACUuB,UAAUC,OAAO,iCAAkCH,uBAI7DJ,OAAS,WAGPf,WAAWuB,KACbrB,sBACAH,YACAgB,KAAKS,OACLC,eACA,KACAC,aAIJC,iBAAiBC,UAAU1B,2BAI/BJ,oBAAoBuB,UAAUC,OAC1B,kDACA,yBAIFlB,eAAeE,GAAG,oBAAoB,KACpCqB,iBAAiBE,iBAGrBxB,aAAayB,iBAAiB,UAAUrB,UAC9BsB,OAAS3B,cAAcd,cAAc,oBACrC0C,SAAWD,OAAOpB,QAAQC,SAC1BqB,UAAYF,OAAOpB,QAAQE,cAC7B,IAAIqB,IAAIH,OAAOpB,QAAQE,cAAcsB,QAAQ,SAAU,MACvD,IAAID,IAAIE,SAASC,MACrBJ,UAAUK,aAAaC,IAAI,KAAMP,UACjCC,UAAUK,aAAaC,IAAI,QAAS9B,EAAE+B,OAAOC,OAE7CL,SAASC,KAAOJ,UAAUI,KAE1B5B,EAAEiC,sBAUJjB,aAAe,IACV,IACI,CAACD,OAAQmB,iBACO,KAAfA,kBACOnB,OAEXmB,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBrB,OAAOsB,SAASC,QACMA,MAAMC,KAAKJ,cACfK,SAASN,aACnBE,cAAcK,KAAKH,UAGpBF,eAUbnB,YAAeyB,iBACX/C,cAAgBf,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,UAClFY,aAAeD,cAAcd,cAAc,2CAE/Cc,eAAegD,SAAS,QAEtB/C,aAAaoC,OAASU,WACtB9C,aAAaoC,MAAQU,SACrB9C,aAAagD,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS"} \ No newline at end of file +{"version":3,"file":"group.min.js","sources":["../../src/searchwidget/group.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 widget to search groups within the gradebook.\n *\n * @module core_grades/searchwidget/group\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as FocusLockManager from 'core/local/aria/focuslock';\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport $ from 'jquery';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\n\n/**\n * Whether this module is already initialised.\n *\n * @type {boolean}\n */\nlet initialised = false;\n\n/**\n * Our entry point into starting to build the group search widget.\n *\n * It'll eventually, based upon the listeners, open the search widget and allow filtering.\n *\n * @method init\n */\nexport const init = () => {\n if (!initialised && document.querySelector(Selectors.elements.getSearchWidgetSelector('group'))) {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n }\n initialised = true;\n};\n\n/**\n * Register event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('group'));\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('group'));\n const inputElement = menuContainer.querySelector('input[name=\"group\"]');\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n $(menuContainer).on('show.bs.dropdown', async(e) => {\n const courseID = e.relatedTarget.dataset.courseid;\n // Display a loading icon in the dropdown menu container until the body promise is resolved.\n await WidgetBase.showLoader(dropdownMenuContainer);\n\n // If an error occurs while fetching the data, display the error within the dropdown menu.\n const data = await Repository.groupFetch(courseID).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 // Early return if there is no module data.\n if (data === []) {\n return;\n }\n await WidgetBase.init(\n dropdownMenuContainer,\n bodyPromise,\n data.groups,\n searchGroups(),\n null,\n afterSelect\n );\n\n // Lock tab control. It has to be locked because the dropdown's role is dialog.\n FocusLockManager.trapFocus(dropdownMenuContainer);\n });\n\n // Resolvers for passed functions in the dropdown creation.\n bodyPromiseResolver(Templates.render(\n 'core_grades/searchwidget/group/groupsearch_body',\n []\n ));\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n $(menuContainer).on('hide.bs.dropdown', () => {\n FocusLockManager.untrapFocus();\n });\n\n inputElement.addEventListener('change', e => {\n const toggle = menuContainer.querySelector('.dropdown-toggle');\n const courseId = toggle.dataset.courseid;\n const actionUrl = toggle.dataset.actionBaseUrl ?\n new URL(toggle.dataset.actionBaseUrl.replace(/&/g, \"&\")) :\n new URL(location.href);\n actionUrl.searchParams.set('id', courseId);\n actionUrl.searchParams.set('group', e.target.value);\n\n location.href = actionUrl.href;\n\n e.stopPropagation();\n });\n};\n\n/**\n * Define how we want to search and filter groups when the user decides to input a search value.\n *\n * @method searchGroups\n * @returns {function(): function(*, *): (*)}\n */\nconst searchGroups = () => {\n return () => {\n return (groups, searchTerm) => {\n if (searchTerm === '') {\n return groups;\n }\n searchTerm = searchTerm.toLowerCase();\n const searchResults = [];\n groups.forEach((group) => {\n const groupName = group.name.toLowerCase();\n if (groupName.includes(searchTerm)) {\n searchResults.push(group);\n }\n });\n return searchResults;\n };\n };\n};\n\n/**\n * Define the action to be performed when an item is selected by the search widget.\n *\n * @param {String} selected The selected item's value.\n */\nconst afterSelect = (selected) => {\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('group'));\n const inputElement = menuContainer.querySelector('input[name=\"group\"]');\n\n $(menuContainer).dropdown('hide'); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n if (inputElement.value != selected) {\n inputElement.value = selected;\n inputElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n};\n"],"names":["initialised","document","querySelector","Selectors","elements","getSearchWidgetSelector","pendingPromise","Pending","registerListenerEvents","resolve","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","dropdownMenuContainer","getSearchWidgetDropdownSelector","menuContainer","inputElement","on","async","courseID","e","relatedTarget","dataset","courseid","showLoader","data","Repository","groupFetch","catch","errorTemplateData","message","Templates","render","init","groups","searchGroups","afterSelect","FocusLockManager","trapFocus","untrapFocus","addEventListener","toggle","courseId","actionUrl","actionBaseUrl","URL","replace","location","href","searchParams","set","target","value","stopPropagation","searchTerm","toLowerCase","searchResults","forEach","group","name","includes","push","selected","dropdown","dispatchEvent","Event","bubbles"],"mappings":";;;;;;;2ZAoCIA,aAAc,gBASE,SACXA,aAAeC,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,UAAW,OACvFC,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,UAEnBT,aAAc,SAQZQ,uBAAyB,SACvBE,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,6BAC9CC,sBAAwBb,SAASC,cAAcC,UAAUC,SAASW,gCAAgC,UAClGC,cAAgBf,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,UAClFY,aAAeD,cAAcd,cAAc,2CAG/Cc,eAAeE,GAAG,oBAAoBC,MAAAA,UAC9BC,SAAWC,EAAEC,cAAcC,QAAQC,eAEnCZ,WAAWa,WAAWX,6BAGtBY,WAAaC,WAAWC,WAAWR,UAAUS,OAAMV,MAAAA,UAC/CW,kBAAoB,cACNT,EAAEU,SAEtBrB,0BACUsB,UAAUC,OAAO,iCAAkCH,uBAI7DJ,OAAS,WAGPd,WAAWsB,KACbpB,sBACAH,YACAe,KAAKS,OACLC,eACA,KACAC,aAIJC,iBAAiBC,UAAUzB,2BAI/BJ,oBAAoBsB,UAAUC,OAC1B,kDACA,yBAIFjB,eAAeE,GAAG,oBAAoB,KACpCoB,iBAAiBE,iBAGrBvB,aAAawB,iBAAiB,UAAUpB,UAC9BqB,OAAS1B,cAAcd,cAAc,oBACrCyC,SAAWD,OAAOnB,QAAQC,SAC1BoB,UAAYF,OAAOnB,QAAQsB,cAC7B,IAAIC,IAAIJ,OAAOnB,QAAQsB,cAAcE,QAAQ,SAAU,MACvD,IAAID,IAAIE,SAASC,MACrBL,UAAUM,aAAaC,IAAI,KAAMR,UACjCC,UAAUM,aAAaC,IAAI,QAAS9B,EAAE+B,OAAOC,OAE7CL,SAASC,KAAOL,UAAUK,KAE1B5B,EAAEiC,sBAUJlB,aAAe,IACV,IACI,CAACD,OAAQoB,iBACO,KAAfA,kBACOpB,OAEXoB,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBtB,OAAOuB,SAASC,QACMA,MAAMC,KAAKJ,cACfK,SAASN,aACnBE,cAAcK,KAAKH,UAGpBF,eAUbpB,YAAe0B,iBACX/C,cAAgBf,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,UAClFY,aAAeD,cAAcd,cAAc,2CAE/Cc,eAAegD,SAAS,QAEtB/C,aAAaoC,OAASU,WACtB9C,aAAaoC,MAAQU,SACrB9C,aAAagD,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS"} \ No newline at end of file diff --git a/grade/amd/build/searchwidget/repository.min.js b/grade/amd/build/searchwidget/repository.min.js index 0184bc0af4e..d75efe5572b 100644 --- a/grade/amd/build/searchwidget/repository.min.js +++ b/grade/amd/build/searchwidget/repository.min.js @@ -5,6 +5,6 @@ define("core_grades/searchwidget/repository",["exports","core/ajax"],(function(_ * @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.groupFetch=_exports.gradeitemFetch=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.userFetch=(courseid,actionBaseUrl,groupId)=>{const request={methodname:"core_grades_get_enrolled_users_for_search_widget",args:{courseid:courseid,actionbaseurl:actionBaseUrl,groupid:groupId}};return _ajax.default.call([request])[0]};_exports.groupFetch=(courseid,actionBaseUrl)=>{const request={methodname:"core_grades_get_groups_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]}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.userFetch=_exports.groupFetch=_exports.gradeitemFetch=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.userFetch=(courseid,groupId)=>{const request={methodname:"core_grades_get_enrolled_users_for_selector",args:{courseid:courseid,groupid:groupId}};return _ajax.default.call([request])[0]};_exports.groupFetch=courseid=>{const request={methodname:"core_grades_get_groups_for_selector",args:{courseid:courseid}};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 index f1c8e502a86..fb7d40ce5c3 100644 --- a/grade/amd/build/searchwidget/repository.min.js.map +++ b/grade/amd/build/searchwidget/repository.min.js.map @@ -1 +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 * @param {int} groupId ID of the group to fetch the users of.\n * @return {object} jQuery promise\n */\nexport const userFetch = (courseid, actionBaseUrl, groupId) => {\n const request = {\n methodname: 'core_grades_get_enrolled_users_for_search_widget',\n args: {\n courseid: courseid,\n actionbaseurl: actionBaseUrl,\n groupid: groupId,\n },\n };\n return ajax.call([request])[0];\n};\n\n/**\n * Given a course ID, we want to fetch the groups, so we may fetch their users.\n *\n * @method groupFetch\n * @param {int} courseid ID of the course to fetch the users of.\n * @param {string} actionBaseUrl The base URL for the group action.\n * @return {object} jQuery promise\n */\nexport const groupFetch = (courseid, actionBaseUrl) => {\n const request = {\n methodname: 'core_grades_get_groups_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","groupId","request","methodname","args","actionbaseurl","groupid","ajax","call"],"mappings":";;;;;;;0MAkCyB,CAACA,SAAUC,cAAeC,iBACzCC,QAAU,CACZC,WAAY,mDACZC,KAAM,CACFL,SAAUA,SACVM,cAAeL,cACfM,QAASL,iBAGVM,cAAKC,KAAK,CAACN,UAAU,wBAWN,CAACH,SAAUC,uBAC3BE,QAAU,CACZC,WAAY,2CACZC,KAAM,CACFL,SAAUA,SACVM,cAAeL,uBAGhBO,cAAKC,KAAK,CAACN,UAAU,4BAWDH,iBACrBG,QAAU,CACZC,WAAY,2DACZC,KAAM,CACFL,SAAUA,kBAGXQ,cAAKC,KAAK,CAACN,UAAU"} \ No newline at end of file +{"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 {int} groupId ID of the group to fetch the users of.\n * @return {object} jQuery promise\n */\nexport const userFetch = (courseid, groupId) => {\n const request = {\n methodname: 'core_grades_get_enrolled_users_for_selector',\n args: {\n courseid: courseid,\n groupid: groupId,\n },\n };\n return ajax.call([request])[0];\n};\n\n/**\n * Given a course ID, we want to fetch the groups, so we may fetch their users.\n *\n * @method groupFetch\n * @param {int} courseid ID of the course to fetch the users of.\n * @return {object} jQuery promise\n */\nexport const groupFetch = (courseid) => {\n const request = {\n methodname: 'core_grades_get_groups_for_selector',\n args: {\n courseid: courseid,\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","groupId","request","methodname","args","groupid","ajax","call"],"mappings":";;;;;;;0MAiCyB,CAACA,SAAUC,iBAC1BC,QAAU,CACZC,WAAY,8CACZC,KAAM,CACFJ,SAAUA,SACVK,QAASJ,iBAGVK,cAAKC,KAAK,CAACL,UAAU,wBAULF,iBACjBE,QAAU,CACZC,WAAY,sCACZC,KAAM,CACFJ,SAAUA,kBAGXM,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/group.js b/grade/amd/src/searchwidget/group.js index e5d214dc957..a311cfc8525 100644 --- a/grade/amd/src/searchwidget/group.js +++ b/grade/amd/src/searchwidget/group.js @@ -66,12 +66,11 @@ const registerListenerEvents = () => { // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed). $(menuContainer).on('show.bs.dropdown', async(e) => { const courseID = e.relatedTarget.dataset.courseid; - const actionBaseUrl = e.relatedTarget.dataset.actionBaseUrl; // Display a loading icon in the dropdown menu container until the body promise is resolved. await WidgetBase.showLoader(dropdownMenuContainer); // If an error occurs while fetching the data, display the error within the dropdown menu. - const data = await Repository.groupFetch(courseID, actionBaseUrl).catch(async(e) => { + const data = await Repository.groupFetch(courseID).catch(async(e) => { const errorTemplateData = { 'errormessage': e.message }; diff --git a/grade/amd/src/searchwidget/repository.js b/grade/amd/src/searchwidget/repository.js index 5346967b0b9..420513cac8a 100644 --- a/grade/amd/src/searchwidget/repository.js +++ b/grade/amd/src/searchwidget/repository.js @@ -28,16 +28,14 @@ import ajax from 'core/ajax'; * * @method userFetch * @param {int} courseid ID of the course to fetch the users of. - * @param {string} actionBaseUrl The base URL for the user option. * @param {int} groupId ID of the group to fetch the users of. * @return {object} jQuery promise */ -export const userFetch = (courseid, actionBaseUrl, groupId) => { +export const userFetch = (courseid, groupId) => { const request = { - methodname: 'core_grades_get_enrolled_users_for_search_widget', + methodname: 'core_grades_get_enrolled_users_for_selector', args: { courseid: courseid, - actionbaseurl: actionBaseUrl, groupid: groupId, }, }; @@ -49,15 +47,13 @@ export const userFetch = (courseid, actionBaseUrl, groupId) => { * * @method groupFetch * @param {int} courseid ID of the course to fetch the users of. - * @param {string} actionBaseUrl The base URL for the group action. * @return {object} jQuery promise */ -export const groupFetch = (courseid, actionBaseUrl) => { +export const groupFetch = (courseid) => { const request = { - methodname: 'core_grades_get_groups_for_search_widget', + methodname: 'core_grades_get_groups_for_selector', args: { courseid: courseid, - actionbaseurl: actionBaseUrl, }, }; 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 index e343793f0e1..09faf68770d 100644 --- a/grade/classes/external/get_enrolled_users_for_search_widget.php +++ b/grade/classes/external/get_enrolled_users_for_search_widget.php @@ -38,6 +38,7 @@ require_once($CFG->dirroot.'/grade/lib.php'); * @copyright 2022 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 4.1 + * @deprecated */ class get_enrolled_users_for_search_widget extends external_api { @@ -45,6 +46,7 @@ class get_enrolled_users_for_search_widget extends external_api { * Returns description of method parameters. * * @return external_function_parameters + * @deprecated since 4.2 */ public static function execute_parameters(): external_function_parameters { return new external_function_parameters ( @@ -67,6 +69,7 @@ class get_enrolled_users_for_search_widget extends external_api { * @throws invalid_parameter_exception * @throws moodle_exception * @throws restricted_context_exception + * @deprecated since 4.2 */ public static function execute(int $courseid, string $actionbaseurl, ?int $groupid = 0): array { global $DB, $PAGE; @@ -108,7 +111,7 @@ class get_enrolled_users_for_search_widget extends external_api { $userpicture->size = 1; $user->profileimage = $userpicture->get_url($PAGE)->out(false); $user->email = $guiuser->email; - $user->active = false; // @TODO MDL-76246 + $user->active = false; $users[] = $user; } @@ -124,6 +127,7 @@ class get_enrolled_users_for_search_widget extends external_api { * Returns description of method result value. * * @return external_single_structure + * @deprecated since 4.2 */ public static function execute_returns(): external_single_structure { return new external_single_structure([ @@ -159,4 +163,12 @@ class get_enrolled_users_for_search_widget extends external_api { ]; return new external_single_structure($userfields); } + + /** + * Mark the function as deprecated. + * @return bool + */ + public static function execute_is_deprecated() { + return true; + } } diff --git a/grade/classes/external/get_enrolled_users_for_selector.php b/grade/classes/external/get_enrolled_users_for_selector.php new file mode 100644 index 00000000000..bc5001f6f11 --- /dev/null +++ b/grade/classes/external/get_enrolled_users_for_selector.php @@ -0,0 +1,150 @@ +. + +namespace core_grades\external; + +use core_external\external_api; +use core_external\external_description; +use core_external\external_function_parameters; +use core_external\external_multiple_structure; +use core_external\external_single_structure; +use core_external\external_value; +use core_external\external_warnings; +use core_external\restricted_context_exception; +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.2 + */ +class get_enrolled_users_for_selector 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), + '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 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, ?int $groupid = 0): array { + global $DB, $PAGE; + + $params = self::validate_parameters( + self::execute_parameters(), + [ + 'courseid' => $courseid, + '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; + $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 + ), + '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/classes/external/get_groups_for_search_widget.php b/grade/classes/external/get_groups_for_search_widget.php index 201bc30b562..9486271d356 100644 --- a/grade/classes/external/get_groups_for_search_widget.php +++ b/grade/classes/external/get_groups_for_search_widget.php @@ -32,6 +32,7 @@ use core_external\external_warnings; * @copyright 2022 Mathew May * @category external * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @deprecated */ class get_groups_for_search_widget extends external_api { @@ -39,6 +40,7 @@ class get_groups_for_search_widget extends external_api { * Returns description of method parameters. * * @return external_function_parameters + * @deprecated since 4.2 */ public static function execute_parameters(): external_function_parameters { return new external_function_parameters ( @@ -59,6 +61,7 @@ class get_groups_for_search_widget extends external_api { * @throws invalid_parameter_exception * @throws moodle_exception * @throws restricted_context_exception + * @deprecated since 4.2 */ protected static function execute(int $courseid, string $actionbaseurl): array { global $DB, $USER, $COURSE; @@ -111,7 +114,7 @@ class get_groups_for_search_widget extends external_api { 'id' => $group->id, 'name' => format_string($group->name, true, ['context' => $context]), 'url' => $url->out(false), - 'active' => false // @TODO MDL-76246 + 'active' => false ]; }, $groupsmenu); } @@ -126,6 +129,7 @@ class get_groups_for_search_widget extends external_api { * Returns description of what the group search for the widget should return. * * @return external_single_structure + * @deprecated since 4.2 */ public static function execute_returns(): external_single_structure { return new external_single_structure([ @@ -148,4 +152,12 @@ class get_groups_for_search_widget extends external_api { ]; return new external_single_structure($groupfields); } + + /** + * Mark the function as deprecated. + * @return bool + */ + public static function execute_is_deprecated() { + return true; + } } diff --git a/grade/classes/external/get_groups_for_selector.php b/grade/classes/external/get_groups_for_selector.php new file mode 100644 index 00000000000..f3fcc4c6011 --- /dev/null +++ b/grade/classes/external/get_groups_for_selector.php @@ -0,0 +1,140 @@ +. + +namespace core_grades\external; + +use context_course; +use core_external\external_api; +use core_external\external_description; +use core_external\external_function_parameters; +use core_external\external_multiple_structure; +use core_external\external_single_structure; +use core_external\external_value; +use core_external\external_warnings; + +/** + * External group report API implementation + * + * @package core_grades + * @copyright 2022 Mathew May + * @category external + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class get_groups_for_selector 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), + ] + ); + } + + /** + * Given a course ID find the existing user groups and map some fields to the returned array of group objects. + * + * @param int $courseid + * @return array Groups and warnings to pass back to the calling widget. + * @throws coding_exception + * @throws invalid_parameter_exception + * @throws moodle_exception + * @throws restricted_context_exception + */ + protected static function execute(int $courseid): array { + global $DB, $USER; + + $params = self::validate_parameters( + self::execute_parameters(), + [ + 'courseid' => $courseid, + ] + ); + + $warnings = []; + $context = context_course::instance($params['courseid']); + parent::validate_context($context); + + $mappedgroups = []; + $course = $DB->get_record('course', ['id' => $params['courseid']]); + // Initialise the grade tracking object. + if ($groupmode = $course->groupmode) { + $aag = has_capability('moodle/site:accessallgroups', $context); + + $usergroups = []; + $groupuserid = 0; + if ($groupmode == VISIBLEGROUPS || $aag) { + // Get user's own groups and put to the top. + $usergroups = groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid); + } else { + $groupuserid = $USER->id; + } + $allowedgroups = groups_get_all_groups($course->id, $groupuserid, $course->defaultgroupingid); + + $allgroups = array_merge($allowedgroups, $usergroups); + // Filter out any duplicate groups. + $groupsmenu = array_intersect_key($allgroups, array_unique(array_column($allgroups, 'name'))); + + if (!$allowedgroups || $groupmode == VISIBLEGROUPS || $aag) { + array_unshift($groupsmenu, (object) [ + 'id' => 0, + 'name' => get_string('allparticipants'), + ]); + } + + $mappedgroups = array_map(function($group) use ($context) { + return (object) [ + 'id' => $group->id, + 'name' => format_string($group->name, true, ['context' => $context]) + ]; + }, $groupsmenu); + } + + return [ + 'groups' => $mappedgroups, + 'warnings' => $warnings, + ]; + } + + /** + * Returns description of what the group search for the widget should return. + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'groups' => new external_multiple_structure(self::group_description()), + 'warnings' => new external_warnings(), + ]); + } + + /** + * Create group return value description. + * + * @return external_description + */ + public static function group_description(): external_description { + $groupfields = [ + 'id' => new external_value(PARAM_ALPHANUM, 'An ID for the group', VALUE_REQUIRED), + 'name' => new external_value(PARAM_TEXT, 'The full name of the group', VALUE_REQUIRED) + ]; + return new external_single_structure($groupfields); + } +} diff --git a/grade/report/singleview/amd/build/user.min.js b/grade/report/singleview/amd/build/user.min.js index ef7a5ff46c7..3a34290b375 100644 --- a/grade/report/singleview/amd/build/user.min.js +++ b/grade/report/singleview/amd/build/user.min.js @@ -5,6 +5,6 @@ define("gradereport_singleview/user",["exports","core/local/aria/focuslock","cor * @module gradereport_singleview/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,FocusLockManager=_interopRequireWildcard(FocusLockManager),_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);_exports.init=()=>{if(document.querySelector(Selectors.elements.getSearchWidgetSelector("user"))){const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()}};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("user")),menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("user")),inputElement=menuContainer.querySelector('input[name="userid"]');(0,_jquery.default)(menuContainer).on("show.bs.dropdown",(async e=>{const courseId=e.relatedTarget.dataset.courseid,groupId=e.relatedTarget.dataset.groupid,actionBaseUrl=_url.default.relativeUrl("/grade/report/singleview/index.php",{item:"user"},!1);await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.userFetch(courseId,actionBaseUrl,groupId).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&(await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.users,searchUsers(),null,afterSelect),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",[])),FocusLockManager.trapFocus(dropdownMenuContainer))})),(0,_jquery.default)(menuContainer).on("hide.bs.dropdown",(()=>{FocusLockManager.untrapFocus()})),inputElement.addEventListener("change",(e=>{const courseId=menuContainer.querySelector(".dropdown-toggle").dataset.courseid,actionUrl=_url.default.relativeUrl("/grade/report/singleview/index.php",{id:courseId,item:"user",userid:e.target.value},!1);location.href=actionUrl,e.stopPropagation()}))},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},afterSelect=selected=>{const menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("user")),inputElement=menuContainer.querySelector('input[name="userid"]');(0,_jquery.default)(menuContainer).dropdown("hide"),inputElement.value!=selected&&(inputElement.value=selected,inputElement.dispatchEvent(new Event("change",{bubbles:!0})))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,FocusLockManager=_interopRequireWildcard(FocusLockManager),_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);_exports.init=()=>{if(document.querySelector(Selectors.elements.getSearchWidgetSelector("user"))){const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()}};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("user")),menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("user")),inputElement=menuContainer.querySelector('input[name="userid"]');(0,_jquery.default)(menuContainer).on("show.bs.dropdown",(async e=>{const courseId=e.relatedTarget.dataset.courseid,groupId=e.relatedTarget.dataset.groupid;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.userFetch(courseId,groupId).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&(await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.users,searchUsers(),null,afterSelect),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",[])),FocusLockManager.trapFocus(dropdownMenuContainer))})),(0,_jquery.default)(menuContainer).on("hide.bs.dropdown",(()=>{FocusLockManager.untrapFocus()})),inputElement.addEventListener("change",(e=>{const courseId=menuContainer.querySelector(".dropdown-toggle").dataset.courseid,actionUrl=_url.default.relativeUrl("/grade/report/singleview/index.php",{id:courseId,item:"user",userid:e.target.value},!1);location.href=actionUrl,e.stopPropagation()}))},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},afterSelect=selected=>{const menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("user")),inputElement=menuContainer.querySelector('input[name="userid"]');(0,_jquery.default)(menuContainer).dropdown("hide"),inputElement.value!=selected&&(inputElement.value=selected,inputElement.dispatchEvent(new Event("change",{bubbles:!0})))}})); //# sourceMappingURL=user.min.js.map \ No newline at end of file diff --git a/grade/report/singleview/amd/build/user.min.js.map b/grade/report/singleview/amd/build/user.min.js.map index ba15da5b91d..c51a8bf890c 100644 --- a/grade/report/singleview/amd/build/user.min.js.map +++ b/grade/report/singleview/amd/build/user.min.js.map @@ -1 +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_singleview/user\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as FocusLockManager from 'core/local/aria/focuslock';\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport Url from 'core/url';\nimport $ from 'jquery';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\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 if (document.querySelector(Selectors.elements.getSearchWidgetSelector('user'))) {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n }\n};\n\n/**\n * Register user search widget related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('user'));\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('user'));\n const inputElement = menuContainer.querySelector('input[name=\"userid\"]');\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n $(menuContainer).on('show.bs.dropdown', async(e) => {\n const courseId = e.relatedTarget.dataset.courseid;\n const groupId = e.relatedTarget.dataset.groupid;\n const actionBaseUrl = Url.relativeUrl('/grade/report/singleview/index.php', {item: 'user'}, false);\n // Display a loading icon in the dropdown menu container until the body promise is resolved.\n await WidgetBase.showLoader(dropdownMenuContainer);\n\n // If an error occurs while fetching the data, display the error within the dropdown menu.\n const data = await Repository.userFetch(courseId, actionBaseUrl, groupId).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 await WidgetBase.init(\n dropdownMenuContainer,\n bodyPromise,\n data.users,\n searchUsers(),\n null,\n afterSelect\n );\n\n // Resolvers for passed functions in the dropdown menu creation.\n bodyPromiseResolver(Templates.render('core_grades/searchwidget/user/usersearch_body', []));\n\n // Lock tab control. It has to be locked because the dropdown's role is dialog.\n FocusLockManager.trapFocus(dropdownMenuContainer);\n });\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n $(menuContainer).on('hide.bs.dropdown', () => {\n FocusLockManager.untrapFocus();\n });\n\n inputElement.addEventListener('change', e => {\n const toggle = menuContainer.querySelector('.dropdown-toggle');\n const courseId = toggle.dataset.courseid;\n const actionUrl = Url.relativeUrl(\n '/grade/report/singleview/index.php',\n {\n id: courseId,\n item: 'user',\n userid: e.target.value\n },\n false\n );\n location.href = actionUrl;\n\n e.stopPropagation();\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 searchUsers\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\n/**\n * Define the action to be performed when an item is selected by the search widget.\n *\n * @param {String} selected The selected item's value.\n */\nconst afterSelect = (selected) => {\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('user'));\n const inputElement = menuContainer.querySelector('input[name=\"userid\"]');\n\n $(menuContainer).dropdown('hide'); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n if (inputElement.value != selected) {\n inputElement.value = selected;\n inputElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n};\n"],"names":["document","querySelector","Selectors","elements","getSearchWidgetSelector","pendingPromise","Pending","registerListenerEvents","resolve","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","dropdownMenuContainer","getSearchWidgetDropdownSelector","menuContainer","inputElement","on","async","courseId","e","relatedTarget","dataset","courseid","groupId","groupid","actionBaseUrl","Url","relativeUrl","item","showLoader","data","Repository","userFetch","catch","errorTemplateData","message","Templates","render","init","users","searchUsers","afterSelect","FocusLockManager","trapFocus","untrapFocus","addEventListener","actionUrl","id","userid","target","value","location","href","stopPropagation","searchTerm","toLowerCase","searchResults","forEach","user","fullname","includes","push","selected","dropdown","dispatchEvent","Event","bubbles"],"mappings":";;;;;;;ucAsCoB,QACZA,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,SAAU,OACtEC,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,kBASjBD,uBAAyB,SACvBE,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,6BAC9CC,sBAAwBb,SAASC,cAAcC,UAAUC,SAASW,gCAAgC,SAClGC,cAAgBf,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,SAClFY,aAAeD,cAAcd,cAAc,4CAG/Cc,eAAeE,GAAG,oBAAoBC,MAAAA,UAC9BC,SAAWC,EAAEC,cAAcC,QAAQC,SACnCC,QAAUJ,EAAEC,cAAcC,QAAQG,QAClCC,cAAgBC,aAAIC,YAAY,qCAAsC,CAACC,KAAM,SAAS,SAEtFlB,WAAWmB,WAAWjB,6BAGtBkB,WAAaC,WAAWC,UAAUd,SAAUO,cAAeF,SAASU,OAAMhB,MAAAA,UACtEiB,kBAAoB,cACNf,EAAEgB,SAEtB3B,0BACU4B,UAAUC,OAAO,iCAAkCH,uBAK7DJ,OAAS,WAIPpB,WAAW4B,KACb1B,sBACAH,YACAqB,KAAKS,MACLC,cACA,KACAC,aAIJjC,oBAAoB4B,UAAUC,OAAO,gDAAiD,KAGtFK,iBAAiBC,UAAU/B,+CAI7BE,eAAeE,GAAG,oBAAoB,KACpC0B,iBAAiBE,iBAGrB7B,aAAa8B,iBAAiB,UAAU1B,UAE9BD,SADSJ,cAAcd,cAAc,oBACnBqB,QAAQC,SAC1BwB,UAAYpB,aAAIC,YAClB,qCACA,CACIoB,GAAI7B,SACJU,KAAM,OACNoB,OAAQ7B,EAAE8B,OAAOC,QAErB,GAEJC,SAASC,KAAON,UAEhB3B,EAAEkC,sBAUJb,YAAc,IACT,IACI,CAACD,MAAOe,iBACQ,KAAfA,kBACOf,MAEXe,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBjB,MAAMkB,SAASC,OACMA,KAAKC,SAASJ,cAClBK,SAASN,aAClBE,cAAcK,KAAKH,SAGpBF,eAUbf,YAAeqB,iBACXhD,cAAgBf,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,SAClFY,aAAeD,cAAcd,cAAc,4CAE/Cc,eAAeiD,SAAS,QAEtBhD,aAAamC,OAASY,WACtB/C,aAAamC,MAAQY,SACrB/C,aAAaiD,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS"} \ No newline at end of file +{"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_singleview/user\n * @copyright 2022 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as FocusLockManager from 'core/local/aria/focuslock';\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\nimport * as Repository from 'core_grades/searchwidget/repository';\nimport * as WidgetBase from 'core_grades/searchwidget/basewidget';\nimport Url from 'core/url';\nimport $ from 'jquery';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\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 if (document.querySelector(Selectors.elements.getSearchWidgetSelector('user'))) {\n const pendingPromise = new Pending();\n registerListenerEvents();\n pendingPromise.resolve();\n }\n};\n\n/**\n * Register user search widget related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('user'));\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('user'));\n const inputElement = menuContainer.querySelector('input[name=\"userid\"]');\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n $(menuContainer).on('show.bs.dropdown', async(e) => {\n const courseId = e.relatedTarget.dataset.courseid;\n const groupId = e.relatedTarget.dataset.groupid;\n // Display a loading icon in the dropdown menu container until the body promise is resolved.\n await WidgetBase.showLoader(dropdownMenuContainer);\n\n // If an error occurs while fetching the data, display the error within the dropdown menu.\n const data = await Repository.userFetch(courseId, groupId).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 await WidgetBase.init(\n dropdownMenuContainer,\n bodyPromise,\n data.users,\n searchUsers(),\n null,\n afterSelect\n );\n\n // Resolvers for passed functions in the dropdown menu creation.\n bodyPromiseResolver(Templates.render('core_grades/searchwidget/user/usersearch_body', []));\n\n // Lock tab control. It has to be locked because the dropdown's role is dialog.\n FocusLockManager.trapFocus(dropdownMenuContainer);\n });\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n $(menuContainer).on('hide.bs.dropdown', () => {\n FocusLockManager.untrapFocus();\n });\n\n inputElement.addEventListener('change', e => {\n const toggle = menuContainer.querySelector('.dropdown-toggle');\n const courseId = toggle.dataset.courseid;\n const actionUrl = Url.relativeUrl(\n '/grade/report/singleview/index.php',\n {\n id: courseId,\n item: 'user',\n userid: e.target.value\n },\n false\n );\n location.href = actionUrl;\n\n e.stopPropagation();\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 searchUsers\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\n/**\n * Define the action to be performed when an item is selected by the search widget.\n *\n * @param {String} selected The selected item's value.\n */\nconst afterSelect = (selected) => {\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('user'));\n const inputElement = menuContainer.querySelector('input[name=\"userid\"]');\n\n $(menuContainer).dropdown('hide'); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n if (inputElement.value != selected) {\n inputElement.value = selected;\n inputElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n};\n"],"names":["document","querySelector","Selectors","elements","getSearchWidgetSelector","pendingPromise","Pending","registerListenerEvents","resolve","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","dropdownMenuContainer","getSearchWidgetDropdownSelector","menuContainer","inputElement","on","async","courseId","e","relatedTarget","dataset","courseid","groupId","groupid","showLoader","data","Repository","userFetch","catch","errorTemplateData","message","Templates","render","init","users","searchUsers","afterSelect","FocusLockManager","trapFocus","untrapFocus","addEventListener","actionUrl","Url","relativeUrl","id","item","userid","target","value","location","href","stopPropagation","searchTerm","toLowerCase","searchResults","forEach","user","fullname","includes","push","selected","dropdown","dispatchEvent","Event","bubbles"],"mappings":";;;;;;;ucAsCoB,QACZA,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,SAAU,OACtEC,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,kBASjBD,uBAAyB,SACvBE,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,6BAC9CC,sBAAwBb,SAASC,cAAcC,UAAUC,SAASW,gCAAgC,SAClGC,cAAgBf,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,SAClFY,aAAeD,cAAcd,cAAc,4CAG/Cc,eAAeE,GAAG,oBAAoBC,MAAAA,UAC9BC,SAAWC,EAAEC,cAAcC,QAAQC,SACnCC,QAAUJ,EAAEC,cAAcC,QAAQG,cAElCd,WAAWe,WAAWb,6BAGtBc,WAAaC,WAAWC,UAAUV,SAAUK,SAASM,OAAMZ,MAAAA,UACvDa,kBAAoB,cACNX,EAAEY,SAEtBvB,0BACUwB,UAAUC,OAAO,iCAAkCH,uBAK7DJ,OAAS,WAIPhB,WAAWwB,KACbtB,sBACAH,YACAiB,KAAKS,MACLC,cACA,KACAC,aAIJ7B,oBAAoBwB,UAAUC,OAAO,gDAAiD,KAGtFK,iBAAiBC,UAAU3B,+CAI7BE,eAAeE,GAAG,oBAAoB,KACpCsB,iBAAiBE,iBAGrBzB,aAAa0B,iBAAiB,UAAUtB,UAE9BD,SADSJ,cAAcd,cAAc,oBACnBqB,QAAQC,SAC1BoB,UAAYC,aAAIC,YAClB,qCACA,CACIC,GAAI3B,SACJ4B,KAAM,OACNC,OAAQ5B,EAAE6B,OAAOC,QAErB,GAEJC,SAASC,KAAOT,UAEhBvB,EAAEiC,sBAUJhB,YAAc,IACT,IACI,CAACD,MAAOkB,iBACQ,KAAfA,kBACOlB,MAEXkB,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBpB,MAAMqB,SAASC,OACMA,KAAKC,SAASJ,cAClBK,SAASN,aAClBE,cAAcK,KAAKH,SAGpBF,eAUblB,YAAewB,iBACX/C,cAAgBf,SAASC,cAAcC,UAAUC,SAASC,wBAAwB,SAClFY,aAAeD,cAAcd,cAAc,4CAE/Cc,eAAegD,SAAS,QAEtB/C,aAAakC,OAASY,WACtB9C,aAAakC,MAAQY,SACrB9C,aAAagD,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS"} \ No newline at end of file diff --git a/grade/report/singleview/amd/src/user.js b/grade/report/singleview/amd/src/user.js index 730fe44c045..4e6518501c5 100644 --- a/grade/report/singleview/amd/src/user.js +++ b/grade/report/singleview/amd/src/user.js @@ -59,12 +59,11 @@ const registerListenerEvents = () => { $(menuContainer).on('show.bs.dropdown', async(e) => { const courseId = e.relatedTarget.dataset.courseid; const groupId = e.relatedTarget.dataset.groupid; - const actionBaseUrl = Url.relativeUrl('/grade/report/singleview/index.php', {item: 'user'}, false); // Display a loading icon in the dropdown menu container until the body promise is resolved. await WidgetBase.showLoader(dropdownMenuContainer); // If an error occurs while fetching the data, display the error within the dropdown menu. - const data = await Repository.userFetch(courseId, actionBaseUrl, groupId).catch(async(e) => { + const data = await Repository.userFetch(courseId, groupId).catch(async(e) => { const errorTemplateData = { 'errormessage': e.message }; diff --git a/grade/report/singleview/classes/external/singleview.php b/grade/report/singleview/classes/external/singleview.php index 8d9189548af..9641a5ca825 100644 --- a/grade/report/singleview/classes/external/singleview.php +++ b/grade/report/singleview/classes/external/singleview.php @@ -23,7 +23,6 @@ use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; use core_external\external_warnings; -use moodle_url; use grade_tree; defined('MOODLE_INTERNAL') || die; @@ -82,15 +81,7 @@ class singleview extends core_course_external { $gradeitems = array_map(function ($gradeitem) use ($PAGE, $USER, $params) { $item = new \stdClass(); $item->id = $gradeitem->id; - $url = new moodle_url('/grade/report/singleview/index.php', [ - 'id' => $params['courseid'], - 'itemid' => $gradeitem->id, - 'item' => 'grade' - ] - ); $item->name = $gradeitem->get_name(true); - $item->url = $url->out(false); - $item->active = false; // @TODO MDL-76246 return $item; }, $gradeableitems); @@ -112,13 +103,7 @@ class singleview extends core_course_external { 'gradeitems' => new external_multiple_structure( new external_single_structure([ 'id' => new external_value(PARAM_INT, 'ID of the grade item', VALUE_OPTIONAL), - 'url' => new external_value( - PARAM_URL, - 'The link to the grade report', - VALUE_OPTIONAL - ), - 'name' => new external_value(PARAM_TEXT, 'The full name of the grade item', VALUE_OPTIONAL), - 'active' => new external_value(PARAM_BOOL, 'Are we currently on this item?', VALUE_REQUIRED) + 'name' => new external_value(PARAM_TEXT, 'The full name of the grade item', VALUE_OPTIONAL) ]) ), 'warnings' => new external_warnings(), diff --git a/grade/report/user/amd/build/user.min.js b/grade/report/user/amd/build/user.min.js index 6e21018774c..02979875cc0 100644 --- a/grade/report/user/amd/build/user.min.js +++ b/grade/report/user/amd/build/user.min.js @@ -5,6 +5,6 @@ define("gradereport_user/user",["exports","core/local/aria/focuslock","core/pend * @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,FocusLockManager=_interopRequireWildcard(FocusLockManager),_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("user")),menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("user")),inputElement=menuContainer.querySelector('input[name="userid"]');(0,_jquery.default)(menuContainer).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,groupId=e.relatedTarget.dataset.groupid,actionBaseUrl=_url.default.relativeUrl("/grade/report/user/index.php",{},!1);await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.userFetch(courseID,actionBaseUrl,groupId).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("gradereport_user/all_users_item",{id:0,name:allUsersOptionName,url:_url.default.relativeUrl("/grade/report/user/index.php",{id:courseID,userid:0},!1)});await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.users,searchUsers(),allUsersOption,afterSelect),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",{displayunsearchablecontent:!0})),FocusLockManager.trapFocus(dropdownMenuContainer)})),(0,_jquery.default)(menuContainer).on("hide.bs.dropdown",(()=>{FocusLockManager.untrapFocus()})),inputElement.addEventListener("change",(e=>{const courseID=menuContainer.querySelector(".dropdown-toggle").dataset.courseid,actionUrl=_url.default.relativeUrl("/grade/report/user/index.php",{id:courseID,userid:e.target.value},!1);location.href=actionUrl,e.stopPropagation()}))},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},afterSelect=selected=>{const menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("user")),inputElement=menuContainer.querySelector('input[name="userid"]');(0,_jquery.default)(menuContainer).dropdown("hide"),inputElement.value!=selected&&(inputElement.value=selected,inputElement.dispatchEvent(new Event("change",{bubbles:!0})))}})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,FocusLockManager=_interopRequireWildcard(FocusLockManager),_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_url=_interopRequireDefault(_url),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("user")),menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("user")),inputElement=menuContainer.querySelector('input[name="userid"]');(0,_jquery.default)(menuContainer).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,groupId=e.relatedTarget.dataset.groupid;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.userFetch(courseID,groupId).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("gradereport_user/all_users_item",{id:0,name:allUsersOptionName,url:_url.default.relativeUrl("/grade/report/user/index.php",{id:courseID,userid:0},!1)});await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.users,searchUsers(),allUsersOption,afterSelect),bodyPromiseResolver(Templates.render("core_grades/searchwidget/user/usersearch_body",{displayunsearchablecontent:!0})),FocusLockManager.trapFocus(dropdownMenuContainer)})),(0,_jquery.default)(menuContainer).on("hide.bs.dropdown",(()=>{FocusLockManager.untrapFocus()})),inputElement.addEventListener("change",(e=>{const courseID=menuContainer.querySelector(".dropdown-toggle").dataset.courseid,actionUrl=_url.default.relativeUrl("/grade/report/user/index.php",{id:courseID,userid:e.target.value},!1);location.href=actionUrl,e.stopPropagation()}))},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},afterSelect=selected=>{const menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("user")),inputElement=menuContainer.querySelector('input[name="userid"]');(0,_jquery.default)(menuContainer).dropdown("hide"),inputElement.value!=selected&&(inputElement.value=selected,inputElement.dispatchEvent(new Event("change",{bubbles:!0})))}})); //# 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 index 1ed3ee555db..df2bbef282b 100644 --- a/grade/report/user/amd/build/user.min.js.map +++ b/grade/report/user/amd/build/user.min.js.map @@ -1 +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 widget 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 * as FocusLockManager from 'core/local/aria/focuslock';\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\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';\nimport $ from 'jquery';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\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 let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('user'));\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('user'));\n const inputElement = menuContainer.querySelector('input[name=\"userid\"]');\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n $(menuContainer).on('show.bs.dropdown', async(e) => {\n const courseID = e.relatedTarget.dataset.courseid;\n const groupId = e.relatedTarget.dataset.groupid;\n const actionBaseUrl = Url.relativeUrl('/grade/report/user/index.php', {}, false);\n // Display a loading icon in the dropdown menu container until the body promise is resolved.\n await WidgetBase.showLoader(dropdownMenuContainer);\n\n // If an error occurs while fetching the data, display the error within the dropdown menu.\n const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).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('gradereport_user/all_users_item', {\n id: 0,\n name: allUsersOptionName,\n url: Url.relativeUrl('/grade/report/user/index.php', {id: courseID, userid: 0}, false),\n });\n\n await WidgetBase.init(\n dropdownMenuContainer,\n bodyPromise,\n data.users,\n searchUsers(),\n allUsersOption,\n afterSelect\n );\n\n // Resolvers for passed functions in the dropdown menu creation.\n bodyPromiseResolver(Templates.render(\n 'core_grades/searchwidget/user/usersearch_body', {displayunsearchablecontent: true}\n ));\n\n // Lock tab control. It has to be locked because the dropdown's role is dialog.\n FocusLockManager.trapFocus(dropdownMenuContainer);\n });\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n $(menuContainer).on('hide.bs.dropdown', () => {\n FocusLockManager.untrapFocus();\n });\n\n inputElement.addEventListener('change', e => {\n const toggle = menuContainer.querySelector('.dropdown-toggle');\n const courseID = toggle.dataset.courseid;\n const actionUrl = Url.relativeUrl('/grade/report/user/index.php', {id: courseID, userid: e.target.value}, false);\n location.href = actionUrl;\n\n e.stopPropagation();\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 searchUsers\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\n/**\n * Define the action to be performed when an item is selected by the search widget.\n *\n * @param {String} selected The selected item's value.\n */\nconst afterSelect = (selected) => {\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('user'));\n const inputElement = menuContainer.querySelector('input[name=\"userid\"]');\n\n $(menuContainer).dropdown('hide'); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n if (inputElement.value != selected) {\n inputElement.value = selected;\n inputElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n};\n"],"names":["pendingPromise","Pending","registerListenerEvents","resolve","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","dropdownMenuContainer","document","querySelector","Selectors","elements","getSearchWidgetDropdownSelector","menuContainer","getSearchWidgetSelector","inputElement","on","async","courseID","e","relatedTarget","dataset","courseid","groupId","groupid","actionBaseUrl","Url","relativeUrl","showLoader","data","Repository","userFetch","catch","errorTemplateData","message","Templates","render","allUsersOptionName","users","length","allUsersOption","id","name","url","userid","init","searchUsers","afterSelect","displayunsearchablecontent","FocusLockManager","trapFocus","untrapFocus","addEventListener","actionUrl","target","value","location","href","stopPropagation","searchTerm","toLowerCase","searchResults","forEach","user","fullname","includes","push","selected","dropdown","dispatchEvent","Event","bubbles"],"mappings":";;;;;;;ucAuCoB,WACVA,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,iBAQbD,uBAAyB,SACvBE,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,6BAC9CC,sBAAwBC,SAASC,cAAcC,UAAUC,SAASC,gCAAgC,SAClGC,cAAgBL,SAASC,cAAcC,UAAUC,SAASG,wBAAwB,SAClFC,aAAeF,cAAcJ,cAAc,4CAG/CI,eAAeG,GAAG,oBAAoBC,MAAAA,UAC9BC,SAAWC,EAAEC,cAAcC,QAAQC,SACnCC,QAAUJ,EAAEC,cAAcC,QAAQG,QAClCC,cAAgBC,aAAIC,YAAY,+BAAgC,IAAI,SAEpEtB,WAAWuB,WAAWrB,6BAGtBsB,WAAaC,WAAWC,UAAUb,SAAUO,cAAeF,SAASS,OAAMf,MAAAA,UACtEgB,kBAAoB,cACNd,EAAEe,SAEtB/B,0BACUgC,UAAUC,OAAO,iCAAkCH,0BAK7DJ,OAAS,gBAKPQ,yBAA2B,mBAAU,cAAe,mBAAoBR,KAAKS,MAAMC,QACnFC,qBAAuBL,UAAUC,OAAO,kCAAmC,CAC7EK,GAAI,EACJC,KAAML,mBACNM,IAAKjB,aAAIC,YAAY,+BAAgC,CAACc,GAAIvB,SAAU0B,OAAQ,IAAI,WAG9EvC,WAAWwC,KACbtC,sBACAH,YACAyB,KAAKS,MACLQ,cACAN,eACAO,aAIJ5C,oBAAoBgC,UAAUC,OAC1B,gDAAiD,CAACY,4BAA4B,KAIlFC,iBAAiBC,UAAU3C,8CAI7BM,eAAeG,GAAG,oBAAoB,KACpCiC,iBAAiBE,iBAGrBpC,aAAaqC,iBAAiB,UAAUjC,UAE9BD,SADSL,cAAcJ,cAAc,oBACnBY,QAAQC,SAC1B+B,UAAY3B,aAAIC,YAAY,+BAAgC,CAACc,GAAIvB,SAAU0B,OAAQzB,EAAEmC,OAAOC,QAAQ,GAC1GC,SAASC,KAAOJ,UAEhBlC,EAAEuC,sBAUJZ,YAAc,IACT,IACI,CAACR,MAAOqB,iBACQ,KAAfA,kBACOrB,MAEXqB,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBvB,MAAMwB,SAASC,OACMA,KAAKC,SAASJ,cAClBK,SAASN,aAClBE,cAAcK,KAAKH,SAGpBF,eAUbd,YAAeoB,iBACXtD,cAAgBL,SAASC,cAAcC,UAAUC,SAASG,wBAAwB,SAClFC,aAAeF,cAAcJ,cAAc,4CAE/CI,eAAeuD,SAAS,QAEtBrD,aAAawC,OAASY,WACtBpD,aAAawC,MAAQY,SACrBpD,aAAasD,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS"} \ No newline at end of file +{"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 widget 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 * as FocusLockManager from 'core/local/aria/focuslock';\nimport Pending from 'core/pending';\nimport * as Templates from 'core/templates';\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';\nimport $ from 'jquery';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\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 let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();\n const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('user'));\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('user'));\n const inputElement = menuContainer.querySelector('input[name=\"userid\"]');\n\n // Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).\n $(menuContainer).on('show.bs.dropdown', async(e) => {\n const courseID = e.relatedTarget.dataset.courseid;\n const groupId = e.relatedTarget.dataset.groupid;\n // Display a loading icon in the dropdown menu container until the body promise is resolved.\n await WidgetBase.showLoader(dropdownMenuContainer);\n\n // If an error occurs while fetching the data, display the error within the dropdown menu.\n const data = await Repository.userFetch(courseID, groupId).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('gradereport_user/all_users_item', {\n id: 0,\n name: allUsersOptionName,\n url: Url.relativeUrl('/grade/report/user/index.php', {id: courseID, userid: 0}, false),\n });\n\n await WidgetBase.init(\n dropdownMenuContainer,\n bodyPromise,\n data.users,\n searchUsers(),\n allUsersOption,\n afterSelect\n );\n\n // Resolvers for passed functions in the dropdown menu creation.\n bodyPromiseResolver(Templates.render(\n 'core_grades/searchwidget/user/usersearch_body', {displayunsearchablecontent: true}\n ));\n\n // Lock tab control. It has to be locked because the dropdown's role is dialog.\n FocusLockManager.trapFocus(dropdownMenuContainer);\n });\n\n // Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).\n $(menuContainer).on('hide.bs.dropdown', () => {\n FocusLockManager.untrapFocus();\n });\n\n inputElement.addEventListener('change', e => {\n const toggle = menuContainer.querySelector('.dropdown-toggle');\n const courseID = toggle.dataset.courseid;\n const actionUrl = Url.relativeUrl('/grade/report/user/index.php', {id: courseID, userid: e.target.value}, false);\n location.href = actionUrl;\n\n e.stopPropagation();\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 searchUsers\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\n/**\n * Define the action to be performed when an item is selected by the search widget.\n *\n * @param {String} selected The selected item's value.\n */\nconst afterSelect = (selected) => {\n const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('user'));\n const inputElement = menuContainer.querySelector('input[name=\"userid\"]');\n\n $(menuContainer).dropdown('hide'); // Otherwise the dropdown stays open when user choose an option using keyboard.\n\n if (inputElement.value != selected) {\n inputElement.value = selected;\n inputElement.dispatchEvent(new Event('change', {bubbles: true}));\n }\n};\n"],"names":["pendingPromise","Pending","registerListenerEvents","resolve","bodyPromiseResolver","bodyPromise","WidgetBase","promisesAndResolvers","dropdownMenuContainer","document","querySelector","Selectors","elements","getSearchWidgetDropdownSelector","menuContainer","getSearchWidgetSelector","inputElement","on","async","courseID","e","relatedTarget","dataset","courseid","groupId","groupid","showLoader","data","Repository","userFetch","catch","errorTemplateData","message","Templates","render","allUsersOptionName","users","length","allUsersOption","id","name","url","Url","relativeUrl","userid","init","searchUsers","afterSelect","displayunsearchablecontent","FocusLockManager","trapFocus","untrapFocus","addEventListener","actionUrl","target","value","location","href","stopPropagation","searchTerm","toLowerCase","searchResults","forEach","user","fullname","includes","push","selected","dropdown","dispatchEvent","Event","bubbles"],"mappings":";;;;;;;ucAuCoB,WACVA,eAAiB,IAAIC,iBAC3BC,yBACAF,eAAeG,iBAQbD,uBAAyB,SACvBE,oBAACA,oBAADC,YAAsBA,aAAeC,WAAWC,6BAC9CC,sBAAwBC,SAASC,cAAcC,UAAUC,SAASC,gCAAgC,SAClGC,cAAgBL,SAASC,cAAcC,UAAUC,SAASG,wBAAwB,SAClFC,aAAeF,cAAcJ,cAAc,4CAG/CI,eAAeG,GAAG,oBAAoBC,MAAAA,UAC9BC,SAAWC,EAAEC,cAAcC,QAAQC,SACnCC,QAAUJ,EAAEC,cAAcC,QAAQG,cAElCnB,WAAWoB,WAAWlB,6BAGtBmB,WAAaC,WAAWC,UAAUV,SAAUK,SAASM,OAAMZ,MAAAA,UACvDa,kBAAoB,cACNX,EAAEY,SAEtB5B,0BACU6B,UAAUC,OAAO,iCAAkCH,0BAK7DJ,OAAS,gBAKPQ,yBAA2B,mBAAU,cAAe,mBAAoBR,KAAKS,MAAMC,QACnFC,qBAAuBL,UAAUC,OAAO,kCAAmC,CAC7EK,GAAI,EACJC,KAAML,mBACNM,IAAKC,aAAIC,YAAY,+BAAgC,CAACJ,GAAIpB,SAAUyB,OAAQ,IAAI,WAG9EtC,WAAWuC,KACbrC,sBACAH,YACAsB,KAAKS,MACLU,cACAR,eACAS,aAIJ3C,oBAAoB6B,UAAUC,OAC1B,gDAAiD,CAACc,4BAA4B,KAIlFC,iBAAiBC,UAAU1C,8CAI7BM,eAAeG,GAAG,oBAAoB,KACpCgC,iBAAiBE,iBAGrBnC,aAAaoC,iBAAiB,UAAUhC,UAE9BD,SADSL,cAAcJ,cAAc,oBACnBY,QAAQC,SAC1B8B,UAAYX,aAAIC,YAAY,+BAAgC,CAACJ,GAAIpB,SAAUyB,OAAQxB,EAAEkC,OAAOC,QAAQ,GAC1GC,SAASC,KAAOJ,UAEhBjC,EAAEsC,sBAUJZ,YAAc,IACT,IACI,CAACV,MAAOuB,iBACQ,KAAfA,kBACOvB,MAEXuB,WAAaA,WAAWC,oBAClBC,cAAgB,UACtBzB,MAAM0B,SAASC,OACMA,KAAKC,SAASJ,cAClBK,SAASN,aAClBE,cAAcK,KAAKH,SAGpBF,eAUbd,YAAeoB,iBACXrD,cAAgBL,SAASC,cAAcC,UAAUC,SAASG,wBAAwB,SAClFC,aAAeF,cAAcJ,cAAc,4CAE/CI,eAAesD,SAAS,QAEtBpD,aAAauC,OAASY,WACtBnD,aAAauC,MAAQY,SACrBnD,aAAaqD,cAAc,IAAIC,MAAM,SAAU,CAACC,SAAS"} \ No newline at end of file diff --git a/grade/report/user/amd/src/user.js b/grade/report/user/amd/src/user.js index 0aa06256c62..93935cb114e 100644 --- a/grade/report/user/amd/src/user.js +++ b/grade/report/user/amd/src/user.js @@ -58,12 +58,11 @@ const registerListenerEvents = () => { $(menuContainer).on('show.bs.dropdown', async(e) => { const courseID = e.relatedTarget.dataset.courseid; const groupId = e.relatedTarget.dataset.groupid; - const actionBaseUrl = Url.relativeUrl('/grade/report/user/index.php', {}, false); // Display a loading icon in the dropdown menu container until the body promise is resolved. await WidgetBase.showLoader(dropdownMenuContainer); // If an error occurs while fetching the data, display the error within the dropdown menu. - const data = await Repository.userFetch(courseID, actionBaseUrl, groupId).catch(async(e) => { + const data = await Repository.userFetch(courseID, groupId).catch(async(e) => { const errorTemplateData = { 'errormessage': e.message }; diff --git a/grade/templates/searchwidget/searchitem.mustache b/grade/templates/searchwidget/searchitem.mustache index bc0b72bbabf..f0be7a3df61 100644 --- a/grade/templates/searchwidget/searchitem.mustache +++ b/grade/templates/searchwidget/searchitem.mustache @@ -23,8 +23,7 @@ { "id": "1", "name": "Quiz 1", - "fullname": "Cameron Greeve", - "active": true + "fullname": "Cameron Greeve" } }}
  • 'read', 'ajax' => true, 'loginrequired' => true, - ), - 'core_backup_get_async_backup_links_restore' => array( + ), + 'core_backup_get_async_backup_links_restore' => array( 'classname' => 'core_backup_external', 'classpath' => 'backup/externallib.php', 'methodname' => 'get_async_backup_links_restore', @@ -965,13 +965,31 @@ $functions = array( ), 'core_grades_get_enrolled_users_for_search_widget' => array ( 'classname' => 'core_grades\external\get_enrolled_users_for_search_widget', + 'description' => '** DEPRECATED ** Please do not call this function any more. ' . + 'Use core_grades_get_enrolled_users_for_selector instead. ' . + '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_grades_get_enrolled_users_for_selector' => array ( + 'classname' => 'core_grades\external\get_enrolled_users_for_selector', '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_grades_get_groups_for_search_widget' => [ - 'classname' => 'core_grades\external\get_groups_for_search_widget', + 'classname' => 'core_grades\external\get_groups_for_selector', + 'description' => '** DEPRECATED ** Please do not call this function any more. ' . + 'Use core_grades_get_groups_for_selector instead. ' . + 'Get the group/(s) for a course', + 'type' => 'read', + 'ajax' => true, + 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], + ], + 'core_grades_get_groups_for_selector' => [ + 'classname' => 'core_grades\external\get_groups_for_selector', 'description' => 'Get the group/(s) for a course', 'type' => 'read', 'ajax' => true, diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 6ed2e663a43..4b051e44f63 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -97,6 +97,10 @@ information provided here is intended especially for developers. - `createCsvDataSet` - `createArrayDataSet` - `loadDataSet` +* The external function core_grades_get_enrolled_users_for_search_widget is now deprecated. + Please use the new core_grades_get_enrolled_users_for_selector external function instead. +* The external function core_grades_get_groups_for_search_widget is now deprecated. + Please use the new core_grades_get_groups_for_selector external function instead. === 4.1 === diff --git a/version.php b/version.php index 2f233674aab..5b826aae633 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2023032200.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2023032200.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.2dev+ (Build: 20230322)'; // Human-friendly version name