diff --git a/admin/tests/behat/task_logs.feature b/admin/tests/behat/task_logs.feature index bf9acf5ef25..3c11386952f 100644 --- a/admin/tests/behat/task_logs.feature +++ b/admin/tests/behat/task_logs.feature @@ -19,6 +19,7 @@ Feature: View task logs report and use its filters | Name value | | And I click on "Apply" "button" in the "[data-region='report-filters']" "css_element" Then I should see "Filters applied" + And I should see "Filters (1)" in the "#dropdownFiltersButton" "css_element" And the following should exist in the "reportbuilder-table" table: | Type | Name | | Scheduled | | @@ -61,9 +62,12 @@ Feature: View task logs report and use its filters | Result value | Fail | And I click on "Apply" "button" in the "[data-region='report-filters']" "css_element" Then I should see "Filters applied" + And I should see "Filters (1)" in the "#dropdownFiltersButton" "css_element" And I should see "Nothing to display" And I click on "Reset" "button" in the "[data-region='report-filters']" "css_element" And I should see "Filters reset" + And I should not see "Filters (1)" in the "#dropdownFiltersButton" "css_element" + And I should see "Filters" in the "#dropdownFiltersButton" "css_element" And "[data-region='report-filters']" "css_element" should be visible And the following fields in the "Result" "core_reportbuilder > Filter" match these values: | Result operator | Is any value | diff --git a/lang/en/reportbuilder.php b/lang/en/reportbuilder.php index 9ebdc7e45b6..cf1a090ee4e 100644 --- a/lang/en/reportbuilder.php +++ b/lang/en/reportbuilder.php @@ -60,6 +60,7 @@ $string['filterisnotequalto'] = 'Is not equal to'; $string['filterlessthan'] = 'Less than'; $string['filterrange'] = 'Range'; $string['filtersapplied'] = 'Filters applied'; +$string['filtersappliedx'] = 'Filters ({$a})'; $string['filtersreset'] = 'Filters reset'; $string['filterstartswith'] = 'Starts with'; $string['privacy:metadata:preference:reportfilter'] = 'Stored report filter values'; diff --git a/reportbuilder/amd/build/filters.min.js b/reportbuilder/amd/build/filters.min.js index ff7cddf3c8d..cb9aeb7436d 100644 --- a/reportbuilder/amd/build/filters.min.js +++ b/reportbuilder/amd/build/filters.min.js @@ -1,2 +1,2 @@ -function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_reportbuilder/filters",["exports","core/event_dispatcher","core/fragment","core/notification","core/pending","core/str","core/templates","core/toast","core_form/dynamicform","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/filters"],function(a,b,c,d,e,f,g,h,i,j,k,l){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;d=o(d);e=o(e);g=o(g);i=o(i);j=n(j);k=n(k);function m(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;m=function(){return a};return a}function n(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=m();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function o(a){return a&&a.__esModule?a:{default:a}}var p=function(a,m){var n=document.querySelector(k.forSystemReport(a)),o=n.querySelector(k.regions.filtersForm),p=new i.default(o,"\\core_reportbuilder\\form\\filter");p.addEventListener(p.events.FORM_SUBMITTED,function(a){a.preventDefault();(0,b.dispatchEvent)(j.tableReload,{},n);(0,f.get_string)("filtersapplied","core_reportbuilder").then(h.add).catch(d.default.exception)});p.addEventListener(p.events.NOSUBMIT_BUTTON_PRESSED,function(i){i.preventDefault();var k=new e.default("core_reportbuilder/filters:reset");(0,l.reset)(a).then(function(){return(0,f.get_string)("filtersreset","core_reportbuilder")}).then(h.add).then(function(){return(0,c.loadFragment)("core_reportbuilder","filters_form",m,{reportid:a,parameters:n.dataset.parameter})}).then(function(a,c){g.default.replaceNodeContents(o,a,c);(0,b.dispatchEvent)(j.tableReload,{},n);return k.resolve()}).catch(d.default.exception)});document.querySelector("#region-main").style.overflowX="visible"};a.init=p}); +function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core_reportbuilder/filters",["exports","core/event_dispatcher","core/fragment","core/notification","core/pending","core/str","core/templates","core/toast","core_form/dynamicform","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/filters"],function(a,b,c,d,e,f,g,h,i,j,k,l){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;d=o(d);e=o(e);g=o(g);i=o(i);j=n(j);k=n(k);function m(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;m=function(){return a};return a}function n(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=m();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function o(a){return a&&a.__esModule?a:{default:a}}function p(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function q(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var h=a.apply(b,c);function f(a){p(h,d,e,f,g,"next",a)}function g(a){p(h,d,e,f,g,"throw",a)}f(void 0)})}}var r=function(){var a=q(regeneratorRuntime.mark(function a(b,c){var d;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:d=b.querySelector(k.regions.filterButtonLabel);if(!(0.\n\n/**\n * Report builder filter management\n *\n * @module core_reportbuilder/filters\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {loadFragment} from 'core/fragment';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_string as getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {add as addToast} from 'core/toast';\nimport DynamicForm from 'core_form/dynamicform';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {reset as resetFilters} from 'core_reportbuilder/local/repository/filters';\n\n/**\n * Initialise module for given report\n *\n * @method\n * @param {Number} reportId\n * @param {Number} contextId\n */\nexport const init = (reportId, contextId) => {\n const reportElement = document.querySelector(reportSelectors.forSystemReport(reportId));\n const filterFormContainer = reportElement.querySelector(reportSelectors.regions.filtersForm);\n const filterForm = new DynamicForm(filterFormContainer, '\\\\core_reportbuilder\\\\form\\\\filter');\n\n // Submit report filters.\n filterForm.addEventListener(filterForm.events.FORM_SUBMITTED, event => {\n event.preventDefault();\n\n // After the form has been submitted, we should trigger report table reload.\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n\n getString('filtersapplied', 'core_reportbuilder')\n .then(addToast)\n .catch(Notification.exception);\n });\n\n // Reset report filters.\n filterForm.addEventListener(filterForm.events.NOSUBMIT_BUTTON_PRESSED, event => {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/filters:reset');\n\n resetFilters(reportId)\n .then(() => getString('filtersreset', 'core_reportbuilder'))\n .then(addToast)\n .then(() => loadFragment('core_reportbuilder', 'filters_form', contextId, {\n reportid: reportId,\n parameters: reportElement.dataset.parameter,\n }))\n .then((html, js) => {\n Templates.replaceNodeContents(filterFormContainer, html, js);\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n });\n\n // Modify \"region-main\" overflow for big filter forms.\n document.querySelector('#region-main').style.overflowX = \"visible\";\n};\n"],"file":"filters.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/filters.js"],"names":["setFilterButtonCount","reportElement","filterCount","filterButtonLabel","querySelector","reportSelectors","regions","textContent","init","reportId","contextId","document","forSystemReport","filterFormContainer","filtersForm","filterForm","DynamicForm","addEventListener","events","FORM_SUBMITTED","event","preventDefault","reportEvents","tableReload","detail","then","addToast","catch","Notification","exception","NOSUBMIT_BUTTON_PRESSED","pendingPromise","Pending","reportid","parameters","dataset","parameter","html","js","Templates","replaceNodeContents","resolve","style","overflowX"],"mappings":"msBAyBA,OACA,OAEA,OAEA,OACA,OACA,O,25BASMA,CAAAA,CAAoB,4CAAG,WAAMC,CAAN,CAAqBC,CAArB,yFACnBC,CADmB,CACCF,CAAa,CAACG,aAAd,CAA4BC,CAAe,CAACC,OAAhB,CAAwBH,iBAApD,CADD,MAGP,CAAd,CAAAD,CAHqB,iCAIiB,iBAAU,iBAAV,CAA6B,oBAA7B,CAAmDA,CAAnD,CAJjB,QAIrBC,CAAiB,CAACI,WAJG,8CAMiB,iBAAU,SAAV,CAAqB,QAArB,CANjB,QAMrBJ,CAAiB,CAACI,WANG,iDAAH,uD,CAiBbC,CAAI,CAAG,SAACC,CAAD,CAAWC,CAAX,CAAyB,IACnCT,CAAAA,CAAa,CAAGU,QAAQ,CAACP,aAAT,CAAuBC,CAAe,CAACO,eAAhB,CAAgCH,CAAhC,CAAvB,CADmB,CAEnCI,CAAmB,CAAGZ,CAAa,CAACG,aAAd,CAA4BC,CAAe,CAACC,OAAhB,CAAwBQ,WAApD,CAFa,CAGnCC,CAAU,CAAG,GAAIC,UAAJ,CAAgBH,CAAhB,CAAqC,oCAArC,CAHsB,CAMzCE,CAAU,CAACE,gBAAX,CAA4BF,CAAU,CAACG,MAAX,CAAkBC,cAA9C,CAA8D,SAAAC,CAAK,CAAI,CACnEA,CAAK,CAACC,cAAN,GAGA,oBAAcC,CAAY,CAACC,WAA3B,CAAwC,EAAxC,CAA4CtB,CAA5C,EACAD,CAAoB,CAACC,CAAD,CAAgBmB,CAAK,CAACI,MAAtB,CAApB,CAEA,iBAAU,gBAAV,CAA4B,oBAA5B,EACKC,IADL,CACUC,KADV,EAEKC,KAFL,CAEWC,UAAaC,SAFxB,CAGH,CAVD,EAaAd,CAAU,CAACE,gBAAX,CAA4BF,CAAU,CAACG,MAAX,CAAkBY,uBAA9C,CAAuE,SAAAV,CAAK,CAAI,CAC5EA,CAAK,CAACC,cAAN,GAEA,GAAMU,CAAAA,CAAc,CAAG,GAAIC,UAAJ,CAAY,kCAAZ,CAAvB,CAEA,YAAavB,CAAb,EACKgB,IADL,CACU,iBAAM,iBAAU,cAAV,CAA0B,oBAA1B,CAAN,CADV,EAEKA,IAFL,CAEUC,KAFV,EAGKD,IAHL,CAGU,iBAAM,mBAAa,oBAAb,CAAmC,cAAnC,CAAmDf,CAAnD,CAA8D,CACtEuB,QAAQ,CAAExB,CAD4D,CAEtEyB,UAAU,CAAEjC,CAAa,CAACkC,OAAd,CAAsBC,SAFoC,CAA9D,CAAN,CAHV,EAOKX,IAPL,CAOU,SAACY,CAAD,CAAOC,CAAP,CAAc,CAChBC,UAAUC,mBAAV,CAA8B3B,CAA9B,CAAmDwB,CAAnD,CAAyDC,CAAzD,EAEA,oBAAchB,CAAY,CAACC,WAA3B,CAAwC,EAAxC,CAA4CtB,CAA5C,EACAD,CAAoB,CAACC,CAAD,CAAgB,CAAhB,CAApB,CAEA,MAAO8B,CAAAA,CAAc,CAACU,OAAf,EACV,CAdL,EAeKd,KAfL,CAeWC,UAAaC,SAfxB,CAgBH,CArBD,EAwBAlB,QAAQ,CAACP,aAAT,CAAuB,cAAvB,EAAuCsC,KAAvC,CAA6CC,SAA7C,CAAyD,SAC5D,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Report builder filter management\n *\n * @module core_reportbuilder/filters\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {loadFragment} from 'core/fragment';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_string as getString} from 'core/str';\nimport Templates from 'core/templates';\nimport {add as addToast} from 'core/toast';\nimport DynamicForm from 'core_form/dynamicform';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {reset as resetFilters} from 'core_reportbuilder/local/repository/filters';\n\n/**\n * Update filter button text to indicate applied filter count\n *\n * @param {Element} reportElement\n * @param {Number} filterCount\n */\nconst setFilterButtonCount = async(reportElement, filterCount) => {\n const filterButtonLabel = reportElement.querySelector(reportSelectors.regions.filterButtonLabel);\n\n if (filterCount > 0) {\n filterButtonLabel.textContent = await getString('filtersappliedx', 'core_reportbuilder', filterCount);\n } else {\n filterButtonLabel.textContent = await getString('filters', 'moodle');\n }\n};\n\n/**\n * Initialise module for given report\n *\n * @method\n * @param {Number} reportId\n * @param {Number} contextId\n */\nexport const init = (reportId, contextId) => {\n const reportElement = document.querySelector(reportSelectors.forSystemReport(reportId));\n const filterFormContainer = reportElement.querySelector(reportSelectors.regions.filtersForm);\n const filterForm = new DynamicForm(filterFormContainer, '\\\\core_reportbuilder\\\\form\\\\filter');\n\n // Submit report filters.\n filterForm.addEventListener(filterForm.events.FORM_SUBMITTED, event => {\n event.preventDefault();\n\n // After the form has been submitted, we should trigger report table reload.\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n setFilterButtonCount(reportElement, event.detail);\n\n getString('filtersapplied', 'core_reportbuilder')\n .then(addToast)\n .catch(Notification.exception);\n });\n\n // Reset report filters.\n filterForm.addEventListener(filterForm.events.NOSUBMIT_BUTTON_PRESSED, event => {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/filters:reset');\n\n resetFilters(reportId)\n .then(() => getString('filtersreset', 'core_reportbuilder'))\n .then(addToast)\n .then(() => loadFragment('core_reportbuilder', 'filters_form', contextId, {\n reportid: reportId,\n parameters: reportElement.dataset.parameter,\n }))\n .then((html, js) => {\n Templates.replaceNodeContents(filterFormContainer, html, js);\n\n dispatchEvent(reportEvents.tableReload, {}, reportElement);\n setFilterButtonCount(reportElement, 0);\n\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n });\n\n // Modify \"region-main\" overflow for big filter forms.\n document.querySelector('#region-main').style.overflowX = \"visible\";\n};\n"],"file":"filters.min.js"} \ No newline at end of file diff --git a/reportbuilder/amd/build/local/selectors.min.js b/reportbuilder/amd/build/local/selectors.min.js index 87dbccacf22..3e84ec64249 100644 --- a/reportbuilder/amd/build/local/selectors.min.js +++ b/reportbuilder/amd/build/local/selectors.min.js @@ -1,2 +1,2 @@ -define ("core_reportbuilder/local/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b={regions:{systemReport:"[data-region=\"core_reportbuilder/system-report\"]",filtersForm:"[data-region=\"filters-form\"]"},actions:{reportActionPopup:"[data-action=\"report-action-popup\"]"}};b.forSystemReport=function(a){return"".concat(b.regions.systemReport,"[data-reportid=\"").concat(a,"\"]")};a.default=b;return a.default}); +define ("core_reportbuilder/local/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b={regions:{systemReport:"[data-region=\"core_reportbuilder/system-report\"]",filterButtonLabel:"[data-region=\"filter-button-label\"]",filtersForm:"[data-region=\"filters-form\"]"},actions:{reportActionPopup:"[data-action=\"report-action-popup\"]"}};b.forSystemReport=function(a){return"".concat(b.regions.systemReport,"[data-reportid=\"").concat(a,"\"]")};a.default=b;return a.default}); //# sourceMappingURL=selectors.min.js.map diff --git a/reportbuilder/amd/build/local/selectors.min.js.map b/reportbuilder/amd/build/local/selectors.min.js.map index 675710c7263..908c8bab73f 100644 --- a/reportbuilder/amd/build/local/selectors.min.js.map +++ b/reportbuilder/amd/build/local/selectors.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../../src/local/selectors.js"],"names":["SELECTORS","regions","systemReport","filtersForm","actions","reportActionPopup","forSystemReport","reportId"],"mappings":"oJA8BA,GAAMA,CAAAA,CAAS,CAAG,CACdC,OAAO,CAAE,CACLC,YAAY,CAAE,oDADT,CAELC,WAAW,CAAE,gCAFR,CADK,CAKdC,OAAO,CAAE,CACLC,iBAAiB,CAAE,uCADd,CALK,CAAlB,CAiBAL,CAAS,CAACM,eAAV,CAA4B,SAAAC,CAAQ,kBAAOP,CAAS,CAACC,OAAV,CAAkBC,YAAzB,6BAAwDK,CAAxD,QAApC,C,UAEeP,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Report builder selectors\n *\n * @module core_reportbuilder/local/selectors\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Selectors for the Report builder subsystem\n *\n * @property {Object} regions\n * @property {String} regions.systemReport System report page region\n * @property {String} regions.filtersForm Filters form page region\n */\nconst SELECTORS = {\n regions: {\n systemReport: '[data-region=\"core_reportbuilder/system-report\"]',\n filtersForm: '[data-region=\"filters-form\"]',\n },\n actions: {\n reportActionPopup: '[data-action=\"report-action-popup\"]',\n }\n};\n\n/**\n * Selector for given report\n *\n * @method forSystemReport\n * @param {Number} reportId\n * @return {String}\n */\nSELECTORS.forSystemReport = reportId => `${SELECTORS.regions.systemReport}[data-reportid=\"${reportId}\"]`;\n\nexport default SELECTORS;\n"],"file":"selectors.min.js"} \ No newline at end of file +{"version":3,"sources":["../../src/local/selectors.js"],"names":["SELECTORS","regions","systemReport","filterButtonLabel","filtersForm","actions","reportActionPopup","forSystemReport","reportId"],"mappings":"oJA+BA,GAAMA,CAAAA,CAAS,CAAG,CACdC,OAAO,CAAE,CACLC,YAAY,CAAE,oDADT,CAELC,iBAAiB,CAAE,uCAFd,CAGLC,WAAW,CAAE,gCAHR,CADK,CAMdC,OAAO,CAAE,CACLC,iBAAiB,CAAE,uCADd,CANK,CAAlB,CAkBAN,CAAS,CAACO,eAAV,CAA4B,SAAAC,CAAQ,kBAAOR,CAAS,CAACC,OAAV,CAAkBC,YAAzB,6BAAwDM,CAAxD,QAApC,C,UAEeR,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Report builder selectors\n *\n * @module core_reportbuilder/local/selectors\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Selectors for the Report builder subsystem\n *\n * @property {Object} regions\n * @property {String} regions.systemReport System report page region\n * @property {String} regions.filterButtonLabel Filters form toggle region\n * @property {String} regions.filtersForm Filters form page region\n */\nconst SELECTORS = {\n regions: {\n systemReport: '[data-region=\"core_reportbuilder/system-report\"]',\n filterButtonLabel: '[data-region=\"filter-button-label\"]',\n filtersForm: '[data-region=\"filters-form\"]',\n },\n actions: {\n reportActionPopup: '[data-action=\"report-action-popup\"]',\n }\n};\n\n/**\n * Selector for given report\n *\n * @method forSystemReport\n * @param {Number} reportId\n * @return {String}\n */\nSELECTORS.forSystemReport = reportId => `${SELECTORS.regions.systemReport}[data-reportid=\"${reportId}\"]`;\n\nexport default SELECTORS;\n"],"file":"selectors.min.js"} \ No newline at end of file diff --git a/reportbuilder/amd/src/filters.js b/reportbuilder/amd/src/filters.js index 3e65fe4c55f..dffb4467ea1 100644 --- a/reportbuilder/amd/src/filters.js +++ b/reportbuilder/amd/src/filters.js @@ -33,6 +33,22 @@ import * as reportEvents from 'core_reportbuilder/local/events'; import * as reportSelectors from 'core_reportbuilder/local/selectors'; import {reset as resetFilters} from 'core_reportbuilder/local/repository/filters'; +/** + * Update filter button text to indicate applied filter count + * + * @param {Element} reportElement + * @param {Number} filterCount + */ +const setFilterButtonCount = async(reportElement, filterCount) => { + const filterButtonLabel = reportElement.querySelector(reportSelectors.regions.filterButtonLabel); + + if (filterCount > 0) { + filterButtonLabel.textContent = await getString('filtersappliedx', 'core_reportbuilder', filterCount); + } else { + filterButtonLabel.textContent = await getString('filters', 'moodle'); + } +}; + /** * Initialise module for given report * @@ -51,6 +67,7 @@ export const init = (reportId, contextId) => { // After the form has been submitted, we should trigger report table reload. dispatchEvent(reportEvents.tableReload, {}, reportElement); + setFilterButtonCount(reportElement, event.detail); getString('filtersapplied', 'core_reportbuilder') .then(addToast) @@ -72,7 +89,9 @@ export const init = (reportId, contextId) => { })) .then((html, js) => { Templates.replaceNodeContents(filterFormContainer, html, js); + dispatchEvent(reportEvents.tableReload, {}, reportElement); + setFilterButtonCount(reportElement, 0); return pendingPromise.resolve(); }) diff --git a/reportbuilder/amd/src/local/selectors.js b/reportbuilder/amd/src/local/selectors.js index c1d34c6968d..1c0972b3061 100644 --- a/reportbuilder/amd/src/local/selectors.js +++ b/reportbuilder/amd/src/local/selectors.js @@ -26,11 +26,13 @@ * * @property {Object} regions * @property {String} regions.systemReport System report page region + * @property {String} regions.filterButtonLabel Filters form toggle region * @property {String} regions.filtersForm Filters form page region */ const SELECTORS = { regions: { systemReport: '[data-region="core_reportbuilder/system-report"]', + filterButtonLabel: '[data-region="filter-button-label"]', filtersForm: '[data-region="filters-form"]', }, actions: { diff --git a/reportbuilder/classes/external/system_report_exporter.php b/reportbuilder/classes/external/system_report_exporter.php index 3f77f92caff..2c1ec850732 100644 --- a/reportbuilder/classes/external/system_report_exporter.php +++ b/reportbuilder/classes/external/system_report_exporter.php @@ -67,6 +67,7 @@ class system_report_exporter extends persistent_exporter { 'table' => ['type' => PARAM_RAW], 'parameters' => ['type' => PARAM_RAW], 'filterspresent' => ['type' => PARAM_BOOL], + 'filtersapplied' => ['type' => PARAM_INT], 'filtersform' => [ 'type' => PARAM_RAW, 'optional' => true, @@ -109,6 +110,7 @@ class system_report_exporter extends persistent_exporter { 'table' => $output->render($table), 'parameters' => $this->related['parameters'], 'filterspresent' => $filterspresent, + 'filtersapplied' => $source->get_applied_filter_count(), 'filtersform' => $filterspresent ? $filtersform->render() : '', ]; } diff --git a/reportbuilder/classes/form/filter.php b/reportbuilder/classes/form/filter.php index 54f43fb7b78..11cfb466c17 100644 --- a/reportbuilder/classes/form/filter.php +++ b/reportbuilder/classes/form/filter.php @@ -70,15 +70,16 @@ class filter extends dynamic_form { /** * Process the form submission * - * @return bool + * @return int Number of applied filter instances */ public function process_dynamic_submission() { $values = $this->get_data(); - // Remove some unneeded fields. + // Remove some unneeded fields, apply filters. unset($values->reportid, $values->parameters); + $this->get_system_report()->set_filter_values((array) $values); - return $this->get_system_report()->set_filter_values((array) $values); + return $this->get_system_report()->get_applied_filter_count(); } /** diff --git a/reportbuilder/classes/local/filters/base.php b/reportbuilder/classes/local/filters/base.php index 2f279504ce5..b58df250f9c 100644 --- a/reportbuilder/classes/local/filters/base.php +++ b/reportbuilder/classes/local/filters/base.php @@ -85,4 +85,18 @@ abstract class base { * @return array [$sql, [...$params]] */ abstract public function get_sql_filter(array $values): array; + + /** + * Given an array of current filter values for the report, determine whether the filter would apply to the report (i.e. user + * has configured it from it's initial "Any value" state). A filter would typically be considered applied if it returns SQL + * filter clauses, but child classes may override this method if they use different logic + * + * @param array $values + * @return bool + */ + public function applies_to_values(array $values): bool { + [$filtersql] = $this->get_sql_filter($values); + + return $filtersql !== ''; + } } diff --git a/reportbuilder/classes/local/report/base.php b/reportbuilder/classes/local/report/base.php index cbdac87a6a0..4ab8a5b3982 100644 --- a/reportbuilder/classes/local/report/base.php +++ b/reportbuilder/classes/local/report/base.php @@ -442,6 +442,21 @@ abstract class base { return user_filter_manager::get($this->report->get('id')); } + /** + * Return the number of filter instances that are being applied based on the report's filter values (i.e. user has + * configured them from their initial "Any value" state) + * + * @return int + */ + final public function get_applied_filter_count(): int { + $values = $this->get_filter_values(); + $applied = array_filter($this->get_filter_instances(), static function(filter_base $filter) use ($values): bool { + return $filter->applies_to_values($values); + }); + + return count($applied); + } + /** * Set if the report can be downloaded. * diff --git a/reportbuilder/templates/local/filters/area.mustache b/reportbuilder/templates/local/filters/area.mustache index a7365e3af30..7e09cafe3a8 100644 --- a/reportbuilder/templates/local/filters/area.mustache +++ b/reportbuilder/templates/local/filters/area.mustache @@ -23,6 +23,7 @@ { "id": 3, "contextid": 1, + "filtersapplied": 3, "filtersform": "form" } }} @@ -31,7 +32,10 @@