first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
define("gradereport_singleview/bulkactions",["exports","core/pending","core/custom_interaction_events","core/modal_save_cancel","core/templates","core/modal_events","core/str","core/notification","gradereport_singleview/selectors"],(function(_exports,_pending,_custom_interaction_events,_modal_save_cancel,_templates,_modal_events,Str,_notification,_selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Javascript module for bulk actions.
*
* @module gradereport_singleview/bulkactions
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_templates=_interopRequireDefault(_templates),_modal_events=_interopRequireDefault(_modal_events),Str=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Str),_notification=_interopRequireDefault(_notification),_selectors=_interopRequireDefault(_selectors);_exports.init=()=>{const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()};const registerListenerEvents=()=>{const events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events),events.forEach((event=>{document.addEventListener(event,(async e=>{const trigger=e.target.closest(_selectors.default.actions.bulkaction);if(trigger)if("overrideallgrades"===trigger.dataset.action||"overridenonegrades"===trigger.dataset.action){const override=document.querySelectorAll(_selectors.default.elements.override);"overridenonegrades"===trigger.dataset.action?Str.get_strings([{key:"removeoverride",component:"gradereport_singleview"},{key:"overridenoneconfirm",component:"gradereport_singleview"},{key:"removeoverridesave",component:"gradereport_singleview"},{key:"cancel",component:"moodle"}]).then((strings=>_notification.default.confirm(strings[0],strings[1],strings[2],strings[3],(()=>{override.forEach((el=>{el.checked&&el.click()}))})))).catch(_notification.default.exception):override.forEach((el=>{el.checked||el.click()}))}else if("excludeallgrades"===trigger.dataset.action||"excludenonegrades"===trigger.dataset.action){const exclude=document.querySelectorAll(_selectors.default.elements.exclude),checked="excludeallgrades"===trigger.dataset.action;exclude.forEach((el=>{el.checked=checked}))}else"bulklegend"===trigger.dataset.action&&Str.get_strings([{key:"bulklegend",component:"gradereport_singleview"},{key:"save",component:"moodle"}]).then((strings=>_modal_save_cancel.default.create({body:_templates.default.render("gradereport_singleview/bulkinsert",{id:"bulkinsertmodal",name:"bulkinsertmodal"}),title:strings[0],buttons:{save:strings[1]},removeOnClose:!0,show:!0}))).then((modal=>(modal.getFooter().find(_selectors.default.elements.modalsave).attr("disabled",!0),modal.getRoot().on("change",_selectors.default.elements.warningcheckbox,(e=>{if(e.preventDefault(),e.target.checked){modal.getRoot().find(_selectors.default.elements.modalformdata).removeClass("dimmed_text"),modal.getRoot().find(_selectors.default.elements.modalradio).removeAttr("disabled"),modal.getRoot().find(_selectors.default.elements.modalinput).removeAttr("disabled");modal.getRoot().find(_selectors.default.elements.modalradiochecked).val()&&modal.getFooter().find(_selectors.default.elements.modalsave).removeAttr("disabled")}else modal.getRoot().find(_selectors.default.elements.modalformdata).addClass("dimmed_text"),modal.getRoot().find(_selectors.default.elements.modalradio).attr("disabled",!0),modal.getRoot().find(_selectors.default.elements.modalinput).attr("disabled",!0),modal.getFooter().find(_selectors.default.elements.modalsave).attr("disabled",!0)})),modal.getRoot().on("change",_selectors.default.elements.modalradio,(e=>{e.preventDefault(),modal.getFooter().find(_selectors.default.elements.modalsave).removeAttr("disabled")})),modal.getRoot().on(_modal_events.default.save,(()=>{document.querySelector(_selectors.default.elements.enablebulkinsert).checked=!0;const formRadioData=modal.getRoot().find(_selectors.default.elements.modalradiochecked).val();document.querySelector(_selectors.default.elements.formradio).value=formRadioData;const formData=modal.getRoot().find(_selectors.default.elements.modalgrade).val();document.querySelector(_selectors.default.elements.formgrade).value=formData,document.querySelector(_selectors.default.elements.formsave).click()})),modal))).catch(_notification.default.exception)}))}))}}));
//# sourceMappingURL=bulkactions.min.js.map
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
define("gradereport_singleview/grade",["exports","core_grades/comboboxsearch/grade","core/url"],(function(_exports,_grade,_url){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_grade=_interopRequireDefault(_grade),_url=_interopRequireDefault(_url);const selectors_component=".grade-search",selectors_courseid='[data-region="courseid"]',component=document.querySelector(selectors_component);class GradeItems extends _grade.default{constructor(){var obj,key,value;super(),obj=this,key="courseID",value=component.querySelector(selectors_courseid).dataset.courseid,key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}static init(){return new GradeItems}selectOneLink(gradeID){return _url.default.relativeUrl("/grade/report/singleview/index.php",{id:this.courseID,gradesearchvalue:this.getSearchTerm(),item:"grade",itemid:gradeID},!1)}}return _exports.default=GradeItems,_exports.default}));
//# sourceMappingURL=grade.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"grade.min.js","sources":["../src/grade.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Allow the user to search for grades within the singleview report.\n *\n * @module gradereport_singleview/grade\n * @copyright 2023 Mathew May <mathew.solutions>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport GradeItemSearch from 'core_grades/comboboxsearch/grade';\nimport Url from 'core/url';\n\n// Define our standard lookups.\nconst selectors = {\n component: '.grade-search',\n courseid: '[data-region=\"courseid\"]',\n};\nconst component = document.querySelector(selectors.component);\n\nexport default class GradeItems extends GradeItemSearch {\n\n courseID = component.querySelector(selectors.courseid).dataset.courseid;\n\n constructor() {\n super();\n }\n\n static init() {\n return new GradeItems();\n }\n\n /**\n * Build up the link that is dedicated to a particular result.\n *\n * @param {Number} gradeID The ID of the grade item selected.\n * @returns {string|*}\n */\n selectOneLink(gradeID) {\n return Url.relativeUrl('/grade/report/singleview/index.php', {\n id: this.courseID,\n gradesearchvalue: this.getSearchTerm(),\n item: 'grade',\n itemid: gradeID,\n }, false);\n }\n}\n"],"names":["selectors","component","document","querySelector","GradeItems","GradeItemSearch","constructor","dataset","courseid","selectOneLink","gradeID","Url","relativeUrl","id","this","courseID","gradesearchvalue","getSearchTerm","item","itemid"],"mappings":"gXA0BMA,oBACS,gBADTA,mBAEQ,2BAERC,UAAYC,SAASC,cAAcH,2BAEpBI,mBAAmBC,eAIpCC,sEAFWL,UAAUE,cAAcH,oBAAoBO,QAAQC,+IAOpD,IAAIJ,WASfK,cAAcC,gBACHC,aAAIC,YAAY,qCAAsC,CACzDC,GAAIC,KAAKC,SACTC,iBAAkBF,KAAKG,gBACvBC,KAAM,QACNC,OAAQT,UACT"}
+3
View File
@@ -0,0 +1,3 @@
define("gradereport_singleview/group",["exports","core_group/comboboxsearch/group","core/url"],(function(_exports,_group,_url){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_group=_interopRequireDefault(_group),_url=_interopRequireDefault(_url);class Group extends _group.default{constructor(item){super(),_defineProperty(this,"courseID",void 0),_defineProperty(this,"item",null),this.item=item,this.selectors={...this.selectors,courseid:'[data-region="courseid"]'};const component=document.querySelector(this.componentSelector());this.courseID=component.querySelector(this.selectors.courseid).dataset.courseid}static init(item){return new Group(item)}selectOneLink(groupID){return _url.default.relativeUrl("/grade/report/singleview/index.php",{id:this.courseID,groupsearchvalue:this.getSearchTerm(),group:groupID,item:this.item},!1)}}return _exports.default=Group,_exports.default}));
//# sourceMappingURL=group.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"group.min.js","sources":["../src/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 <http://www.gnu.org/licenses/>.\n\n/**\n * Allow the user to search for groups within the singleview report.\n *\n * @module gradereport_singleview/group\n * @copyright 2023 Mathew May <mathew.solutions>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport GroupSearch from 'core_group/comboboxsearch/group';\nimport Url from 'core/url';\n\nexport default class Group extends GroupSearch {\n\n courseID;\n\n item = null;\n\n /**\n * Construct the class.\n *\n * @param {string} item The page type we are currently on.\n */\n constructor(item) {\n super();\n this.item = item;\n\n // Define our standard lookups.\n this.selectors = {...this.selectors,\n courseid: '[data-region=\"courseid\"]',\n };\n const component = document.querySelector(this.componentSelector());\n this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;\n }\n\n /**\n * Allow the class to be invoked via PHP.\n *\n * @param {string} item The page type we are currently on.\n * @returns {Group}\n */\n static init(item) {\n return new Group(item);\n }\n\n /**\n * Build up the link that is dedicated to a particular result.\n *\n * @param {Number} groupID The ID of the group selected.\n * @returns {string|*}\n */\n selectOneLink(groupID) {\n return Url.relativeUrl('/grade/report/singleview/index.php', {\n id: this.courseID,\n groupsearchvalue: this.getSearchTerm(),\n group: groupID,\n item: this.item\n }, false);\n }\n}\n"],"names":["Group","GroupSearch","constructor","item","selectors","this","courseid","component","document","querySelector","componentSelector","courseID","dataset","selectOneLink","groupID","Url","relativeUrl","id","groupsearchvalue","getSearchTerm","group"],"mappings":"mhBAyBqBA,cAAcC,eAW/BC,YAAYC,kFAPL,WASEA,KAAOA,UAGPC,UAAY,IAAIC,KAAKD,UACtBE,SAAU,kCAERC,UAAYC,SAASC,cAAcJ,KAAKK,0BACzCC,SAAWJ,UAAUE,cAAcJ,KAAKD,UAAUE,UAAUM,QAAQN,qBASjEH,aACD,IAAIH,MAAMG,MASrBU,cAAcC,gBACHC,aAAIC,YAAY,qCAAsC,CACzDC,GAAIZ,KAAKM,SACTO,iBAAkBb,KAAKc,gBACvBC,MAAON,QACPX,KAAME,KAAKF,OACZ"}
+3
View File
@@ -0,0 +1,3 @@
define("gradereport_singleview/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var name,value,_default={actions:{bulkaction:(name="role",value="bulkaction","[data-".concat(name,'="').concat(value,'"]'))},elements:{override:"input[type=checkbox][name^=override]",exclude:"input[type=checkbox][name^=exclude]",modalsave:'[data-action="save"]',warningcheckbox:'input[type="checkbox"]',modalformdata:".formdata",modalradio:'input[type="radio"]',modalinput:'input[type="text"]',modalradiochecked:'input[type="radio"]:checked',enablebulkinsert:'input[type="checkbox"][name^=bulk]',formradio:"select[name^=bulk]",modalgrade:".form-control",formgrade:'input[type="text"][name^=bulk]',formsave:'input[type="submit"]'}};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=selectors.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Define all of the selectors we will be using on the grading interface.\n *\n * @module gradereport_singleview/selectors\n * @copyright 2022 Ilya Tregubov <ilya@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n actions: {\n bulkaction: getDataSelector('role', 'bulkaction'),\n },\n elements: {\n override: 'input[type=checkbox][name^=override]',\n exclude: 'input[type=checkbox][name^=exclude]',\n modalsave: '[data-action=\"save\"]',\n warningcheckbox: 'input[type=\"checkbox\"]',\n modalformdata: '.formdata',\n modalradio: 'input[type=\"radio\"]',\n modalinput: 'input[type=\"text\"]',\n modalradiochecked: 'input[type=\"radio\"]:checked',\n enablebulkinsert: 'input[type=\"checkbox\"][name^=bulk]',\n formradio: 'select[name^=bulk]',\n modalgrade: '.form-control',\n formgrade: 'input[type=\"text\"][name^=bulk]',\n formsave: 'input[type=\"submit\"]',\n },\n};\n"],"names":["name","value","actions","bulkaction","elements","override","exclude","modalsave","warningcheckbox","modalformdata","modalradio","modalinput","modalradiochecked","enablebulkinsert","formradio","modalgrade","formgrade","formsave"],"mappings":"8JA8ByBA,KAAMC,eAIhB,CACXC,QAAS,CACLC,YANiBH,KAMW,OANLC,MAMa,6BALxBD,kBAASC,cAOzBG,SAAU,CACNC,SAAU,uCACVC,QAAS,sCACTC,UAAW,uBACXC,gBAAiB,yBACjBC,cAAe,YACfC,WAAY,sBACZC,WAAY,qBACZC,kBAAmB,8BACnBC,iBAAkB,qCAClBC,UAAW,qBACXC,WAAY,gBACZC,UAAW,iCACXC,SAAU"}
+10
View File
@@ -0,0 +1,10 @@
define("gradereport_singleview/user",["exports","core_user/comboboxsearch/user","core/url","core/templates","core_grades/searchwidget/repository"],(function(_exports,_user,_url,_templates,Repository){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Allow the user to search for learners within the singleview report.
*
* @module gradereport_singleview/user
* @copyright 2023 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_user=_interopRequireDefault(_user),_url=_interopRequireDefault(_url),Repository=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Repository);class User extends _user.default{constructor(){super()}static init(){return new User}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("core_user/comboboxsearch/resultset",{instance:this.instance,users:this.getMatchedResults().slice(0,5),hasresults:this.getMatchedResults().length>0,searchterm:this.getSearchTerm()});(0,_templates.replaceNodeContents)(this.getHTMLElements().searchDropdown,html,js),this.searchInput.removeAttribute("aria-activedescendant")}selectAllResultsLink(){return null}selectOneLink(userID){return _url.default.relativeUrl("/grade/report/singleview/index.php",{id:this.courseID,searchvalue:this.getSearchTerm(),item:"user",userid:userID},!1)}fetchDataset(){const gts="string"==typeof this.groupID&&""===this.groupID?0:this.groupID;return Repository.userFetch(this.courseID,gts).then((r=>r.users))}}return _exports.default=User,_exports.default}));
//# sourceMappingURL=user.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"user.min.js","sources":["../src/user.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Allow the user to search for learners within the singleview report.\n *\n * @module gradereport_singleview/user\n * @copyright 2023 Mathew May <mathew.solutions>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport UserSearch from 'core_user/comboboxsearch/user';\nimport Url from 'core/url';\nimport {renderForPromise, replaceNodeContents} from 'core/templates';\nimport * as Repository from 'core_grades/searchwidget/repository';\n\nexport default class User extends UserSearch {\n\n constructor() {\n super();\n }\n\n static init() {\n return new User();\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('core_user/comboboxsearch/resultset', {\n instance: this.instance,\n users: this.getMatchedResults().slice(0, 5),\n hasresults: this.getMatchedResults().length > 0,\n searchterm: this.getSearchTerm(),\n });\n replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);\n // Remove aria-activedescendant when the available options change.\n this.searchInput.removeAttribute('aria-activedescendant');\n }\n\n /**\n * Stub out default required function unused here.\n * @returns {null}\n */\n selectAllResultsLink() {\n return null;\n }\n\n /**\n * Build up the view all link that is dedicated to a particular result.\n *\n * @param {Number} userID The ID of the user selected.\n * @returns {string|*}\n */\n selectOneLink(userID) {\n return Url.relativeUrl('/grade/report/singleview/index.php', {\n id: this.courseID,\n searchvalue: this.getSearchTerm(),\n item: 'user',\n userid: userID,\n }, false);\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {Promise<*>}\n */\n fetchDataset() {\n // Small typing checks as sometimes groups don't exist therefore the element returns a empty string.\n const gts = typeof (this.groupID) === \"string\" && this.groupID === '' ? 0 : this.groupID;\n return Repository.userFetch(this.courseID, gts).then((r) => r.users);\n }\n}\n"],"names":["User","UserSearch","constructor","html","js","instance","this","users","getMatchedResults","slice","hasresults","length","searchterm","getSearchTerm","getHTMLElements","searchDropdown","searchInput","removeAttribute","selectAllResultsLink","selectOneLink","userID","Url","relativeUrl","id","courseID","searchvalue","item","userid","fetchDataset","gts","groupID","Repository","userFetch","then","r"],"mappings":";;;;;;;q0BA2BqBA,aAAaC,cAE9BC,2CAKW,IAAIF,kCAOLG,KAACA,KAADC,GAAOA,UAAY,+BAAiB,qCAAsC,CAC5EC,SAAUC,KAAKD,SACfE,MAAOD,KAAKE,oBAAoBC,MAAM,EAAG,GACzCC,WAAYJ,KAAKE,oBAAoBG,OAAS,EAC9CC,WAAYN,KAAKO,qDAEDP,KAAKQ,kBAAkBC,eAAgBZ,KAAMC,SAE5DY,YAAYC,gBAAgB,yBAOrCC,8BACW,KASXC,cAAcC,eACHC,aAAIC,YAAY,qCAAsC,CACzDC,GAAIjB,KAAKkB,SACTC,YAAanB,KAAKO,gBAClBa,KAAM,OACNC,OAAQP,SACT,GAQPQ,qBAEUC,IAAgC,iBAAlBvB,KAAKwB,SAA0C,KAAjBxB,KAAKwB,QAAiB,EAAIxB,KAAKwB,eAC1EC,WAAWC,UAAU1B,KAAKkB,SAAUK,KAAKI,MAAMC,GAAMA,EAAE3B"}
@@ -0,0 +1,177 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Javascript module for bulk actions.
*
* @module gradereport_singleview/bulkactions
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Pending from 'core/pending';
import CustomEvents from "core/custom_interaction_events";
import ModalSaveCancel from 'core/modal_save_cancel';
import Templates from 'core/templates';
import ModalEvents from 'core/modal_events';
import * as Str from 'core/str';
import Notification from 'core/notification';
import selectors from 'gradereport_singleview/selectors';
/**
* Initialize module.
*/
export const init = () => {
const pendingPromise = new Pending();
registerListenerEvents();
pendingPromise.resolve();
};
/**
* Register bulk actions related event listeners.
*
* @method registerListenerEvents
*/
const registerListenerEvents = () => {
const events = [
'click',
CustomEvents.events.activate,
CustomEvents.events.keyboardActivate
];
CustomEvents.define(document, events);
// Register events.
events.forEach((event) => {
document.addEventListener(event, async(e) => {
const trigger = e.target.closest(selectors.actions.bulkaction);
if (!trigger) {
return;
}
if ((trigger.dataset.action === 'overrideallgrades') || (trigger.dataset.action === 'overridenonegrades')) {
const override = document.querySelectorAll(selectors.elements.override);
if (trigger.dataset.action === 'overridenonegrades') {
// Alert for removing all grade overrides on page.
Str.get_strings([
{key: 'removeoverride', component: 'gradereport_singleview'},
{key: 'overridenoneconfirm', component: 'gradereport_singleview'},
{key: 'removeoverridesave', component: 'gradereport_singleview'},
{key: 'cancel', component: 'moodle'},
])
.then((strings) => {
return Notification.confirm(
strings[0],
strings[1],
strings[2],
strings[3],
() => {
// Uncheck each override checkbox - this will make grade and feedback input fields disabled.
override.forEach((el) => {
if (el.checked) {
el.click();
}
});
});
})
.catch(Notification.exception);
} else {
// Check each override checkbox - this will make grade and feedback input fields enabled.
override.forEach((el) => {
if (!el.checked) {
el.click();
}
});
}
} else if ((trigger.dataset.action === 'excludeallgrades') || (trigger.dataset.action === 'excludenonegrades')) {
const exclude = document.querySelectorAll(selectors.elements.exclude);
const checked = (trigger.dataset.action === 'excludeallgrades');
// Uncheck or check each exclude checkbox.
exclude.forEach((el) => {
el.checked = checked;
});
} else if (trigger.dataset.action === 'bulklegend') {
// Modal for bulk insert grades.
Str.get_strings([
{key: 'bulklegend', component: 'gradereport_singleview'},
{key: 'save', component: 'moodle'},
])
.then((strings) => {
return ModalSaveCancel.create({
body: Templates.render('gradereport_singleview/bulkinsert', {
id: 'bulkinsertmodal',
name: 'bulkinsertmodal'
}),
title: strings[0],
buttons: {
save: strings[1],
},
removeOnClose: true,
show: true,
});
})
.then((modal) => {
modal.getFooter().find(selectors.elements.modalsave).attr('disabled', true);
// We need to acknowledge that we understand risks of loosing data.
// Only when acknowledge checkbox is checked we allow selecting insert options.
modal.getRoot().on('change', selectors.elements.warningcheckbox,
(e) => {
e.preventDefault();
if (e.target.checked) {
modal.getRoot().find(selectors.elements.modalformdata).removeClass('dimmed_text');
modal.getRoot().find(selectors.elements.modalradio).removeAttr('disabled');
modal.getRoot().find(selectors.elements.modalinput).removeAttr('disabled');
const formRadioData = modal.getRoot().find(selectors.elements.modalradiochecked).val();
// We allow saving grades only when all needed data present on form.
if (formRadioData) {
modal.getFooter().find(selectors.elements.modalsave).removeAttr('disabled');
}
} else {
modal.getRoot().find(selectors.elements.modalformdata).addClass('dimmed_text');
modal.getRoot().find(selectors.elements.modalradio).attr('disabled', true);
modal.getRoot().find(selectors.elements.modalinput).attr('disabled', true);
modal.getFooter().find(selectors.elements.modalsave).attr('disabled', true);
}
});
// We allow saving grades only when all needed data present on form.
modal.getRoot().on('change', selectors.elements.modalradio, (e) => {
e.preventDefault();
modal.getFooter().find(selectors.elements.modalsave).removeAttr('disabled');
});
modal.getRoot().on(ModalEvents.save, () => {
// When save button is clicked in modal form we insert data from modal
// into preexisted hidden bulk insert form and Save button for table form.
document.querySelector(selectors.elements.enablebulkinsert).checked = true;
const formRadioData = modal.getRoot().find(selectors.elements.modalradiochecked).val();
const $select = document.querySelector(selectors.elements.formradio);
$select.value = formRadioData;
const formData = modal.getRoot().find(selectors.elements.modalgrade).val();
document.querySelector(selectors.elements.formgrade).value = formData;
document.querySelector(selectors.elements.formsave).click();
});
return modal;
})
.catch(Notification.exception);
}
});
});
};
+59
View File
@@ -0,0 +1,59 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Allow the user to search for grades within the singleview report.
*
* @module gradereport_singleview/grade
* @copyright 2023 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import GradeItemSearch from 'core_grades/comboboxsearch/grade';
import Url from 'core/url';
// Define our standard lookups.
const selectors = {
component: '.grade-search',
courseid: '[data-region="courseid"]',
};
const component = document.querySelector(selectors.component);
export default class GradeItems extends GradeItemSearch {
courseID = component.querySelector(selectors.courseid).dataset.courseid;
constructor() {
super();
}
static init() {
return new GradeItems();
}
/**
* Build up the link that is dedicated to a particular result.
*
* @param {Number} gradeID The ID of the grade item selected.
* @returns {string|*}
*/
selectOneLink(gradeID) {
return Url.relativeUrl('/grade/report/singleview/index.php', {
id: this.courseID,
gradesearchvalue: this.getSearchTerm(),
item: 'grade',
itemid: gradeID,
}, false);
}
}
+73
View File
@@ -0,0 +1,73 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Allow the user to search for groups within the singleview report.
*
* @module gradereport_singleview/group
* @copyright 2023 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import GroupSearch from 'core_group/comboboxsearch/group';
import Url from 'core/url';
export default class Group extends GroupSearch {
courseID;
item = null;
/**
* Construct the class.
*
* @param {string} item The page type we are currently on.
*/
constructor(item) {
super();
this.item = item;
// Define our standard lookups.
this.selectors = {...this.selectors,
courseid: '[data-region="courseid"]',
};
const component = document.querySelector(this.componentSelector());
this.courseID = component.querySelector(this.selectors.courseid).dataset.courseid;
}
/**
* Allow the class to be invoked via PHP.
*
* @param {string} item The page type we are currently on.
* @returns {Group}
*/
static init(item) {
return new Group(item);
}
/**
* Build up the link that is dedicated to a particular result.
*
* @param {Number} groupID The ID of the group selected.
* @returns {string|*}
*/
selectOneLink(groupID) {
return Url.relativeUrl('/grade/report/singleview/index.php', {
id: this.courseID,
groupsearchvalue: this.getSearchTerm(),
group: groupID,
item: this.item
}, false);
}
}
@@ -0,0 +1,54 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Define all of the selectors we will be using on the grading interface.
*
* @module gradereport_singleview/selectors
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* A small helper function to build queryable data selectors.
* @method getDataSelector
* @param {String} name
* @param {String} value
* @return {string}
*/
const getDataSelector = (name, value) => {
return `[data-${name}="${value}"]`;
};
export default {
actions: {
bulkaction: getDataSelector('role', 'bulkaction'),
},
elements: {
override: 'input[type=checkbox][name^=override]',
exclude: 'input[type=checkbox][name^=exclude]',
modalsave: '[data-action="save"]',
warningcheckbox: 'input[type="checkbox"]',
modalformdata: '.formdata',
modalradio: 'input[type="radio"]',
modalinput: 'input[type="text"]',
modalradiochecked: 'input[type="radio"]:checked',
enablebulkinsert: 'input[type="checkbox"][name^=bulk]',
formradio: 'select[name^=bulk]',
modalgrade: '.form-control',
formgrade: 'input[type="text"][name^=bulk]',
formsave: 'input[type="submit"]',
},
};
+86
View File
@@ -0,0 +1,86 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Allow the user to search for learners within the singleview report.
*
* @module gradereport_singleview/user
* @copyright 2023 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import UserSearch from 'core_user/comboboxsearch/user';
import Url from 'core/url';
import {renderForPromise, replaceNodeContents} from 'core/templates';
import * as Repository from 'core_grades/searchwidget/repository';
export default class User extends UserSearch {
constructor() {
super();
}
static init() {
return new User();
}
/**
* Build the content then replace the node.
*/
async renderDropdown() {
const {html, js} = await renderForPromise('core_user/comboboxsearch/resultset', {
instance: this.instance,
users: this.getMatchedResults().slice(0, 5),
hasresults: this.getMatchedResults().length > 0,
searchterm: this.getSearchTerm(),
});
replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);
// Remove aria-activedescendant when the available options change.
this.searchInput.removeAttribute('aria-activedescendant');
}
/**
* Stub out default required function unused here.
* @returns {null}
*/
selectAllResultsLink() {
return null;
}
/**
* Build up the view all link that is dedicated to a particular result.
*
* @param {Number} userID The ID of the user selected.
* @returns {string|*}
*/
selectOneLink(userID) {
return Url.relativeUrl('/grade/report/singleview/index.php', {
id: this.courseID,
searchvalue: this.getSearchTerm(),
item: 'user',
userid: userID,
}, false);
}
/**
* Get the data we will be searching against in this component.
*
* @returns {Promise<*>}
*/
fetchDataset() {
// Small typing checks as sometimes groups don't exist therefore the element returns a empty string.
const gts = typeof (this.groupID) === "string" && this.groupID === '' ? 0 : this.groupID;
return Repository.userFetch(this.courseID, gts).then((r) => r.users);
}
}
@@ -0,0 +1,60 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Single view report viewed event.
*
* @package gradereport_singleview
* @copyright 2014 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\event;
defined('MOODLE_INTERNAL') || die();
/**
* User report viewed event class.
*
* @package gradereport_singleview
* @since Moodle 2.8
* @copyright 2014 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_report_viewed extends \core\event\grade_report_viewed {
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventgradereportviewed', 'gradereport_singleview');
}
/**
* Custom validation.
*
* Throw \coding_exception notice in case of any problems.
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->relateduserid)) {
throw new \coding_exception('The \'relateduserid\' value must be set.');
}
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace gradereport_singleview\external;
use context_course;
use core_course_external;
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 grade_tree;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot.'/course/externallib.php');
require_once($CFG->dirroot.'/grade/lib.php');
/**
* External grade report singleview API
*
* @package gradereport_singleview
* @copyright 2022 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class singleview extends core_course_external {
/**
* Describes the parameters for get_users_for_course.
*
* @return external_function_parameters
*/
public static function get_grade_items_for_search_widget_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
*
* @param int $courseid
* @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
*/
protected static function get_grade_items_for_search_widget(int $courseid): array {
global $PAGE, $USER, $CFG;
$params = self::validate_parameters(
self::get_grade_items_for_search_widget_parameters(),
[
'courseid' => $courseid,
]
);
$warnings = [];
$coursecontext = context_course::instance($params['courseid']);
parent::validate_context($coursecontext);
$gtree = new grade_tree($params['courseid'], false, true, null, !$CFG->enableoutcomes);
$gradeableitems = $gtree->get_items();
$gradeitems = array_map(function ($gradeitem) use ($PAGE, $USER, $params) {
$item = new \stdClass();
$item->id = $gradeitem->id;
$item->name = $gradeitem->get_name(true);
return $item;
}, $gradeableitems);
return [
'gradeitems' => $gradeitems,
'warnings' => $warnings,
];
}
/**
* Returns description of what the user search for the widget should return.
*
* @return external_single_structure
*/
public static function get_grade_items_for_search_widget_returns(): external_single_structure {
return new external_single_structure([
'gradeitems' => new external_multiple_structure(
new external_single_structure([
'id' => new external_value(PARAM_INT, 'ID of the grade item', VALUE_OPTIONAL),
'name' => new external_value(PARAM_TEXT, 'The full name of the grade item', VALUE_OPTIONAL)
])
),
'warnings' => new external_warnings(),
]);
}
}
@@ -0,0 +1,44 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The gradebook interface for a filterable class.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\screen;
defined('MOODLE_INTERNAL') || die;
/**
* The gradebook interface for a filterable class.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface filterable_items {
/**
* Return true/false if this item should be filtered.
* @param mixed $item (user or grade_item)
* @return bool
*/
public static function filter($item): bool;
}
@@ -0,0 +1,428 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The screen with a list of users.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\screen;
use grade_report;
use gradereport_singleview\local\ui\range;
use gradereport_singleview\local\ui\bulk_insert;
use grade_grade;
use grade_item;
use moodle_url;
use pix_icon;
use html_writer;
use gradereport_singleview;
defined('MOODLE_INTERNAL') || die;
/**
* The screen with a list of users.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade extends tablelike implements selectable_items, filterable_items {
/**
* Used for paging
* @var int $totalitemcount
*/
private $totalitemcount = 0;
/**
* True if this is a manual grade item
* @var bool $requiresextra
*/
private $requiresextra = false;
/**
* True if there are more users than our limit.
* @var bool $requirepaging
*/
private $requirespaging = true;
/**
* To store UI element that generates a grade_item min/max range.
* @var range;
*/
protected $range;
/**
* Returns a grade_item instance or false if none found.
* @var grade_item|bool
*/
public $item;
/**
* True if $CFG->grade_overridecat is true
*
* @return bool
*/
public static function allowcategories(): bool {
return get_config('moodle', 'grade_overridecat');
}
/**
* Filter the list excluding category items (if required)?
* @param grade_item $item The grade item.
* @return bool
*/
public static function filter($item): bool {
return get_config('moodle', 'grade_overridecat') ||
!($item->is_course_item() || $item->is_category_item());
}
/**
* Get the label for the select box that chooses items for this page.
* @return string
*/
public function select_label(): string {
return get_string('selectuser', 'gradereport_singleview');
}
/**
* Get the description of this page
* @return string
*/
public function description(): string {
return get_string('users');
}
/**
* Convert this list of items into an options list
*
* @return array
*/
public function options(): array {
$options = [];
foreach ($this->items as $userid => $user) {
$options[$userid] = fullname($user);
}
return $options;
}
/**
* Return the type of the things in this list.
* @return string
*/
public function item_type(): string {
return 'user';
}
/**
* Get the original settings for this item
* @return array
*/
public function original_definition(): array {
return [
'finalgrade',
'feedback',
'override',
'exclude'
];
}
/**
* Init this page
*
* @param bool $selfitemisempty True if we have not selected a user.
*/
public function init($selfitemisempty = false) {
$this->items = grade_report::get_gradable_users($this->courseid, $this->groupid);
$this->totalitemcount = count($this->items);
if ($selfitemisempty) {
return;
}
// If we change perpage on pagination we might end up with a page that doesn't exist.
if ($this->perpage) {
$numpages = intval($this->totalitemcount / $this->perpage) + 1;
if ($numpages <= $this->page) {
$this->page = 0;
}
} else {
$this->page = 0;
}
$params = [
'id' => $this->itemid,
'courseid' => $this->courseid
];
$this->item = grade_item::fetch($params);
if (!self::filter($this->item)) {
$this->items = [];
$this->set_init_error(get_string('gradeitemcannotbeoverridden', 'gradereport_singleview'));
}
$this->requiresextra = !$this->item->is_manual_item();
$this->setup_structure();
$this->set_definition($this->original_definition());
$this->set_headers($this->original_headers());
}
/**
* Get the table headers
*
* @return array
*/
public function original_headers() {
return [
get_string('fullnameuser', 'core'),
'', // For filter icon.
get_string('gradenoun'),
get_string('range', 'grades'),
get_string('feedback', 'grades'),
get_string('override', 'gradereport_singleview'),
get_string('exclude', 'gradereport_singleview'),
];
}
/**
* Format a row in the table
*
* @param stdClass $item
* @return array
*/
public function format_line($item): array {
global $OUTPUT;
$grade = $this->fetch_grade_or_default($this->item, $item->id);
$gradestatus = '';
$context = [
'hidden' => $grade->is_hidden(),
'locked' => $grade->is_locked(),
];
if (in_array(true, $context)) {
$context['classes'] = 'gradestatus';
$gradestatus = $OUTPUT->render_from_template('core_grades/status_icons', $context);
}
if (has_capability('moodle/site:viewfullnames', \context_course::instance($this->courseid))) {
$fullname = fullname($item, true);
} else {
$fullname = fullname($item);
}
$item->imagealt = $fullname;
$url = new moodle_url("/user/view.php", ['id' => $item->id, 'course' => $this->courseid]);
$grade->label = $fullname;
$userpic = $OUTPUT->user_picture($item, ['link' => false, 'visibletoscreenreaders' => false]);
$formatteddefinition = $this->format_definition($grade);
$line = [
html_writer::link($url, $userpic . $fullname),
$this->get_user_action_menu($item),
$formatteddefinition['finalgrade'] . $gradestatus,
$this->item_range(),
$formatteddefinition['feedback'],
$formatteddefinition['override'],
$formatteddefinition['exclude'],
];
$lineclasses = [
'user',
'action',
'grade',
'range',
];
$outputline = [];
$i = 0;
foreach ($line as $key => $value) {
$cell = new \html_table_cell($value);
if ($isheader = $i == 0) {
$cell->header = $isheader;
$cell->scope = "row";
}
if (array_key_exists($key, $lineclasses)) {
$cell->attributes['class'] = $lineclasses[$key];
}
$outputline[] = $cell;
$i++;
}
return $outputline;
}
/**
* Get the range ui element for this grade_item
*
* @return element;
*/
public function item_range() {
if (empty($this->range)) {
$this->range = new range($this->item);
}
return $this->range;
}
/**
* Does this page require paging?
*
* @return bool
*/
public function supports_paging(): bool {
return $this->requirespaging;
}
/**
* Get the pager for this page.
*
* @return string
*/
public function pager(): string {
global $OUTPUT;
return $OUTPUT->paging_bar(
$this->totalitemcount, $this->page, $this->perpage,
new moodle_url('/grade/report/singleview/index.php', [
'perpage' => $this->perpage,
'id' => $this->courseid,
'group' => $this->groupid,
'itemid' => $this->itemid,
'item' => 'grade'
])
);
}
/**
* Get the heading for this page.
*
* @return string
*/
public function heading(): string {
global $PAGE;
$headinglangstring = $PAGE->user_is_editing() ? 'gradeitemedit' : 'gradeitem';
return get_string($headinglangstring, 'gradereport_singleview', $this->item->get_name());
}
/**
* Get the summary for this table.
*
* @return string
*/
public function summary(): string {
return get_string('summarygrade', 'gradereport_singleview');
}
/**
* Process the data from the form.
*
* @param array $data
* @return \stdClass of warnings
*/
public function process($data): \stdClass {
$bulk = new bulk_insert($this->item);
// Bulk insert messages the data to be passed in
// ie: for all grades of empty grades apply the specified value.
if ($bulk->is_applied($data)) {
$filter = $bulk->get_type($data);
$insertvalue = $bulk->get_insert_value($data);
// Appropriately massage data that may not exist.
if ($this->supports_paging()) {
$gradeitem = grade_item::fetch([
'courseid' => $this->courseid,
'id' => $this->item->id
]);
$null = $gradeitem->gradetype == GRADE_TYPE_SCALE ? -1 : '';
foreach ($this->items as $itemid => $item) {
$field = "finalgrade_{$gradeitem->id}_{$itemid}";
if (isset($data->$field)) {
continue;
}
$grade = grade_grade::fetch([
'itemid' => $gradeitem->id,
'userid' => $itemid
]);
$data->$field = empty($grade) ? $null : $grade->finalgrade;
$data->{"old$field"} = $data->$field;
}
}
foreach ($data as $varname => $value) {
if (preg_match('/^oldoverride_(\d+)_(\d+)/', $varname, $matches)) {
// If we've selected overriding all grades.
if ($filter == 'all') {
$override = "override_{$matches[1]}_{$matches[2]}";
$data->$override = '1';
}
}
if (!preg_match('/^finalgrade_(\d+)_/', $varname, $matches)) {
continue;
}
$gradeitem = grade_item::fetch([
'courseid' => $this->courseid,
'id' => $matches[1]
]);
$isscale = ($gradeitem->gradetype == GRADE_TYPE_SCALE);
$empties = (trim($value) === '' || ($isscale && $value == -1));
if ($filter == 'all' || $empties) {
$data->$varname = ($isscale && empty($insertvalue)) ?
-1 : $insertvalue;
}
}
}
return parent::process($data);
}
/**
* Return the action menu HTML for the user item.
*
* @param \stdClass $user
* @return mixed
*/
private function get_user_action_menu(\stdClass $user) {
global $OUTPUT;
$menuitems = [];
$url = new moodle_url($this->format_link('user', $user->id));
$title = get_string('showallgrades', 'core_grades');
$menuitems[] = new \action_menu_link_secondary($url, null, $title);
$menu = new \action_menu($menuitems);
$icon = $OUTPUT->pix_icon('i/moremenu', get_string('actions'));
$extraclasses = 'btn btn-link btn-icon icon-size-3 d-flex align-items-center justify-content-center';
$menu->set_menu_trigger($icon, $extraclasses);
$menu->set_menu_left();
$menu->set_boundary('window');
return $OUTPUT->render($menu);
}
}
@@ -0,0 +1,85 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace gradereport_singleview\local\screen;
/**
* The grade search screen.
*
* @package gradereport_singleview
* @copyright 2022 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_select extends screen {
public function init($selfitemisempty = false) {
}
/**
* Return the HTML for the page.
*
* @return string
*/
public function html(): string {
global $OUTPUT, $COURSE, $USER;
$userlink = new \moodle_url('/grade/report/singleview/index.php', ['id' => $COURSE->id, 'item' => 'user_select']);
$gradelink = new \moodle_url('/grade/report/singleview/index.php', ['id' => $COURSE->id, 'item' => 'grade_select']);
$gpr = new \grade_plugin_return(['type' => 'report', 'plugin' => 'singleview', 'courseid' => $COURSE->id,
'userid' => $USER->id]);
$context = [
'courseid' => $gpr->courseid,
'imglink' => $OUTPUT->image_url('zero_state_grade', 'gradereport_singleview'),
'userzerolink' => $userlink->out(false),
'userselectactive' => false,
'gradezerolink' => $gradelink->out(false),
'gradeselectactive' => true,
'displaylabel' => true,
'groupmodeenabled' => $COURSE->groupmode,
'groupactionbaseurl' => 'index.php?item=grade_select',
'groupid' => $gpr->groupid
];
return $OUTPUT->render_from_template('gradereport_singleview/zero_state_grade', $context);
}
public function item_type(): ?string {
return false;
}
/**
* Should we show the base singlereport group selector?
* @return bool
*/
public function display_group_selector(): bool {
return false;
}
/**
* Get the heading for the screen.
*
* @return string
*/
public function heading(): string {
return ' ';
}
/**
* Does this screen support paging?
*
* @return bool
*/
public function supports_paging(): bool {
return false;
}
}
@@ -0,0 +1,466 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Abstract class used as a base for the 3 screens.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\screen;
use context_course;
use grade_report;
use moodle_url;
use html_writer;
use grade_structure;
use grade_grade;
use grade_item;
use stdClass;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot . '/grade/report/lib.php');
/**
* Abstract class used as a base for the 3 screens.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class screen {
/**
* The id of the course
* @var int $courseid
*/
protected $courseid;
/**
* Either a user id or a grade_item id
* @var int|null $itemid
*/
protected $itemid;
/**
* The currently set groupid (if set)
* @var int $groupid
*/
protected $groupid;
/**
* The course context
* @var context_course $context
*/
protected $context;
/**
* The page number
* @var int $page
*/
protected $page;
/**
* Results per page
* @var int $perpage
*/
protected $perpage;
/**
* List of items on the page, they could be users or grade_items
* @var array $items
*/
protected $items;
/** @var int Maximum number of students that can be shown on one page */
protected static $maxperpage = 5000;
/**
* List of allowed values for 'perpage' setting
* @var array $validperpage
*/
protected static $validperpage = [20, 100];
/**
* To store course data
* @var stdClass
*/
protected $course;
/**
* General structure representing grade items in course
* @var grade_structure
*/
protected $structure;
/**
* Constructor
*
* @param int $courseid The course id
* @param int|null $itemid The item id
* @param int|null $groupid The group id
*/
public function __construct(int $courseid, ?int $itemid, ?int $groupid = null) {
global $DB;
$this->courseid = $courseid;
$this->itemid = $itemid;
$this->groupid = $groupid;
$this->context = context_course::instance($this->courseid);
$this->course = $DB->get_record('course', ['id' => $courseid]);
$this->page = optional_param('page', 0, PARAM_INT);
$cache = \cache::make_from_params(\cache_store::MODE_SESSION, 'gradereport_singleview', 'perpage');
$perpage = optional_param('perpage', null, PARAM_INT);
if (!in_array($perpage, self::$validperpage) && ($perpage !== 0)) {
// Get from cache.
$perpage = $cache->get(get_class($this));
} else {
// Save to cache.
$cache->set(get_class($this), $perpage);
}
if (isset($perpage) && $perpage) {
$this->perpage = $perpage;
} else {
// Get from cache.
$perpage = $cache->get(get_class($this));
$this->perpage = ($perpage === 0) ? $perpage : min(self::$validperpage);
}
$this->init(empty($itemid));
}
/**
* Cache the grade_structure class
*/
public function setup_structure() {
$this->structure = new grade_structure();
$this->structure->modinfo = get_fast_modinfo($this->course);
}
/**
* Create a nice link from a thing (user or grade_item).
*
* @param string $screen
* @param int $itemid
* @param bool|null $display Should we wrap this in an anchor ?
* @return string The link
*/
public function format_link(string $screen, int $itemid, bool $display = null): string {
$url = new moodle_url('/grade/report/singleview/index.php', [
'id' => $this->courseid,
'item' => $screen,
'itemid' => $itemid,
'group' => $this->groupid,
]);
if ($display) {
return html_writer::link($url, $display);
} else {
return $url;
}
}
/**
* Get the grade_grade
*
* @param grade_item $item The grade_item
* @param int $userid The user id
* @return grade_grade
*/
public function fetch_grade_or_default(grade_item $item, int $userid): grade_grade {
$grade = grade_grade::fetch([
'itemid' => $item->id, 'userid' => $userid
]);
if (!$grade) {
$default = new stdClass;
$default->userid = $userid;
$default->itemid = $item->id;
$default->feedback = '';
$grade = new grade_grade($default, false);
}
$grade->grade_item = $item;
return $grade;
}
/**
* Get the default heading for the screen.
*
* @return string
*/
public function heading(): string {
return get_string('entrypage', 'gradereport_singleview');
}
/**
* Override this to init the screen.
*
* @param boolean $selfitemisempty True if no item has been selected yet.
*/
abstract public function init(bool $selfitemisempty = false);
/**
* Get the type of items in the list.
*
* @return null|string
*/
abstract public function item_type(): ?string;
/**
* Get the entire screen as a string.
*
* @return string
*/
abstract public function html(): string;
/**
* Does this screen support paging?
*
* @return bool
*/
public function supports_paging(): bool {
return true;
}
/**
* Default pager
*
* @return string
*/
public function pager(): string {
return '';
}
/**
* Initialise the js for this screen.
*/
public function js() {
global $PAGE;
$module = [
'name' => 'gradereport_singleview',
'fullpath' => '/grade/report/singleview/js/singleview.js',
'requires' => ['base', 'dom', 'event', 'event-simulate', 'io-base']
];
$PAGE->requires->strings_for_js(['overridenoneconfirm', 'removeoverride', 'removeoverridesave'],
'gradereport_singleview');
$PAGE->requires->js_init_call('M.gradereport_singleview.init', [], false, $module);
}
/**
* Process the data from a form submission.
*
* @param array|object $data
* @return stdClass of warnings
*/
public function process($data): stdClass {
$warnings = [];
$fields = $this->definition();
// Avoiding execution timeouts when updating
// a large amount of grades.
$progress = 0;
$progressbar = new \core\progress\display_if_slow();
$progressbar->start_html();
$progressbar->start_progress(get_string('savegrades', 'gradereport_singleview'), count((array) $data) - 1);
$changecount = [];
// This array is used to determine if the override should be excluded from being counted as a change.
$ignorevalues = [];
foreach ($data as $varname => $throw) {
$progressbar->progress($progress);
$progress++;
if (preg_match("/(\w+)_(\d+)_(\d+)/", $varname, $matches)) {
$itemid = $matches[2];
$userid = $matches[3];
} else {
continue;
}
$gradeitem = grade_item::fetch([
'id' => $itemid, 'courseid' => $this->courseid
]);
if (preg_match('/^old[oe]{1}/', $varname)) {
$elementname = preg_replace('/^old/', '', $varname);
if (!isset($data->$elementname)) {
// Decrease the progress because we've increased the
// size of the array we are iterating through.
$progress--;
$data->$elementname = false;
}
}
if (!in_array($matches[1], $fields)) {
continue;
}
if (!$gradeitem) {
continue;
}
$grade = $this->fetch_grade_or_default($gradeitem, $userid);
$classname = '\\gradereport_singleview\\local\\ui\\' . $matches[1];
$element = new $classname($grade);
$name = $element->get_name();
$oldname = "old$name";
$posted = $data->$name;
$format = $element->determine_format();
if ($format->is_textbox() and trim($data->$name) === '') {
$data->$name = null;
}
// Same value; skip.
if (isset($data->$oldname) && $data->$oldname == $posted) {
continue;
}
// If the user submits Exclude grade elements without the proper.
// permissions then we should refuse to update.
if ($matches[1] === 'exclude' && !has_capability('moodle/grade:manage', $this->context)){
$warnings[] = get_string('nopermissions', 'error', get_string('grade:manage', 'role'));
continue;
}
$msg = $element->set($posted);
// Value to check against our list of matchelements to ignore.
$check = explode('_', $varname, 2);
// Optional type.
if (!empty($msg)) {
$warnings[] = $msg;
if ($element instanceof \gradereport_singleview\local\ui\finalgrade) {
// Add this value to this list so that the override object that is coming next will also be skipped.
$ignorevalues[$check[1]] = $check[1];
// This item wasn't changed so don't add to the changecount.
continue;
}
}
// Check to see if this value has already been skipped.
if (array_key_exists($check[1], $ignorevalues)) {
continue;
}
if (preg_match('/_(\d+)_(\d+)/', $varname, $matchelement)) {
$changecount[$matchelement[0]] = 1;
}
}
// Some post-processing.
$eventdata = new stdClass;
$eventdata->warnings = $warnings;
$eventdata->post_data = $data;
$eventdata->instance = $this;
$eventdata->changecount = $changecount;
$progressbar->end_html();
return $eventdata;
}
/**
* By default, there are no options.
* @return array
*/
public function options(): array {
return [];
}
/**
* Should we show the group selector?
* @return bool
*/
public function display_group_selector(): bool {
return true;
}
/**
* Should we show the next prev selector?
* @return bool
*/
public function supports_next_prev(): bool {
return true;
}
/**
* Load a valid list of users for this gradebook as the screen "items".
*
* @deprecated since Moodle 4.3
* @return array A list of enroled users.
*/
protected function load_users(): array {
debugging('The function ' . __FUNCTION__ . '() is deprecated. Please use grade_report::get_gradable_users() instead.',
DEBUG_DEVELOPER);
return grade_report::get_gradable_users($this->courseid, $this->groupid);
}
/**
* Allow selection of number of items to display per page.
* @return string
*/
public function perpage_select(): string {
global $PAGE, $OUTPUT;
$url = new moodle_url($PAGE->url);
$numusers = count($this->items);
// Print per-page dropdown.
$pagingoptions = self::$validperpage;
if ($this->perpage) {
$pagingoptions[] = $this->perpage; // To make sure the current preference is within the options.
}
$pagingoptions = array_unique($pagingoptions);
sort($pagingoptions);
$pagingoptions = array_combine($pagingoptions, $pagingoptions);
if ($numusers > self::$maxperpage) {
$pagingoptions['0'] = self::$maxperpage;
} else {
$pagingoptions['0'] = get_string('all');
}
$perpagedata = [
'baseurl' => $url->out(false),
'options' => []
];
foreach ($pagingoptions as $key => $name) {
$perpagedata['options'][] = [
'name' => $name,
'value' => $key,
'selected' => $key == $this->perpage,
];
}
// The number of students per page is always limited even if it is claimed to be unlimited.
$this->perpage = $this->perpage ?: self::$maxperpage;
$perpagedata['pagingbar'] = $this->pager();
return $OUTPUT->render_from_template('gradereport_singleview/perpage', $perpagedata);;
}
}
@@ -0,0 +1,205 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The gradebook simple view - initial view to select your search options
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\screen;
use gradereport_singleview;
use moodle_url;
defined('MOODLE_INTERNAL') || die;
/**
* The gradebook simple view - initial view to select your search options
*
* @deprecated since Moodle 4.3
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class select extends screen {
/** @var \stdClass course data. */
public $item;
/**
* Initialise this screen
*
* @deprecated since Moodle 4.3
* @param bool $selfitemisempty Has an item been selected (will be false)
*/
public function init($selfitemisempty = false) {
global $DB;
debugging('The function ' . __FUNCTION__ . '() is deprecated as part of the deprecation process of the ' .
'\gradereport_singleview\local\screen\select class which is no longer used.', DEBUG_DEVELOPER);
$roleids = explode(',', get_config('moodle', 'gradebookroles'));
$this->items = [];
foreach ($roleids as $roleid) {
// Keeping the first user appearance.
$this->items = $this->items + get_role_users(
$roleid, $this->context, false, '',
'u.id, u.lastname, u.firstname', null, $this->groupid,
$this->perpage * $this->page, $this->perpage
);
}
$this->item = $DB->get_record('course', ['id' => $this->courseid]);
}
/**
* Get the type of items on this screen, not valid so return false.
*
* @deprecated since Moodle 4.3
* @return string|null
*/
public function item_type(): ?string {
debugging('The function ' . __FUNCTION__ . '() is deprecated as part of the deprecation process of the ' .
'\gradereport_singleview\local\screen\select class which is no longer used.', DEBUG_DEVELOPER);
return false;
}
/**
* Return the HTML for the page.
*
* @deprecated since Moodle 4.3
* @return string
*/
public function html(): string {
global $OUTPUT, $COURSE;
debugging('The function ' . __FUNCTION__ . '() is deprecated as part of the deprecation process of the ' .
'\gradereport_singleview\local\screen\select class which is no longer used.', DEBUG_DEVELOPER);
if ($this->itemid === null) {
$userlink = new \moodle_url('/grade/report/singleview/index.php', ['id' => $COURSE->id, 'item' => 'user_select']);
$gradelink = new \moodle_url('/grade/report/singleview/index.php', ['id' => $COURSE->id, 'item' => 'grade_select']);
$context = [
'courseid' => $COURSE->id,
'imglink' => $OUTPUT->image_url('zero_state', 'gradereport_singleview'),
'userzerolink' => $userlink->out(false),
'userselectactive' => false,
'gradezerolink' => $gradelink->out(false),
'gradeselectactive' => false,
'displaylabel' => false
];
return $OUTPUT->render_from_template('gradereport_singleview/zero_state', $context);
}
$html = '';
$types = gradereport_singleview\report\singleview::valid_screens();
foreach ($types as $type) {
$classname = "gradereport_singleview\\local\\screen\\{$type}";
$screen = new $classname($this->courseid, null, $this->groupid);
if (!$screen instanceof selectable_items) {
continue;
}
$options = $screen->options();
if (empty($options)) {
continue;
}
$params = [
'id' => $this->courseid,
'item' => $screen->item_type(),
'group' => $this->groupid
];
$url = new moodle_url('/grade/report/singleview/index.php', $params);
$select = new \single_select($url, 'itemid', $options, '', ['' => $screen->select_label()]);
$select->set_label($screen->select_label(), ['class' => 'accesshide']);
$html .= $OUTPUT->render($select);
}
$html = $OUTPUT->container($html, 'selectitems');
if (empty($html)) {
$OUTPUT->notification(get_string('noscreens', 'gradereport_singleview'));
}
return $html;
}
/**
* Should we show the next prev selector?
*
* @deprecated since Moodle 4.3
* @return bool
*/
public function supports_next_prev(): bool {
debugging('The function ' . __FUNCTION__ . '() is deprecated as part of the deprecation process of the ' .
'\gradereport_singleview\local\screen\select class which is no longer used.', DEBUG_DEVELOPER);
return false;
}
/**
* Should we show the base singlereport group selector?
*
* @deprecated since Moodle 4.3
* @return bool
*/
public function display_group_selector(): bool {
debugging('The function ' . __FUNCTION__ . '() is deprecated as part of the deprecation process of the ' .
'\gradereport_singleview\local\screen\select class which is no longer used.', DEBUG_DEVELOPER);
if ($this->itemid === null) {
return false;
}
return true;
}
/**
* Get the heading for the screen.
*
* @deprecated since Moodle 4.3
* @return string
*/
public function heading(): string {
debugging('The function ' . __FUNCTION__ . '() is deprecated as part of the deprecation process of the ' .
'\gradereport_singleview\local\screen\select class which is no longer used.', DEBUG_DEVELOPER);
return ' ';
}
/**
* Does this screen support paging?
*
* @deprecated since Moodle 4.3
* @return bool
*/
public function supports_paging(): bool {
debugging('The function ' . __FUNCTION__ . '() is deprecated as part of the deprecation process of the ' .
'\gradereport_singleview\local\screen\select class which is no longer used.', DEBUG_DEVELOPER);
return false;
}
}
@@ -0,0 +1,60 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Interface for a list of selectable things.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\screen;
defined('MOODLE_INTERNAL') || die;
/**
* Interface for a list of selectable things.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface selectable_items {
/**
* Get the description of this list
* @return string
*/
public function description(): string;
/**
* Get the label for the select box that chooses items for this page.
* @return string
*/
public function select_label(): string;
/**
* Get the list of options to show.
* @return array
*/
public function options(): array;
/**
* Get type of things in the list (gradeitem or user)
* @return string
*/
public function item_type(): string;
}
@@ -0,0 +1,265 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The gradebook simple view - base class for the table
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\screen;
use gradereport_singleview\local\ui\be_readonly;
use html_table;
use html_writer;
use stdClass;
use grade_grade;
use gradereport_singleview\local\ui\bulk_insert;
defined('MOODLE_INTERNAL') || die;
/**
* The gradebook simple view - base class for the table
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class tablelike extends screen implements be_readonly {
/**
* A list of table headers
* @var array $headers
*/
protected $headers = [];
/**
* A list of errors that mean we should not show the table
* @var array $initerrors
*/
protected $initerrors = [];
/**
* Describes the columns in the table
* @var array $definition
*/
protected $definition = [];
/**
* Total items
* @var int $total
*/
protected $total;
/**
* Table tab index
* @var int $index
*/
protected $index;
/**
* The grade item or user.
* @var mixed $item
*/
protected $item;
/**
* Format a row of the table
*
* @var mixed $item
* @return array
*/
abstract public function format_line($item): array;
/**
* Get the summary for this table.
*
* @return string
*/
abstract public function summary(): string;
/**
* Get the table headers
*
* @return array
*/
public function headers(): array {
return $this->headers;
}
/**
* Set the table headers
*
* @param array $overwrite New headers
* @return tablelike This
*/
public function set_headers(array $overwrite): tablelike {
$this->headers = $overwrite;
return $this;
}
/**
* Get the list of errors
*
* @return array
*/
public function init_errors(): array {
return $this->initerrors;
}
/**
* Set an error detected while building the page.
*
* @param string $mesg
*/
public function set_init_error(string $mesg) {
$this->initerrors[] = $mesg;
}
/**
* Get the table definition
*
* @return array The definition.
*/
public function definition(): array {
return $this->definition;
}
/**
* Set the table definition
*
* @param array $overwrite New definition
* @return tablelike This
*/
public function set_definition(array $overwrite): tablelike {
$this->definition = $overwrite;
return $this;
}
/**
* Get a element to generate the HTML for this table row
* @param grade_grade $grade The grade.
* @return array
*/
public function format_definition(grade_grade $grade): array {
$line = [];
foreach ($this->definition() as $i => $field) {
// Table tab index.
$tab = ($i * $this->total) + $this->index;
$classname = '\\gradereport_singleview\\local\\ui\\' . $field;
$html = new $classname($grade, $tab);
if ($field == 'finalgrade' and !empty($this->structure)) {
$html .= $this->structure->get_grade_action_menu($grade);
}
// Singleview users without proper permissions should be presented
// disabled checkboxes for the Exclude grade attribute.
if ($field == 'exclude' && !has_capability('moodle/grade:manage', $this->context)) {
$html->disabled = true;
}
$line[$field] = $html;
}
return $line;
}
/**
* Get the HTML for the whole table
* @return string
*/
public function html(): string {
global $OUTPUT;
if (!empty($this->initerrors)) {
$warnings = '';
foreach ($this->initerrors as $mesg) {
$warnings .= $OUTPUT->notification($mesg);
}
return $warnings;
}
$table = new html_table();
$table->id = 'singleview-grades';
$table->head = $this->headers();
$summary = $this->summary();
if (!empty($summary)) {
$table->caption = $summary;
$table->captionhide = true;
}
// To be used for extra formatting.
$this->index = 0;
$this->total = count($this->items);
foreach ($this->items as $item) {
if ($this->index >= ($this->perpage * $this->page) &&
$this->index < ($this->perpage * ($this->page + 1))) {
$table->data[] = $this->format_line($item);
}
$this->index++;
}
$data = new stdClass();
$data->table = $table;
$data->instance = $this;
$html = html_writer::table($table);
return html_writer::div($html, 'reporttable position-relative');
}
/**
* Get the HTML for the bulk insert form
*
* @return string
*/
public function bulk_insert() {
return html_writer::tag(
'div',
(new bulk_insert($this->item))->html(),
['class' => 'singleview_bulk', 'hidden' => 'hidden']
);
}
/**
* Return true if this is read-only.
*
* @return bool
*/
public function is_readonly(): bool {
global $USER;
return empty($USER->editing);
}
/**
* Get the buttons for saving changes.
* @param bool $disabled If button is disabled
*
* @return array
*/
public function buttons(bool $disabled = false): array {
global $OUTPUT;
$params = ['type' => 'submit', 'value' => get_string('save', 'gradereport_singleview')];
if ($disabled) {
$params['disabled'] = 'disabled';
}
return [$OUTPUT->render_from_template('gradereport_singleview/button', $params)];
}
}
@@ -0,0 +1,432 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The user screen.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\screen;
use grade_seq;
use gradereport_singleview;
use moodle_url;
use pix_icon;
use html_writer;
use gradereport_singleview\local\ui\range;
use gradereport_singleview\local\ui\bulk_insert;
use grade_item;
use grade_grade;
use stdClass;
defined('MOODLE_INTERNAL') || die;
/**
* The user screen.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user extends tablelike implements selectable_items {
/** @var array $categories A cache for grade_item categories */
private $categories = [];
/** @var int $requirespaging Do we have more items than the paging limit? */
private $requirespaging = true;
/** @var array get a valid user. */
public $item = [];
/**
* Get the label for the select box that chooses items for this page.
* @return string
*/
public function select_label(): string {
return get_string('selectgrade', 'gradereport_singleview');
}
/**
* Get the description for the screen.
*
* @return string
*/
public function description(): string {
return get_string('gradeitems', 'grades');
}
/**
* Convert the list of items to a list of options.
*
* @return array
*/
public function options(): array {
$result = [];
foreach ($this->items as $itemid => $item) {
$result[$itemid] = $item->get_name();
}
return $result;
}
/**
* Get the type of items on this screen.
*
* @return string
*/
public function item_type(): string {
return 'grade';
}
/**
* Init the screen
*
* @param bool $selfitemisempty Have we selected an item yet?
*/
public function init($selfitemisempty = false) {
if (!$selfitemisempty) {
$validusers = \grade_report::get_gradable_users($this->courseid, $this->groupid);
if (!isset($validusers[$this->itemid])) {
// If the passed user id is not valid, show the first user from the list instead.
$this->item = reset($validusers);
$this->itemid = $this->item->id;
} else {
$this->item = $validusers[$this->itemid];
}
}
$seq = new grade_seq($this->courseid, true);
$this->items = [];
foreach ($seq->items as $itemid => $item) {
if (grade::filter($item)) {
$this->items[$itemid] = $item;
}
}
// If we change perpage on pagination we might end up with a page that doesn't exist.
if ($this->perpage) {
$numpages = intval(count($this->items) / $this->perpage) + 1;
if ($numpages <= $this->page) {
$this->page = 0;
}
} else {
$this->page = 0;
}
$this->requirespaging = count($this->items) > $this->perpage;
$this->setup_structure();
$this->definition = [
'finalgrade', 'feedback', 'override', 'exclude'
];
$this->set_headers($this->original_headers());
}
/**
* Get the list of headers for the table.
*
* @return array List of headers
*/
public function original_headers(): array {
return [
get_string('assessmentname', 'gradereport_singleview'),
'', // For filter icon.
get_string('gradecategory', 'grades'),
get_string('gradenoun'),
get_string('range', 'grades'),
get_string('feedback', 'grades'),
get_string('override', 'gradereport_singleview'),
get_string('exclude', 'gradereport_singleview'),
];
}
/**
* Format each row of the table.
*
* @param grade_item $item
* @return array
*/
public function format_line($item): array {
global $OUTPUT;
$grade = $this->fetch_grade_or_default($item, $this->item->id);
$gradestatus = '';
$context = [
'hidden' => $grade->is_hidden(),
'locked' => $grade->is_locked(),
];
if (in_array(true, $context)) {
$context['classes'] = 'gradestatus';
$gradestatus = $OUTPUT->render_from_template('core_grades/status_icons', $context);
}
// Create a fake gradetreeitem so we can call get_element_header().
// The type logic below is from grade_category->_get_children_recursion().
$gradetreeitem = [];
$type = in_array($item->itemtype, ['course', 'category']) ? "{$item->itemtype}item" : 'item';
$gradetreeitem['type'] = $type;
$gradetreeitem['object'] = $item;
$gradetreeitem['userid'] = $this->item->id;
$itemname = \grade_helper::get_element_header($gradetreeitem, true, false, false, false, true);
$grade->label = $item->get_name();
$formatteddefinition = $this->format_definition($grade);
$itemicon = html_writer::div($this->format_icon($item), 'mr-1');
$itemtype = \html_writer::span(\grade_helper::get_element_type_string($gradetreeitem),
'd-block text-uppercase small dimmed_text');
$itemtitle = html_writer::div($itemname, 'rowtitle');
$itemcontent = html_writer::div($itemtype . $itemtitle);
$line = [
html_writer::div($itemicon . $itemcontent, "{$type} d-flex align-items-center"),
$this->get_item_action_menu($item),
$this->category($item),
$formatteddefinition['finalgrade'] . $gradestatus,
new range($item),
$formatteddefinition['feedback'],
$formatteddefinition['override'],
$formatteddefinition['exclude'],
];
$lineclasses = [
'gradeitem',
'action',
'category',
'grade',
'range',
];
$outputline = [];
$i = 0;
foreach ($line as $key => $value) {
$cell = new \html_table_cell($value);
if ($isheader = $i == 0) {
$cell->header = $isheader;
$cell->scope = "row";
}
if (array_key_exists($key, $lineclasses)) {
$cell->attributes['class'] = $lineclasses[$key];
}
$outputline[] = $cell;
$i++;
}
return $outputline;
}
/**
* Helper to get the icon for an item.
*
* @param grade_item $item
* @return string
*/
private function format_icon($item): string {
$element = ['type' => 'item', 'object' => $item];
return \grade_helper::get_element_icon($element);
}
/**
* Return the action menu HTML for the grade item.
*
* @param grade_item $item
* @return mixed
*/
private function get_item_action_menu(grade_item $item) {
global $OUTPUT;
$menuitems = [];
$url = new moodle_url($this->format_link('grade', $item->id));
$title = get_string('showallgrades', 'core_grades');
$menuitems[] = new \action_menu_link_secondary($url, null, $title);
$menu = new \action_menu($menuitems);
$icon = $OUTPUT->pix_icon('i/moremenu', get_string('actions'));
$extraclasses = 'btn btn-link btn-icon icon-size-3 d-flex align-items-center justify-content-center';
$menu->set_menu_trigger($icon, $extraclasses);
$menu->set_menu_left();
$menu->set_boundary('window');
return $OUTPUT->render($menu);
}
/**
* Helper to get the category for an item.
*
* @param grade_item $item
* @return string
*/
private function category(grade_item $item): string {
global $DB;
if (empty($item->categoryid)) {
if ($item->itemtype == 'course') {
return $this->course->fullname;
}
$params = ['id' => $item->iteminstance];
$elem = $DB->get_record('grade_categories', $params);
return $elem->fullname;
}
if (!isset($this->categories[$item->categoryid])) {
$category = $item->get_parent_category();
$this->categories[$category->id] = $category;
}
return $this->categories[$item->categoryid]->get_name();
}
/**
* Get the heading for the page.
*
* @return string
*/
public function heading(): string {
global $PAGE;
$headinglangstring = $PAGE->user_is_editing() ? 'gradeuseredit' : 'gradeuser';
return get_string($headinglangstring, 'gradereport_singleview', fullname($this->item));
}
/**
* Get the summary for this table.
*
* @return string
*/
public function summary(): string {
return get_string('summaryuser', 'gradereport_singleview');
}
/**
* Default pager
*
* @return string
*/
public function pager(): string {
global $OUTPUT;
if (!$this->supports_paging()) {
return '';
}
return $OUTPUT->paging_bar(
count($this->items), $this->page, $this->perpage,
new moodle_url('/grade/report/singleview/index.php', [
'perpage' => $this->perpage,
'id' => $this->courseid,
'group' => $this->groupid,
'itemid' => $this->itemid,
'item' => 'user'
])
);
}
/**
* Does this page require paging?
*
* @return bool
*/
public function supports_paging(): bool {
return $this->requirespaging;
}
/**
* Process the data from the form.
*
* @param array $data
* @return stdClass of warnings
*/
public function process($data): stdClass {
$bulk = new bulk_insert($this->item);
// Bulk insert messages the data to be passed in
// ie: for all grades of empty grades apply the specified value.
if ($bulk->is_applied($data)) {
$filter = $bulk->get_type($data);
$insertvalue = $bulk->get_insert_value($data);
$userid = $this->item->id;
foreach ($this->items as $gradeitemid => $gradeitem) {
$null = $gradeitem->gradetype == GRADE_TYPE_SCALE ? -1 : '';
$field = "finalgrade_{$gradeitem->id}_{$this->itemid}";
if (isset($data->$field)) {
continue;
}
$oldfinalgradefield = "oldfinalgrade_{$gradeitem->id}_{$this->itemid}";
// Bulk grade changes for all grades need to be processed and shouldn't be skipped if they had a previous grade.
if ($gradeitem->is_course_item() || ($filter != 'all' && !empty($data->$oldfinalgradefield))) {
if ($gradeitem->is_course_item()) {
// The course total should not be overridden.
unset($data->$field);
unset($data->oldfinalgradefield);
$oldoverride = "oldoverride_{$gradeitem->id}_{$this->itemid}";
unset($data->$oldoverride);
$oldfeedback = "oldfeedback_{$gradeitem->id}_{$this->itemid}";
unset($data->$oldfeedback);
}
continue;
}
$grade = grade_grade::fetch([
'itemid' => $gradeitemid,
'userid' => $userid
]);
$data->$field = empty($grade) ? $null : $grade->finalgrade;
$data->{"old$field"} = $data->$field;
}
foreach ($data as $varname => $value) {
if (preg_match('/^oldoverride_(\d+)_(\d+)/', $varname, $matches)) {
// If we've selected overriding all grades.
if ($filter == 'all') {
$override = "override_{$matches[1]}_{$matches[2]}";
$data->$override = '1';
}
}
if (!preg_match('/^finalgrade_(\d+)_(\d+)/', $varname, $matches)) {
continue;
}
$gradeitem = grade_item::fetch([
'courseid' => $this->courseid,
'id' => $matches[1],
]);
$isscale = ($gradeitem->gradetype == GRADE_TYPE_SCALE);
$empties = (trim($value ?? '') === '' || ($isscale && $value == -1));
if ($filter == 'all' || $empties) {
$data->$varname = ($isscale && empty($insertvalue)) ? -1 : $insertvalue;
}
}
}
return parent::process($data);
}
}
@@ -0,0 +1,87 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace gradereport_singleview\local\screen;
use gradereport_singleview;
/**
* The user search screen.
*
* @package gradereport_singleview
* @copyright 2022 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_select extends screen {
public function init($selfitemisempty = false) {
}
/**
* Return the HTML for the page.
*
* @return string
*/
public function html(): string {
global $OUTPUT, $COURSE, $USER;
$userlink = new \moodle_url('/grade/report/singleview/index.php', ['id' => $COURSE->id, 'item' => 'user_select']);
$gradelink = new \moodle_url('/grade/report/singleview/index.php', ['id' => $COURSE->id, 'item' => 'grade_select']);
$gpr = new \grade_plugin_return(['type' => 'report', 'plugin' => 'singleview', 'courseid' => $COURSE->id,
'userid' => $USER->id]);
$context = [
'courseid' => $gpr->courseid,
'imglink' => $OUTPUT->image_url('zero_state_user', 'gradereport_singleview'),
'userzerolink' => $userlink->out(false),
'userselectactive' => true,
'gradezerolink' => $gradelink->out(false),
'gradeselectactive' => false,
'displaylabel' => true,
'groupmodeenabled' => $COURSE->groupmode,
'groupactionbaseurl' => 'index.php?item=user_select',
'groupid' => $gpr->groupid
];
return $OUTPUT->render_from_template('gradereport_singleview/zero_state_user', $context);
}
public function item_type(): ?string {
return false;
}
/**
* Should we show the base singlereport group selector?
* @return bool
*/
public function display_group_selector(): bool {
return false;
}
/**
* Get the heading for the screen.
*
* @return string
*/
public function heading(): string {
return ' ';
}
/**
* Does this screen support paging?
*
* @return bool
*/
public function supports_paging(): bool {
return false;
}
}
@@ -0,0 +1,52 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class that builds an element tree that can be converted to a string
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* Class that builds an element tree that can be converted to a string
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class attribute_format {
/**
* Used to convert this class to an "element" which can be converted to a string.
* @return element
*/
abstract public function determine_format(): element;
/**
* Convert this to an element and then to a string
* @return string
*/
public function __toString(): string {
return $this->determine_format()->html();
}
}
@@ -0,0 +1,42 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Is this thing checked?
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* Is this thing checked?
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface be_checked {
/**
* Return true if this is checked.
* @return bool
*/
public function is_checked(): bool;
}
@@ -0,0 +1,40 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* be_disabled interface.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* Simple interface implemented to add behaviour that an element can be checked to see
* if it should be disabled.
*/
interface be_disabled {
/**
* Am I disabled ?
*
* @return bool
*/
public function is_disabled(): bool;
}
@@ -0,0 +1,38 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* be_readonly interface.
*
* @package gradereport_singleview
* @copyright 2022 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
/**
* Simple interface implemented to add behaviour that an element can be checked to see
* if it should be read-only.
*/
interface be_readonly {
/**
* Return true if this is read-only.
*
* @return bool
*/
public function is_readonly(): bool;
}
@@ -0,0 +1,125 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Checkbox element used for bulk inserting values in the gradebook.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* Checkbox element used for bulk inserting values in the gradebook.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class bulk_insert extends element {
/** @var string $applyname To store the "apply" suffix. */
protected $applyname;
/** @var string $selectname To store the "type" suffix. */
protected $selectname;
/** @var string $insertname To store the "value" suffix. */
protected $insertname;
/**
* Constructor
*
* @param mixed $item The grade item or user.
*/
public function __construct($item) {
$this->name = 'bulk_' . $item->id;
$this->applyname = $this->name_for('apply');
$this->selectname = $this->name_for('type');
$this->insertname = $this->name_for('value');
}
/**
* Is this checkbox checked?
*
* @param array|object $data The form data
* @return bool
*/
public function is_applied($data): bool {
return isset($data->{$this->applyname});
}
/**
* Get the type of this input (user or grade)
*
* @param array|object $data The form data
* @return string
*/
public function get_type($data): string {
return $data->{$this->selectname};
}
/**
* Get the value from either the user or grade.
*
* @param array|object $data The form data
* @return string
*/
public function get_insert_value($data): string {
return $data->{$this->insertname};
}
/**
* Generate the html for this form element.
*
* @return string HTML
*/
public function html(): string {
global $OUTPUT;
$text = new text_attribute($this->insertname, "0", 'bulk');
$context = (object) [
'label' => get_string('bulklegend', 'gradereport_singleview'),
'applylabel' => get_string('bulkperform', 'gradereport_singleview'),
'applyname' => $this->applyname,
'menuname' => $this->selectname,
'menulabel' => get_string('bulkappliesto', 'gradereport_singleview'),
'menuoptions' => [
['value' => 'all', 'name' => get_string('all_grades', 'gradereport_singleview')],
['value' => 'blanks', 'name' => get_string('blanks', 'gradereport_singleview'), 'selected' => true],
],
'valuename' => $this->insertname,
'valuefield' => $text->html()
];
return $OUTPUT->render_from_template('gradereport_singleview/bulk_insert', $context);
}
/**
* This form element has 3 elements with different suffixes.
* Generate the name with the suffix.
*
* @param string $extend The suffix.
* @return string
*/
private function name_for($extend) {
return "{$this->name}_$extend";
}
}
@@ -0,0 +1,122 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* A checkbox ui element.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
use html_writer;
defined('MOODLE_INTERNAL') || die;
/**
* A checkbox ui element.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class checkbox_attribute extends element {
/** @var bool $ischecked Is it checked? */
private $ischecked;
/** @var bool If this is a read-only input. */
private bool $isreadonly;
/** @var int Is this element locked either 0 or a time. */
protected int $locked;
/**
* Constructor
*
* @param string $name The element name
* @param string $label The label for the form element
* @param bool $ischecked Is this thing on?
* @param int $locked Is this element locked either 0 or a time.
* @param bool $isreadonly If this is a read-only input.
*/
public function __construct(string $name, string $label, bool $ischecked = false, int $locked=0, bool $isreadonly = false) {
$this->ischecked = $ischecked;
$this->locked = $locked;
$this->isreadonly = $isreadonly;
parent::__construct($name, 1, $label);
}
/**
* Nasty function allowing checkbox logic to escape the class.
* @return bool
*/
public function is_checkbox(): bool {
return true;
}
/**
* Generate the HTML
*
* @return string
*/
public function html(): string {
global $OUTPUT;
$attributes = [
'type' => 'checkbox',
'name' => $this->name,
'value' => 1,
'id' => $this->name
];
// UCSB fixed user should not be able to override locked grade.
if ( $this->locked) {
$attributes['disabled'] = 'DISABLED';
}
$hidden = [
'type' => 'hidden',
'name' => 'old' . $this->name
];
if ($this->ischecked) {
$attributes['checked'] = 'CHECKED';
$hidden['value'] = 1;
}
$type = "override";
if (preg_match("/^exclude/", $this->name)) {
$type = "exclude";
}
if (!$this->isreadonly) {
return (
html_writer::tag('label',
get_string($type . 'for', 'gradereport_singleview', $this->label),
['for' => $this->name, 'class' => 'accesshide']) .
html_writer::empty_tag('input', $attributes) .
html_writer::empty_tag('input', $hidden)
);
} else if ($this->ischecked) {
return $OUTPUT->pix_icon('i/checked', get_string('selected', 'core_form'),
'moodle', ['class' => 'overrideexcludecheck']);
} else {
return '';
}
}
}
@@ -0,0 +1,122 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Drop down list (select list) element
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* Drop down list (select list) element
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dropdown_attribute extends element {
/**
* Who is selected?
* @var string $selected
*/
private $selected;
/**
* List of options
* @var array $options
*/
private $options;
/**
* Is this input disabled.
* @var bool $isdisabled
*/
private $isdisabled;
/** @var bool If this is a read-only input. */
private bool $isreadonly;
/**
* Constructor
*
* @param string $name The first bit of the name of this input.
* @param array $options The options list for this select.
* @param string $label The form label for this input.
* @param string $selected The name of the selected item in this input.
* @param bool $isdisabled Are we disabled?
* @param bool $isreadonly If this is a read-only input.
*/
public function __construct(
string $name,
array $options,
string $label,
string $selected = '',
bool $isdisabled = false,
bool $isreadonly = false
) {
$this->selected = $selected;
$this->options = $options;
$this->isdisabled = $isdisabled;
$this->isreadonly = $isreadonly;
parent::__construct($name, $selected, $label);
}
/**
* Nasty function spreading dropdown logic around.
*
* @return bool
*/
public function is_dropdown(): bool {
return true;
}
/**
* Render this element as html.
*
* @return string
*/
public function html(): string {
global $OUTPUT;
$options = $this->options;
$selected = $this->selected;
$context = [
'name' => $this->name,
'value' => $this->selected,
'text' => $options[$selected],
'disabled' => !empty($this->isdisabled),
'readonly' => $this->isreadonly,
'options' => array_map(function($option) use ($options, $selected) {
return [
'name' => $options[$option],
'value' => $option,
'selected' => $selected == $option
];
}, array_keys($options)),
'label' => get_string('gradefor', 'gradereport_singleview', $this->label),
];
return $OUTPUT->render_from_template('gradereport_singleview/dropdown_attribute', $context);
}
}
@@ -0,0 +1,98 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* UI Element for an excluded grade_grade.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* UI Element for an excluded grade_grade.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class element {
/**
* The first bit of the name for this input.
* @var string $name
*/
public $name;
/**
* The value for this input.
* @var string $value
*/
public $value;
/**
* The form label for this input.
* @var string $label
*/
public $label;
/**
* Constructor
*
* @param string $name The first bit of the name for this input
* @param string $value The value for this input
* @param string $label The label for this form field
*/
public function __construct(string $name, string $value, string $label) {
$this->name = $name;
$this->value = $value;
$this->label = $label;
}
/**
* Nasty function used for spreading checkbox logic all around
* @return bool
*/
public function is_checkbox(): bool {
return false;
}
/**
* Nasty function used for spreading textbox logic all around
* @return bool
*/
public function is_textbox(): bool {
return false;
}
/**
* Nasty function used for spreading dropdown logic all around
* @return bool
*/
public function is_dropdown(): bool {
return false;
}
/**
* Return the HTML
* @return string
*/
abstract public function html(): string;
}
@@ -0,0 +1,62 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Element that just generates some text.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* Element that just generates some text.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class empty_element extends element {
/** @var string message text. */
private $text;
/**
* Constructor
*
* @param string|null $msg The text
*/
public function __construct(?string $msg = null) {
if (is_null($msg)) {
$this->text = '&nbsp;';
} else {
$this->text = $msg;
}
}
/**
* Generate the html (simple case)
*
* @return string HTML
*/
public function html(): string {
return $this->text;
}
}
@@ -0,0 +1,142 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class that represents the exclude checkbox on a grade_grade.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
use grade_grade;
/**
* Class that represents the exclude checkbox on a grade_grade.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class exclude extends grade_attribute_format implements be_checked, be_disabled, be_readonly {
/**
* The name of the input
* @var string $name
*/
public $name = 'exclude';
/**
* Is the checkbox disabled?
* @var bool $disabled
*/
public $disabled = false;
/**
* Is it checked?
*
* @return bool
*/
public function is_checked(): bool {
return $this->grade->is_excluded();
}
/**
* Is it disabled?
*
* @return bool
*/
public function is_disabled(): bool {
return $this->disabled;
}
/**
* Return true if this is read-only.
*
* @return bool
*/
public function is_readonly(): bool {
global $USER;
return empty($USER->editing);
}
/**
* Generate the element used to render the UI
*
* @return element
*/
public function determine_format(): element {
return new checkbox_attribute(
$this->get_name(),
$this->get_label(),
$this->is_checked(),
$this->is_disabled(),
$this->is_readonly()
);
}
/**
* Get the label for the form input
*
* @return string
*/
public function get_label(): string {
if (!isset($this->grade->label)) {
$this->grade->label = '';
}
return $this->grade->label;
}
/**
* Set the value that was changed in the form.
*
* @param string $value The value to set.
* @return mixed string|bool An error message or false.
*/
public function set($value) {
if (empty($this->grade->id)) {
if (empty($value)) {
return false;
}
$gradeitem = $this->grade->grade_item;
// Fill in arbitrary grade to be excluded.
$gradeitem->update_final_grade(
$this->grade->userid, null, 'singleview', null, FORMAT_MOODLE
);
$gradeparams = [
'userid' => $this->grade->userid,
'itemid' => $this->grade->itemid
];
$this->grade = grade_grade::fetch($gradeparams);
$this->grade->grade_item = $gradeitem;
}
$state = !($value == 0);
$this->grade->set_excluded($state);
$this->grade->grade_item->get_parent_category()->force_regrading();
return false;
}
}
@@ -0,0 +1,136 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class used to render a feedback input box.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* Class used to render a feedback input box.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class feedback extends grade_attribute_format implements unique_value, be_disabled, be_readonly {
/**
* Name of this input
* @var string $name
*/
public $name = 'feedback';
/**
* Get the value for this input.
*
* @return string The value
*/
public function get_value(): ?string {
return $this->grade->feedback ? $this->grade->feedback : '';
}
/**
* Get the string to use in the label for this input.
*
* @return string The label text
*/
public function get_label(): string {
if (!isset($this->grade->label)) {
$this->grade->label = '';
}
return $this->grade->label;
}
/**
* Determine if this input should be disabled based on the other settings.
*
* @return boolean Should this input be disabled when the page loads.
*/
public function is_disabled(): bool {
$locked = 0;
$gradeitemlocked = 0;
$overridden = 0;
/* Disable editing if grade item or grade score is locked
* if any of these items are set, then we will disable editing
* at some point, we might want to show the reason for the lock
* this code could be simplified, but its more readable for steve's little mind
*/
if (!empty($this->grade->locked)) {
$locked = 1;
}
if (!empty($this->grade->grade_item->locked)) {
$gradeitemlocked = 1;
}
if ($this->grade->grade_item->is_overridable_item() and !$this->grade->is_overridden()) {
$overridden = 1;
}
return ($locked || $gradeitemlocked || $overridden);
}
/**
* Return true if this is read-only.
*
* @return bool
*/
public function is_readonly(): bool {
global $USER;
return empty($USER->editing);
}
/**
* Create a text_attribute for this ui element.
*
* @return element
*/
public function determine_format(): element {
return new text_attribute(
$this->get_name(),
$this->get_value(),
$this->get_label(),
$this->is_disabled(),
$this->is_readonly()
);
}
/**
* Update the value for this input.
*
* @param string $value The new feedback value.
* @return null|string Any error message
*/
public function set($value) {
$finalgrade = false;
$trimmed = trim($value);
if (empty($trimmed)) {
$feedback = null;
} else {
$feedback = $value;
}
$this->grade->grade_item->update_final_grade(
$this->grade->userid, $finalgrade, 'singleview',
$feedback, FORMAT_MOODLE
);
}
}
@@ -0,0 +1,218 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* UI element representing the finalgrade column.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
use stdClass;
/**
* UI element representing the finalgrade column.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class finalgrade extends grade_attribute_format implements unique_value, be_disabled, be_readonly {
/**
* Name of this input
* @var string $name
*/
public $name = 'finalgrade';
/**
* Get the value for this input.
*
* @return null|string The value based on the grade_grade.
*/
public function get_value(): ?string {
$this->label = $this->grade->grade_item->itemname;
$val = $this->grade->finalgrade;
if ($this->grade->grade_item->scaleid) {
return $val ? (int)$val : -1;
} else {
return $val ? format_float($val, $this->grade->grade_item->get_decimals()) : '';
}
}
/**
* Get the label for this input.
*
* @return string The label for this form input.
*/
public function get_label(): string {
if (!isset($this->grade->label)) {
$this->grade->label = '';
}
return $this->grade->label;
}
/**
* Is this input field disabled.
*
* @return bool Set disabled on this input or not.
*/
public function is_disabled(): bool {
$locked = 0;
$gradeitemlocked = 0;
$overridden = 0;
// Disable editing if grade item or grade score is locked
// if any of these items are set, then we will disable editing
// at some point, we might want to show the reason for the lock
// this code could be simplified, but its more readable for steve's little mind.
if (!empty($this->grade->locked)) {
$locked = 1;
}
if (!empty($this->grade->grade_item->locked)) {
$gradeitemlocked = 1;
}
if ($this->grade->grade_item->is_overridable_item() and !$this->grade->is_overridden()) {
$overridden = 1;
}
return ($locked || $gradeitemlocked || $overridden);
}
/**
* Return true if this is read-only.
*
* @return bool
*/
public function is_readonly(): bool {
global $USER;
return empty($USER->editing);
}
/**
* Create the element for this column.
*
* @return element
*/
public function determine_format(): element {
global $CFG;
if ($this->grade->grade_item->load_scale()) {
$scale = $this->grade->grade_item->load_scale();
$options = [-1 => get_string('nograde')];
foreach ($scale->scale_items as $i => $name) {
$options[$i + 1] = $name;
}
return new dropdown_attribute(
$this->get_name(),
$options,
$this->get_label(),
$this->get_value(),
$this->is_disabled(),
$this->is_readonly()
);
} else {
$textattribute = new text_attribute(
$this->get_name(),
$this->get_value(),
$this->get_label(),
$this->is_disabled(),
$this->is_readonly()
);
// Set min/max attributes, if applicable.
$textattribute->set_type('number');
$gradeitem = $this->grade->grade_item;
$decimals = $gradeitem->get_decimals();
// Min attribute.
$minvalue = null;
if (isset($gradeitem->grademin)) {
$minvalue = format_float($gradeitem->grademin, $decimals);
}
$textattribute->set_min($minvalue);
// Max attribute.
$maxvalue = null;
if (isset($gradeitem->grademax) && empty($CFG->unlimitedgrades)) {
$maxvalue = format_float($gradeitem->grademax, $decimals);
}
$textattribute->set_max($maxvalue);
return $textattribute;
}
}
/**
* Save the altered value for this field.
*
* @param string $value The new value.
* @return string Any error string
*/
public function set($value) {
$userid = $this->grade->userid;
$gradeitem = $this->grade->grade_item;
$feedback = false;
$feedbackformat = false;
if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
$value = (int)unformat_float($value);
if ($value == -1) {
$finalgrade = null;
} else {
$finalgrade = $value;
}
} else {
$finalgrade = unformat_float($value);
}
$errorstr = '';
if ($finalgrade) {
$bounded = $gradeitem->bounded_grade($finalgrade);
if ($bounded > $finalgrade) {
$errorstr = 'lessthanmin';
} else if ($bounded < $finalgrade) {
$errorstr = 'morethanmax';
}
}
if ($errorstr) {
$user = get_complete_user_data('id', $userid);
$gradestr = new stdClass;
if (has_capability('moodle/site:viewfullnames', \context_course::instance($gradeitem->courseid))) {
$gradestr->username = fullname($user, true);
} else {
$gradestr->username = fullname($user);
}
$gradestr->itemname = $this->grade->grade_item->get_name();
$errorstr = get_string($errorstr, 'grades', $gradestr);
return $errorstr;
}
// Only update grades if there are no errors.
$gradeitem->update_final_grade($userid, $finalgrade, 'singleview', $feedback, FORMAT_MOODLE,
null, null, true);
return '';
}
}
@@ -0,0 +1,83 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Abstract class for a form element representing something about a grade_grade.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
use grade_grade;
defined('MOODLE_INTERNAL') || die;
/**
* Abstract class for a form element representing something about a grade_grade.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class grade_attribute_format extends attribute_format implements unique_name {
/**
* The first part of the name attribute of the form input
* @var string $name
*/
public $name;
/**
* The label of the input
* @var null|string $label
*/
public $label;
/**
* The grade_grade of the input
* @var grade_grade $grade
*/
public $grade;
/**
* Constructor
*
* @param grade_grade $grade The grade_grade we are editing.
*/
public function __construct($grade = 0) {
$this->grade = $grade;
}
/**
* Get a unique name for this form input
*
* @return string The form input name attribute.
*/
public function get_name(): string {
return "{$this->name}_{$this->grade->itemid}_{$this->grade->userid}";
}
/**
* Should be overridden by the child class to save the value returned in this input.
*
* @param string $value The value from the form.
* @return string Any error message
*/
abstract public function set($value);
}
@@ -0,0 +1,126 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* An override grade checkbox element
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* An override grade checkbox element
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class override extends grade_attribute_format implements be_checked, be_disabled, be_readonly {
/**
* The name for this input
* @var string $name
*/
public $name = 'override';
/**
* Is this input checked
*
* @return bool
*/
public function is_checked(): bool {
return $this->grade->is_overridden();
}
/**
* Is this input disabled
*
* @return bool
*/
public function is_disabled(): bool {
$lockedgrade = $lockedgradeitem = 0;
if (!empty($this->grade->locked)) {
$lockedgrade = 1;
}
if (!empty($this->grade->grade_item->locked)) {
$lockedgradeitem = 1;
}
return ($lockedgrade || $lockedgradeitem);
}
/**
* Return true if this is read-only.
*
* @return bool
*/
public function is_readonly(): bool {
global $USER;
return empty($USER->editing);
}
/**
* Get the label for this form element.
*
* @return string
*/
public function get_label(): string {
if (!isset($this->grade->label)) {
$this->grade->label = '';
}
return $this->grade->label;
}
/**
* Generate the element for this form input.
*
* @return element
*/
public function determine_format(): element {
if (!$this->grade->grade_item->is_overridable_item()) {
return new empty_element();
}
return new checkbox_attribute(
$this->get_name(),
$this->get_label(),
$this->is_checked(),
$this->is_disabled(),
$this->is_readonly()
);
}
/**
* Save the modified value of this form element.
*
* @param string $value The new value to set
* @return mixed string|false Any error message
*/
public function set($value) {
if (empty($this->grade->id)) {
return false;
}
$state = !($value == 0);
$this->grade->set_overridden($state);
$this->grade->grade_item->force_regrading();
return false;
}
}
@@ -0,0 +1,64 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* UI element that generates a min/max range (text only).
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
use grade_item;
defined('MOODLE_INTERNAL') || die;
/**
* UI element that generates a grade_item min/max range (text only).
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class range extends attribute_format {
/** @var grade_item To store the grade item. */
private grade_item $item;
/**
* Constructor
* @param grade_item $item The grade item
*/
public function __construct(grade_item $item) {
$this->item = $item;
}
/**
* Build this UI element.
*
* @return element
*/
public function determine_format(): element {
$decimals = $this->item->get_decimals();
$min = format_float($this->item->grademin, $decimals);
$max = format_float($this->item->grademax, $decimals);
return new empty_element("$min - $max");
}
}
@@ -0,0 +1,152 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* UI element for a text input field.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* UI element for a text input field.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class text_attribute extends element {
/**
* Is this input disabled?
* @var bool $isdisabled
*/
private $isdisabled;
/** @var bool If this is a read-only input. */
private bool $isreadonly;
/**
* @var string|null The input type to pass to the template.
* This defaults to text but can be overridden to number for grade inputs.
*/
private $type = null;
/**
* @var string|null The value to set for the input's `min` attribute.
* This is set if a minimum grade is provided for the grade input field.
*/
private $min = null;
/**
* @var string|null The value to set for the input's `max` attribute.
* This is set if a maximum grade is provided for the grade input field.
*/
private $max = null;
/**
* Constructor
*
* @param string $name The input name (the first bit)
* @param string $value The input initial value.
* @param string $label The label for this input field.
* @param bool $isdisabled Is this input disabled.
* @param bool $isreadonly If this is a read-only input.
*/
public function __construct(string $name, string $value, string $label, bool $isdisabled = false, bool $isreadonly = false) {
$this->isdisabled = $isdisabled;
$this->isreadonly = $isreadonly;
parent::__construct($name, $value, $label);
}
/**
* Nasty function allowing custom textbox behaviour outside the class.
* @return bool Is this a textbox.
*/
public function is_textbox(): bool {
return true;
}
/**
* Render the html for this field.
* @return string The HTML.
*/
public function html(): string {
global $OUTPUT;
$context = (object) [
'id' => $this->name,
'name' => $this->name,
'value' => $this->value,
'disabled' => $this->isdisabled,
'readonly' => $this->isreadonly,
];
$context->label = '';
if (preg_match("/^feedback/", $this->name)) {
$context->label = get_string('feedbackfor', 'gradereport_singleview', $this->label);
} else if (preg_match("/^finalgrade/", $this->name)) {
$context->label = get_string('gradefor', 'gradereport_singleview', $this->label);
}
// Set this input field with type="number" if the decimal separator for current language is set to a period.
// Other decimal separators may not be recognised by browsers yet which may cause issues when entering grades.
$decsep = get_string('decsep', 'core_langconfig');
$context->isnumeric = $this->type === 'number' && $decsep === '.';
if ($context->isnumeric) {
$context->type = $this->type;
$context->min = $this->min;
$context->max = $this->max;
}
return $OUTPUT->render_from_template('gradereport_singleview/text_attribute', $context);
}
/**
* Input type setter.
*
* @param string|null $type
* @return void
*/
public function set_type(?string $type): void {
$this->type = $type;
}
/**
* Min attribute setter.
*
* @param string|null $min
* @return void
*/
public function set_min(?string $min): void {
$this->min = $min;
}
/**
* Max attribute setter.
*
* @param string|null $max
* @return void
*/
public function set_max(?string $max): void {
$this->max = $max;
}
}
@@ -0,0 +1,43 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* A form element with a name field.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* A form element with a name field.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface unique_name {
/**
* Get the name for this form element
* @return string
*/
public function get_name(): string;
}
@@ -0,0 +1,42 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The gradebook simple view - UI factory
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\local\ui;
defined('MOODLE_INTERNAL') || die;
/**
* Simple interface for an item with a value.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface unique_value {
/**
* Get the value for this item.
* @return null|string
*/
public function get_value(): ?string;
}
@@ -0,0 +1,103 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace gradereport_singleview\output;
use moodle_url;
use renderer_base;
use gradereport_singleview\report\singleview;
/**
* Renderable class for the action bar elements in the single view report page.
*
* @package gradereport_singleview
* @copyright 2022 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class action_bar extends \core_grades\output\action_bar {
/** @var singleview $report The single view report class. */
protected singleview $report;
/** @var string $itemtype The single view item type. */
protected string $itemtype;
/**
* The class constructor.
*
* @param \context $context The context object.
* @param singleview $report The single view report class.
* @param string $itemtype The single view item type.
*/
public function __construct(\context $context, singleview $report, string $itemtype) {
parent::__construct($context);
$this->report = $report;
$this->itemtype = $itemtype;
}
/**
* Returns the template for the action bar.
*
* @return string
*/
public function get_template(): string {
return 'gradereport_singleview/action_bar';
}
/**
* Export the data for the mustache template.
*
* @param \renderer_base $output renderer to be used to render the action bar elements.
* @return array
*/
public function export_for_template(renderer_base $output) {
global $USER;
$courseid = $this->context->instanceid;
// Get the data used to output the general navigation selector.
$generalnavselector = new \core_grades\output\general_action_bar(
$this->context,
new moodle_url('/grade/report/singleview/index.php', ['id' => $courseid]),
'report',
'singleview'
);
$data = $generalnavselector->export_for_template($output);
// The data required to output the page toggle element.
$data['pagetoggler'] = [
'displaylabel' => true,
'userselectactive' => $this->itemtype === 'user',
'gradeselectactive' => $this->itemtype === 'grade',
'gradezerolink' => (new moodle_url('/grade/report/singleview/index.php',
['id' => $courseid, 'item' => 'grade_select']))->out(false),
'userzerolink' => (new moodle_url('/grade/report/singleview/index.php',
['id' => $courseid, 'item' => 'user_select']))->out(false)
];
$data['groupselector'] = $this->report->group_selector;
$data['itemselector'] = $this->report->itemselector;
$data['pbarurl'] = $this->report->pbarurl->out(false);
if (!empty($USER->editing) && isset($this->report->screen->item)) {
$data['bulkactions'] = $this->report->bulk_actions_menu($output);
}
return $data;
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for gradereport_singleview.
*
* @package gradereport_singleview
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace gradereport_singleview\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for gradereport_singleview implementing null_provider.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,196 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace gradereport_singleview\report;
use context_course;
use grade_report;
use moodle_url;
use renderer_base;
use stdClass;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot . '/grade/report/lib.php');
/**
* This class is the main class that must be implemented by a grade report plugin.
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class singleview extends grade_report {
/** @var string|null $itemselector The raw HTML of the item selector based on the selected single view item type. */
public ?string $itemselector = null;
/** @var \gradereport_singleview\local\screen\screen screen type. */
public $screen;
/**
* Return the list of valid screens, used to validate the input.
*
* @return array List of screens.
*/
public static function valid_screens(): array {
// This is a list of all the known classes representing a screen in this plugin.
return ['user', 'select', 'grade', 'user_select', 'grade_select'];
}
/**
* Process data from a form submission. Delegated to the current screen.
*
* @param array $data The data from the form
* @return array|object List of warnings
*/
public function process_data($data) {
if (has_capability('moodle/grade:edit', $this->context)) {
return $this->screen->process($data);
}
}
/**
* Unused - abstract function declared in the parent class.
*
* @param string $target
* @param string $action
*/
public function process_action($target, $action) {
}
/**
* Constructor for this report. Creates the appropriate screen class based on itemtype.
*
* @param int $courseid The course id.
* @param object $gpr grade plugin return tracking object
* @param context_course $context
* @param string $itemtype Should be user, select or grade
* @param int|null $itemid The id of the user or grade item
* @param string|null $unused Used to be group id but that was removed and this is now unused.
*/
public function __construct(
int $courseid,
object $gpr,
context_course $context,
string $itemtype,
?int $itemid,
?string $unused = null
) {
parent::__construct($courseid, $gpr, $context);
$base = '/grade/report/singleview/index.php';
$idparams = ['id' => $courseid];
$this->baseurl = new moodle_url($base, $idparams);
$this->pbarurl = new moodle_url($base, $idparams + [
'item' => $itemtype,
'itemid' => $itemid
]);
// The setup_group method is used to validate group mode and permissions and define the currentgroup value.
$this->setup_groups();
if (($itemtype !== 'grade') && ($itemtype !== 'user')) {
$itemid = null;
}
$this->setup_item_selector($itemtype, $itemid);
$screenclass = "\\gradereport_singleview\\local\\screen\\{$itemtype}";
$this->screen = new $screenclass($courseid, $itemid, $this->currentgroup);
// Load custom or predifined js.
$this->screen->js();
}
/**
* Build the html for the screen.
* @return string HTML to display
*/
public function output(): string {
global $OUTPUT;
return $OUTPUT->container($this->screen->html());
}
protected function setup_groups() {
parent::setup_groups();
$this->group_selector = static::groups_course_menu($this->course);
}
/**
* Ideally we should move this function to the base class and call it from the setup_groups in the base class,
* so all reports would automatically use it.
*
* @param stdClass $course
* @return string
*/
protected static function groups_course_menu(stdClass $course) {
global $PAGE;
$renderer = $PAGE->get_renderer('core_grades');
return $renderer->group_selector($course);
}
/**
* Function used to set the appropriate item selector (raw HTML) based on the selected single view item type.
*
* @param string $itemtype The single view item type.
* @param int|null $itemid The item ID.
*/
protected function setup_item_selector(string $itemtype, ?int $itemid) {
global $PAGE;
$renderer = $PAGE->get_renderer('gradereport_singleview');
if ($itemtype === 'user' || $itemtype === 'user_select' ) {
$this->itemselector = $renderer->users_selector($this->course, $itemid, $this->currentgroup);
} else if ($itemtype === 'grade' || $itemtype === 'grade_select' ) {
$this->itemselector = $renderer->grade_items_selector($this->course, $itemid);
}
}
/**
* Adds bulk actions menu.
*
* @param renderer_base $output
* @return string HTML to display
*/
public function bulk_actions_menu(renderer_base $output): string {
$options = [
'overrideallgrades' => get_string('overrideallgrades', 'gradereport_singleview'),
'overridenonegrades' => get_string('overridenonegrades', 'gradereport_singleview'),
'excludeallgrades' => get_string('excludeallgrades', 'gradereport_singleview'),
'excludenonegrades' => get_string('excludenonegrades', 'gradereport_singleview'),
'bulklegend' => get_string('bulklegend', 'gradereport_singleview')
];
$menu = new \action_menu();
$menu->set_menu_trigger(get_string('actions'), 'text-dark');
foreach ($options as $type => $option) {
$action = new \action_menu_link_secondary(new \moodle_url('#'), null, $option,
['data-action' => $type, 'data-role' => 'bulkaction']);
$menu->add($action);
}
$menu->attributes['class'] .= ' float-left my-auto';
return $output->render($menu);
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The gradebook simple view - Database file
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$capabilities = array(
'gradereport/singleview:view' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW,
)
)
);
+36
View File
@@ -0,0 +1,36 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* singleview grade report external functions and service definitions.
*
* @package gradereport_singleview
* @copyright 2022 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
$functions = [
'gradereport_singleview_get_grade_items_for_search_widget' => [
'methodname' => 'get_grade_items_for_search_widget',
'classname' => 'gradereport_singleview\\external\\singleview',
'description' => 'Get the gradeitem/(s) for a course',
'type' => 'read',
'ajax' => true,
'capabilities' => 'gradereport/singleview:view',
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
],
];
+258
View File
@@ -0,0 +1,258 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Displays the Single view
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('NO_OUTPUT_BUFFERING', true);
require_once('../../../config.php');
require_once($CFG->dirroot.'/lib/gradelib.php');
require_once($CFG->dirroot.'/grade/lib.php');
require_once($CFG->dirroot.'/grade/report/lib.php');
$courseid = required_param('id', PARAM_INT);
$groupid = optional_param('group', null, PARAM_INT);
// Making this work with profile reports.
$userid = optional_param('userid', null, PARAM_INT);
$itemid = optional_param('itemid', null, PARAM_INT);
$itemtype = optional_param('item', null, PARAM_TEXT);
$page = optional_param('page', 0, PARAM_INT);
$perpage = optional_param('perpage', null, PARAM_INT);
$edit = optional_param('edit', -1, PARAM_BOOL); // Sticky editing mode.
$courseparams = ['id' => $courseid];
$PAGE->set_pagelayout('report');
$PAGE->set_other_editing_capability('moodle/grade:edit');
if (!$course = $DB->get_record('course', $courseparams)) {
throw new \moodle_exception('invalidcourseid');
}
require_login($course);
$context = context_course::instance($course->id);
// This is the normal requirements.
require_capability('gradereport/singleview:view', $context);
require_capability('moodle/grade:viewall', $context);
require_capability('moodle/grade:edit', $context);
$gpr = new grade_plugin_return([
'type' => 'report',
'plugin' => 'singleview',
'courseid' => $courseid
]);
// Last selected report session tracking.
if (!isset($USER->grade_last_report)) {
$USER->grade_last_report = [];
}
$USER->grade_last_report[$course->id] = 'singleview';
// If the item type is not explicitly defined or not valid, try to use the last viewed one (obtain in from the session)
// or fallback to the user select (zero) state.
if (!$itemtype || !in_array($itemtype, \gradereport_singleview\report\singleview::valid_screens())) {
$itemtype = isset($SESSION->gradereport_singleview["itemtype-{$context->id}"]) ?
$SESSION->gradereport_singleview["itemtype-{$context->id}"] : 'user_select';
}
$currentgroup = $gpr->groupid;
// To make some other functions work better later.
if (!$currentgroup) {
$currentgroup = null;
}
$lastvieweduseritemid = $SESSION->gradereport_singleview["useritem-{$context->id}"] ?? null;
$lastviewedgradeitemid = $SESSION->gradereport_singleview["gradeitem-{$context->id}"] ?? null;
switch ($itemtype) {
case 'user_select':
// If there is a stored user item (last viewed) in a session variable, bypass the user select zero state
// and display this user item. Also, make sure that the stored last viewed user is part of the current
// list of gradable users in this course.
if ($lastvieweduseritemid &&
array_key_exists($lastvieweduseritemid, grade_report::get_gradable_users($courseid, $currentgroup))) {
$itemtype = 'user';
$itemid = $lastvieweduseritemid;
} else {
$itemid = null;
}
break;
case 'user':
if (is_null($itemid)) {
$itemid = $userid ?? $lastvieweduseritemid;
}
// If the item id (user id) cannot be defined or the user id is not part of the list of gradable users,
// display the user select zero state.
if (is_null($itemid) || !array_key_exists($itemid, grade_report::get_gradable_users($courseid, $currentgroup))) {
$itemtype = 'user_select';
}
break;
case 'grade_select':
// If there is a stored grade item (last viewed) in a session variable, bypass the grade item select zero state
// and display this grade item.
if ($lastviewedgradeitemid) {
$itemtype = 'grade';
$itemid = $lastviewedgradeitemid;
} else {
$itemid = null;
}
break;
case 'grade':
// If there is a stored grade item (last viewed) in a session variable, use it.
if (is_null($itemid) && $lastviewedgradeitemid) {
$itemid = $lastviewedgradeitemid;
}
$gtree = new grade_tree($courseid, false, false, null, !$CFG->enableoutcomes);
$gradeableitems = $gtree->get_items();
// The item id (grade item id) cannot be defined, display the grade select zero state.
if (is_null($itemid) || !array_key_exists($itemid, $gtree->get_items())) {
$itemtype = 'grade_select';
}
break;
}
$report = new gradereport_singleview\report\singleview($courseid, $gpr, $context, $itemtype, $itemid);
$pageparams = [
'id' => $courseid,
'userid' => $userid,
'itemid' => $itemid,
'item' => $itemtype,
'page' => $page,
'perpage' => $perpage,
];
if (!is_null($groupid)) {
$pageparams['group'] = $groupid;
}
$PAGE->set_url(new moodle_url('/grade/report/singleview/index.php', $pageparams));
// Build editing on/off button for themes that need it.
$button = '';
if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
if ($edit != - 1) {
$USER->editing = $edit;
}
// Page params for the turn editing on button.
$options = $gpr->get_options();
$button = $OUTPUT->edit_button(new moodle_url($PAGE->url, $options), 'get');
}
$reportname = $report->screen->heading();
if ($itemtype == 'user' || $itemtype == 'user_select') {
$PAGE->requires->js_call_amd('gradereport_singleview/user', 'init');
$actionbar = new \gradereport_singleview\output\action_bar($context, $report, 'user');
} else if ($itemtype == 'grade' || $itemtype == 'grade_select') {
$PAGE->requires->js_call_amd('gradereport_singleview/grade', 'init');
$actionbar = new \gradereport_singleview\output\action_bar($context, $report, 'grade');
} else {
$actionbar = new \core_grades\output\general_action_bar($context, new moodle_url('/grade/report/singleview/index.php',
['id' => $courseid]), 'report', 'singleview');
}
if ($course->groupmode && $itemtype !== 'select') {
$PAGE->requires->js_call_amd('gradereport_singleview/group', 'init', [$itemtype]);
}
if ($itemtype == 'user') {
print_grade_page_head($course->id, 'report', 'singleview', $reportname, false, $button,
true, null, null, $report->screen->item, $actionbar);
} else {
print_grade_page_head($course->id, 'report', 'singleview', $reportname, false, $button,
true, null, null, null, $actionbar);
}
if ($data = data_submitted()) {
// Must have a sesskey for all actions.
require_sesskey();
$result = $report->process_data($data);
// If result is not null (because somedata was processed), warnings and success message should be displayed.
if (!is_null($result)) {
if (!empty($result->warnings)) {
foreach ($result->warnings as $warning) {
\core\notification::add($warning);
}
}
// And notify the user of the success result.
\core\notification::add(
get_string('savegradessuccess', 'gradereport_singleview', count((array) $result->changecount)),
\core\notification::SUCCESS
);
}
}
// Make sure we have proper final grades.
grade_regrade_final_grades_if_required($course);
// Save the screen state in a session variable as last viewed state.
$SESSION->gradereport_singleview["itemtype-{$context->id}"] = $itemtype;
if ($itemid) {
$SESSION->gradereport_singleview["{$itemtype}item-{$context->id}"] = $itemid;
}
$stickyfooter = '';
if (($itemtype !== 'select') && ($itemtype !== 'grade_select') &&($itemtype !== 'user_select')) {
$item = (isset($userid)) ? $userid : $itemid;
$defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
$showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol);
$showonlyactiveenrol = $showonlyactiveenrol || !has_capability('moodle/course:viewsuspendedusers', $context);
$gui = new graded_users_iterator($course, null, $currentgroup);
$gui->require_active_enrolment($showonlyactiveenrol);
$gui->init();
$userreportrenderer = $PAGE->get_renderer('gradereport_singleview');
// Add previous/next user navigation.
$footercontent = $userreportrenderer->report_navigation($gpr, $courseid, $context, $report, $groupid, $itemtype, $itemid);
$buttonhtml = implode(' ', $report->screen->buttons($report->screen->is_readonly()));
$footercontent .= $report->screen->bulk_insert() . $buttonhtml;
$stickyfooter = new core\output\sticky_footer($footercontent);
$stickyfooter = $OUTPUT->render($stickyfooter);
$gui->close();
}
echo $OUTPUT->render_from_template('gradereport_singleview/report', [
'table' => $report->output(),
'stickyfooter' => $stickyfooter,
'sesskey' => sesskey()
]);
$event = \gradereport_singleview\event\grade_report_viewed::create(
[
'context' => $context,
'courseid' => $courseid,
'relateduserid' => $USER->id,
]
);
$event->trigger();
echo $OUTPUT->footer();
+119
View File
@@ -0,0 +1,119 @@
M.gradereport_singleview = {};
M.gradereport_singleview.init = function(Y) {
if (this.initialised) {
return;
}
this.initialised = true;
var getColumnIndex = function(cell) {
var rowNode = cell.ancestor('tr');
if (!rowNode || !cell) {
return;
}
var cells = rowNode.all('td, th');
return cells.indexOf(cell);
},
getNextCell = function(cell) {
var n = cell || document.activeElement,
next = n.next('td.cell, th.cell');
if (!next) {
return null;
}
// Continue on until we find a navigable cell
if (!next || !Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a')) {
return getNextCell(next);
}
return next;
},
getPrevCell = function(cell) {
var n = cell || document.activeElement,
next = n.previous('td.cell, th.cell');
if (!next) {
return null;
}
// Continue on until we find a navigable cell
if (!Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a')) {
return getPrevCell(next);
}
return next;
},
getAboveCell = function(cell) {
var n = cell || document.activeElement,
tr = n.ancestor('tr').previous('tr'),
columnIndex = getColumnIndex(n),
next = null;
if (tr) {
next = tr.all('td, th').item(columnIndex);
} else {
return null;
}
// Continue on until we find a navigable cell
if (!Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a')) {
return getAboveCell(next);
}
return next;
},
getBelowCell = function(cell) {
var n = cell || document.activeElement,
tr = n.ancestor('tr').next('tr'),
columnIndex = getColumnIndex(n),
next = null;
if (tr) {
next = tr.all('td, th').item(columnIndex);
} else {
return null;
}
// Continue on until we find a navigable cell
if (!Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a')) {
return getBelowCell(next);
}
return next;
};
// Add ctrl+arrow controls for navigation
Y.one(Y.config.doc.body).delegate('key', function(e) {
e.preventDefault();
e.stopPropagation();
var next = null;
switch (e.keyCode) {
case 37:
next = getPrevCell(this.ancestor('td, th'));
break;
case 38:
next = getAboveCell(this.ancestor('td, th'));
break;
case 39:
next = getNextCell(this.ancestor('td, th'));
break;
case 40:
next = getBelowCell(this.ancestor('td, th'));
break;
}
if (next) {
Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a').focus();
}
return;
}, 'down:37,38,39,40+ctrl', 'table input, table select, table a');
// Override Toggle.
Y.all('input[name^=override_]').each(function(input) {
input.on('change', function() {
var checked = input.getDOMNode().checked;
var names = input.getAttribute('name').split("_");
var itemid = names[1];
var userid = names[2];
var interest = '_' + itemid + '_' + userid;
Y.all('input[name$=' + interest + ']').filter('input[data-uielement=text]').each(function(text) {
text.getDOMNode().disabled = !checked;
});
// deal with scales that are not text... UCSB
Y.all('select[name$=' + interest + ']').each(function(select) {
select.getDOMNode().disabled = !checked;
});
});
});
};
@@ -0,0 +1,2 @@
filtergrades,gradereport_singleview
viewsingleuserorgradeitem,gradereport_singleview
@@ -0,0 +1,93 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for Single view
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// General Strings.
$string['all_grades'] = 'All grades';
$string['assessmentname'] = 'Grade item';
$string['blanks'] = 'Empty grades';
$string['bulkappliesto'] = 'For';
$string['bulkinsertvalue'] = 'Insert new grade';
$string['bulklegend'] = 'Bulk insert grades';
$string['bulkchoice'] = 'Bulk insert lets you replace multiple grades at once. Choose the grades you want to replace:';
$string['bulkperform'] = 'Perform bulk insert';
$string['bulkfor'] = 'Grades for {$a}';
$string['entrypage'] = 'Grade user or grade item';
$string['exclude'] = 'Exclude';
$string['excludeall'] = 'Exclude all grades';
$string['excludeallgrades'] = 'Exclude all';
$string['excludefor'] = 'Exclude for {$a}';
$string['excludenone'] = 'Do not exclude any grades';
$string['excludenonegrades'] = 'Exclude none';
$string['eventgradereportviewed'] = 'Grade single view report viewed.';
$string['feedbackfor'] = 'Feedback for {$a}';
$string['gradefor'] = 'Grade for {$a}';
$string['gradeitem'] = 'Viewing grades for {$a}';
$string['gradeitemedit'] = 'Editing grades for {$a}';
$string['gradeuser'] = 'Viewing {$a}\'s grades';
$string['gradeuseredit'] = 'Editing {$a}\'s grades';
$string['gotonextreport'] = 'Go to next user report';
$string['gotopreviousreport'] = 'Go to previous user report';
$string['noscreens'] = 'Could not find a suitable single view screen.';
$string['gradeitemcannotbeoverridden'] = 'This grade item cannot be overridden.';
$string['itemsperpage'] = 'Items per page';
$string['notvalid'] = 'Not a valid Single view screen: {$a}';
$string['override'] = 'Override';
$string['overrideall'] = 'Override all grades';
$string['overrideallgrades'] = 'Override all';
$string['overridefor'] = 'Override for {$a}';
$string['overridenone'] = 'Do not override any grades';
$string['overridenonegrades'] = 'Override none';
$string['overridenoneconfirm'] = 'This will remove all previously entered overridden grades on this page when you save changes.';
$string['pluginname'] = 'Single view';
$string['privacy:metadata'] = 'The Grade single view report only shows data stored in other locations.';
$string['removeoverride'] = 'Remove grade overrides';
$string['removeoverridesave'] = 'Remove overrides';
$string['savegrades'] = 'Saving grades';
$string['save'] = 'Save';
$string['savegradessuccess'] = 'Grades were set for {$a} items';
$string['singleview:view'] = 'View single view';
$string['singleviewreport_user'] = 'Single view for this user';
$string['singleviewreport_gradeitem'] = 'Single view for this item';
$string['summarygrade'] = 'A table of users, with columns for range, grade, feedback, and whether to override or exclude a particular grade.';
$string['summaryuser'] = 'A table of grade items, with columns for grade category, range, grade, feedback, and whether to override or exclude a particular grade.';
$string['unsavedataalert'] = 'If you have unsaved changes on the page, you will lose them if you proceed with bulk insert grades.';
$string['unsavedataconfirm'] = 'I understand that my unsaved changes will be lost.';
$string['userselect'] = 'Select activity';
$string['ariareporttype'] = 'Select a report type to view';
$string['viewby'] = 'View by';
$string['viewsingleuser'] = 'Search for a user to view all their grades';
$string['viewsinglegradeitem'] = 'Select a grade item above';
$string['selectagrade'] = 'Select a grade item';
$string['selectgradeitemlink'] = 'Select a grade item';
$string['singleviewdescription'] = 'In single view you can override or exclude selected grades and bulk insert grades.';
$string['unsaveddatawarning'] = 'Unsaved data warning';
// Deprecated since Moodle 4.1.
$string['filtergrades'] = 'Show grades for {$a}.';
// Deprecated since Moodle 4.3.
$string['viewsingleuserorgradeitem'] = 'View all the grades of a single user or grade item.';
+80
View File
@@ -0,0 +1,80 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Singleview report generic functions
*
* @package gradereport_singleview
* @copyright 2023 Ilya Tregubov
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Returns link to singleview report for the current element
*
* @param context_course $context Course context
* @param int $courseid Course ID
* @param array $element An array representing an element in the grade_tree
* @param grade_plugin_return $gpr A grade_plugin_return object
* @param string $mode Mode - gradeitem or user
* @param ?stdClass $templatecontext Template context
* @return stdClass|null
*/
function gradereport_singleview_get_report_link(context_course $context, int $courseid,
array $element, grade_plugin_return $gpr, string $mode, ?stdClass $templatecontext): ?stdClass {
$reportstring = get_string('singleviewreport_' . $mode, 'gradereport_singleview');
if (!isset($templatecontext)) {
$templatecontext = new stdClass();
}
if ($mode == 'gradeitem') {
// View all grades items.
// FIXME: MDL-52678 This is extremely hacky we should have an API for inserting grade column links.
if (get_capability_info('gradereport/singleview:view')) {
if (has_all_capabilities(['gradereport/singleview:view', 'moodle/grade:viewall',
'moodle/grade:edit'], $context)) {
$url = new moodle_url('/grade/report/singleview/index.php', [
'id' => $courseid,
'item' => 'grade',
'itemid' => $element['object']->id
]);
$gpr->add_url_params($url);
$templatecontext->reporturl0 = html_writer::link($url, $reportstring,
['class' => 'dropdown-item', 'aria-label' => $reportstring, 'role' => 'menuitem']);
return $templatecontext;
}
}
} else if ($mode == 'user') {
// FIXME: MDL-52678 This get_capability_info is hacky and we should have an API for inserting grade row links instead.
$canseesingleview = false;
if (get_capability_info('gradereport/singleview:view')) {
$canseesingleview = has_all_capabilities(['gradereport/singleview:view',
'moodle/grade:viewall', 'moodle/grade:edit'], $context);
}
if ($canseesingleview) {
$url = new moodle_url('/grade/report/singleview/index.php',
['id' => $courseid, 'itemid' => $element['userid'], 'item' => 'user']);
$gpr->add_url_params($url);
$templatecontext->reporturl0 = html_writer::link($url, $reportstring,
['class' => 'dropdown-item', 'aria-label' => $reportstring, 'role' => 'menuitem']);
return $templatecontext;
}
}
return null;
}
@@ -0,0 +1,12 @@
<svg width="109" height="132" viewBox="0 0 109 132" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet">
<path d="M54.54 2.29492e-05C43.7604 -0.00986828 33.22 3.17783 24.2524 9.15969C15.2847 15.1416 8.29284 23.6488 4.16124 33.6052C0.0296474 43.5617 -1.056 54.5199 1.04167 65.0935C3.13934 75.6671 8.32603 85.381 15.9456 93.0061C23.5652 100.631 33.2752 105.825 43.8473 107.931C54.4193 110.036 65.3783 108.958 75.3378 104.834C85.2973 100.71 93.8098 93.7242 99.7982 84.761C105.787 75.7978 108.982 65.2597 108.98 54.48C108.98 40.0379 103.246 26.1868 93.0373 15.971C82.829 5.75514 68.9821 0.0106265 54.54 2.29492e-05ZM54.54 92.24C47.0714 92.242 39.7699 90.0292 33.5589 85.8812C27.348 81.7333 22.5067 75.8366 19.6472 68.937C16.7877 62.0375 16.0385 54.4449 17.4944 47.1195C18.9502 39.7941 22.5457 33.0651 27.8262 27.7832C33.1066 22.5014 39.8347 18.9041 47.1597 17.4463C54.4847 15.9885 62.0775 16.7357 68.9778 19.5934C75.8782 22.4511 81.7761 27.2908 85.9256 33.5006C90.0752 39.7104 92.29 47.0114 92.29 54.48C92.2913 59.4382 91.3159 64.3483 89.4194 68.9295C87.5228 73.5107 84.7424 77.6734 81.2369 81.1798C77.7313 84.6863 73.5693 87.4678 68.9886 89.3655C64.4079 91.2632 59.4983 92.24 54.54 92.24Z" fill="#C4E5FF"/>
<path d="M54.5391 0V16.72C64.5519 16.7226 74.1538 20.7021 81.233 27.7832C88.3122 34.8643 92.2891 44.4671 92.2891 54.48H108.979C108.979 40.0379 103.245 26.1868 93.0364 15.9709C82.828 5.75511 68.9811 0.0106036 54.5391 0Z" fill="#85CAFF"/>
<path d="M18.8507 66.8201C16.886 61.1315 16.3092 55.0558 17.1681 49.0991C18.0271 43.1425 20.2971 37.4773 23.789 32.5756C27.2808 27.6739 31.8934 23.6776 37.2425 20.9196C42.5917 18.1616 48.5224 16.7217 54.5407 16.72V8.99755e-07C45.8564 -0.00157725 37.2975 2.07289 29.5776 6.05054C21.8578 10.0282 15.2006 15.7938 10.1613 22.8665C5.12197 29.9391 1.84642 38.114 0.607778 46.7096C-0.63086 55.3052 0.203305 64.0723 3.0407 72.28L18.8507 66.8201Z" fill="#0056B2"/>
<path d="M38.7519 49.2401C43.7666 49.2401 47.8319 45.1748 47.8319 40.1601C47.8319 35.1453 43.7666 31.0801 38.7519 31.0801C33.7371 31.0801 29.6719 35.1453 29.6719 40.1601C29.6719 45.1748 33.7371 49.2401 38.7519 49.2401Z" fill="#85CAFF"/>
<path d="M74.8719 55.3203H34.2119C31.7045 55.3203 29.6719 57.353 29.6719 59.8604C29.6719 62.3677 31.7045 64.4003 34.2119 64.4003H74.8719C77.3792 64.4003 79.4119 62.3677 79.4119 59.8604C79.4119 57.353 77.3792 55.3203 74.8719 55.3203Z" fill="#85CAFF"/>
<path d="M74.8719 70.1504H34.2119C31.7045 70.1504 29.6719 72.183 29.6719 74.6904C29.6719 77.1977 31.7045 79.2304 34.2119 79.2304H74.8719C77.3792 79.2304 79.4119 77.1977 79.4119 74.6904C79.4119 72.183 77.3792 70.1504 74.8719 70.1504Z" fill="#C4E5FF"/>
<path d="M76.9799 41.5205H55.7698C54.4278 41.5205 53.3398 42.6084 53.3398 43.9505V43.9605C53.3398 45.3026 54.4278 46.3905 55.7698 46.3905H76.9799C78.3219 46.3905 79.4099 45.3026 79.4099 43.9605V43.9505C79.4099 42.6084 78.3219 41.5205 76.9799 41.5205Z" fill="#85CAFF"/>
<path d="M65.4499 33.5205H55.7698C54.4278 33.5205 53.3398 34.6084 53.3398 35.9505V35.9605C53.3398 37.3026 54.4278 38.3905 55.7698 38.3905H65.4499C66.7919 38.3905 67.8799 37.3026 67.8799 35.9605V35.9505C67.8799 34.6084 66.7919 33.5205 65.4499 33.5205Z" fill="#85CAFF"/>
<path d="M90.2096 91.0605C89.8473 90.6761 89.408 90.3723 88.9204 90.1689C88.4328 89.9656 87.9078 89.8673 87.3797 89.8805C86.3275 89.8936 85.3228 90.3209 84.5835 91.0696C83.8441 91.8183 83.4296 92.8282 83.4297 93.8805V95.0005H82.2997V90.6005C82.3027 89.6738 81.9886 88.7741 81.4096 88.0505C80.8592 87.3125 80.0534 86.8062 79.1496 86.6305C78.8818 86.5869 78.611 86.5635 78.3396 86.5605C77.2891 86.5604 76.2806 86.9736 75.5322 87.7108C74.7838 88.448 74.3554 89.4501 74.3396 90.5005V95.0105H73.2096V90.7205C73.2276 89.5646 72.8181 88.4429 72.0597 87.5706C71.704 87.1312 71.2625 86.7689 70.7622 86.5059C70.2619 86.2428 69.7132 86.0845 69.1496 86.0405C68.9968 86.0228 68.8425 86.0228 68.6897 86.0405C67.4935 86.0405 66.3464 86.5157 65.5006 87.3615C64.6548 88.2072 64.1797 89.3544 64.1797 90.5505V95.0505H63.0497V80.6305V64.8304C63.062 63.957 62.788 63.1036 62.2696 62.4005C62.1514 62.2436 62.0245 62.0934 61.8896 61.9506C61.2202 61.2652 60.3165 60.858 59.3597 60.8105H58.8296C58.2565 60.8347 57.6968 60.9907 57.1938 61.2665C56.6909 61.5422 56.2583 61.9303 55.9297 62.4005C55.4113 63.1036 55.1373 63.957 55.1496 64.8304V91.4805C55.1541 91.5437 55.1541 91.6073 55.1496 91.6705V106.771L54.0196 105.771V99.7104C54.0306 99.1811 53.9364 98.6549 53.7424 98.1622C53.5484 97.6696 53.2586 97.2202 52.8896 96.8405C52.5246 96.4594 52.0849 96.158 51.5979 95.955C51.1109 95.7519 50.5872 95.6517 50.0597 95.6605C49.5401 95.6593 49.0254 95.7622 48.5462 95.9631C48.0671 96.1641 47.633 96.459 47.2696 96.8304C46.897 97.1903 46.6019 97.6226 46.4024 98.1007C46.203 98.5788 46.1033 99.0925 46.1097 99.6105V107.5C46.1104 108.041 46.2206 108.576 46.4338 109.073C46.6469 109.569 46.9584 110.018 47.3497 110.391L58.3497 120.791C58.8118 121.264 59.1726 121.826 59.4099 122.443C59.6472 123.061 59.756 123.72 59.7297 124.38C59.7449 124.974 59.9883 125.539 60.4091 125.958C60.8299 126.377 61.396 126.618 61.9897 126.63H84.5896C84.8847 126.632 85.177 126.574 85.4487 126.458C85.7204 126.343 85.9657 126.174 86.1697 125.96C86.3838 125.757 86.5539 125.512 86.6692 125.24C86.7844 124.968 86.8424 124.676 86.8396 124.38V123.5C86.835 122.582 86.9528 121.668 87.1897 120.781L90.9897 105.431C91.226 104.55 91.3437 103.642 91.3396 102.73V94.0706C91.3698 93.518 91.2849 92.9651 91.0904 92.447C90.8959 91.9289 90.596 91.4568 90.2096 91.0605Z" fill="white"/>
<path d="M95.8601 93.9202C95.8729 92.8045 95.6597 91.6978 95.2335 90.6666C94.8073 89.6355 94.1769 88.7013 93.3801 87.9202C92.6096 87.1075 91.678 86.4644 90.6449 86.0322C89.6118 85.6 88.4998 85.3882 87.3801 85.4102C86.8867 85.6145 86.356 85.7131 85.8221 85.6996C85.2882 85.6861 84.763 85.5609 84.2806 85.3319C83.7981 85.1029 83.3691 84.7753 83.0211 84.3702C82.673 83.9652 82.4138 83.4916 82.2601 82.9801C81.0517 82.3394 79.7077 81.9966 78.3401 81.9801C76.8096 81.9761 75.3078 82.3949 74.0001 83.1902C72.4951 82.0277 70.6417 81.4075 68.7401 81.4302C68.366 81.4294 67.9921 81.4494 67.6201 81.4901V64.7301C67.6306 63.6147 67.4164 62.5087 66.9903 61.4779C66.5642 60.4471 65.935 59.5126 65.1401 58.7301C64.0823 57.6274 62.7335 56.8472 61.2501 56.4801H61.0801L60.4901 56.3801H60.1401C59.8378 56.3482 59.5341 56.3315 59.2301 56.3301H59.0801C58.7794 56.3312 58.4791 56.348 58.1801 56.3801H57.8301L57.2401 56.4801H57.0601C55.573 56.8449 54.2203 57.6253 53.1601 58.7301C52.3684 59.5144 51.7419 60.4496 51.3177 61.4801C50.8934 62.5106 50.6801 63.6157 50.6901 64.7301V91.0502C50.5071 91.0363 50.3231 91.0363 50.1401 91.0502C47.8978 91.0581 45.7496 91.9516 44.1631 93.5361C42.5767 95.1207 41.6806 97.2679 41.6701 99.5101V107.4C41.6624 108.557 41.8979 109.702 42.3613 110.761C42.8247 111.821 43.5057 112.771 44.3601 113.55L55.2401 123.96C55.2039 125.813 55.8928 127.607 57.1601 128.96C57.7745 129.635 58.5281 130.169 59.3689 130.525C60.2097 130.881 61.1176 131.05 62.0301 131.02H84.6201C85.5103 131.024 86.3924 130.852 87.2156 130.513C88.0387 130.174 88.7866 129.676 89.4161 129.046C90.0455 128.417 90.5441 127.669 90.8829 126.846C91.2217 126.022 91.394 125.14 91.3901 124.25C91.366 123.412 91.4364 122.573 91.6001 121.75L95.4301 106.4C95.7275 105.155 95.8818 103.88 95.8901 102.6L95.8601 93.9202ZM90.9801 105.29L87.1801 120.64C86.9432 121.527 86.8255 122.442 86.8301 123.36V124.24C86.8329 124.535 86.7749 124.828 86.6596 125.1C86.5443 125.372 86.3743 125.617 86.1601 125.82C85.9561 126.033 85.7108 126.203 85.4391 126.318C85.1675 126.433 84.8752 126.492 84.5801 126.49H61.9801C61.3864 126.477 60.8204 126.237 60.3995 125.818C59.9787 125.399 59.7354 124.834 59.7201 124.24C59.7464 123.579 59.6376 122.92 59.4003 122.303C59.163 121.686 58.8023 121.123 58.3401 120.65L47.3401 110.25C46.9489 109.877 46.6373 109.429 46.4242 108.932C46.2111 108.435 46.1008 107.901 46.1001 107.36V99.4701C46.0938 98.9521 46.1934 98.4384 46.3929 97.9603C46.5924 97.4823 46.8875 97.05 47.2601 96.6902C47.6235 96.3187 48.0575 96.0237 48.5367 95.8228C49.0159 95.6218 49.5305 95.5189 50.0501 95.5201C50.5777 95.5113 51.1013 95.6115 51.5883 95.8146C52.0753 96.0176 52.5151 96.3192 52.8801 96.7002C53.2491 97.08 53.5389 97.5292 53.7328 98.0219C53.9268 98.5145 54.0211 99.0408 54.0101 99.5702V105.63L55.1401 106.63V91.5302C55.1446 91.4669 55.1446 91.4033 55.1401 91.3401V64.6902C55.1278 63.8167 55.4017 62.9632 55.9201 62.2601C56.2487 61.79 56.6813 61.4019 57.1843 61.1261C57.6872 60.8503 58.247 60.6944 58.8201 60.6702H59.3501C60.307 60.7176 61.2107 61.1248 61.8801 61.8102C62.015 61.953 62.1419 62.1032 62.2601 62.2601C62.7785 62.9632 63.0524 63.8167 63.0401 64.6902V80.4901V94.9102H64.1701V90.4102C64.1701 89.214 64.6452 88.0669 65.491 87.2211C66.3368 86.3753 67.484 85.9001 68.6801 85.9001C68.8329 85.8825 68.9873 85.8825 69.1401 85.9001C69.7036 85.9441 70.2524 86.1024 70.7527 86.3655C71.253 86.6285 71.6945 86.9908 72.0501 87.4302C72.8085 88.3026 73.218 89.4242 73.2001 90.5801V94.8701H74.3301V90.3601C74.3458 89.3097 74.7743 88.3077 75.5227 87.5704C76.2711 86.8332 77.2795 86.42 78.3301 86.4202C78.6015 86.4231 78.8722 86.4465 79.1401 86.4901C80.0438 86.6658 80.8497 87.1721 81.4001 87.9102C81.9791 88.6337 82.2931 89.5334 82.2901 90.4601V94.8601H83.4201V93.7401C83.42 92.6879 83.8345 91.6779 84.5739 90.9292C85.3133 90.1805 86.3179 89.7533 87.3701 89.7401C87.8983 89.7269 88.4232 89.8252 88.9108 90.0286C89.3984 90.2319 89.8377 90.5357 90.2001 90.9202C90.569 91.2999 90.8589 91.7492 91.0529 92.2418C91.2468 92.7345 91.341 93.2608 91.3301 93.7902V102.45C91.3469 103.409 91.2291 104.365 90.9801 105.29Z" fill="#0056B2"/>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

@@ -0,0 +1,21 @@
<svg width="130" height="143" viewBox="0 0 130 143" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet">
<path d="M54.7321 109.464C84.9598 109.464 109.464 84.9599 109.464 54.7322C109.464 24.5045 84.9598 0 54.7321 0C24.5044 0 0 24.5045 0 54.7322C0 84.9599 24.5044 109.464 54.7321 109.464Z" fill="#C4E5FF"/>
<path d="M50.7351 22.1533H28.602C27.9945 22.1533 27.502 22.6459 27.502 23.2534V53.4505C27.502 54.058 27.9945 54.5505 28.602 54.5505H50.7351C51.3427 54.5505 51.8352 54.058 51.8352 53.4505V23.2534C51.8352 22.6459 51.3427 22.1533 50.7351 22.1533Z" fill="white" stroke="#0056B2" stroke-width="0.75" stroke-miterlimit="10"/>
<path d="M46.7692 30.3281H32.5689C31.6938 30.3281 30.9844 31.0376 30.9844 31.9127C30.9844 32.7878 31.6938 33.4972 32.5689 33.4972H46.7692C47.6443 33.4972 48.3537 32.7878 48.3537 31.9127C48.3537 31.0376 47.6443 30.3281 46.7692 30.3281Z" fill="#85CAFF"/>
<path d="M46.7692 36.9902H32.5689C31.6938 36.9902 30.9844 37.6997 30.9844 38.5748C30.9844 39.4499 31.6938 40.1593 32.5689 40.1593H46.7692C47.6443 40.1593 48.3537 39.4499 48.3537 38.5748C48.3537 37.6997 47.6443 36.9902 46.7692 36.9902Z" fill="#C4E5FF"/>
<path d="M46.7692 43.6504H32.5689C31.6938 43.6504 30.9844 44.3598 30.9844 45.2349C30.9844 46.1101 31.6938 46.8195 32.5689 46.8195H46.7692C47.6443 46.8195 48.3537 46.1101 48.3537 45.2349C48.3537 44.3598 47.6443 43.6504 46.7692 43.6504Z" fill="#C4E5FF"/>
<path d="M81.0007 22.1533H58.8677C58.2601 22.1533 57.7676 22.6459 57.7676 23.2534V53.4505C57.7676 54.058 58.2601 54.5505 58.8677 54.5505H81.0007C81.6083 54.5505 82.1008 54.058 82.1008 53.4505V23.2534C82.1008 22.6459 81.6083 22.1533 81.0007 22.1533Z" fill="white" stroke="#0056B2" stroke-width="0.75" stroke-miterlimit="10"/>
<path d="M77.0465 30.3281H62.8462C61.9711 30.3281 61.2617 31.0376 61.2617 31.9127C61.2617 32.7878 61.9711 33.4972 62.8462 33.4972H77.0465C77.9216 33.4972 78.6311 32.7878 78.6311 31.9127C78.6311 31.0376 77.9216 30.3281 77.0465 30.3281Z" fill="#85CAFF"/>
<path d="M77.0465 36.9902H62.8462C61.9711 36.9902 61.2617 37.6997 61.2617 38.5748C61.2617 39.4499 61.9711 40.1593 62.8462 40.1593H77.0465C77.9216 40.1593 78.6311 39.4499 78.6311 38.5748C78.6311 37.6997 77.9216 36.9902 77.0465 36.9902Z" fill="#C4E5FF"/>
<path d="M77.0465 43.6504H62.8462C61.9711 43.6504 61.2617 44.3598 61.2617 45.2349C61.2617 46.1101 61.9711 46.8195 62.8462 46.8195H77.0465C77.9216 46.8195 78.6311 46.1101 78.6311 45.2349C78.6311 44.3598 77.9216 43.6504 77.0465 43.6504Z" fill="#C4E5FF"/>
<path d="M50.7351 60.3242H28.602C27.9945 60.3242 27.502 60.8167 27.502 61.4243V91.6214C27.502 92.2289 27.9945 92.7214 28.602 92.7214H50.7351C51.3427 92.7214 51.8352 92.2289 51.8352 91.6214V61.4243C51.8352 60.8167 51.3427 60.3242 50.7351 60.3242Z" fill="white" stroke="#0056B2" stroke-width="0.75" stroke-miterlimit="10"/>
<path d="M46.7692 68.499H32.5689C31.6938 68.499 30.9844 69.2085 30.9844 70.0836C30.9844 70.9587 31.6938 71.6681 32.5689 71.6681H46.7692C47.6443 71.6681 48.3537 70.9587 48.3537 70.0836C48.3537 69.2085 47.6443 68.499 46.7692 68.499Z" fill="#85CAFF"/>
<path d="M46.7692 75.1592H32.5689C31.6938 75.1592 30.9844 75.8686 30.9844 76.7437C30.9844 77.6188 31.6938 78.3282 32.5689 78.3282H46.7692C47.6443 78.3282 48.3537 77.6188 48.3537 76.7437C48.3537 75.8686 47.6443 75.1592 46.7692 75.1592Z" fill="#C4E5FF"/>
<path d="M46.7692 81.8115H32.5689C31.6938 81.8115 30.9844 82.521 30.9844 83.3961C30.9844 84.2712 31.6938 84.9806 32.5689 84.9806H46.7692C47.6443 84.9806 48.3537 84.2712 48.3537 83.3961C48.3537 82.521 47.6443 81.8115 46.7692 81.8115Z" fill="#C4E5FF"/>
<path d="M93.9605 59.9199H58.1923C57.2113 59.9199 56.416 60.7152 56.416 61.6962V110.494C56.416 111.475 57.2113 112.27 58.1923 112.27H93.9605C94.9415 112.27 95.7368 111.475 95.7368 110.494V61.6962C95.7368 60.7152 94.9415 59.9199 93.9605 59.9199Z" fill="white" stroke="#0056B2" stroke-width="1.22" stroke-miterlimit="10"/>
<path d="M87.5509 75.7656H64.6104C63.1946 75.7656 62.0469 76.9133 62.0469 78.3291C62.0469 79.7449 63.1946 80.8927 64.6104 80.8927H87.5509C88.9667 80.8927 90.1144 79.7449 90.1144 78.3291C90.1144 76.9133 88.9667 75.7656 87.5509 75.7656Z" fill="#85CAFF"/>
<path d="M87.5509 86.1006H64.6104C63.1946 86.1006 62.0469 87.2484 62.0469 88.6641C62.0469 90.0799 63.1946 91.2276 64.6104 91.2276H87.5509C88.9667 91.2276 90.1144 90.0799 90.1144 88.6641C90.1144 87.2484 88.9667 86.1006 87.5509 86.1006Z" fill="#C4E5FF"/>
<path d="M87.5509 96.4346H64.6104C63.1946 96.4346 62.0469 97.5823 62.0469 98.9981C62.0469 100.414 63.1946 101.562 64.6104 101.562H87.5509C88.9667 101.562 90.1144 100.414 90.1144 98.9981C90.1144 97.5823 88.9667 96.4346 87.5509 96.4346Z" fill="#C4E5FF"/>
<path d="M124.29 106.709C123.964 106.361 123.568 106.085 123.128 105.901C122.688 105.717 122.213 105.628 121.736 105.64C120.793 105.65 119.891 106.03 119.225 106.698C118.559 107.366 118.182 108.269 118.174 109.212V110.222H117.165V106.255C117.169 105.418 116.883 104.605 116.357 103.954C115.868 103.287 115.149 102.827 114.339 102.662C114.098 102.626 113.855 102.606 113.612 102.602C112.667 102.604 111.761 102.98 111.092 103.648C110.423 104.315 110.044 105.219 110.039 106.164V110.201H109.03V106.326C109.05 105.286 108.691 104.275 108.021 103.48C107.695 103.087 107.294 102.762 106.842 102.525C106.39 102.287 105.895 102.142 105.386 102.097C105.249 102.085 105.11 102.085 104.973 102.097C103.902 102.097 102.875 102.522 102.118 103.28C101.361 104.037 100.936 105.063 100.936 106.134V110.171H99.9264V97.1617V82.9716C99.9374 82.1808 99.6892 81.408 99.2199 80.7714C99.118 80.6295 99.0068 80.4946 98.8868 80.3677C98.2762 79.7627 97.4643 79.4035 96.6059 79.3584H96.1215C95.2631 79.4035 94.4512 79.7627 93.8405 80.3677C93.7178 80.4985 93.6033 80.6367 93.4974 80.7815C93.036 81.4183 92.7886 82.1852 92.7909 82.9716V107.022C92.7954 107.083 92.7954 107.143 92.7909 107.204V120.829L91.7816 119.88V114.41C91.7947 113.934 91.7121 113.46 91.5388 113.016C91.3654 112.572 91.1048 112.168 90.7724 111.826C90.4487 111.478 90.0544 111.202 89.6159 111.017C89.1773 110.833 88.7046 110.744 88.229 110.757C87.2856 110.764 86.383 111.142 85.715 111.808C85.0469 112.474 84.6668 113.376 84.6563 114.319V121.384C84.6535 121.873 84.7515 122.358 84.9442 122.808C85.137 123.257 85.4203 123.663 85.7766 123.998L95.6672 133.394C96.0839 133.821 96.4089 134.329 96.6224 134.886C96.8359 135.443 96.9332 136.038 96.9086 136.634C96.9058 136.9 96.9583 137.164 97.0625 137.409C97.1667 137.654 97.3205 137.874 97.5143 138.057C97.7012 138.248 97.9241 138.399 98.1701 138.503C98.4161 138.607 98.6803 138.661 98.9473 138.662H119.345C119.88 138.662 120.393 138.45 120.772 138.071C121.15 137.693 121.363 137.179 121.363 136.644V135.857C121.355 135.029 121.46 134.204 121.676 133.404L125.107 119.547C125.322 118.751 125.427 117.929 125.42 117.105V109.283C125.413 108.8 125.31 108.324 125.116 107.882C124.921 107.44 124.641 107.041 124.29 106.709Z" fill="white"/>
<path d="M129.386 109.292C129.4 108.28 129.21 107.276 128.827 106.34C128.443 105.403 127.875 104.554 127.156 103.842C126.458 103.11 125.617 102.531 124.684 102.142C123.75 101.753 122.747 101.562 121.736 101.581C121.104 101.576 120.473 101.647 119.859 101.794C119.151 100.784 118.209 99.9597 117.115 99.3921C116.02 98.8245 114.804 98.5299 113.571 98.5335C112.19 98.5278 110.834 98.9052 109.655 99.6236C108.296 98.5703 106.621 98.0082 104.901 98.0289C104.558 98.0289 104.225 98.0289 103.892 98.0895V82.9506C103.902 81.94 103.709 80.9376 103.324 80.003C102.939 79.0685 102.371 78.221 101.652 77.5107C100.696 76.5185 99.4777 75.8185 98.1393 75.4922H97.9879C97.8114 75.4521 97.6328 75.4218 97.453 75.4014H97.1402C96.8677 75.4014 96.5951 75.4014 96.3226 75.4014H96.1813C95.9088 75.4014 95.6364 75.4014 95.3639 75.4014H95.0509C94.8711 75.4218 94.6925 75.4521 94.516 75.4922H94.3647C93.0263 75.8185 91.8081 76.5185 90.8524 77.5107C90.1315 78.2196 89.5614 79.067 89.1764 80.0018C88.7913 80.9366 88.5993 81.9396 88.6119 82.9506V106.709C88.4439 106.696 88.2752 106.696 88.1072 106.709C86.0853 106.711 84.1466 107.514 82.715 108.942C81.2834 110.37 80.4752 112.306 80.4672 114.328V121.454C80.4563 122.5 80.6675 123.537 81.0868 124.495C81.506 125.454 82.1239 126.313 82.8995 127.015L92.7196 136.411C92.6797 138.086 93.2994 139.71 94.4454 140.933C95.0018 141.541 95.6831 142.022 96.4426 142.342C97.2021 142.663 98.0218 142.815 98.8458 142.79H119.233C120.037 142.798 120.836 142.645 121.581 142.341C122.326 142.038 123.003 141.588 123.572 141.02C124.141 140.451 124.592 139.775 124.897 139.031C125.202 138.286 125.356 137.488 125.349 136.684C125.325 135.925 125.389 135.166 125.541 134.423L128.992 120.566C129.262 119.442 129.401 118.29 129.406 117.134L129.386 109.292ZM125.006 119.556L121.574 133.414C121.359 134.213 121.253 135.038 121.261 135.866V136.653C121.261 137.189 121.049 137.702 120.67 138.081C120.292 138.459 119.778 138.672 119.243 138.672H98.8458C98.5787 138.671 98.3145 138.617 98.0685 138.513C97.8225 138.409 97.5995 138.257 97.4126 138.066C97.2189 137.884 97.0651 137.663 96.9609 137.418C96.8567 137.173 96.8043 136.909 96.807 136.643C96.8317 136.047 96.7343 135.452 96.5208 134.895C96.3074 134.338 95.9823 133.83 95.5657 133.403L85.675 124.007C85.3188 123.672 85.0354 123.267 84.8427 122.817C84.6499 122.367 84.5518 121.883 84.5546 121.393V114.328C84.5652 113.385 84.9454 112.484 85.6134 111.817C86.2814 111.151 87.1841 110.774 88.1274 110.766C88.603 110.753 89.0758 110.842 89.5143 111.027C89.9528 111.211 90.3471 111.487 90.6708 111.836C91.0032 112.177 91.2639 112.581 91.4372 113.025C91.6106 113.469 91.6931 113.943 91.6801 114.419V119.91L92.6893 120.858V107.233C92.6938 107.173 92.6938 107.112 92.6893 107.052V82.9708C92.687 82.1844 92.9343 81.4175 93.3957 80.7807C93.5016 80.6359 93.6163 80.4976 93.739 80.3668C94.3497 79.7619 95.1615 79.4027 96.0199 79.3576H96.5043C97.3627 79.4027 98.1746 79.7619 98.7853 80.3668C98.9052 80.4938 99.0164 80.6287 99.1183 80.7706C99.5876 81.4072 99.8357 82.18 99.8247 82.9708V97.2316V110.241H100.834V106.204C100.834 105.133 101.259 104.106 102.016 103.349C102.774 102.592 103.8 102.167 104.871 102.167C105.009 102.155 105.147 102.155 105.285 102.167C105.793 102.212 106.288 102.357 106.74 102.595C107.192 102.832 107.593 103.157 107.919 103.55C108.589 104.345 108.948 105.356 108.928 106.396V110.271H109.938V106.234C109.943 105.289 110.321 104.385 110.991 103.717C111.66 103.05 112.565 102.674 113.51 102.672C113.754 102.676 113.996 102.696 114.237 102.732C115.047 102.896 115.767 103.357 116.255 104.024C116.782 104.675 117.067 105.488 117.063 106.325V110.291H118.072V109.282C118.08 108.339 118.458 107.436 119.124 106.768C119.79 106.1 120.691 105.72 121.635 105.709C122.112 105.697 122.586 105.787 123.026 105.971C123.466 106.155 123.862 106.431 124.188 106.779C124.518 107.122 124.778 107.527 124.951 107.97C125.124 108.414 125.208 108.887 125.198 109.363V117.185C125.238 117.98 125.173 118.778 125.006 119.556Z" fill="#0056B2"/>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1,13 @@
<svg width="147" height="122" viewBox="0 0 147 122" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet">
<path d="M54.47 9.67969C43.6953 9.68167 33.1631 12.8785 24.2053 18.8661C15.2474 24.8536 8.26617 33.363 4.14424 43.3181C0.0223103 53.2731 -1.0552 64.2269 1.04805 74.7944C3.1513 85.3618 8.34084 95.0682 15.9604 102.686C23.58 110.305 33.2874 115.492 43.8552 117.594C54.423 119.695 65.3767 118.615 75.331 114.492C85.2853 110.368 93.7934 103.385 99.7793 94.4261C105.765 85.4672 108.96 74.9344 108.96 64.1597C108.96 57.0045 107.551 49.9192 104.812 43.3088C102.074 36.6983 98.0597 30.6921 92.9997 25.633C87.9397 20.5739 81.9327 16.5611 75.3217 13.8239C68.7108 11.0866 61.6253 9.67838 54.47 9.67969Z" fill="#C4E5FF"/>
<path d="M38.0894 62.8701C46.9149 62.8701 54.0694 55.7156 54.0694 46.8901C54.0694 38.0646 46.9149 30.9102 38.0894 30.9102C29.2639 30.9102 22.1094 38.0646 22.1094 46.8901C22.1094 55.7156 29.2639 62.8701 38.0894 62.8701Z" fill="#85CAFF"/>
<path d="M49.5087 56.3195C49.5087 56.2095 49.4387 56.0995 49.4087 55.9995V55.9295C49.4087 55.8295 49.3387 55.7295 49.2987 55.6395L49.1487 55.3295L48.9987 55.0795C48.8888 54.91 48.7614 54.7524 48.6187 54.6095L48.4187 54.4195C48.2671 54.2917 48.1066 54.1747 47.9386 54.0695C47.8578 54.01 47.7706 53.9597 47.6786 53.9195C46.8756 53.539 46.0245 53.2699 45.1487 53.1195C43.6387 52.8095 41.5886 51.3395 41.3586 50.4195C41.3259 50.025 41.3816 49.6282 41.5217 49.258C41.6619 48.8878 41.883 48.5535 42.1687 48.2795C42.2487 48.1795 42.3187 48.0895 42.3787 47.9995L42.4386 47.9195C43.0857 47.064 43.5087 46.0602 43.6687 44.9995C43.6687 44.9095 43.6687 44.8195 43.6687 44.7295C43.9683 43.1826 43.9273 41.589 43.5487 40.0595C43.2491 39.0602 42.6596 38.1723 41.855 37.5083C41.0503 36.8443 40.0667 36.434 39.0287 36.3295H38.8887C38.6522 36.3146 38.415 36.3146 38.1786 36.3295C37.9421 36.315 37.7051 36.315 37.4686 36.3295H37.2386C36.2002 36.4328 35.216 36.8425 34.4111 37.5067C33.6063 38.1709 33.0171 39.0595 32.7186 40.0595C32.3352 41.5883 32.2942 43.183 32.5986 44.7295C32.5986 44.8195 32.5986 44.9095 32.5986 44.9995C32.7635 46.0588 33.1858 47.0615 33.8286 47.9195L33.8887 47.9995L34.0986 48.2795C34.3861 48.5522 34.6083 48.8863 34.7485 49.2568C34.8888 49.6274 34.9436 50.0248 34.9087 50.4195C34.6887 51.3395 32.6387 52.8095 31.1187 53.1195C30.2457 53.2683 29.3976 53.5375 28.5986 53.9195C28.5078 53.9619 28.4208 54.0121 28.3386 54.0695C28.1706 54.1747 28.0102 54.2917 27.8586 54.4195L27.6587 54.6095C27.5189 54.755 27.3917 54.9123 27.2787 55.0795L27.1287 55.3295L26.9786 55.6395L26.8586 55.9295V55.9995C26.8194 56.1042 26.786 56.2111 26.7587 56.3195C26.7587 56.3795 26.3987 57.5795 26.3887 57.6495C27.882 59.2801 29.6982 60.5821 31.7219 61.4729C33.7456 62.3637 35.9326 62.8237 38.1437 62.8237C40.3548 62.8237 42.5416 62.3637 44.5653 61.4729C46.589 60.5821 48.4053 59.2801 49.8987 57.6495C49.8987 57.5895 49.5786 56.3495 49.5686 56.2895L49.5087 56.3195Z" fill="white"/>
<path d="M38.0894 99.5498C46.9149 99.5498 54.0694 92.3953 54.0694 83.5698C54.0694 74.7443 46.9149 67.5898 38.0894 67.5898C29.2639 67.5898 22.1094 74.7443 22.1094 83.5698C22.1094 92.3953 29.2639 99.5498 38.0894 99.5498Z" fill="#85CAFF"/>
<path d="M49.5087 92.9604C49.5087 92.8504 49.4387 92.7504 49.4087 92.6404V92.5704C49.4087 92.4704 49.3387 92.3704 49.2987 92.2804C49.2987 92.1804 49.1987 92.0704 49.1487 91.9804L48.9987 91.7304V91.6804C48.8888 91.5109 48.7614 91.3534 48.6187 91.2104C48.5569 91.1421 48.4901 91.0785 48.4187 91.0204C48.2673 90.8894 48.1068 90.7691 47.9386 90.6604L47.6786 90.5104C46.875 90.1335 46.024 89.8677 45.1487 89.7204C43.6387 89.4004 41.5886 87.9304 41.3586 87.0104C41.3273 86.616 41.3838 86.2196 41.5238 85.8496C41.6638 85.4796 41.8841 85.1452 42.1687 84.8704C42.245 84.782 42.3152 84.6885 42.3787 84.5904L42.4386 84.5204C43.0829 83.6595 43.5054 82.6533 43.6687 81.5904C43.6687 81.5104 43.6687 81.4104 43.6687 81.3204C43.9681 79.7701 43.9272 78.1733 43.5487 76.6404C43.2498 75.6416 42.6602 74.7545 41.8552 74.092C41.0502 73.4294 40.0663 73.0216 39.0287 72.9204H38.8887C38.6522 72.9054 38.415 72.9054 38.1786 72.9204C37.9421 72.9057 37.7051 72.9057 37.4686 72.9204H37.2386C36.2006 73.0203 35.216 73.4277 34.4108 74.0904C33.6056 74.7531 33.0163 75.641 32.7186 76.6404C32.3353 78.1727 32.2943 79.7705 32.5986 81.3204C32.5986 81.4104 32.5986 81.5104 32.5986 81.5904C32.7662 82.652 33.1882 83.6573 33.8286 84.5204L33.8887 84.5904L34.0986 84.8704C34.3855 85.1435 34.6073 85.4777 34.7476 85.8481C34.8878 86.2186 34.9429 86.6158 34.9087 87.0104C34.6887 87.9304 32.6387 89.4004 31.1187 89.7204C30.2462 89.8662 29.3981 90.132 28.5986 90.5104L28.3386 90.6604C28.1704 90.7691 28.01 90.8894 27.8586 91.0204L27.6587 91.2104C27.5189 91.356 27.3917 91.5132 27.2787 91.6804V91.7304L27.1287 91.9704C27.0787 92.0604 27.0286 92.1704 26.9786 92.2704L26.8586 92.5604V92.6304C26.8586 92.7404 26.7887 92.8404 26.7587 92.9504C26.7587 93.0104 26.3987 94.2104 26.3887 94.2704C27.8828 95.8996 29.6993 97.2004 31.7229 98.0903C33.7465 98.9802 35.9331 99.4397 38.1437 99.4397C40.3543 99.4397 42.5407 98.9802 44.5643 98.0903C46.5879 97.2004 48.4045 95.8996 49.8987 94.2704C49.8987 94.2104 49.5786 92.9704 49.5686 92.9104L49.5087 92.9604Z" fill="white"/>
<path d="M74.4702 99.5498C83.2957 99.5498 90.4502 92.3953 90.4502 83.5698C90.4502 74.7443 83.2957 67.5898 74.4702 67.5898C65.6447 67.5898 58.4902 74.7443 58.4902 83.5698C58.4902 92.3953 65.6447 99.5498 74.4702 99.5498Z" fill="#85CAFF"/>
<path d="M85.8801 92.9604C85.8801 92.8504 85.8202 92.7504 85.7802 92.6404V92.5704L85.6602 92.2804L85.5101 91.9804L85.3601 91.7304V91.6804C85.2471 91.5132 85.1199 91.356 84.9801 91.2104L84.7802 91.0204C84.6287 90.8894 84.4684 90.7691 84.3002 90.6604L84.0402 90.5104C83.2407 90.132 82.3926 89.8662 81.5201 89.7204C80.0001 89.4004 77.9501 87.9304 77.7301 87.0104C77.6959 86.6158 77.751 86.2186 77.8912 85.8481C78.0315 85.4777 78.2533 85.1435 78.5402 84.8704L78.7501 84.5904L78.8101 84.5204C79.4505 83.6573 79.8726 82.652 80.0402 81.5904C80.0402 81.5104 80.0402 81.4104 80.0402 81.3204C80.3445 79.7705 80.3035 78.1727 79.9202 76.6404C79.6225 75.641 79.0332 74.7531 78.228 74.0904C77.4228 73.4277 76.4382 73.0203 75.4001 72.9204H75.2401C75.0037 72.9057 74.7666 72.9057 74.5302 72.9204C74.2937 72.9054 74.0565 72.9054 73.8201 72.9204H73.6801C72.6424 73.0216 71.6585 73.4294 70.8535 74.092C70.0485 74.7545 69.4591 75.6416 69.1602 76.6404C68.7817 78.1733 68.7407 79.7701 69.0402 81.3204C69.0402 81.4104 69.0402 81.5104 69.0402 81.5904C69.2034 82.6533 69.6259 83.6595 70.2701 84.5204L70.3301 84.5904C70.3936 84.6885 70.4638 84.782 70.5402 84.8704C70.8248 85.1452 71.0449 85.4796 71.1849 85.8496C71.325 86.2196 71.3814 86.616 71.3501 87.0104C71.1201 87.9304 69.0701 89.4004 67.5601 89.7204C66.6848 89.8677 65.8337 90.1335 65.0302 90.5104L64.7701 90.6604C64.6019 90.7691 64.4416 90.8894 64.2902 91.0204C64.2187 91.0785 64.1518 91.1421 64.0901 91.2104C63.9473 91.3534 63.82 91.5109 63.7101 91.6804V91.7304L63.5601 91.9704L63.4102 92.2704C63.4102 92.3604 63.3402 92.4604 63.3002 92.5604V92.6304C63.3002 92.7404 63.2301 92.8404 63.2001 92.9504C63.2001 93.0104 62.8501 94.2104 62.8301 94.2704C64.3284 95.8945 66.1468 97.1906 68.1708 98.0771C70.1948 98.9636 72.3805 99.4214 74.5901 99.4214C76.7997 99.4214 78.9854 98.9636 81.0094 98.0771C83.0334 97.1906 84.8518 95.8945 86.3501 94.2704C86.3501 94.2104 86.0301 92.9704 86.0101 92.9104L85.8801 92.9604Z" fill="white"/>
<path d="M86.9099 58.14C102.412 58.14 114.98 45.5726 114.98 30.07C114.98 14.5673 102.412 2 86.9099 2C71.4072 2 58.8398 14.5673 58.8398 30.07C58.8398 45.5726 71.4072 58.14 86.9099 58.14Z" fill="#85CAFF" stroke="#0056B2" stroke-width="3.37" stroke-miterlimit="10"/>
<path d="M105.759 45.5699C105.759 45.3899 105.649 45.2199 105.589 45.0499C105.593 45.01 105.593 44.9698 105.589 44.9299C105.529 44.7599 105.469 44.5999 105.399 44.4499C105.319 44.2699 105.239 44.1099 105.149 43.9499L104.909 43.5399V43.47C104.726 43.189 104.515 42.9276 104.279 42.6899C104.176 42.5758 104.066 42.4689 103.949 42.3699C103.705 42.1512 103.441 41.9569 103.159 41.7899C103.021 41.7002 102.877 41.6201 102.729 41.5499C101.401 40.928 99.9949 40.4906 98.5488 40.2499C96.0588 39.7199 92.6688 37.3 92.2988 35.77C91.9788 34.43 92.8588 33.2699 93.6388 32.2399L93.9888 31.78L94.0787 31.66C95.1374 30.2418 95.8249 28.5814 96.0787 26.8299C96.0787 26.6899 96.1388 26.5299 96.1588 26.3799C96.1635 26.3602 96.1635 26.3397 96.1588 26.3199C96.6533 23.7664 96.5884 21.136 95.9688 18.6099C95.474 16.9586 94.4999 15.4915 93.1699 14.3948C91.84 13.298 90.2141 12.6211 88.4988 12.4499H88.2788C87.8907 12.402 87.4998 12.3786 87.1088 12.3799C86.7177 12.3789 86.3269 12.4023 85.9387 12.4499H85.7188C84.0048 12.6217 82.3805 13.299 81.0522 14.3958C79.724 15.4926 78.7517 16.9594 78.2588 18.6099C77.6358 21.1353 77.5675 23.7657 78.0587 26.3199C78.0587 26.3199 78.0587 26.3199 78.0587 26.3799C78.0587 26.5299 78.0588 26.6899 78.1388 26.8299C78.3963 28.5804 79.0834 30.2399 80.1388 31.66L80.2288 31.78L80.5687 32.2399C81.3487 33.2399 82.2388 34.43 81.9088 35.77C81.5488 37.3 78.1588 39.7699 75.6588 40.2499C74.2155 40.489 72.8122 40.9265 71.4888 41.5499C71.3402 41.6201 71.1964 41.7002 71.0587 41.7899C70.7787 41.9595 70.5144 42.1536 70.2688 42.3699L69.9387 42.6899C69.7087 42.9305 69.5011 43.1916 69.3187 43.47L69.2688 43.5399C69.1788 43.6699 69.0988 43.8099 69.0188 43.9499C68.9288 44.1099 68.8488 44.2699 68.7688 44.4499C68.7688 44.4499 68.6387 44.7599 68.5787 44.9299V45.0499C68.5187 45.2199 68.4588 45.3899 68.4088 45.5699C68.4131 45.5897 68.4131 45.6102 68.4088 45.6299C68.4088 45.7299 67.8288 47.7099 67.7988 47.8199C70.2631 50.5154 73.2607 52.6691 76.6016 54.1445C79.9424 55.6198 83.5535 56.3846 87.2056 56.3903C90.8577 56.3959 94.4711 55.6423 97.8165 54.1772C101.162 52.7122 104.166 50.5677 106.639 47.8799C106.639 47.7699 106.109 45.7299 106.079 45.6299L105.759 45.5699Z" fill="white"/>
<path d="M139.788 75.3801C139.37 74.9357 138.862 74.5848 138.299 74.3505C137.735 74.1161 137.129 74.0035 136.518 74.0201C135.311 74.0253 134.154 74.5074 133.3 75.3615C132.446 76.2155 131.964 77.3723 131.958 78.5801V79.8901H130.648V74.8101C130.672 73.7246 130.318 72.6645 129.648 71.8101C129.006 70.9587 128.074 70.3719 127.028 70.1601C126.717 70.1139 126.403 70.0872 126.088 70.0801C124.881 70.0853 123.724 70.5674 122.87 71.4214C122.016 72.2755 121.534 73.4323 121.528 74.6401V79.86H120.238V74.86C120.258 73.5244 119.784 72.2285 118.908 71.2201C118.495 70.713 117.983 70.2948 117.404 69.9907C116.825 69.6865 116.191 69.5026 115.538 69.4501C115.363 69.4273 115.184 69.4273 115.008 69.4501C114.323 69.4488 113.645 69.5826 113.012 69.8438C112.378 70.105 111.803 70.4885 111.318 70.9725C110.833 71.4564 110.448 72.0312 110.186 72.664C109.924 73.2968 109.788 73.9751 109.788 74.6601V79.86H108.478V63.2501V44.98C108.49 43.9691 108.17 42.9822 107.568 42.17C107.439 41.9856 107.295 41.8117 107.138 41.6501C106.366 40.8579 105.323 40.3864 104.218 40.3301H103.598C102.494 40.3864 101.451 40.8579 100.678 41.6501C100.518 41.8116 100.371 41.9854 100.238 42.17C99.6394 42.9832 99.3232 43.9702 99.3384 44.98V75.79C99.3425 75.8633 99.3425 75.9368 99.3384 76.0101V93.4701L98.0385 92.2501V85.2501C98.0501 84.6376 97.9402 84.0289 97.7154 83.459C97.4905 82.8892 97.1551 82.3696 96.7284 81.9301C96.31 81.4857 95.8024 81.1348 95.2388 80.9005C94.6753 80.6661 94.0685 80.5535 93.4584 80.5701C92.2497 80.578 91.0929 81.0623 90.2392 81.9179C89.3854 82.7735 88.9037 83.9314 88.8985 85.1401V94.2501C88.8958 94.875 89.0213 95.4938 89.2673 96.0683C89.5132 96.6428 89.8744 97.1607 90.3284 97.5901L102.988 109.59C103.525 110.135 103.945 110.785 104.22 111.499C104.495 112.213 104.621 112.976 104.588 113.74C104.601 114.427 104.881 115.083 105.368 115.568C105.855 116.053 106.511 116.33 107.198 116.34H133.318C134.007 116.335 134.667 116.06 135.155 115.573C135.643 115.087 135.921 114.429 135.928 113.74V112.74C135.922 111.68 136.06 110.623 136.338 109.6L140.728 91.8501C141.002 90.8296 141.137 89.7767 141.128 88.7201V78.7201C141.141 78.1019 141.029 77.4874 140.799 76.9136C140.568 76.3397 140.225 75.8181 139.788 75.3801Z" fill="white"/>
<path d="M146.319 78.6899C146.342 77.3901 146.101 76.0992 145.609 74.8958C145.118 73.6923 144.386 72.6016 143.459 71.6899C142.566 70.7528 141.488 70.0112 140.293 69.5121C139.099 69.0129 137.814 68.767 136.519 68.7899C135.708 68.7865 134.899 68.8805 134.109 69.0699C133.212 67.7686 132.006 66.7103 130.599 65.9899C129.198 65.2679 127.645 64.8908 126.069 64.8899C124.307 64.889 122.578 65.3696 121.069 66.2799C119.325 64.9495 117.183 64.245 114.989 64.2799C114.555 64.2807 114.121 64.3074 113.689 64.3599V44.9699C113.709 43.6706 113.466 42.3807 112.974 41.1777C112.483 39.9748 111.753 38.8837 110.829 37.9699C109.601 36.7005 108.042 35.7999 106.329 35.3699H106.129C105.899 35.3699 105.679 35.2799 105.449 35.2499C105.219 35.2199 105.179 35.2499 105.039 35.1899C104.707 35.1512 104.374 35.1312 104.039 35.1299H103.859C103.525 35.1318 103.191 35.1518 102.859 35.1899L102.459 35.2499C102.229 35.2499 101.999 35.3199 101.779 35.3699H101.579C99.8662 35.7999 98.3075 36.7005 97.0793 37.9699C96.1535 38.8823 95.4227 39.9732 94.9311 41.1765C94.4395 42.3797 94.1973 43.6703 94.2193 44.9699V75.3999C94.0062 75.3848 93.7923 75.3848 93.5793 75.3999C90.987 75.4078 88.503 76.4404 86.669 78.2725C84.835 80.1046 83.7999 82.5876 83.7894 85.1799V94.2999C83.7787 95.637 84.0502 96.9614 84.5861 98.1865C85.122 99.4117 85.9102 100.51 86.8993 101.41L99.4793 113.41C99.4345 115.559 100.231 117.64 101.699 119.21C102.413 119.986 103.285 120.6 104.256 121.01C105.227 121.421 106.275 121.618 107.329 121.59H133.449C134.479 121.594 135.499 121.394 136.45 121.002C137.402 120.61 138.267 120.033 138.995 119.305C139.723 118.577 140.299 117.713 140.691 116.761C141.083 115.809 141.283 114.789 141.279 113.76C141.252 112.791 141.332 111.821 141.519 110.87L145.949 93.1299C146.288 91.6874 146.466 90.2116 146.479 88.7299L146.319 78.6899ZM140.709 91.8399L136.319 109.59C136.041 110.613 135.903 111.669 135.909 112.73V113.73C135.902 114.419 135.624 115.077 135.136 115.563C134.647 116.049 133.988 116.325 133.299 116.33H107.179C106.492 116.32 105.836 116.043 105.349 115.558C104.862 115.073 104.582 114.417 104.569 113.73C104.601 112.965 104.476 112.203 104.201 111.489C103.925 110.775 103.506 110.125 102.969 109.58L90.3093 97.5799C89.8552 97.1505 89.4941 96.6326 89.2481 96.0581C89.0021 95.4836 88.8767 94.8648 88.8793 94.2399V85.1299C88.8827 84.2241 89.1546 83.3397 89.6606 82.5884C90.1666 81.8372 90.8839 81.2528 91.722 80.9092C92.5601 80.5656 93.4812 80.4782 94.369 80.6581C95.2567 80.838 96.0711 81.2771 96.7093 81.9199C97.136 82.3594 97.4715 82.8791 97.6963 83.4489C97.9212 84.0187 98.031 84.6274 98.0193 85.2399V92.2399L99.3193 93.4599V76.0099C99.3233 75.9366 99.3233 75.8631 99.3193 75.7899V44.9799C99.3041 43.97 99.6202 42.983 100.219 42.1699C100.352 41.9852 100.499 41.8114 100.659 41.6499C101.432 40.8577 102.474 40.3862 103.579 40.3299H104.199C105.304 40.3862 106.347 40.8577 107.119 41.6499C107.276 41.8115 107.42 41.9854 107.549 42.1699C108.151 42.9821 108.471 43.969 108.459 44.9799V63.2499V79.9099H109.769V74.7099C109.769 74.0249 109.904 73.3466 110.167 72.7138C110.429 72.081 110.814 71.5063 111.299 71.0223C111.784 70.5384 112.359 70.1549 112.992 69.8936C113.626 69.6324 114.304 69.4986 114.989 69.4999C115.165 69.4771 115.343 69.4771 115.519 69.4999C116.171 69.5524 116.806 69.7363 117.385 70.0405C117.964 70.3446 118.476 70.7628 118.889 71.2699C119.765 72.2783 120.239 73.5743 120.219 74.9099V79.9099H121.519V74.6899C121.525 73.4821 122.007 72.3253 122.861 71.4713C123.715 70.6173 124.872 70.1351 126.079 70.1299C126.394 70.137 126.708 70.1637 127.019 70.2099C128.065 70.4217 128.997 71.0085 129.639 71.8599C130.309 72.7143 130.663 73.7744 130.639 74.8599V79.9399H131.949V78.6299C131.957 77.4229 132.44 76.2677 133.294 75.4142C134.147 74.5607 135.302 74.0777 136.509 74.0699C137.119 74.0533 137.726 74.1659 138.29 74.4003C138.853 74.6347 139.361 74.9855 139.779 75.4299C140.208 75.8681 140.545 76.3875 140.769 76.9576C140.994 77.5278 141.103 78.1371 141.089 78.7499V88.7499C141.101 89.7922 140.973 90.8314 140.709 91.8399Z" fill="#0056B2"/>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

+192
View File
@@ -0,0 +1,192 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Renderer for the grade single view report.
*
* @package gradereport_singleview
* @copyright 2022 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core\output\comboboxsearch;
use gradereport_singleview\report\singleview;
/**
* Custom renderer for the single view report.
*
* To get an instance of this use the following code:
* $renderer = $PAGE->get_renderer('gradereport_singleview');
*
* @copyright 2022 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class gradereport_singleview_renderer extends plugin_renderer_base {
/**
* Renders the user selector trigger element.
*
* @param object $course The course object.
* @param int|null $userid The user ID.
* @param int|null $groupid The group ID.
* @return string The raw HTML to render.
*/
public function users_selector(object $course, ?int $userid = null, ?int $groupid = null): string {
$resetlink = new moodle_url('/grade/report/singleview/index.php', ['id' => $course->id, 'group' => $groupid ?? 0]);
$submitteduserid = optional_param('userid', '', PARAM_INT);
if ($submitteduserid) {
$user = core_user::get_user($submitteduserid);
$currentvalue = fullname($user);
} else {
$currentvalue = '';
}
$data = [
'currentvalue' => $currentvalue,
'courseid' => $course->id,
'instance' => rand(),
'group' => $groupid ?? 0,
'resetlink' => $resetlink->out(false),
'name' => 'userid',
'value' => $submitteduserid ?? '',
];
$dropdown = new comboboxsearch(
true,
$this->render_from_template('core_user/comboboxsearch/user_selector', $data),
null,
'user-search d-flex',
null,
'usersearchdropdown overflow-auto',
null,
false,
);
return $this->render_from_template($dropdown->get_template(), $dropdown->export_for_template($this));
}
/**
* Renders the grade items selector trigger element.
*
* @param object $course The course object.
* @param int|null $gradeitemid The grade item ID.
* @return string The raw HTML to render.
*/
public function grade_items_selector(object $course, ?int $gradeitemid = null): string {
$data = [
'name' => 'itemid',
'courseid' => $course->id,
'instance' => rand(),
];
// If a particular grade item option is selected (not in zero state).
if ($gradeitemid) {
$gradeitemname = grade_item::fetch(['id' => $gradeitemid])->get_name(true);
$data['selectedoption'] = [
'text' => $gradeitemname,
];
$data['itemid'] = $gradeitemid;
}
$sbody = $this->render_from_template('core/local/comboboxsearch/searchbody', [
'courseid' => $course->id,
'currentvalue' => optional_param('gradesearchvalue', '', PARAM_NOTAGS),
'instance' => $data['instance'],
]);
$dropdown = new comboboxsearch(
false,
$this->render_from_template('gradereport_singleview/grade_item_selector', $data),
$sbody,
'grade-search h-100',
'gradesearchwidget h-100',
'gradesearchdropdown overflow-auto',
null,
true,
get_string('selectagrade', 'gradereport_singleview'),
'itemid',
$gradeitemid
);
return $this->render_from_template($dropdown->get_template(), $dropdown->export_for_template($this));
}
/**
* Creates and renders previous/next user/grade item navigation.
*
* @param object $gpr grade plugin return tracking object
* @param int $courseid The course ID.
* @param \context_course $context Context of the report.
* @param singleview $report The single view report class.
* @param int|null $groupid Group ID
* @param string $itemtype User or Grade item type
* @param int $itemid Either User ID or Grade item ID
* @return string The raw HTML to render.
* @throws moodle_exception
*/
public function report_navigation(object $gpr, int $courseid, \context_course $context, singleview $report,
?int $groupid, string $itemtype, int $itemid): string {
$navigation = '';
$options = $report->screen->options();
$optionkeys = array_keys($options);
$optionitemid = array_shift($optionkeys);
$relreport = new gradereport_singleview\report\singleview(
$courseid, $gpr, $context,
$report->screen->item_type(), $optionitemid
);
$reloptions = $relreport->screen->options();
$reloptionssorting = array_keys($relreport->screen->options());
$i = array_search($itemid, $reloptionssorting);
$navparams = ['item' => $itemtype, 'id' => $courseid, 'group' => $groupid];
// Determine directionality so that icons can be modified to suit language.
$previousarrow = right_to_left() ? 'right' : 'left';
$nextarrow = right_to_left() ? 'left' : 'right';
if ($i > 0) {
$navparams['itemid'] = $reloptionssorting[$i - 1];
$link = (new moodle_url('/grade/report/singleview/index.php', $navparams))
->out(false);
$navigationdata['previoususer'] = [
'name' => $reloptions[$navparams['itemid']],
'url' => $link,
'previousarrow' => $previousarrow
];
}
if ($i < count($reloptionssorting) - 1) {
$navparams['itemid'] = $reloptionssorting[$i + 1];
$link = (new moodle_url('/grade/report/singleview/index.php', $navparams))
->out(false);
$navigationdata['nextuser'] = [
'name' => $reloptions[$navparams['itemid']],
'url' => $link,
'nextarrow' => $nextarrow
];
}
if ($report->screen->supports_paging()) {
$navigationdata['perpageselect'] = $report->screen->perpage_select();
}
if (isset($navigationdata)) {
$navigation = $this->render_from_template('gradereport_singleview/report_navigation', $navigationdata);
}
return $navigation;
}
}
+157
View File
@@ -0,0 +1,157 @@
.path-grade-report-singleview div.reporttable {
margin: 20px 0 30px 0;
padding: 10px 10px;
background-color: #f8f9fa;
text-align: center;
}
.path-grade-report-singleview div.groupselector,
.path-grade-report-singleview div.selectitems {
display: block;
text-align: right;
clear: both;
}
.path-grade-report-singleview div.singleselect + div.singleselect select,
.path-grade-report-singleview div.groupselector select {
margin-right: 0;
}
.path-grade-report-singleview div.reporttable div.singleselect form div {
text-align: center;
}
.path-grade-report-singleview div.reporttable table.reporttable {
margin: 0 auto 15px auto;
}
.path-grade-report-singleview div.reporttable form div {
text-align: left;
}
.path-grade-report-singleview div.reporttable h2 {
text-align: left;
}
.path-grade-report-singleview .overrideexcludecheck {
width: 14.75px;
height: 12px;
color: #2ca14f;
}
.path-grade-report-singleview .report-navigation .container {
max-width: 1060px;
margin-left: 0;
}
.path-grade-report-singleview .zero-state img {
width: 185px;
}
.path-grade-report-singleview .reporttable thead th,
.path-grade-report-singleview .reporttable thead td {
white-space: nowrap;
border: none;
}
.path-grade-report-singleview .reporttable tbody tr,
.path-grade-report-singleview .reporttable tbody tr:nth-of-type(odd),
.path-grade-report-singleview .reporttable tbody tr:hover {
color: inherit;
background-color: white;
border-top: none;
border-bottom: 1px solid #dee2e6;
}
.path-grade-report-singleview .reporttable tbody th,
.path-grade-report-singleview .reporttable tbody td.range {
white-space: nowrap;
font-weight: normal;
}
.path-grade-report-singleview .reporttable tbody th > * {
display: inline-block;
vertical-align: middle;
margin: 0 2px;
}
.path-grade-report-singleview .itemnav {
font-size: small;
display: inline;
padding-bottom: 0.5em;
}
.path-grade-report-singleview .itemnav.previtem {
float: left;
}
.path-grade-report-singleview div.nextitem {
float: right;
}
.path-grade-report-singleview .reporttable {
width: 100%;
}
.path-grade-report-singleview .reporttable .generaltable th,
.path-grade-report-singleview .reporttable .generaltable td {
text-align: left;
vertical-align: middle;
border: none;
}
.path-grade-report-singleview div.reporttable form div.singleview_bulk {
display: inline-block;
text-align: left;
margin-bottom: 1em;
}
.path-grade-report-singleview .singleview_bulk div > *,
.path-grade-report-singleview .singleview_bulk fieldset > * {
display: inline-block;
vertical-align: middle;
margin: 0;
}
.path-grade-report-singleview .singleselect select,
.path-grade-report-singleview div.reporttable form .singleview_bulk select,
.path-grade-report-singleview div.reporttable form .singleview_bulk input {
margin-left: 10px;
margin-right: 10px;
}
#bulkinsertmodal {
width: 181px;
height: 39px;
top: 309px;
border-radius: 4px;
}
.path-grade-report-singleview .singleview_bulk > fieldset {
display: block;
}
.path-grade-report-singleview div.reporttable form .singleview_bulk > div.enable {
margin-bottom: 0.5em;
text-align: left;
}
.searchresultitemscontainer {
max-height: 150px;
height: 150px;
overflow: auto;
}
.path-grade-report-singleview .search-widget[data-searchtype="user"] .userinitials {
width: 40px;
height: 40px;
}
.path-grade-report-singleview .reporttable .gradeitem span.small {
font-size: 70%;
}
.path-grade-report-singleview .reporttable div.categoryitem .gradeitemheader,
.path-grade-report-singleview .reporttable div.courseitem .gradeitemheader {
font-weight: bold;
}
@@ -0,0 +1,110 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/action_bar
Context variables required for this template:
* generalnavselector - The data object containing the required properties to render the general navigation selector.
* groupselector - (optional) HTML that outputs the group selector
* itemselector - (optional) HTML that outputs the user or grade item selector
* pagetoggler - (optional) HTML that outputs the user/grade item view toggler
* bulkactions - (optional) HTML that outputs the bulk actione menu
Example context (json):
{
"generalnavselector": {
"name": "Gradebook tertiary navigation selector",
"value": "opt2",
"baseid": "select-menu56789",
"selectedoption": "Gradebook setup",
"options": [
{
"selected": false,
"isgroup": {
"name": "View",
"id": "select-menu-group1",
"options": [
{
"name": "Grader report",
"value": "opt1",
"id": "select-menu-option1",
"selected": false
}
]
}
},
{
"selected": false,
"isgroup": {
"name": "Setup",
"id": "select-menu-group2",
"options": [
{
"name": "Gradebook setup",
"value": "opt2",
"id": "select-menu-option2",
"selected": true
}
]
}
}
]
},
"groupselector": "<div class='group-selector'></div>",
"itemselector": "<div class='user-selector'></div>",
"pagetoggler": "<div class='page-toggler'></div>",
"bulkactions": "<div class='action-menu'></div>"
}
}}
<div class="container-fluid tertiary-navigation full-width-bottom-border">
<div class="row">
{{#generalnavselector}}
<div class="navitem order-1">
{{>core/tertiary_navigation_selector}}
</div>
<div class="navitem-divider d-none d-sm-flex order-1"></div>
{{/generalnavselector}}
{{#groupselector}}
<div class="navitem order-2">
{{{.}}}
</div>
<div class="navitem-divider d-none d-sm-flex order-2"></div>
{{/groupselector}}
{{#itemselector}}
<div class="navitem order-3">
{{{.}}}
</div>
<div class="navitem-divider d-none d-sm-flex order-3"></div>
{{/itemselector}}
{{#pagetoggler}}
<div class="d-flex row ml-0 ml-sm-auto order-4 px-0">
{{#bulkactions}}
<div class="d-flex navitem ml-0 ml-sm-auto mr-3">{{{bulkactions}}}</div>
{{#js}}
require(['gradereport_singleview/bulkactions'], function(bulkactions) {
bulkactions.init();
});
{{/js}}
<div class="navitem-divider d-none d-sm-flex"></div>
{{/bulkactions}}
<div class="d-flex navitem ml-0 mr-auto mr-sm-0 ml-sm-auto">
{{>gradereport_singleview/page_toggler}}
</div>
</div>
{{/pagetoggler}}
</div>
</div>
@@ -0,0 +1,52 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/bulk_insert
Bulk insert attribute.
Example context (json):
{
"applyname": "Apply-name",
"applylabel": "Perform bulk insert",
"label": "Bulk insert",
"menuname": "Menu-name",
"menulabel": "For",
"menuoptions": "JSON object",
"value": "all",
"selected": "",
"name": "All grades",
"valuename": "Value-name",
"valuelabel": "Insert value",
"valuefield": "PARSED HTML"
}
}}
<div class="enable">
<input type="checkbox" name="{{applyname}}" value="1" id="{{applyname}}" class="ml-0">
<label for="{{applyname}}">{{applylabel}}</label>
</div>
<fieldset class="d-flex flex-wrap align-items-center">
<legend class="accesshide">{{label}}</legend>
<label for="{{menuname}}">{{menulabel}}</label>
<select name="{{menuname}}" id="{{menuname}}" class="form-control custom-select">
{{#menuoptions}}
<option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
{{/menuoptions}}
</select>
<label for="{{valuename}}">{{valuelabel}}</label>
{{{valuefield}}}
</fieldset>
@@ -0,0 +1,54 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/bulkinsert
Template for the bulk insert grades modal.
Example context (json):
{
"name": "bulksinsertmodal",
"id": "bulksinsertmodal"
}
}}
<form>
<div class="alert alert-warning">
{{#str}}unsavedataalert, gradereport_singleview{{/str}}
<span class="sr-only">{{#str}}unsaveddatawarning, gradereport_singleview{{/str}}</span>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" value="1" id="override">
<label class="form-check-label" for="override">
{{#str}}unsavedataconfirm, gradereport_singleview{{/str}}
</label>
</div>
</div>
<div class="formdata dimmed_text">
{{#str}}bulkchoice, gradereport_singleview{{/str}}
<div class="pt-3 px-3" role="radiogroup">
<label class="form-check">
<input class="form-check-input" type="radio" name="bulkinsert" value="all" disabled/>
{{#str}}all_grades, gradereport_singleview{{/str}}
</label>
<label class="form-check">
<input class="form-check-input" type="radio" name="bulkinsert" value="blanks" disabled/>
{{#str}}blanks, gradereport_singleview{{/str}}
</label>
</div>
<p class ="font-weight-bold">
{{#str}}bulkinsertvalue, gradereport_singleview{{/str}}
</p>
<label for="{{name}}" class="sr-only">{{#str}}bulkinsertvalue, gradereport_singleview{{/str}}</label>
<input type="text" name="{{name}}" id="{{id}}" value="0" class="form-control text-ltr" disabled {{#readonly}}readonly{{/readonly}}>
</div>
</form>
@@ -0,0 +1,28 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/button
Button.
Example context (json):
{
"type": "submit",
"value": "Save"
}
}}
<input type="{{type}}" value="{{value}}" {{disabled}} class="btn btn-primary">
@@ -0,0 +1,42 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/dropdown_attribute
Dropdown attribute.
Example context (json):
{
"name": "Awesome-test",
"disabled": "true",
"options": "true",
"value": "1",
"selected": "true"
}
}}
{{#readonly}}
{{text}}
{{/readonly}}
{{^readonly}}
<label for="{{name}}" class="accesshide">{{label}}</label>
<select id="{{name}}" name="{{name}}" class="custom-select" {{#disabled}}disabled{{/disabled}}>
{{#options}}
<option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
{{/options}}
</select>
<input type="hidden" name="old{{name}}" value="{{value}}">
{{/readonly}}
@@ -0,0 +1,55 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/grade_item_selector
The grade item selector trigger element.
Context variables required for this template:
* itemid - The value of the grade item selector element (id of the preselected grade item)
* instance - The instance ID of the combo box.
* courseid - The course ID.
* selectedoption - (optional) Object containing information about the selected option.
* text - The text of the selected option.
Example context (json):
{
"itemid": "21",
"instance": "25",
"courseid": "2",
"selectedoption": {
"text": "Grade item 1"
}
}
}}
<div class="align-items-center d-flex">
{{#selectedoption}}
<div class="d-block pr-3 text-truncate">
<span class="d-block small" aria-hidden="true">
{{#str}} selectagrade, gradereport_singleview {{/str}}
</span>
<span class="p-0 font-weight-bold">
{{text}}
</span>
</div>
{{/selectedoption}}
{{^selectedoption}}
<div class="user-info d-block pr-3">
{{#str}} selectagrade, gradereport_singleview {{/str}}
</div>
{{/selectedoption}}
</div>
<span class="d-none" data-region="courseid" data-courseid="{{courseid}}"></span>
<span class="d-none" data-region="itemid" data-itemid="{{itemid}}"></span>
<span class="d-none" data-region="instance" data-instance="{{instance}}"></span>
@@ -0,0 +1,41 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/gradesearch_body
The body of the grade item widget.
Example context (json):
{}
}}
{{< core/search_input_auto }}
{{$label}}{{#str}}
searchgrades, gradereport_singleview
{{/str}}{{/label}}
{{$placeholder}}{{#str}}
searchgrades, gradereport_singleview
{{/str}}{{/placeholder}}
{{$additionalattributes}}
role="combobox"
aria-expanded="true"
aria-controls="listbox-{{uniqid}}"
aria-autocomplete="list"
data-input-element="input-{{uniqid}}"
{{/additionalattributes}}
{{/ core/search_input_auto }}
<input type="hidden" name="search" id="input-{{uniqid}}"/>
<div role="listbox" id="listbox-{{uniqid}}" class="searchresultscontainer" data-region="search-results-container-widget"></div>
@@ -0,0 +1,53 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/page_toggler
The small selector at the top right of the page to switch between zero states.
Example context (json):
{
"displaylabel": true,
"userzerolink": "http://foo.bar/gradereport/?userid=25",
"userselectactive": "false",
"gradepagepage": "http://foo.bar/gradereport/?userid=25",
"gradeselectactive": "true"
}
}}
<div class="page-toggler d-block d-sm-flex text-nowrap">
{{#displaylabel}}
<p class="my-auto mr-3 text-uppercase">{{#str}}viewby, gradereport_singleview{{/str}}</p>
{{/displaylabel}}
<div class="btn-group align-items-center" role="group" aria-label="{{#str}}ariareporttype, gradereport_singleview{{/str}}">
<a href="{{userzerolink}}"
role="button"
class="btn btn-outline-primary {{#userselectactive}}active{{/userselectactive}}"
{{#userselectactive}}aria-pressed="true"{{/userselectactive}}
{{^userselectactive}}aria-pressed="false"{{/userselectactive}}
>
{{#str}}users{{/str}}
</a>
<a href="{{gradezerolink}}"
role="button"
class="btn btn-outline-primary {{#gradeselectactive}}active{{/gradeselectactive}}"
{{#gradeselectactive}}aria-pressed="true"{{/gradeselectactive}}
{{^gradeselectactive}}aria-pressed="false"{{/gradeselectactive}}
>
{{#str}}gradeitems, grades{{/str}}
</a>
</div>
</div>
@@ -0,0 +1,58 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_grader/perpage
The students per page dropdown element.
Context variables required for this template:
* baseurl - The base URL for the perpage action.
* options - Choices
Example context (json):
{
"baseurl": "http://example.com/grade/report/grader/index.php?id=2&report=grader",
"options": [
{"name": "20", "value": "20"},
{"name": "100", "value": "100", "selected": true},
{"name": "All", "value": "0"}
]
}
}}
<div class="col-auto">
<label>
{{#str}}show{{/str}}
<select name="perpage" class="mt-1 custom-select ignoredirty" id="{{uniqid}}">
{{#options}}
<option value="{{value}}" {{#selected}}selected{{/selected}}>{{name}}</option>
{{/options}}
</select>
</label>
</div>
{{#pagingbar}}
<div class="col">
{{{pagingbar}}}
</div>
{{/pagingbar}}
{{#js}}
document.getElementById('{{uniqid}}').addEventListener('change', function(e) {
var url = new URL('{{{baseurl}}}');
url.searchParams.set('perpage', e.target.value);
window.location.href = url;
});
{{/js}}
@@ -0,0 +1,38 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/report
Report table and sticky footer.
Context variables required for this template:
* sesskey - string - The session key.
* table - string - HTML content of the grade items or users table.
* stickyfooter - string - HTML content of the sticky footer.
Example context (json):
{
"sesskey": "fakesesskey",
"table": "<div><div class='reporttable'><div class='tableresponsive'><table class='generaltable' id='singleview-grades'><thead> <tr><th>Name</th><th>User fullname</th><th>Grade</th><th>Actions</th> </tr></thead><tbody></tbody></table></div></div></div>",
"stickyfooter": "<div id='sticky-footer'></div>"
}
}}
<form method="post">
{{{table}}}
{{{stickyfooter}}}
<input type="hidden" name="sesskey" value="{{sesskey}}">
</form>
@@ -0,0 +1,64 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/report_navigation
The previous/next user/item navigation for the singleview report.
Context variables required for this template:
* previoususer - (optional) The object containing information about the previous user.
* name - The name of the previous user/item.
* url - The URL to the previous user/item report.
* nextuser - (optional) The object containing information about the next user.
* name - The name of the next user/item.
* url - The URL to the next user/item report.
Example context (json):
{
"previoususer": {
"name": "John Smith",
"url": "https://example.com/grade/report/singleview/index.php?id=2&itemid=3"
},
"previoususer": {
"name": "Jane Doe",
"url": "https://example.com/grade/report/singleview/index.php?id=2&itemid=5"
}
}
}}
<div class="report-navigation w-100">
<div class="container w-100 d-flex">
{{#perpageselect}}
<div class="d-flex report-paging">
{{{perpageselect}}}
</div>
{{/perpageselect}}
<div class="d-flex ml-auto mt-2">
{{#previoususer}}
<div class="previous d-flex">
<a href="{{url}}" aria-label="{{#str}} gotopreviousreport, gradereport_singleview {{/str}}">
<i class="fa fa-caret-{{previousarrow}} fa-lg pr-1"></i>
{{name}}
</a>
</div>
{{/previoususer}}
</div>
<div class="d-flex ml-auto mt-2">
{{#nextuser}}
<div class="next d-flex ml-auto">
<a href="{{url}}" aria-label="{{#str}} gotonextreport, gradereport_singleview {{/str}}">
{{name}}
<i class="fa fa-caret-{{nextarrow}} fa-lg pl-1"></i>
</a>
</div>
{{/nextuser}}
</div>
</div>
</div>
@@ -0,0 +1,42 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/text_attribute
Text attribute.
Example context (json):
{
"name": "Awesome-report",
"label": "Text label",
"value": "Text information",
"disabled": false,
"type": "number",
"isnumeric": true,
"min": "0",
"max": "100"
}
}}
{{#readonly}}
{{value}}
{{/readonly}}
{{^readonly}}
{{#label}}<label for="{{name}}" class="accesshide">{{label}}</label>{{/label}}
<input id="{{name}}" data-uielement="text" name="{{name}}" type="{{#type}}{{.}}{{/type}}{{^type}}text{{/type}}" value="{{value}}" class="form-control" {{#disabled}}disabled{{/disabled}}{{!
}} {{#isnumeric}} {{#min}}min="{{.}}" {{/min}} {{#max}}max="{{.}}" {{/max}} step="any" {{/isnumeric}}>
<input type="hidden" name="old{{name}}" value="{{value}}">
{{/readonly}}
@@ -0,0 +1,36 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/zero_state
The zero state of the user report that contains the image and trigger for the search widget.
Example context (json):
{
"imglink": "http://foo.bar/gradereport/?userid=25",
"userzerolink": "http://foo.bar/gradereport/?userid=25",
"gradezerolink": "http://foo.bar/gradereport/?userid=25"
}
}}
<div class="zero-state mx-auto w-50 text-center my-6">
<img src="{{imglink}}" alt="{{#str}}pluginname, gradereport_singleview{{/str}}" aria-hidden="true" class="my-5">
<h3>{{#str}}pluginname, gradereport_singleview{{/str}}</h3>
<p>{{#str}}viewsingleuserorgradeitem, gradereport_singleview{{/str}}</p>
<div class="justify-content-center d-flex">
{{>gradereport_singleview/page_toggler}}
</div>
</div>
@@ -0,0 +1,31 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/zero_state_grade
The zero state of the user report that contains the image and trigger for the search widget.
Example context (json):
{
"imglink": "http://foo.bar/gradereport/?userid=25"
}
}}
<div class="zero-state mx-auto w-50 text-center my-6">
<img src="{{imglink}}" alt="{{#str}}viewsinglegradeitem, gradereport_singleview{{/str}}" aria-hidden="true" class="my-5">
<h2 class="h3">{{#str}}viewsinglegradeitem, gradereport_singleview{{/str}}</h2>
<p>{{#str}}singleviewdescription, gradereport_singleview{{/str}}</p>
</div>
@@ -0,0 +1,31 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template gradereport_singleview/zero_state_user
The zero state of the user report that contains the image and trigger for the search widget.
Example context (json):
{
"imglink": "http://foo.bar/gradereport/?userid=25"
}
}}
<div class="zero-state mx-auto w-50 text-center my-6">
<img src="{{imglink}}" alt="{{#str}}viewsingleuser, gradereport_singleview{{/str}}" aria-hidden="true" class="my-5">
<h2 class="h3">{{#str}}viewsingleuser, gradereport_singleview{{/str}}</h2>
<p>{{#str}}singleviewdescription, gradereport_singleview{{/str}}</p>
</div>
@@ -0,0 +1,140 @@
@core @core_grades @gradereport_singleview @javascript
Feature: We can bulk insert grades for students in a course
As a teacher
In order to quickly grade items
I can bulk insert values for all or empty grades.
Background:
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "users" exist:
| username | firstname | lastname | email | idnumber | alternatename |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 | fred |
| student1 | Student | 1 | student1@example.com | s1 | james |
| student2 | Student | 2 | student1@example.com | s2 | holly |
| student3 | Student | 3 | student1@example.com | s3 | anna |
| student4 | Student | 4 | student1@example.com | s4 | zac |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
And the following "activities" exist:
| activity | course | idnumber | name | intro |
| assign | C1 | a1 | Test assignment one | Submit something! |
| assign | C1 | a2 | Test assignment two | Submit something! |
| assign | C1 | a3 | Test assignment three | Submit something! |
| assign | C1 | a4 | Test assignment four | Submit nothing! |
And I am on the "Course 1" "Course" page logged in as "teacher1"
And I turn editing mode on
Scenario: I can not save bulk insert until I fill required form elements
Given I navigate to "View > Grader report" in the course gradebook
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
And I click on "Actions" "link"
When I click on "Bulk insert" "link"
And the "Empty grades" "radio" should be disabled
And the "All grades" "radio" should be disabled
And the "[name=bulkinsertmodal]" "css_element" should be disabled
And the "[data-action=save]" "css_element" should be disabled
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And the "Empty grades" "radio" should be enabled
And the "All grades" "radio" should be enabled
And the "[name=bulkinsertmodal]" "css_element" should be enabled
And the "[data-action=save]" "css_element" should be disabled
And I click on "Empty grades" "radio"
And the "Empty grades" "radio" should be enabled
And the "All grades" "radio" should be enabled
And the "[name=bulkinsertmodal]" "css_element" should be enabled
Then the "[data-action=save]" "css_element" should be enabled
Scenario: I can bulk insert grades and check their override flags for grade view.
Given I am on the "Test assignment one" "assign activity" page
And I follow "View all submissions"
And I click on "Grade" "link" in the "Student 1" "table_row"
And I set the following fields to these values:
| Grade out of 100 | 50 |
And I press "Save changes"
And I am on "Course 1" course homepage with editing mode on
And I navigate to "View > Grader report" in the course gradebook
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
And the field "Grade for Student 1" matches value "50.00"
And the field "Override for Student 1" matches value "0"
And I click on "Actions" "link"
And I click on "Bulk insert" "link"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "Empty grades" "radio"
And I set the field "Insert new grade" to "1.0"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
And the field "Grade for Student 1" matches value "50.00"
And the field "Override for Student 1" matches value "0"
And the field "Grade for Student 2" matches value "1.00"
And the field "Override for Student 2" matches value "1"
And the field "Grade for Student 3" matches value "1.00"
And the field "Override for Student 3" matches value "1"
And the field "Grade for Student 4" matches value "1.00"
And the field "Override for Student 4" matches value "1"
And I click on "Actions" "link"
When I click on "Bulk insert" "link"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "All grades" "radio"
And I set the field "Insert new grade" to "2.0"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
And the field "Grade for Student 1" matches value "2.00"
And the field "Override for Student 1" matches value "1"
And the field "Grade for Student 2" matches value "2.00"
And the field "Override for Student 2" matches value "1"
And the field "Grade for Student 3" matches value "2.00"
And the field "Override for Student 3" matches value "1"
And the field "Grade for Student 4" matches value "2.00"
Then the field "Override for Student 4" matches value "1"
Scenario: I can bulk insert grades and check their override flags for user view.
Given I am on the "Test assignment two" "assign activity" page
And I follow "View all submissions"
And I click on "Grade" "link" in the "Student 1" "table_row"
And I set the following fields to these values:
| Grade out of 100 | 50 |
And I press "Save changes"
And I am on "Course 1" course homepage with editing mode on
And I navigate to "View > Grader report" in the course gradebook
And I click on user menu "Student 1"
And I choose "Single view for this user" in the open action menu
And the field "Grade for Test assignment two" matches value "50.00"
And the field "Override for Test assignment two" matches value "0"
And I click on "Actions" "link"
When I click on "Bulk insert" "link"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "Empty grades" "radio"
And I set the field "Insert new grade" to "1.0"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
And the field "Grade for Test assignment two" matches value "50.00"
And the field "Override for Test assignment two" matches value "0"
And the field "Grade for Test assignment one" matches value "1.00"
And the field "Override for Test assignment one" matches value "1"
And the field "Grade for Test assignment three" matches value "1.00"
And the field "Override for Test assignment three" matches value "1"
And the field "Grade for Test assignment four" matches value "1.00"
Then the field "Override for Test assignment four" matches value "1"
Scenario: I can not update grades if the value is out of bounds.
Given I navigate to "View > Grader report" in the course gradebook
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
And I click on "Actions" "link"
When I click on "Bulk insert" "link"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "Empty grades" "radio"
And I set the field "Insert new grade" to "-1"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
And I should see "The grade entered for Test assignment one for Student 1 is less than the minimum allowed"
And I should see "The grade entered for Test assignment one for Student 2 is less than the minimum allowed"
And I should see "The grade entered for Test assignment one for Student 3 is less than the minimum allowed"
And I should see "The grade entered for Test assignment one for Student 4 is less than the minimum allowed"
Then I should see "Grades were set for 0 items"
@@ -0,0 +1,70 @@
@core @core_grades @gradereport_singleview @javascript
Feature: Given we have opted to search for a grade item, Lets find and search them.
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email | idnumber |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 |
| student1 | Student | 1 | student1@example.com | s1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | course | idnumber | name |
| assign | C1 | a1 | Test assignment one |
| assign | C1 | a2 | Test assignment two |
And I am on the "Course 1" "grades > Single view > View" page logged in as "teacher1"
And I change window size to "large"
Scenario: A teacher can search for and find a grade item to view
Given I click on "Grade items" "link" in the ".page-toggler" "css_element"
And I click on ".gradesearchwidget" "css_element"
When I confirm "Test assignment one" in "grade" search within the gradebook widget exists
And I confirm "Test assignment two" in "grade" search within the gradebook widget exists
Then I set the field "Search items" to "two"
And I wait until "Test assignment one" "option_role" does not exist
And I confirm "Test assignment one" in "grade" search within the gradebook widget does not exist
And I confirm "Test assignment two" in "grade" search within the gradebook widget exists
And I click on "Test assignment two" in the "grade" search widget
# The search input remains in the field on reload this is in keeping with other search implementations.
And I click on ".gradesearchwidget" "css_element"
And the field "Search items" matches value "two"
Then I set the field "Search items" to "Turtle"
And I should see "No results for \"Turtle\""
@accessibility
Scenario: A teacher can set focus and search using the input with a keyboard
# Basic tests for the page.
Given I click on "Grade items" "link" in the ".page-toggler" "css_element"
And I click on ".gradesearchwidget" "css_element"
And the page should meet accessibility standards
And the page should meet "wcag131, wcag141, wcag412" accessibility standards
And the page should meet accessibility standards with "wcag131, wcag141, wcag412" extra tests
# Move onto general keyboard navigation testing.
And I click on "Search items" "field"
And I wait until "Test assignment one" "option_role" exists
And I press the down key
And the focused element is "Search items" "field"
And ".active" "css_element" should exist in the "Test assignment one" "option_role"
And I press the up key
And the focused element is "Search items" "field"
And ".active" "css_element" should exist in the "Course total" "option_role"
And I press the down key
And the focused element is "Search items" "field"
And ".active" "css_element" should exist in the "Test assignment one" "option_role"
Then I set the field "Search items" to "Goodmeme"
And I wait until "Test assignment one" "option_role" does not exist
And I press the down key
And the focused element is "Search items" "field"
# Lets check the tabbing order.
And I set the field "Search items" to "one"
And I wait until "Test assignment one" "option_role" exists
And I press the tab key
And the focused element is "Clear search input" "button" in the ".grade-search" "css_element"
And I press the enter key
And I wait until the page is ready
And ".gradesearchwidget" "css_element" should exist
@@ -0,0 +1,95 @@
@gradereport @gradereport_singleview @javascript
Feature: Singleview report pagination
In order to consume the content of the report better
As a teacher
I need the report to be paginated
Background:
Given the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And the following "activities" exist:
| activity | course | idnumber | name | intro | grade |
| assign | C1 | a1 | Test assignment one | Submit something! | 300 |
Scenario: Default is used when teachers have no preference yet on singleview report
Given "41" "users" exist with the following data:
| username | student[count] |
| firstname | Student |
| lastname | [count] |
| email | student[count]@example.com |
And "41" "course enrolments" exist with the following data:
| user | student[count] |
| course | C1 |
| role |student |
When I am on the "Course 1" "grades > Grader report > View" page logged in as "teacher1"
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
Then the field "perpage" matches value "20"
# There is also 1 header row.
And I should see "21" node occurrences of type "tr" in the "singleview-grades" "table"
And I should see "3" in the ".stickyfooter .pagination" "css_element"
And I should not see "4" in the ".stickyfooter .pagination" "css_element"
Scenario: Teachers can have their preference for the number of students on singleview report
Given the following "courses" exist:
| fullname | shortname |
| Course 2 | C2 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C2 | editingteacher |
And the following "activities" exist:
| activity | course | idnumber | name | intro | grade |
| assign | C2 | a2 | Test assignment two | Submit something! | 300 |
When I am on the "Course 1" "grades > Grader report > View" page logged in as "teacher1"
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
And I set the field "perpage" to "100"
And I am on the "Course 2" "grades > Grader report > View" page
And I click on grade item menu "Test assignment two" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
Then the field "perpage" matches value "100"
Scenario: Teachers can change the number of students shown on singleview report
Given "101" "users" exist with the following data:
| username | student[count] |
| firstname | Student |
| lastname | [count] |
| email | student[count]@example.com |
And "101" "course enrolments" exist with the following data:
| user | student[count] |
| course | C1 |
| role |student |
When I am on the "Course 1" "grades > Grader report > View" page logged in as "teacher1"
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
And I set the field "perpage" to "100"
# There is also 1 header row.
Then I should see "101" node occurrences of type "tr" in the "singleview-grades" "table"
And I should see "2" in the ".stickyfooter .pagination" "css_element"
And I should not see "3" in the ".stickyfooter .pagination" "css_element"
@javascript
Scenario: The pagination bar is only displayed when there is more than one page on singleview report
Given "21" "users" exist with the following data:
| username | student[count] |
| firstname | Student |
| lastname | [count] |
| email | student[count]@example.com |
And "21" "course enrolments" exist with the following data:
| user | student[count] |
| course | C1 |
| role |student |
When I am on the "Course 1" "grades > Grader report > View" page logged in as "teacher1"
And I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
# By default, we have 20 students per page.
And ".stickyfooter .pagination" "css_element" should exist
And I set the field "perpage" to "100"
Then ".stickyfooter .pagination" "css_element" should not exist
@@ -0,0 +1,26 @@
@core @core_grades @gradereport_singleview @javascript
Feature: Given we land on the index page, select what type of report we wish to view.
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email | idnumber |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 |
| student1 | Student | 1 | student1@example.com | s1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | course | idnumber | name |
| assign | C1 | a1 | Test assignment one |
Scenario: I switch between the two report types within singleview
Given I am on the "Course 1" "grades > Single view > View" page logged in as "teacher1"
And I click on "Grade items" "link" in the ".page-toggler" "css_element"
And "Search users" "field" should not exist
And I confirm "Test assignment one" in "grade" search within the gradebook widget exists
When I click on "Users" "link" in the ".page-toggler" "css_element"
Then "Search users" "field" should exist
And "Select a grade item" "combobox" should not exist
@@ -0,0 +1,260 @@
@core @core_grades @gradereport_singleview @javascript
Feature: We can use Single view
As a teacher
In order to view and edit grades
For users and activities for a course.
Background:
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "users" exist:
| username | firstname | lastname | email | idnumber | middlename | alternatename | firstnamephonetic | lastnamephonetic |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 | | fred | | |
| teacher2 | No edit | 1 | teacher2@example.com | t2 | | nick | | |
| teacher3 | Teacher | 3 | teacher3@example.com | t3 | | jack | | |
| student1 | Grainne | Beauchamp | student1@example.com | s1 | Ann | Jill | Gronya | Beecham |
| student2 | Niamh | Cholmondely | student2@example.com | s2 | Jane | Nina | Nee | Chumlee |
| student3 | Siobhan | Desforges | student3@example.com | s3 | Sarah | Sev | Shevon | De-forjay |
| student4 | Student | 4 | student4@example.com | s4 | | zac | | |
And the following "scales" exist:
| name | scale |
| Test Scale | Disappointing, Good, Very good, Excellent |
And the following "grade items" exist:
| itemname | course | gradetype | scale |
| new grade item 1 | C1 | Scale | Test Scale |
And the following "scales" exist:
| name | scale |
| Test Scale | Disappointing, Good, Very good, Excellent |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | teacher |
| teacher3 | C1 | teacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
And the following "grade categories" exist:
| fullname | course |
| Sub category 1 | C1|
| Sub category 2 | C1|
And the following "activities" exist:
| activity | course | idnumber | name | intro | grade |
| assign | C1 | a1 | Test assignment one | Submit something! | 300 |
| assign | C1 | a2 | Test assignment two | Submit something! | 100 |
| assign | C1 | a3 | Test assignment three | Submit something! | 150 |
| assign | C1 | a4 | Test assignment four | Submit nothing! | 150 |
And the following "grade items" exist:
| itemname | course | gradetype |
| Test grade item | C1 | Scale |
And the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/grade:edit | Allow | teacher | Course | C1 |
| gradereport/singleview:view | Allow | teacher | Course | C1 |
And the following config values are set as admin:
| fullnamedisplay | firstnamephonetic,lastnamephonetic |
| alternativefullnameformat | middlename, alternatename, firstname, lastname |
And I am on the "Course 1" "grades > Grader report > View" page logged in as "teacher1"
Scenario: I can update grades, add feedback and exclude grades.
Given I navigate to "View > Single view" in the course gradebook
And I click on "Users" "link" in the ".page-toggler" "css_element"
And I click on "Student" in the "user" search widget
And I turn editing mode on
And I set the field "Override for Test assignment one" to "1"
When I set the following fields to these values:
| Grade for Test assignment one | 10.00 |
| Feedback for Test assignment one | test data |
And I set the field "Exclude for Test assignment four" to "1"
And I press "Save"
Then I should see "Grades were set for 2 items"
And the field "Exclude for Test assignment four" matches value "1"
And the field "Grade for Test assignment one" matches value "10.00"
And I set the following fields to these values:
| Test grade item | 45 |
And I press "Save"
Then I should see "Grades were set for 1 items"
And the field "Grade for Test grade item" matches value "45.00"
And the field "Grade for Course total" matches value "55.00"
And I open the action menu in "Test assignment three" "table_row"
And I choose "Show all grades" in the open action menu
And I click on "Override for Ann, Jill, Grainne, Beauchamp" "checkbox"
And I set the following fields to these values:
| Grade for Ann, Jill, Grainne, Beauchamp | 12.05 |
| Feedback for Ann, Jill, Grainne, Beauchamp | test data2 |
And I set the field "Exclude for Jane, Nina, Niamh, Cholmondely" to "1"
And I press "Save"
Then I should see "Grades were set for 2 items"
And the field "Grade for Ann, Jill, Grainne, Beauchamp" matches value "12.05"
And the field "Exclude for Jane, Nina, Niamh, Cholmondely" matches value "1"
And I click on "new grade item 1" in the "grade" search widget
And I set the field "Grade for Ann, Jill, Grainne, Beauchamp" to "Very good"
And I press "Save"
Then I should see "Grades were set for 1 items"
And the following should exist in the "generaltable" table:
| User full name | Grade |
| Ann, Jill, Grainne, Beauchamp | Very good |
And I am on the "Course 1" "grades > Single view > View" page logged in as "teacher2"
And I click on "Users" "link" in the ".page-toggler" "css_element"
And I click on "Student" in the "user" search widget
And I turn editing mode on
And the "Exclude for Test assignment one" "checkbox" should be disabled
And the "Override for Test assignment one" "checkbox" should be enabled
Scenario: Single view links work on grade report.
Given I click on grade item menu "Test assignment one" of type "gradeitem" on "grader" page
And I choose "Single view for this item" in the open action menu
And I should see "Test assignment one"
When I navigate to "View > Grader report" in the course gradebook
And I click on user menu "Grainne Beauchamp"
And I choose "Single view for this user" in the open action menu
Then I should see "Gronya,Beecham"
Scenario: I can bulk update grades.
Given I click on user menu "Grainne Beauchamp"
And I choose "Single view for this user" in the open action menu
And I should see "Gronya,Beecham"
When I turn editing mode on
And I click on "Actions" "link"
And I click on "Bulk insert" "link"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "All grades" "radio"
And I set the field "Insert new grade" to "1.0"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
Then I should see "Grades were set for 6 items"
Scenario: I can bulk update grades with custom decimal separator
Given the following "language customisations" exist:
| component | stringid | value |
| core_langconfig | decsep | # |
And I click on user menu "Grainne Beauchamp"
And I choose "Single view for this user" in the open action menu
And I should see "Gronya,Beecham"
When I turn editing mode on
And I click on "Actions" "link"
And I click on "Bulk insert" "link"
And I click on "I understand that my unsaved changes will be lost." "checkbox"
And I click on "All grades" "radio"
And I set the field "Insert new grade" to "1#25"
And I click on "Save" "button" in the ".modal-dialog" "css_element"
Then I should see "Grades were set for 6 items"
# Custom scale, cast to int
And the field "Grade for new grade item 1" matches value "Disappointing"
# Value grade, float with custom decsep.
And the field "Grade for Test assignment one" matches value "1#25"
# Numerical scale, cast to int, showing as float with custom decsep.
And the field "Grade for Test grade item" matches value "1#00"
Scenario: Navigation works in the Single view.
Given I click on user menu "Grainne Beauchamp"
And I choose "Single view for this user" in the open action menu
Then I should see "Gronya,Beecham"
And I click on "Nee,Chumlee" "link" in the ".stickyfooter" "css_element"
Then I should see "Nee,Chumlee"
And I click on "Gronya,Beecham" "link" in the ".stickyfooter" "css_element"
Then I should see "Gronya,Beecham"
And I open the action menu in "Test assignment four" "table_row"
And I choose "Show all grades" in the open action menu
Then I should see "Test assignment four"
And I click on "Test assignment three" in the "grade" search widget
Then I should see "Test assignment three"
And I click on "Test assignment four" in the "grade" search widget
Then I should see "Test assignment four"
Scenario: Activities are clickable only when it has a valid activity page.
Given I click on user menu "Grainne Beauchamp"
And I choose "Single view for this user" in the open action menu
And "new grade item 1" "link" should not exist in the "//tbody//tr[position()=1]//td[position()=2]" "xpath_element"
Then "Category total" "link" should not exist in the "//tbody//tr[position()=2]//td[position()=2]" "xpath_element"
And "Course total" "link" should not exist in the "//tbody//tr[position()=last()]//td[position()=2]" "xpath_element"
Scenario: Teacher sees his last viewed singleview report type when navigating back to the gradebook singleview report.
Given I navigate to "View > Single view" in the course gradebook
And I should see "Search for a user to view all their grades" in the "region-main" "region"
And I click on "Grade items" "link"
And I should see "Select a grade item above" in the "region-main" "region"
When I am on the "Course 1" "grades > Single view > View" page
Then I should see "Select a grade item above" in the "region-main" "region"
And I am on the "Course 1" "grades > Single view > View" page logged in as "teacher3"
And I should see "Search for a user to view all their grades" in the "region-main" "region"
Scenario: Teacher sees his last viewed user report when navigating back to the gradebook singleview report.
Given I navigate to "View > Single view" in the course gradebook
And I click on "Gronya,Beecham" in the "user" search widget
And I should see "Gronya,Beecham" in the "region-main" "region"
When I am on the "Course 1" "grades > Single view > View" page
Then I should not see "Search for a user to view all their grades" in the "region-main" "region"
And I should see "Gronya,Beecham" in the "region-main" "region"
And I am on the "Course 1" "grades > Single view > View" page logged in as "teacher3"
And I should see "Search for a user to view all their grades" in the "region-main" "region"
Scenario: Teacher sees his last viewed grade item report when navigating back to the gradebook singleview report.
Given I navigate to "View > Single view" in the course gradebook
And I click on "Grade items" "link"
And I click on "Test assignment one" in the "grade" search widget
And I should see "Test assignment one" in the "region-main" "region"
When I am on the "Course 1" "grades > Single view > View" page
Then I should not see "Select a grade item above" in the "region-main" "region"
And I should see "Test assignment one" in the "region-main" "region"
And I am on the "Course 1" "grades > Single view > View" page logged in as "teacher3"
And I should see "Search for a user to view all their grades" in the "region-main" "region"
Scenario: Teacher sees his last viewed user report if the user is a part of the the current group.
Given the following "groups" exist:
| name | course | idnumber | participation |
| Group 1 | C1 | G1 | 1 |
And the following "group members" exist:
| user | group |
| student2 | G1 |
And I am on the "Course 1" "course editing" page
And I expand all fieldsets
And I set the field "Group mode" to "Visible groups"
And I press "Save and display"
And I navigate to "View > Single view" in the course gradebook
And I click on "Nee,Chumlee" in the "user" search widget
And I navigate to "View > Grader report" in the course gradebook
And I click on "Group 1" in the "group" search widget
When I navigate to "View > Single view" in the course gradebook
Then I should see "Nee,Chumlee" in the "region-main" "region"
And I should not see "Search for a user to view all their grades" in the "region-main" "region"
Scenario: Teacher does not see his last viewed user report if the user is not a part of the the current group.
Given the following "groups" exist:
| name | course | idnumber | participation |
| Group 1 | C1 | G1 | 1 |
And the following "group members" exist:
| user | group |
| student2 | G1 |
And I am on the "Course 1" "course editing" page
And I expand all fieldsets
And I set the field "Group mode" to "Visible groups"
And I press "Save and display"
And I navigate to "View > Single view" in the course gradebook
And I click on "Gronya,Beecham" in the "user" search widget
And I navigate to "View > Grader report" in the course gradebook
And I click on "Group 1" in the "group" search widget
When I navigate to "View > Single view" in the course gradebook
Then I should see "Search for a user to view all their grades" in the "region-main" "region"
And I should not see "Gronya,Beecham" in the "region-main" "region"
Scenario: Teacher does not see his last viewed user report if that user is no longer enrolled in the course.
Given I navigate to "View > Single view" in the course gradebook
And I click on "Gronya,Beecham" in the "user" search widget
And I navigate to course participants
And I click on "Unenrol" "icon" in the "Gronya,Beecham" "table_row"
And I click on "Unenrol" "button" in the "Unenrol" "dialogue"
When I am on the "Course 1" "grades > Single view > View" page
Then I should see "Search for a user to view all their grades" in the "region-main" "region"
And I should not see "Gronya,Beecham" in the "region-main" "region"
Scenario: Teacher does not see his last viewed grade item report if the item no longer exists in the course.
Given I navigate to "View > Single view" in the course gradebook
And I click on "Grade items" "link"
And I click on "Test assignment four" in the "grade" search widget
And I am on "Course 1" course homepage with editing mode on
And I delete "Test assignment four" activity
And I run all adhoc tasks
When I navigate to "View > Single view" in the course gradebook
Then I should see "Select a grade item above" in the "region-main" "region"
And I should not see "Test grade item" in the "region-main" "region"
@@ -0,0 +1,222 @@
@core @core_grades @gradereport_singleview @javascript
Feature: Within the singleview report, a teacher can search for users.
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
| Course 2 | C2 | 0 | 0 |
And the following "users" exist:
| username | firstname | lastname | email | idnumber | phone1 | phone2 | department | institution | city | country |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 | 1234567892 | 1234567893 | ABC1 | ABCD | Perth | AU |
| student1 | Student | 1 | student1@example.com | s1 | 3213078612 | 8974325612 | ABC1 | ABCD | Hanoi | VN |
| student2 | Dummy | User | student2@example.com | s2 | 4365899871 | 7654789012 | ABC2 | ABCD | Tokyo | JP |
| student3 | User | Example | student3@example.com | s3 | 3243249087 | 0875421745 | ABC2 | ABCD | Olney | GB |
| student4 | User | Test | student4@example.com | s4 | 0987532523 | 2149871323 | ABC3 | ABCD | Tokyo | JP |
| student5 | Turtle | Manatee | student5@example.com | s5 | 1239087780 | 9873623589 | ABC3 | ABCD | Perth | AU |
# Note: Add groups etc so we can test that the search ignores those filters as well if we go down the filter dataset path.
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher1 | C2 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
| student5 | C1 | student |
And the following "activities" exist:
| activity | course | idnumber | name |
| assign | C1 | a1 | Test assignment one |
And the following config values are set as admin:
| showuseridentity | idnumber,email,city,country,phone1,phone2,department,institution |
And I change window size to "large"
And I am on the "Course 1" "grades > Single view > View" page logged in as "teacher1"
And I click on "Users" "link" in the ".page-toggler" "css_element"
Scenario: A teacher can view and trigger the user search
# Check the placeholder text (no users are initially shown).
Given I should see "Search users"
And I should see "Search for a user to view all their grades"
When I set the field "Search users" to "Turtle"
And I confirm "Turtle Manatee" in "user" search within the gradebook widget exists
And I confirm "User Example" in "user" search within the gradebook widget does not exist
And I click on "Turtle Manatee" "list_item"
# Business case: This will trigger a page reload and can not dynamically update the table.
And I wait until the page is ready
And "Turtle Manatee" "heading" should exist
And "Teacher 1" "heading" should not exist
And "Student 1" "heading" should not exist
And "User Example" "heading" should not exist
And "User Test" "heading" should not exist
And "Dummy User" "heading" should not exist
And I set the field "Search users" to "Turt"
And I wait until "Turtle Manatee" "option_role" exists
And I click on "Clear search input" "button" in the ".user-search" "css_element"
And "Turtle Manatee" "option_role" should not be visible
Scenario: A teacher can search the single view report to find specified users
# Case: Standard search.
Given I click on "Dummy" in the "user" search widget
And "Dummy User" "heading" should exist
And "Teacher 1" "heading" should not exist
And "Student 1" "heading" should not exist
And "User Example" "heading" should not exist
And "User Test" "heading" should not exist
And "Turtle Manatee" "heading" should not exist
# Case: No users found.
When I set the field "Search users" to "Plagiarism"
And I should see "No results for \"Plagiarism\""
# Table remains unchanged as the user had no results to select from the dropdown.
And "Dummy User" "heading" should exist
And "Teacher 1" "heading" should not exist
And "Student 1" "heading" should not exist
And "User Example" "heading" should not exist
And "User Test" "heading" should not exist
And "Turtle Manatee" "heading" should not exist
# Case: Multiple users found and select only one result.
Then I set the field "Search users" to "User"
And I wait until "Dummy User" "option_role" exists
And I confirm "Dummy User" in "user" search within the gradebook widget exists
And I confirm "User Example" in "user" search within the gradebook widget exists
And I confirm "User Test" in "user" search within the gradebook widget exists
And I confirm "Turtle Manatee" in "user" search within the gradebook widget does not exist
# Check if the matched field names (by lines) includes some identifiable info to help differentiate similar users.
And I confirm "User (student2@example.com)" in "user" search within the gradebook widget exists
And I confirm "User (student3@example.com)" in "user" search within the gradebook widget exists
And I confirm "User (student4@example.com)" in "user" search within the gradebook widget exists
And I click on "Dummy User" "list_item"
And I wait until the page is ready
And "Dummy User" "heading" should exist
And "Teacher 1" "heading" should not exist
And "Student 1" "heading" should not exist
And "User Example" "heading" should not exist
And "User Test" "heading" should not exist
And "Turtle Manatee" "heading" should not exist
# Case: No users enrolled.
And I am on the "Course 2" "grades > Single view > View" page
And I click on "Users" "link" in the ".page-toggler" "css_element"
And I set the field "Search users" to "a"
And I wait until "No results for \"a\"" "text" exists
Scenario: A teacher can quickly tell that a search is active on the current table
Given I click on "Turtle" in the "user" search widget
And I wait until the page is ready
# The search input remains in the field on reload this is in keeping with other search implementations.
When the field "Search users" matches value "Turtle Manatee"
# The users get preloaded for accessibility reasons.
And "Turtle Manatee" "option_role" should exist
# Test if we can then further retain the turtle result set and further filter from there.
Then I set the field "Search users" to "Turtle plagiarism"
And I wait until "Turtle Manatee" "option_role" does not exist
And I should see "No results for \"Turtle plagiarism\""
Scenario: A teacher can search for values besides the users' name
Given I set the field "Search users" to "student5@example.com"
And I wait until "Turtle Manatee" "list_item" exists
And I set the field "Search users" to "@example.com"
And I wait until "Dummy User" "list_item" exists
# Note: All learners match this email & showing emails is current default.
And I confirm "Dummy User" in "user" search within the gradebook widget exists
And I confirm "User Example" in "user" search within the gradebook widget exists
And I confirm "User Test" in "user" search within the gradebook widget exists
And I confirm "Student 1" in "user" search within the gradebook widget exists
And I confirm "Turtle Manatee" in "user" search within the gradebook widget exists
# Search on the country field.
When I set the field "Search users" to "JP"
And I wait until "Dummy User" "list_item" exists
And I wait until "Turtle Manatee" "list_item" does not exist
And I confirm "Dummy User" in "user" search within the gradebook widget exists
And I confirm "User Test" in "user" search within the gradebook widget exists
# Search on the city field.
And I set the field "Search users" to "Hanoi"
And I wait until "User Test" "list_item" does not exist
Then I confirm "Student 1" in "user" search within the gradebook widget exists
# Search on the institution field.
And I set the field "Search users" to "ABCD"
And I wait until "Dummy User" "list_item" exists
And I confirm "User Example" in "user" search within the gradebook widget exists
And I confirm "User Test" in "user" search within the gradebook widget exists
And I confirm "Student 1" in "user" search within the gradebook widget exists
And I confirm "Turtle Manatee" in "user" search within the gradebook widget exists
# Search on the department field.
And I set the field "Search users" to "ABC3"
And I wait until "User Example" "list_item" does not exist
And I confirm "User Test" in "user" search within the gradebook widget exists
And I confirm "Turtle Manatee" in "user" search within the gradebook widget exists
# Search on the phone1 field.
And I set the field "Search users" to "4365899871"
And I wait until "User Test" "list_item" does not exist
And I confirm "Dummy User" in "user" search within the gradebook widget exists
# Search on the phone2 field.
And I set the field "Search users" to "2149871323"
And I wait until "Dummy User" "list_item" does not exist
And I confirm "User Test" in "user" search within the gradebook widget exists
# Search on the institution field then press enter to show the record set.
And I set the field "Search users" to "ABC"
And "Turtle Manatee" "list_item" should exist
And I confirm "Dummy User" in "user" search within the gradebook widget exists
And I confirm "User Example" in "user" search within the gradebook widget exists
And I confirm "User Test" in "user" search within the gradebook widget exists
And I confirm "Student 1" in "user" search within the gradebook widget exists
And I press the down key
And I press the enter key
And I wait until the page is ready
And "Student 1" "heading" should exist
And "User Example" "heading" should not exist
And "User Test" "heading" should not exist
And "Dummy User" "heading" should not exist
And "Turtle Manatee" "heading" should not exist
And "Teacher 1" "heading" should not exist
@accessibility
Scenario: A teacher can set focus and search using the input are with a keyboard
Given I set the field "Search users" to "ABC"
# Basic tests for the page.
And the page should meet accessibility standards
And the page should meet "wcag131, wcag141, wcag412" accessibility standards
And the page should meet accessibility standards with "wcag131, wcag141, wcag412" extra tests
# Move onto general keyboard navigation testing.
When I wait until "Turtle Manatee" "option_role" exists
And I press the down key
And ".active" "css_element" should exist in the "Student 1" "option_role"
And I press the up key
And ".active" "css_element" should exist in the "Dummy User" "option_role"
And I press the down key
And ".active" "css_element" should exist in the "Student 1" "option_role"
And I press the escape key
And the focused element is "Search users" "field"
Then I set the field "Search users" to "Goodmeme"
And I press the down key
And the focused element is "Search users" "field"
And I navigate to "View > Single view" in the course gradebook
And I click on "Users" "link" in the ".page-toggler" "css_element"
And I set the field "Search users" to "ABC"
And I wait until "Turtle Manatee" "option_role" exists
And I press the down key
And ".active" "css_element" should exist in the "Student 1" "option_role"
# Lets check the tabbing order.
And I set the field "Search users" to "ABC"
And I wait until "Turtle Manatee" "option_role" exists
And I press the tab key
And the focused element is "Clear search input" "button" in the ".user-search" "css_element"
And I press the tab key
And the focused element is not "Search users" "field"
# Ensure we can interact with the clear search with the keyboard.
# Sometimes with behat we get unattached nodes causing spurious failures.
And I set the field "Search users" to "ABC"
And I wait "1" seconds
And I press the tab key
And the focused element is "Clear search input" "button" in the ".user-search" "css_element"
And I press the enter key
And I confirm "Turtle Manatee" in "user" search within the gradebook widget does not exist
+55
View File
@@ -0,0 +1,55 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Fixtures for single view report screen class testing.
*
* @package gradereport_singleview
* @copyright 2014 onwards Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
class gradereport_singleview_screen_testable extends \gradereport_singleview\local\screen\screen {
/**
* Wrapper to make protected method accessible during testing.
*
* @return array returns array of users.
*/
public function test_load_users(): array {
return $this->load_users();
}
/**
* Return the HTML for the page.
*/
public function init($selfitemisempty = false) {
}
/**
* Get the type of items on this screen, not valid so return false.
*/
public function item_type(): string {
}
/**
* Return the HTML for the page.
*/
public function html(): string {
}
}
@@ -0,0 +1,104 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace gradereport_singleview;
use gradereport_singleview_screen_testable;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once(__DIR__ . '/fixtures/screen.php');
require_once($CFG->libdir . '/gradelib.php');
/**
* Tests for screen class.
*
* @package gradereport_singleview
* @category test
* @copyright 2014 onwards Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class screen_test extends \advanced_testcase {
/**
* Test load_users method.
*/
public function test_load_users(): void {
global $DB;
$this->setAdminUser();
$this->resetAfterTest(true);
$roleteacher = $DB->get_record('role', ['shortname' => 'teacher'], '*', MUST_EXIST);
// Create a course, users and groups.
$course = $this->getDataGenerator()->create_course();
$coursecontext = \context_course::instance($course->id);
$group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
$teacher = $this->getDataGenerator()->create_user();
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, $roleteacher->id);
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
$this->getDataGenerator()->create_group_member(['groupid' => $group->id, 'userid' => $teacher->id]);
$this->getDataGenerator()->create_group_member(['groupid' => $group->id, 'userid' => $user1->id]);
$this->getDataGenerator()->create_group_member(['groupid' => $group->id, 'userid' => $user2->id]);
// Perform a regrade before creating the report.
grade_regrade_final_grades($course->id);
$screentest = new gradereport_singleview_screen_testable($course->id, 0, $group->id);
$groupusers = $screentest->test_load_users();
$this->assertDebuggingCalled('The function load_users() is deprecated. ' .
'Please use grade_report::get_gradable_users() instead.');
$this->assertCount(2, $groupusers);
// Now, let's suspend the enrolment of a user. Should return only one user.
$this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleteacher->id, 'manual', 0, 0, ENROL_USER_SUSPENDED);
$users = $screentest->test_load_users();
$this->assertDebuggingCalled('The function load_users() is deprecated. ' .
'Please use grade_report::get_gradable_users() instead.');
$this->assertCount(1, $users);
// Change the viewsuspendedusers capabilities and set the user preference to display suspended users.
assign_capability('moodle/course:viewsuspendedusers', CAP_ALLOW, $roleteacher->id, $coursecontext, true);
set_user_preference('grade_report_showonlyactiveenrol', false, $teacher);
accesslib_clear_all_caches_for_unit_testing();
$this->setUser($teacher);
$screentest = new gradereport_singleview_screen_testable($course->id, 0, $group->id);
$users = $screentest->test_load_users();
$this->assertDebuggingCalled('The function load_users() is deprecated. ' .
'Please use grade_report::get_gradable_users() instead.');
$this->assertCount(2, $users);
// Change the capability again, now the user can't see the suspended enrolments.
assign_capability('moodle/course:viewsuspendedusers', CAP_PROHIBIT, $roleteacher->id, $coursecontext, true);
set_user_preference('grade_report_showonlyactiveenrol', false, $teacher);
accesslib_clear_all_caches_for_unit_testing();
$users = $screentest->test_load_users();
$this->assertDebuggingCalled('The function load_users() is deprecated. ' .
'Please use grade_report::get_gradable_users() instead.');
$this->assertCount(1, $users);
// Now, activate the user enrolment again. We shall get 2 users now.
$this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleteacher->id, 'manual', 0, 0, ENROL_USER_ACTIVE);
$users = $screentest->test_load_users();
$this->assertDebuggingCalled('The function load_users() is deprecated. ' .
'Please use grade_report::get_gradable_users() instead.');
$this->assertCount(2, $users);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Standard version file
*
* @package gradereport_singleview
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'gradereport_singleview'; // Full name of the plugin (used for diagnostics).
$plugin->version = 2024042200;
$plugin->requires = 2024041600;