MDL-70795 reportbuilder: beginnings of the report editor.

Implement UI elements for editing columns of a report, taking their
definitions from the selected datasource.

Co-authored-By: David Matamoros <[email protected]>
Co-authored-By: Mikel Martín <[email protected]>
This commit is contained in:
Paul Holden
2021-10-19 13:56:02 +01:00
co-authored by David Matamoros Mikel Martín
parent 22d896e063
commit 8ca9e04f9a
63 changed files with 3282 additions and 145 deletions
+12
View File
@@ -23,6 +23,7 @@
*/
$string['actions'] = 'Actions';
$string['addcolumn'] = 'Add column \'{$a}\'';
$string['apply'] = 'Apply';
$string['coursefullnamewithlink'] = 'Course full name with link';
$string['courseidnumberewithlink'] = 'Course ID number with link';
@@ -30,9 +31,14 @@ $string['courseshortnamewithlink'] = 'Course short name with link';
$string['customfieldcolumn'] = '{$a}';
$string['deletereport'] = 'Delete report';
$string['deletereportconfirm'] = 'Are you sure you want to delete the report \'{$a}\' and all associated data?';
$string['editdetails'] = 'Edit details';
$string['editreportcontent'] = 'Edit report content';
$string['editreportdetails'] = 'Edit report details';
$string['editreportname'] = 'Edit report name';
$string['columnmoved'] = 'Moved column \'{$a}\'';
$string['customreports'] = 'Custom reports';
$string['deletecolumn'] = 'Delete column \'{$a}\'';
$string['deletecolumnconfirm'] = 'Are you sure you want to delete the column \'{$a}\'?';
$string['entitycourse'] = 'Course';
$string['entityuser'] = 'User';
$string['errorreportaccess'] = 'You can not view this report';
@@ -73,6 +79,7 @@ $string['filtersreset'] = 'Filters reset';
$string['filterstartswith'] = 'Starts with';
$string['includedefaultsetup'] = 'Include default setup';
$string['includedefaultsetup_help'] = 'Populate report with default layout as defined by the selected source. These include pre-defined columns, filters and conditions.';
$string['movecolumn'] = 'Move column \'{$a}\'';
$string['newreport'] = 'New report';
$string['privacy:metadata:column'] = 'Report column definitions';
$string['privacy:metadata:column:uniqueidentifier'] = 'Unique identifier of the column';
@@ -87,15 +94,20 @@ $string['privacy:metadata:report'] = 'Report definitions';
$string['privacy:metadata:report:name'] = 'The name of the report';
$string['privacy:metadata:report:usercreated'] = 'The ID of the user who created the report';
$string['privacy:metadata:report:usermodified'] = 'The ID of the user who last modified the report';
$string['renamecolumn'] = 'Rename column \'{$a}\'';
$string['reportbuilder'] = 'Report builder';
$string['reportcreated'] = 'Report created';
$string['reportdeleted'] = 'Report deleted';
$string['reportsettingstoggle'] = 'Show/hide settings sidebar';
$string['reportsource'] = 'Report source';
$string['reportsource_help'] = 'The report source defines where the data for the report will come from';
$string['reportupdated'] = 'Report updated';
$string['resetall'] = 'Reset all';
$string['selectareportsource'] = 'Select a report source';
$string['selectcourses'] = 'Select courses';
$string['showhide'] = 'Show/hide \'{$a}\'';
$string['switchedit'] = 'Switch to edit mode';
$string['switchpreview'] = 'Switch to preview mode';
$string['timeadded'] = 'Time added';
$string['timecreated'] = 'Time created';
$string['timemodified'] = 'Time modified';
+78
View File
@@ -2804,6 +2804,84 @@ $functions = array(
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_reports_get' => [
'classname' => 'core_reportbuilder\external\reports\get',
'description' => 'Get custom report',
'type' => 'read',
'ajax' => true,
],
'core_reportbuilder_columns_add' => [
'classname' => 'core_reportbuilder\external\columns\add',
'description' => 'Add column to report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_columns_delete' => [
'classname' => 'core_reportbuilder\external\columns\delete',
'description' => 'Delete column from report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_columns_reorder' => [
'classname' => 'core_reportbuilder\external\columns\reorder',
'description' => 'Re-order column within report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_columns_sort_reorder' => [
'classname' => 'core_reportbuilder\external\columns\sort\reorder',
'description' => 'Re-order column sorting within report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_columns_sort_toggle' => [
'classname' => 'core_reportbuilder\external\columns\sort\toggle',
'description' => 'Toggle sorting of column within report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_conditions_add' => [
'classname' => 'core_reportbuilder\external\conditions\add',
'description' => 'Add condition to report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_conditions_delete' => [
'classname' => 'core_reportbuilder\external\conditions\delete',
'description' => 'Delete condition from report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_conditions_reorder' => [
'classname' => 'core_reportbuilder\external\conditions\reorder',
'description' => 'Re-order condition within report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_conditions_reset' => [
'classname' => 'core_reportbuilder\external\conditions\reset',
'description' => 'Reset conditions for given report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_filters_add' => [
'classname' => 'core_reportbuilder\external\filters\add',
'description' => 'Add filter to report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_filters_delete' => [
'classname' => 'core_reportbuilder\external\filters\delete',
'description' => 'Delete filter from report',
'type' => 'write',
'ajax' => true,
],
'core_reportbuilder_filters_reorder' => [
'classname' => 'core_reportbuilder\external\filters\reorder',
'description' => 'Re-order filter within report',
'type' => 'write',
'ajax' => true,
],
);
$services = array(
+2
View File
@@ -0,0 +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/editor",["exports","core/inplace_editable","core/notification","core/pending","core/templates","core_reportbuilder/local/selectors","core_reportbuilder/local/editor/columns","core_reportbuilder/local/repository/reports"],function(a,b,c,d,e,f,g,h){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;c=k(c);d=k(d);e=k(e);f=j(f);function i(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;i=function(){return a};return a}function j(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=i();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 k(a){return a&&a.__esModule?a:{default:a}}var l=!1,m=function(){var a=document.querySelector(f.regions.report);(0,g.init)(a,l);if(l){return}document.addEventListener("click",function(b){var g=b.target.closest(f.actions.toggleEditPreview);if(g){b.preventDefault();var i=new d.default("core_reportbuilder/reports:get"),j="1"!==g.dataset.editMode,k="";(0,h.getReport)(a.dataset.reportId,j).then(function(a){k=a.javascript;return e.default.render("core_reportbuilder/custom_report",a)}).then(function(b,c){return e.default.replaceNodeContents(a,b,c+k)}).then(function(){return i.resolve()}).catch(c.default.exception)}});l=!0};a.init=m});
//# sourceMappingURL=editor.min.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../src/editor.js"],"names":["initialized","init","reportElement","document","querySelector","reportSelectors","regions","report","addEventListener","event","toggleEditViewMode","target","closest","actions","toggleEditPreview","preventDefault","pendingPromise","Pending","toggledEditMode","dataset","editMode","customjs","reportId","then","response","javascript","Templates","render","html","js","replaceNodeContents","resolve","catch","Notification","exception"],"mappings":"sjBAwBA,a,+DAGA,OACA,OACA,OACA,O,4lBAIIA,CAAAA,CAAW,G,CAKFC,CAAI,CAAG,UAAM,CACtB,GAAMC,CAAAA,CAAa,CAAGC,QAAQ,CAACC,aAAT,CAAuBC,CAAe,CAACC,OAAhB,CAAwBC,MAA/C,CAAtB,CAEA,WAAkBL,CAAlB,CAAiCF,CAAjC,EAGA,GAAIA,CAAJ,CAAiB,CACb,MACH,CAGDG,QAAQ,CAACK,gBAAT,CAA0B,OAA1B,CAAmC,SAAAC,CAAK,CAAI,CAGxC,GAAMC,CAAAA,CAAkB,CAAGD,CAAK,CAACE,MAAN,CAAaC,OAAb,CAAqBP,CAAe,CAACQ,OAAhB,CAAwBC,iBAA7C,CAA3B,CACA,GAAIJ,CAAJ,CAAwB,CACpBD,CAAK,CAACM,cAAN,GADoB,GAGdC,CAAAA,CAAc,CAAG,GAAIC,UAAJ,CAAY,gCAAZ,CAHH,CAIdC,CAAe,CAA2C,GAAxC,GAAAR,CAAkB,CAACS,OAAnB,CAA2BC,QAJ/B,CAMhBC,CAAQ,CAAG,EANK,CAQpB,gBAAUnB,CAAa,CAACiB,OAAd,CAAsBG,QAAhC,CAA0CJ,CAA1C,EACKK,IADL,CACU,SAAAC,CAAQ,CAAI,CACdH,CAAQ,CAAGG,CAAQ,CAACC,UAApB,CACA,MAAOC,WAAUC,MAAV,CAAiB,kCAAjB,CAAqDH,CAArD,CACV,CAJL,EAKKD,IALL,CAKU,SAACK,CAAD,CAAOC,CAAP,CAAc,CAChB,MAAOH,WAAUI,mBAAV,CAA8B5B,CAA9B,CAA6C0B,CAA7C,CAAmDC,CAAE,CAAGR,CAAxD,CACV,CAPL,EAQKE,IARL,CAQU,iBAAMP,CAAAA,CAAc,CAACe,OAAf,EAAN,CARV,EASKC,KATL,CASWC,UAAaC,SATxB,CAUH,CACJ,CAvBD,EAyBAlC,CAAW,GACd,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 <http://www.gnu.org/licenses/>.\n\n/**\n * Report builder editor\n *\n * @module core_reportbuilder/editor\n * @package core_reportbuilder\n * @copyright 2021 David Matamoros <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Templates from 'core/templates';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {init as columnsEditorInit} from 'core_reportbuilder/local/editor/columns';\nimport {getReport} from 'core_reportbuilder/local/repository/reports';\n\nlet initialized = false;\n\n/**\n * Initialise editor and all it's modules\n */\nexport const init = () => {\n const reportElement = document.querySelector(reportSelectors.regions.report);\n\n columnsEditorInit(reportElement, initialized);\n\n // Ensure we only add our listeners once (can be called multiple times by mustache template).\n if (initialized) {\n return;\n }\n\n // Add event handlers to generic report editor elements.\n document.addEventListener('click', event => {\n\n // Toggle between edit and preview mode.\n const toggleEditViewMode = event.target.closest(reportSelectors.actions.toggleEditPreview);\n if (toggleEditViewMode) {\n event.preventDefault();\n\n const pendingPromise = new Pending('core_reportbuilder/reports:get');\n const toggledEditMode = toggleEditViewMode.dataset.editMode !== \"1\";\n\n let customjs = '';\n\n getReport(reportElement.dataset.reportId, toggledEditMode)\n .then(response => {\n customjs = response.javascript;\n return Templates.render('core_reportbuilder/custom_report', response);\n })\n .then((html, js) => {\n return Templates.replaceNodeContents(reportElement, html, js + customjs);\n })\n .then(() => pendingPromise.resolve())\n .catch(Notification.exception);\n }\n });\n\n initialized = true;\n};\n"],"file":"editor.min.js"}
+1 -1
View File
@@ -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}}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<c)){a.next=7;break}a.next=4;return(0,f.get_string)("filtersappliedx","core_reportbuilder",c);case 4:d.textContent=a.sent;a.next=10;break;case 7:a.next=9;return(0,f.get_string)("filters","moodle");case 9:d.textContent=a.sent;case 10:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}(),s=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);r(n,a.detail);(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);r(n,0);return k.resolve()}).catch(d.default.exception)});document.querySelector("#region-main").style.overflowX="visible"};a.init=s});
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<c)){a.next=7;break}a.next=4;return(0,f.get_string)("filtersappliedx","core_reportbuilder",c);case 4:d.textContent=a.sent;a.next=10;break;case 7:a.next=9;return(0,f.get_string)("filters","moodle");case 9:d.textContent=a.sent;case 10:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}(),s=function(a,m){var n=document.querySelector(k.forReport(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);r(n,a.detail);(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.resetFilters)(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);r(n,0);return k.resolve()}).catch(d.default.exception)});document.querySelector("#region-main").style.overflowX="visible"};a.init=s});
//# sourceMappingURL=filters.min.js.map
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +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/local/editor/columns",["exports","jquery","core/event_dispatcher","core/inplace_editable","core/notification","core/pending","core/pubsub","core/sortable_list","core/str","core/toast","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_reportbuilder/local/repository/columns"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=p(b);e=p(e);f=p(f);h=p(h);k=o(k);l=o(l);function n(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;n=function(){return a};return a}function o(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=n();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 p(a){return a&&a.__esModule?a:{default:a}}function q(a,b){return v(a)||u(a,b)||s(a,b)||r()}function r(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function s(a,b){if(!a)return;if("string"==typeof a)return t(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);if("Object"===c&&a.constructor)c=a.constructor.name;if("Map"===c||"Set"===c)return Array.from(c);if("Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c))return t(a,b)}function t(a,b){if(null==b||b>a.length)b=a.length;for(var c=0,d=Array(b);c<b;c++){d[c]=a[c]}return d}function u(a,b){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(a)))return;var c=[],d=!0,e=!1,f=void 0;try{for(var g=a[Symbol.iterator](),h;!(d=(h=g.next()).done);d=!0){c.push(h.value);if(b&&c.length===b)break}}catch(a){e=!0;f=a}finally{try{if(!d&&null!=g["return"])g["return"]()}finally{if(e)throw f}}return c}function v(a){if(Array.isArray(a))return a}var w=function(a,d){if(d){return}a.addEventListener("click",function(b){var d=b.target.closest(l.actions.reportAddColumn);if(d){b.preventDefault();var p=new f.default("core_reportbuilder/columns:add");(0,m.addColumn)(a.dataset.reportId,d.dataset.uniqueIdentifier).then(function(a){return(0,g.publish)(k.publish.reportColumnsUpdated,a)}).then(function(){return(0,i.get_string)("columnadded","core_reportbuilder",d.dataset.name)}).then(j.add).then(function(){(0,c.dispatchEvent)(k.tableReload,{preservePagination:!0},a);return p.resolve()}).catch(e.default.exception)}var h=b.target.closest(l.actions.reportRemoveColumn);if(h){b.preventDefault();var n=h.closest(l.regions.columnHeader),o=n.dataset.columnName;(0,i.get_strings)([{key:"deletecolumn",component:"core_reportbuilder",param:o},{key:"deletecolumnconfirm",component:"core_reportbuilder",param:o},{key:"delete",component:"moodle"}]).then(function(b){var d=q(b,3),h=d[0],l=d[1],p=d[2];e.default.confirm(h,l,p,null,function(){var b=new f.default("core_reportbuilder/columns:remove");(0,m.deleteColumn)(a.dataset.reportId,n.dataset.columnId).then(function(a){return(0,g.publish)(k.publish.reportColumnsUpdated,a)}).then(function(){return(0,i.get_string)("columndeleted","core_reportbuilder",o)}).then(j.add).then(function(){(0,c.dispatchEvent)(k.tableReload,{preservePagination:!0},a);return b.resolve()}).catch(e.default.exception)})}).catch(e.default.exception)}});var n=new h.default("".concat(l.regions.reportTable," thead tr"),{isHorizontal:!0});n.getElementName=function(a){return Promise.resolve(a.data("columnName"))};(0,b.default)(a).on(h.default.EVENTS.DRAG,"th[data-column-id]",function(c,d){var e=d.element.data("columnPosition"),f=d.targetNextElement.data("columnPosition");(0,b.default)(a).find("tbody tr").each(function(){var a=(0,b.default)(this).children("td.c".concat(e-1))[0];if(f){var c=(0,b.default)(this).children("td.c".concat(f-1))[0];this.insertBefore(a,c)}else{this.appendChild(a)}})});(0,b.default)(a).on(h.default.EVENTS.DROP,"th[data-column-id]",function(b,d){if(d.positionChanged){var g=new f.default("core_reportbuilder/columns:reorder"),h=d.element.data("columnId"),l=d.element.data("columnName"),n=d.element.data("columnPosition"),o=d.targetNextElement.data("columnPosition")||d.element.siblings().length+2;if(o>n){o--}(0,m.reorderColumn)(a.dataset.reportId,h,o).then(function(){return(0,i.get_string)("columnmoved","core_reportbuilder",l)}).then(j.add).then(function(){(0,c.dispatchEvent)(k.tableReload,{preservePagination:!0},a);return g.resolve()}).catch(e.default.exception)}})};a.init=w});
//# sourceMappingURL=columns.min.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
define ("core_reportbuilder/local/repository/columns",["exports","core/ajax"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.reorderColumn=a.deleteColumn=a.addColumn=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);var c=function(a,c){return b.default.call([{methodname:"core_reportbuilder_columns_add",args:{reportid:a,uniqueidentifier:c}}])[0]};a.addColumn=c;var d=function(a,c){return b.default.call([{methodname:"core_reportbuilder_columns_delete",args:{reportid:a,columnid:c}}])[0]};a.deleteColumn=d;var e=function(a,c,d){return b.default.call([{methodname:"core_reportbuilder_columns_reorder",args:{reportid:a,columnid:c,position:d}}])[0]};a.reorderColumn=e});
//# sourceMappingURL=columns.min.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/local/repository/columns.js"],"names":["addColumn","reportId","uniqueIdentifier","Ajax","call","methodname","args","reportid","uniqueidentifier","deleteColumn","columnId","columnid","reorderColumn","position"],"mappings":"4MAwBA,uDASO,GAAMA,CAAAA,CAAS,CAAG,SAACC,CAAD,CAAWC,CAAX,CAAgC,CAMrD,MAAOC,WAAKC,IAAL,CAAU,CALD,CACZC,UAAU,CAAE,gCADA,CAEZC,IAAI,CAAE,CAACC,QAAQ,CAAEN,CAAX,CAAqBO,gBAAgB,CAAEN,CAAvC,CAFM,CAKC,CAAV,EAAqB,CAArB,CACV,CAPM,C,cAgBA,GAAMO,CAAAA,CAAY,CAAG,SAACR,CAAD,CAAWS,CAAX,CAAwB,CAMhD,MAAOP,WAAKC,IAAL,CAAU,CALD,CACZC,UAAU,CAAE,mCADA,CAEZC,IAAI,CAAE,CAACC,QAAQ,CAAEN,CAAX,CAAqBU,QAAQ,CAAED,CAA/B,CAFM,CAKC,CAAV,EAAqB,CAArB,CACV,CAPM,C,iBAiBA,GAAME,CAAAA,CAAa,CAAG,SAACX,CAAD,CAAWS,CAAX,CAAqBG,CAArB,CAAkC,CAM3D,MAAOV,WAAKC,IAAL,CAAU,CALD,CACZC,UAAU,CAAE,oCADA,CAEZC,IAAI,CAAE,CAACC,QAAQ,CAAEN,CAAX,CAAqBU,QAAQ,CAAED,CAA/B,CAAyCG,QAAQ,CAAEA,CAAnD,CAFM,CAKC,CAAV,EAAqB,CAArB,CACV,CAPM,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 <http://www.gnu.org/licenses/>.\n\n/**\n * Module to handle column AJAX requests\n *\n * @module core_reportbuilder/local/repository/columns\n * @package core_reportbuilder\n * @copyright 2021 Paul Holden <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Add column to given report\n *\n * @param {Number} reportId\n * @param {String} uniqueIdentifier\n * @return {Promise}\n */\nexport const addColumn = (reportId, uniqueIdentifier) => {\n const request = {\n methodname: 'core_reportbuilder_columns_add',\n args: {reportid: reportId, uniqueidentifier: uniqueIdentifier}\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Remove column from given report\n *\n * @param {Number} reportId\n * @param {Number} columnId\n * @return {Promise}\n */\nexport const deleteColumn = (reportId, columnId) => {\n const request = {\n methodname: 'core_reportbuilder_columns_delete',\n args: {reportid: reportId, columnid: columnId}\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Re-order column within a report\n *\n * @param {Number} reportId\n * @param {Number} columnId\n * @param {Number} position\n * @return {Promise}\n */\nexport const reorderColumn = (reportId, columnId, position) => {\n const request = {\n methodname: 'core_reportbuilder_columns_reorder',\n args: {reportid: reportId, columnid: columnId, position: position}\n };\n\n return Ajax.call([request])[0];\n};\n"],"file":"columns.min.js"}
+1 -1
View File
@@ -1,2 +1,2 @@
define ("core_reportbuilder/local/repository/filters",["exports","core/ajax"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.reset=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);var c=function(a){return b.default.call([{methodname:"core_reportbuilder_filters_reset",args:{reportid:a}}])[0]};a.reset=c});
define ("core_reportbuilder/local/repository/filters",["exports","core/ajax"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.resetFilters=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);var c=function(a){return b.default.call([{methodname:"core_reportbuilder_filters_reset",args:{reportid:a}}])[0]};a.resetFilters=c});
//# sourceMappingURL=filters.min.js.map
@@ -1 +1 @@
{"version":3,"sources":["../../../src/local/repository/filters.js"],"names":["reset","reportId","Ajax","call","methodname","args","reportid"],"mappings":"yKAuBA,uDASO,GAAMA,CAAAA,CAAK,CAAG,SAAAC,CAAQ,CAAI,CAM7B,MAAOC,WAAKC,IAAL,CAAU,CALD,CACZC,UAAU,CAAE,kCADA,CAEZC,IAAI,CAAE,CAACC,QAAQ,CAAEL,CAAX,CAFM,CAKC,CAAV,EAAqB,CAArB,CACV,CAPM,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 <http://www.gnu.org/licenses/>.\n\n/**\n * Module to handle filter AJAX requests\n *\n * @module core_reportbuilder/local/repository/filters\n * @copyright 2021 Paul Holden <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Reset all filters for given report\n *\n * @method\n * @param {Number} reportId\n * @return {Promise}\n */\nexport const reset = reportId => {\n const request = {\n methodname: 'core_reportbuilder_filters_reset',\n args: {reportid: reportId}\n };\n\n return Ajax.call([request])[0];\n};\n"],"file":"filters.min.js"}
{"version":3,"sources":["../../../src/local/repository/filters.js"],"names":["resetFilters","reportId","Ajax","call","methodname","args","reportid"],"mappings":"gLAuBA,uDASO,GAAMA,CAAAA,CAAY,CAAG,SAAAC,CAAQ,CAAI,CAMpC,MAAOC,WAAKC,IAAL,CAAU,CALD,CACZC,UAAU,CAAE,kCADA,CAEZC,IAAI,CAAE,CAACC,QAAQ,CAAEL,CAAX,CAFM,CAKC,CAAV,EAAqB,CAArB,CACV,CAPM,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 <http://www.gnu.org/licenses/>.\n\n/**\n * Module to handle filter AJAX requests\n *\n * @module core_reportbuilder/local/repository/filters\n * @copyright 2021 Paul Holden <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Reset all filters for given report\n *\n * @method\n * @param {Number} reportId\n * @return {Promise}\n */\nexport const resetFilters = reportId => {\n const request = {\n methodname: 'core_reportbuilder_filters_reset',\n args: {reportid: reportId}\n };\n\n return Ajax.call([request])[0];\n};\n"],"file":"filters.min.js"}
+1 -1
View File
@@ -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\"]",filterButtonLabel:"[data-region=\"filter-button-label\"]",filtersForm:"[data-region=\"filters-form\"]"},actions:{reportActionPopup:"[data-action=\"report-action-popup\"]",reportCreate:"[data-action=\"report-create\"]",reportEdit:"[data-action=\"report-edit\"]",reportDelete:"[data-action=\"report-delete\"]"}};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:{report:"[data-region=\"core_reportbuilder/report\"]",reportTable:"[data-region=\"reportbuilder-table\"]",columnHeader:"[data-region=\"column-header\"]",filterButtonLabel:"[data-region=\"filter-button-label\"]",filtersForm:"[data-region=\"filters-form\"]",sidebarMenu:"[data-region=\"sidebar-menu\"]",sidebarCard:"[data-region=\"sidebar-card\"]"},actions:{reportActionPopup:"[data-action=\"report-action-popup\"]",reportCreate:"[data-action=\"report-create\"]",reportEdit:"[data-action=\"report-edit\"]",reportDelete:"[data-action=\"report-delete\"]",reportAddColumn:"[data-action=\"report-add-column\"]",reportRemoveColumn:"[data-action=\"report-remove-column\"]",sidebarSearch:"[data-action=\"sidebar-search\"]"}};b.forReport=function(a){return"".concat(b.regions.report,"[data-report-id=\"").concat(a,"\"]")};a.default=b;return a.default});
//# sourceMappingURL=selectors.min.js.map
@@ -1 +1 @@
{"version":3,"sources":["../../src/local/selectors.js"],"names":["SELECTORS","regions","systemReport","filterButtonLabel","filtersForm","actions","reportActionPopup","reportCreate","reportEdit","reportDelete","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,CAELC,YAAY,CAAE,iCAFT,CAGLC,UAAU,CAAE,+BAHP,CAILC,YAAY,CAAE,iCAJT,CANK,CAAlB,CAqBAT,CAAS,CAACU,eAAV,CAA4B,SAAAC,CAAQ,kBAAOX,CAAS,CAACC,OAAV,CAAkBC,YAAzB,6BAAwDS,CAAxD,QAApC,C,UAEeX,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 <http://www.gnu.org/licenses/>.\n\n/**\n * Report builder selectors\n *\n * @module core_reportbuilder/local/selectors\n * @copyright 2021 Paul Holden <[email protected]>\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 reportCreate: '[data-action=\"report-create\"]',\n reportEdit: '[data-action=\"report-edit\"]',\n reportDelete: '[data-action=\"report-delete\"]',\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"}
{"version":3,"sources":["../../src/local/selectors.js"],"names":["SELECTORS","regions","report","reportTable","columnHeader","filterButtonLabel","filtersForm","sidebarMenu","sidebarCard","actions","reportActionPopup","reportCreate","reportEdit","reportDelete","reportAddColumn","reportRemoveColumn","sidebarSearch","forReport","reportId"],"mappings":"oJA+BA,GAAMA,CAAAA,CAAS,CAAG,CACdC,OAAO,CAAE,CACLC,MAAM,CAAE,6CADH,CAELC,WAAW,CAAE,uCAFR,CAGLC,YAAY,CAAE,iCAHT,CAILC,iBAAiB,CAAE,uCAJd,CAKLC,WAAW,CAAE,gCALR,CAMLC,WAAW,CAAE,gCANR,CAOLC,WAAW,CAAE,gCAPR,CADK,CAUdC,OAAO,CAAE,CACLC,iBAAiB,CAAE,uCADd,CAELC,YAAY,CAAE,iCAFT,CAGLC,UAAU,CAAE,+BAHP,CAILC,YAAY,CAAE,iCAJT,CAKLC,eAAe,CAAE,qCALZ,CAMLC,kBAAkB,CAAE,wCANf,CAOLC,aAAa,CAAE,kCAPV,CAVK,CAAlB,CA4BAhB,CAAS,CAACiB,SAAV,CAAsB,SAAAC,CAAQ,kBAAOlB,CAAS,CAACC,OAAV,CAAkBC,MAAzB,8BAAmDgB,CAAnD,QAA9B,C,UAEelB,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 <http://www.gnu.org/licenses/>.\n\n/**\n * Report builder selectors\n *\n * @module core_reportbuilder/local/selectors\n * @copyright 2021 Paul Holden <[email protected]>\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 report: '[data-region=\"core_reportbuilder/report\"]',\n reportTable: '[data-region=\"reportbuilder-table\"]',\n columnHeader: '[data-region=\"column-header\"]',\n filterButtonLabel: '[data-region=\"filter-button-label\"]',\n filtersForm: '[data-region=\"filters-form\"]',\n sidebarMenu: '[data-region=\"sidebar-menu\"]',\n sidebarCard: '[data-region=\"sidebar-card\"]',\n },\n actions: {\n reportActionPopup: '[data-action=\"report-action-popup\"]',\n reportCreate: '[data-action=\"report-create\"]',\n reportEdit: '[data-action=\"report-edit\"]',\n reportDelete: '[data-action=\"report-delete\"]',\n reportAddColumn: '[data-action=\"report-add-column\"]',\n reportRemoveColumn: '[data-action=\"report-remove-column\"]',\n sidebarSearch: '[data-action=\"sidebar-search\"]',\n },\n};\n\n/**\n * Selector for given report\n *\n * @method forReport\n * @param {Number} reportId\n * @return {String}\n */\nSELECTORS.forReport = reportId => `${SELECTORS.regions.report}[data-report-id=\"${reportId}\"]`;\n\nexport default SELECTORS;\n"],"file":"selectors.min.js"}
+1 -1
View File
@@ -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/report",["exports","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_table/dynamic","core_table/local/dynamic/selectors"],function(a,b,c,d,e){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=g(b);c=g(c);e=g(e);function f(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;f=function(){return a};return a}function g(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=f();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 g=d?Object.getOwnPropertyDescriptor(a,e):null;if(g&&(g.get||g.set)){Object.defineProperty(c,e,g)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function h(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 i(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function f(a){h(i,d,e,f,g,"next",a)}function g(a){h(i,d,e,f,g,"throw",a)}f(void 0)})}}var j=function(a){document.addEventListener(b.tableReload,function(){var b=i(regeneratorRuntime.mark(function b(f){var g,h,i,j;return regeneratorRuntime.wrap(function(b){while(1){switch(b.prev=b.next){case 0:h=f.target.closest(c.forSystemReport(a));if(!(null===h)){b.next=3;break}return b.abrupt("return");case 3:i=h.querySelector(e.main.region);j=(null===(g=f.detail)||void 0===g?void 0:g.preservePagination)?null:1;b.next=7;return(0,d.setPageNumber)(i,j,!1).then(d.refreshTableContent);case 7:case"end":return b.stop();}}},b)}));return function(){return b.apply(this,arguments)}}());document.addEventListener("click",function(a){var b=a.target.closest(c.actions.reportActionPopup);if(null===b){return}a.preventDefault();var d=JSON.parse(b.dataset.popupAction);window.openpopup(a,d.jsfunctionargs)})};a.init=j});
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/report",["exports","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core_table/dynamic","core_table/local/dynamic/selectors"],function(a,b,c,d,e){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=g(b);c=g(c);e=g(e);function f(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;f=function(){return a};return a}function g(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=f();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 g=d?Object.getOwnPropertyDescriptor(a,e):null;if(g&&(g.get||g.set)){Object.defineProperty(c,e,g)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function h(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 i(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function f(a){h(i,d,e,f,g,"next",a)}function g(a){h(i,d,e,f,g,"throw",a)}f(void 0)})}}var j=!1,k=function(){if(j){return}document.addEventListener(b.tableReload,function(){var a=i(regeneratorRuntime.mark(function a(b){var f,g,h,i;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:g=b.target.closest(c.regions.report);if(!(null===g)){a.next=3;break}return a.abrupt("return");case 3:h=g.querySelector(e.main.region);i=(null===(f=b.detail)||void 0===f?void 0:f.preservePagination)?null:1;a.next=7;return(0,d.setPageNumber)(h,i,!1).then(d.refreshTableContent);case 7:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}());document.addEventListener("click",function(a){var b=a.target.closest(c.actions.reportActionPopup);if(null===b){return}a.preventDefault();var d=JSON.parse(b.dataset.popupAction);window.openpopup(a,d.jsfunctionargs)});j=!0};a.init=k});
//# sourceMappingURL=report.min.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["../src/report.js"],"names":["init","reportId","document","addEventListener","reportEvents","tableReload","event","triggerElement","target","closest","reportSelectors","forSystemReport","tableRoot","querySelector","tableSelectors","main","region","pageNumber","detail","preservePagination","then","refreshTableContent","reportActionPopup","actions","preventDefault","popupAction","JSON","parse","dataset","window","openpopup","jsfunctionargs"],"mappings":"ojBAuBA,OACA,OAEA,O,q2BAQO,GAAMA,CAAAA,CAAI,CAAG,SAAAC,CAAQ,CAAI,CAE5BC,QAAQ,CAACC,gBAAT,CAA0BC,CAAY,CAACC,WAAvC,4CAAoD,WAAMC,CAAN,+FAC1CC,CAD0C,CACzBD,CAAK,CAACE,MAAN,CAAaC,OAAb,CAAqBC,CAAe,CAACC,eAAhB,CAAgCV,CAAhC,CAArB,CADyB,MAEzB,IAAnB,GAAAM,CAF4C,mDAM1CK,CAN0C,CAM9BL,CAAc,CAACM,aAAf,CAA6BC,CAAc,CAACC,IAAf,CAAoBC,MAAjD,CAN8B,CAO1CC,CAP0C,CAO7B,WAAAX,CAAK,CAACY,MAAN,uBAAcC,kBAAd,EAAmC,IAAnC,CAA0C,CAPb,gBAS1C,oBAAcP,CAAd,CAAyBK,CAAzB,KACDG,IADC,CACIC,qBADJ,CAT0C,yCAApD,yDAcAnB,QAAQ,CAACC,gBAAT,CAA0B,OAA1B,CAAmC,SAAAG,CAAK,CAAI,CACxC,GAAMgB,CAAAA,CAAiB,CAAGhB,CAAK,CAACE,MAAN,CAAaC,OAAb,CAAqBC,CAAe,CAACa,OAAhB,CAAwBD,iBAA7C,CAA1B,CACA,GAA0B,IAAtB,GAAAA,CAAJ,CAAgC,CAC5B,MACH,CACDhB,CAAK,CAACkB,cAAN,GACA,GAAMC,CAAAA,CAAW,CAAGC,IAAI,CAACC,KAAL,CAAWL,CAAiB,CAACM,OAAlB,CAA0BH,WAArC,CAApB,CACAI,MAAM,CAACC,SAAP,CAAiBxB,CAAjB,CAAwBmB,CAAW,CAACM,cAApC,CACH,CARD,CASH,CAzBM,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 <http://www.gnu.org/licenses/>.\n\n/**\n * Report builder report management\n *\n * @module core_reportbuilder/report\n * @copyright 2021 Paul Holden <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {setPageNumber, refreshTableContent} from 'core_table/dynamic';\nimport * as tableSelectors from 'core_table/local/dynamic/selectors';\n\n/**\n * Initialise module for given report\n *\n * @method\n * @param {Number} reportId\n */\nexport const init = reportId => {\n // Listen for the table reload event.\n document.addEventListener(reportEvents.tableReload, async(event) => {\n const triggerElement = event.target.closest(reportSelectors.forSystemReport(reportId));\n if (triggerElement === null) {\n return;\n }\n\n const tableRoot = triggerElement.querySelector(tableSelectors.main.region);\n const pageNumber = event.detail?.preservePagination ? null : 1;\n\n await setPageNumber(tableRoot, pageNumber, false)\n .then(refreshTableContent);\n });\n\n // Listen for trigger popup events.\n document.addEventListener('click', event => {\n const reportActionPopup = event.target.closest(reportSelectors.actions.reportActionPopup);\n if (reportActionPopup === null) {\n return;\n }\n event.preventDefault();\n const popupAction = JSON.parse(reportActionPopup.dataset.popupAction);\n window.openpopup(event, popupAction.jsfunctionargs);\n });\n};\n"],"file":"report.min.js"}
{"version":3,"sources":["../src/report.js"],"names":["initialized","init","document","addEventListener","reportEvents","tableReload","event","reportElement","target","closest","reportSelectors","regions","report","tableRoot","querySelector","tableSelectors","main","region","pageNumber","detail","preservePagination","then","refreshTableContent","reportActionPopup","actions","preventDefault","popupAction","JSON","parse","dataset","window","openpopup","jsfunctionargs"],"mappings":"ojBAuBA,OACA,OAEA,O,w2BAEIA,CAAAA,CAAW,G,CAOFC,CAAI,CAAG,UAAM,CAEtB,GAAID,CAAJ,CAAiB,CAEb,MACH,CAGDE,QAAQ,CAACC,gBAAT,CAA0BC,CAAY,CAACC,WAAvC,4CAAoD,WAAMC,CAAN,+FAC1CC,CAD0C,CAC1BD,CAAK,CAACE,MAAN,CAAaC,OAAb,CAAqBC,CAAe,CAACC,OAAhB,CAAwBC,MAA7C,CAD0B,MAE1B,IAAlB,GAAAL,CAF4C,mDAM1CM,CAN0C,CAM9BN,CAAa,CAACO,aAAd,CAA4BC,CAAc,CAACC,IAAf,CAAoBC,MAAhD,CAN8B,CAO1CC,CAP0C,CAO7B,WAAAZ,CAAK,CAACa,MAAN,uBAAcC,kBAAd,EAAmC,IAAnC,CAA0C,CAPb,gBAS1C,oBAAcP,CAAd,CAAyBK,CAAzB,KACDG,IADC,CACIC,qBADJ,CAT0C,yCAApD,yDAcApB,QAAQ,CAACC,gBAAT,CAA0B,OAA1B,CAAmC,SAAAG,CAAK,CAAI,CACxC,GAAMiB,CAAAA,CAAiB,CAAGjB,CAAK,CAACE,MAAN,CAAaC,OAAb,CAAqBC,CAAe,CAACc,OAAhB,CAAwBD,iBAA7C,CAA1B,CACA,GAA0B,IAAtB,GAAAA,CAAJ,CAAgC,CAC5B,MACH,CACDjB,CAAK,CAACmB,cAAN,GACA,GAAMC,CAAAA,CAAW,CAAGC,IAAI,CAACC,KAAL,CAAWL,CAAiB,CAACM,OAAlB,CAA0BH,WAArC,CAApB,CACAI,MAAM,CAACC,SAAP,CAAiBzB,CAAjB,CAAwBoB,CAAW,CAACM,cAApC,CACH,CARD,EAUAhC,CAAW,GACd,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 <http://www.gnu.org/licenses/>.\n\n/**\n * Report builder report management\n *\n * @module core_reportbuilder/report\n * @copyright 2021 Paul Holden <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {setPageNumber, refreshTableContent} from 'core_table/dynamic';\nimport * as tableSelectors from 'core_table/local/dynamic/selectors';\n\nlet initialized = false;\n\n/**\n * Initialise module for given report\n *\n * @method\n */\nexport const init = () => {\n\n if (initialized) {\n // We already added the event listeners (can be called multiple times by mustache template).\n return;\n }\n\n // Listen for the table reload event.\n document.addEventListener(reportEvents.tableReload, async(event) => {\n const reportElement = event.target.closest(reportSelectors.regions.report);\n if (reportElement === null) {\n return;\n }\n\n const tableRoot = reportElement.querySelector(tableSelectors.main.region);\n const pageNumber = event.detail?.preservePagination ? null : 1;\n\n await setPageNumber(tableRoot, pageNumber, false)\n .then(refreshTableContent);\n });\n\n // Listen for trigger popup events.\n document.addEventListener('click', event => {\n const reportActionPopup = event.target.closest(reportSelectors.actions.reportActionPopup);\n if (reportActionPopup === null) {\n return;\n }\n event.preventDefault();\n const popupAction = JSON.parse(reportActionPopup.dataset.popupAction);\n window.openpopup(event, popupAction.jsfunctionargs);\n });\n\n initialized = true;\n};\n"],"file":"report.min.js"}
+2
View File
@@ -0,0 +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/sidebar",["exports","core/pending","core/utils","core_reportbuilder/local/selectors"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);d=f(d);function e(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;e=function(){return a};return a}function f(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=e();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in a){if(Object.prototype.hasOwnProperty.call(a,f)){var g=d?Object.getOwnPropertyDescriptor(a,f):null;if(g&&(g.get||g.set)){Object.defineProperty(c,f,g)}else{c[f]=a[f]}}}c.default=a;if(b){b.set(a,c)}return c}var g={EXPANDED:"show",COLLAPSED:"collapsed",SHOW:"d-flex",HIDE:"d-none"},h=function(a,c){var e=new b.default("core_reportbuilder/sidebar:cardFilter"),f=c.querySelectorAll(d.regions.sidebarCard),h=c.querySelectorAll(".list-group-item"),j=a.target.value.toLowerCase();h.forEach(function(a){var b=a.dataset.name.toLowerCase();if(b.includes(j)){a.classList.replace(g.HIDE,g.SHOW)}else{a.classList.replace(g.SHOW,g.HIDE)}});f.forEach(function(a){if(0<a.querySelectorAll(".".concat(g.SHOW)).length){a.classList.remove(g.HIDE);i(a)}else{a.classList.add(g.HIDE)}});e.resolve()},i=function(a){var b=a.querySelector("[data-toggle=\"collapse\"]");if(b.classList.contains(g.COLLAPSED)){b.classList.remove(g.COLLAPSED);b.setAttribute("aria-expanded","true");var c=a.querySelector(b.dataset.target);c.classList.add(g.EXPANDED)}},j=function(){var a=document.querySelector(d.regions.sidebarMenu),e=a.querySelector(d.actions.sidebarSearch);e.addEventListener("keyup",function(d){var e=new b.default("core_reportbuilder/sidebar:keyup");(0,c.debounce)(h,250)(d,a);setTimeout(function(){e.resolve()},250)})};a.init=j});
//# sourceMappingURL=sidebar.min.js.map
File diff suppressed because one or more lines are too long
+77
View File
@@ -0,0 +1,77 @@
// 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/>.
/**
* Report builder editor
*
* @module core_reportbuilder/editor
* @package core_reportbuilder
* @copyright 2021 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
"use strict";
import 'core/inplace_editable';
import Notification from 'core/notification';
import Pending from 'core/pending';
import Templates from 'core/templates';
import * as reportSelectors from 'core_reportbuilder/local/selectors';
import {init as columnsEditorInit} from 'core_reportbuilder/local/editor/columns';
import {getReport} from 'core_reportbuilder/local/repository/reports';
let initialized = false;
/**
* Initialise editor and all it's modules
*/
export const init = () => {
const reportElement = document.querySelector(reportSelectors.regions.report);
columnsEditorInit(reportElement, initialized);
// Ensure we only add our listeners once (can be called multiple times by mustache template).
if (initialized) {
return;
}
// Add event handlers to generic report editor elements.
document.addEventListener('click', event => {
// Toggle between edit and preview mode.
const toggleEditViewMode = event.target.closest(reportSelectors.actions.toggleEditPreview);
if (toggleEditViewMode) {
event.preventDefault();
const pendingPromise = new Pending('core_reportbuilder/reports:get');
const toggledEditMode = toggleEditViewMode.dataset.editMode !== "1";
let customjs = '';
getReport(reportElement.dataset.reportId, toggledEditMode)
.then(response => {
customjs = response.javascript;
return Templates.render('core_reportbuilder/custom_report', response);
})
.then((html, js) => {
return Templates.replaceNodeContents(reportElement, html, js + customjs);
})
.then(() => pendingPromise.resolve())
.catch(Notification.exception);
}
});
initialized = true;
};
+2 -2
View File
@@ -31,7 +31,7 @@ import {add as addToast} from 'core/toast';
import DynamicForm from 'core_form/dynamicform';
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';
import {resetFilters} from 'core_reportbuilder/local/repository/filters';
/**
* Update filter button text to indicate applied filter count
@@ -57,7 +57,7 @@ const setFilterButtonCount = async(reportElement, filterCount) => {
* @param {Number} contextId
*/
export const init = (reportId, contextId) => {
const reportElement = document.querySelector(reportSelectors.forSystemReport(reportId));
const reportElement = document.querySelector(reportSelectors.forReport(reportId));
const filterFormContainer = reportElement.querySelector(reportSelectors.regions.filtersForm);
const filterForm = new DynamicForm(filterFormContainer, '\\core_reportbuilder\\form\\filter');
@@ -0,0 +1,145 @@
// 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/>.
/**
* Report builder columns editor
*
* @module core_reportbuilder/local/editor/columns
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
"use strict";
import $ from 'jquery';
import {dispatchEvent} from 'core/event_dispatcher';
import 'core/inplace_editable';
import Notification from 'core/notification';
import Pending from 'core/pending';
import {publish} from 'core/pubsub';
import SortableList from 'core/sortable_list';
import {get_string as getString, get_strings as getStrings} from 'core/str';
import {add as addToast} from 'core/toast';
import * as reportEvents from 'core_reportbuilder/local/events';
import * as reportSelectors from 'core_reportbuilder/local/selectors';
import {addColumn, deleteColumn, reorderColumn} from 'core_reportbuilder/local/repository/columns';
/**
* Initialise module
*
* @param {Element} reportElement
* @param {Boolean} initialized Ensure we only add our listeners once
*/
export const init = (reportElement, initialized) => {
if (initialized) {
return;
}
reportElement.addEventListener('click', event => {
// Add column to report.
const reportAddColumn = event.target.closest(reportSelectors.actions.reportAddColumn);
if (reportAddColumn) {
event.preventDefault();
const pendingPromise = new Pending('core_reportbuilder/columns:add');
addColumn(reportElement.dataset.reportId, reportAddColumn.dataset.uniqueIdentifier)
.then(data => publish(reportEvents.publish.reportColumnsUpdated, data))
.then(() => getString('columnadded', 'core_reportbuilder', reportAddColumn.dataset.name))
.then(addToast)
.then(() => {
dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);
return pendingPromise.resolve();
})
.catch(Notification.exception);
}
// Remove column from report.
const reportRemoveColumn = event.target.closest(reportSelectors.actions.reportRemoveColumn);
if (reportRemoveColumn) {
event.preventDefault();
const columnHeader = reportRemoveColumn.closest(reportSelectors.regions.columnHeader);
const columnName = columnHeader.dataset.columnName;
getStrings([
{key: 'deletecolumn', component: 'core_reportbuilder', param: columnName},
{key: 'deletecolumnconfirm', component: 'core_reportbuilder', param: columnName},
{key: 'delete', component: 'moodle'},
]).then(([confirmTitle, confirmText, confirmButton]) => {
Notification.confirm(confirmTitle, confirmText, confirmButton, null, () => {
const pendingPromise = new Pending('core_reportbuilder/columns:remove');
deleteColumn(reportElement.dataset.reportId, columnHeader.dataset.columnId)
.then(data => publish(reportEvents.publish.reportColumnsUpdated, data))
.then(() => getString('columndeleted', 'core_reportbuilder', columnName))
.then(addToast)
.then(() => {
dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);
return pendingPromise.resolve();
})
.catch(Notification.exception);
});
return;
}).catch(Notification.exception);
}
});
// Initialize sortable list to handle column moving (note JQuery dependency, see MDL-72293 for resolution).
var columnSortableList = new SortableList(`${reportSelectors.regions.reportTable} thead tr`, {isHorizontal: true});
columnSortableList.getElementName = element => Promise.resolve(element.data('columnName'));
$(reportElement).on(SortableList.EVENTS.DRAG, 'th[data-column-id]', (event, info) => {
const columnPosition = info.element.data('columnPosition');
const targetColumnPosition = info.targetNextElement.data('columnPosition');
$(reportElement).find('tbody tr').each(function() {
const cell = $(this).children(`td.c${columnPosition - 1}`)[0];
if (targetColumnPosition) {
var beforeCell = $(this).children(`td.c${targetColumnPosition - 1}`)[0];
this.insertBefore(cell, beforeCell);
} else {
this.appendChild(cell);
}
});
});
$(reportElement).on(SortableList.EVENTS.DROP, 'th[data-column-id]', (event, info) => {
if (info.positionChanged) {
const pendingPromise = new Pending('core_reportbuilder/columns:reorder');
const columnId = info.element.data('columnId');
const columnName = info.element.data('columnName');
const columnPosition = info.element.data('columnPosition');
// Select target position, if moving to the end then count number of element siblings.
let targetColumnPosition = info.targetNextElement.data('columnPosition') || info.element.siblings().length + 2;
if (targetColumnPosition > columnPosition) {
targetColumnPosition--;
}
reorderColumn(reportElement.dataset.reportId, columnId, targetColumnPosition)
.then(() => getString('columnmoved', 'core_reportbuilder', columnName))
.then(addToast)
.then(() => {
dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);
return pendingPromise.resolve();
})
.catch(Notification.exception);
}
});
};
@@ -0,0 +1,74 @@
// 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/>.
/**
* Module to handle column AJAX requests
*
* @module core_reportbuilder/local/repository/columns
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Ajax from 'core/ajax';
/**
* Add column to given report
*
* @param {Number} reportId
* @param {String} uniqueIdentifier
* @return {Promise}
*/
export const addColumn = (reportId, uniqueIdentifier) => {
const request = {
methodname: 'core_reportbuilder_columns_add',
args: {reportid: reportId, uniqueidentifier: uniqueIdentifier}
};
return Ajax.call([request])[0];
};
/**
* Remove column from given report
*
* @param {Number} reportId
* @param {Number} columnId
* @return {Promise}
*/
export const deleteColumn = (reportId, columnId) => {
const request = {
methodname: 'core_reportbuilder_columns_delete',
args: {reportid: reportId, columnid: columnId}
};
return Ajax.call([request])[0];
};
/**
* Re-order column within a report
*
* @param {Number} reportId
* @param {Number} columnId
* @param {Number} position
* @return {Promise}
*/
export const reorderColumn = (reportId, columnId, position) => {
const request = {
methodname: 'core_reportbuilder_columns_reorder',
args: {reportid: reportId, columnid: columnId, position: position}
};
return Ajax.call([request])[0];
};
@@ -30,7 +30,7 @@ import Ajax from 'core/ajax';
* @param {Number} reportId
* @return {Promise}
*/
export const reset = reportId => {
export const resetFilters = reportId => {
const request = {
methodname: 'core_reportbuilder_filters_reset',
args: {reportid: reportId}
+10 -3
View File
@@ -31,25 +31,32 @@
*/
const SELECTORS = {
regions: {
systemReport: '[data-region="core_reportbuilder/system-report"]',
report: '[data-region="core_reportbuilder/report"]',
reportTable: '[data-region="reportbuilder-table"]',
columnHeader: '[data-region="column-header"]',
filterButtonLabel: '[data-region="filter-button-label"]',
filtersForm: '[data-region="filters-form"]',
sidebarMenu: '[data-region="sidebar-menu"]',
sidebarCard: '[data-region="sidebar-card"]',
},
actions: {
reportActionPopup: '[data-action="report-action-popup"]',
reportCreate: '[data-action="report-create"]',
reportEdit: '[data-action="report-edit"]',
reportDelete: '[data-action="report-delete"]',
reportAddColumn: '[data-action="report-add-column"]',
reportRemoveColumn: '[data-action="report-remove-column"]',
sidebarSearch: '[data-action="sidebar-search"]',
},
};
/**
* Selector for given report
*
* @method forSystemReport
* @method forReport
* @param {Number} reportId
* @return {String}
*/
SELECTORS.forSystemReport = reportId => `${SELECTORS.regions.systemReport}[data-reportid="${reportId}"]`;
SELECTORS.forReport = reportId => `${SELECTORS.regions.report}[data-report-id="${reportId}"]`;
export default SELECTORS;
+14 -5
View File
@@ -26,21 +26,28 @@ import * as reportSelectors from 'core_reportbuilder/local/selectors';
import {setPageNumber, refreshTableContent} from 'core_table/dynamic';
import * as tableSelectors from 'core_table/local/dynamic/selectors';
let initialized = false;
/**
* Initialise module for given report
*
* @method
* @param {Number} reportId
*/
export const init = reportId => {
export const init = () => {
if (initialized) {
// We already added the event listeners (can be called multiple times by mustache template).
return;
}
// Listen for the table reload event.
document.addEventListener(reportEvents.tableReload, async(event) => {
const triggerElement = event.target.closest(reportSelectors.forSystemReport(reportId));
if (triggerElement === null) {
const reportElement = event.target.closest(reportSelectors.regions.report);
if (reportElement === null) {
return;
}
const tableRoot = triggerElement.querySelector(tableSelectors.main.region);
const tableRoot = reportElement.querySelector(tableSelectors.main.region);
const pageNumber = event.detail?.preservePagination ? null : 1;
await setPageNumber(tableRoot, pageNumber, false)
@@ -57,4 +64,6 @@ export const init = reportId => {
const popupAction = JSON.parse(reportActionPopup.dataset.popupAction);
window.openpopup(event, popupAction.jsfunctionargs);
});
initialized = true;
};
+106
View File
@@ -0,0 +1,106 @@
// 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/>.
/**
* Report builder sidebar component
*
* @module core_reportbuilder/sidebar
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Pending from 'core/pending';
import {debounce} from 'core/utils';
import * as reportSelectors from 'core_reportbuilder/local/selectors';
const DEBOUNCE_TIMER = 250;
const CLASSES = {
EXPANDED: 'show',
COLLAPSED: 'collapsed',
SHOW: 'd-flex',
HIDE: 'd-none',
};
/**
* Initialise module
*
* @param {Event} event
* @param {Element} sidebarMenu
*/
const sidebarCardFilter = (event, sidebarMenu) => {
const pendingPromise = new Pending('core_reportbuilder/sidebar:cardFilter');
const sidebarCards = sidebarMenu.querySelectorAll(reportSelectors.regions.sidebarCard);
const sidebarItems = sidebarMenu.querySelectorAll('.list-group-item');
const searchTerm = event.target.value.toLowerCase();
// Toggle items according to match against search term.
sidebarItems.forEach(item => {
let itemName = item.dataset.name.toLowerCase();
if (itemName.includes(searchTerm)) {
item.classList.replace(CLASSES.HIDE, CLASSES.SHOW);
} else {
item.classList.replace(CLASSES.SHOW, CLASSES.HIDE);
}
});
// Toggle cards according to whether they have any visible items.
sidebarCards.forEach(card => {
if (card.querySelectorAll(`.${CLASSES.SHOW}`).length > 0) {
card.classList.remove(CLASSES.HIDE);
expandCard(card);
} else {
card.classList.add(CLASSES.HIDE);
}
});
pendingPromise.resolve();
};
/**
* Show a collapsed card.
* This function simulates the behaviour of JQuery show method on a collapsible element.
*
* @param {Element} card
*/
const expandCard = (card) => {
let cardButton = card.querySelector('[data-toggle="collapse"]');
if (cardButton.classList.contains(CLASSES.COLLAPSED)) {
cardButton.classList.remove(CLASSES.COLLAPSED);
cardButton.setAttribute('aria-expanded', "true");
let cardContent = card.querySelector(cardButton.dataset.target);
cardContent.classList.add(CLASSES.EXPANDED);
}
};
/**
* Initialise module
*/
export const init = () => {
const sidebarMenu = document.querySelector(reportSelectors.regions.sidebarMenu);
const sidebarSearch = sidebarMenu.querySelector(reportSelectors.actions.sidebarSearch);
// Debounce the event listener to allow the user to finish typing.
sidebarSearch.addEventListener('keyup', event => {
const pendingPromise = new Pending('core_reportbuilder/sidebar:keyup');
debounce(sidebarCardFilter, DEBOUNCE_TIMER)(event, sidebarMenu);
setTimeout(() => {
pendingPromise.resolve();
}, DEBOUNCE_TIMER);
});
};
+54
View File
@@ -40,4 +40,58 @@ abstract class datasource extends base {
* @return string
*/
abstract public static function get_name(): string;
/**
* Add columns from the given entity name to be available to use in a custom report
*
* @param string $entityname
* @param array $include Include only these columns, if omitted then include all
* @param array $exclude Exclude these columns, if omitted then exclude none
* @throws coding_exception If both $include and $exclude are non-empty
*/
final protected function add_columns_from_entity(string $entityname, array $include = [], array $exclude = []): void {
if (!empty($include) && !empty($exclude)) {
throw new coding_exception('Cannot specify columns to include and exclude simultaneously');
}
$entity = $this->get_entity($entityname);
// Retrieve filtered columns from entity, respecting given $include/$exclude parameters.
$columns = array_filter($entity->get_columns(), static function(column $column) use ($include, $exclude): bool {
if (!empty($include)) {
return in_array($column->get_name(), $include);
}
if (!empty($exclude)) {
return !in_array($column->get_name(), $exclude);
}
return true;
});
foreach ($columns as $column) {
$this->add_column($column);
}
}
/**
* Add default datasource columns to the report
*
* This method is optional and can be called when the report is created to add the default columns defined in the
* selected datasource.
*/
public function add_default_columns(): void {
$reportid = $this->get_report_persistent()->get('id');
$columnidentifiers = $this->get_default_columns();
foreach ($columnidentifiers as $uniqueidentifier) {
report::add_report_column($reportid, $uniqueidentifier);
}
}
/**
* Return the columns that will be added to the report once is created
*
* @return string[]
*/
abstract public function get_default_columns(): array;
}
+96
View File
@@ -0,0 +1,96 @@
<?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 core_reportbuilder\external\columns;
use core_reportbuilder\external\custom_report_columns_sorting_exporter;
use external_api;
use external_function_parameters;
use external_single_structure;
use external_value;
use core_reportbuilder\manager;
use core_reportbuilder\permission;
use core_reportbuilder\local\helpers\report;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->libdir}/externallib.php");
/**
* External method for adding report columns
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class add extends external_api {
/**
* External method parameters
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'reportid' => new external_value(PARAM_INT, 'Report ID'),
'uniqueidentifier' => new external_value(PARAM_RAW, 'Unique identifier of the column'),
]);
}
/**
* External method execution
*
* @param int $reportid
* @param string $uniqueidentifier
* @return array
*/
public static function execute(int $reportid, string $uniqueidentifier): array {
global $PAGE;
[
'reportid' => $reportid,
'uniqueidentifier' => $uniqueidentifier,
] = self::validate_parameters(self::execute_parameters(), [
'reportid' => $reportid,
'uniqueidentifier' => $uniqueidentifier,
]);
$report = manager::get_report_from_id($reportid);
self::validate_context($report->get_context());
permission::require_can_edit_report($report->get_report_persistent());
report::add_report_column($reportid, $uniqueidentifier);
$exporter = new custom_report_columns_sorting_exporter(null, [
'report' => $report,
]);
return (array) $exporter->export($PAGE->get_renderer('core'));
}
/**
* External method return value
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return custom_report_columns_sorting_exporter::get_read_structure();
}
}
+96
View File
@@ -0,0 +1,96 @@
<?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 core_reportbuilder\external\columns;
use core_reportbuilder\external\custom_report_columns_sorting_exporter;
use external_api;
use external_function_parameters;
use external_single_structure;
use external_value;
use core_reportbuilder\manager;
use core_reportbuilder\permission;
use core_reportbuilder\local\helpers\report;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->libdir}/externallib.php");
/**
* External method for deleting report columns
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class delete extends external_api {
/**
* External method parameters
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'reportid' => new external_value(PARAM_INT, 'Report ID'),
'columnid' => new external_value(PARAM_INT, 'Column ID'),
]);
}
/**
* External method execution
*
* @param int $reportid
* @param int $columnid
* @return array
*/
public static function execute(int $reportid, int $columnid): array {
global $PAGE;
[
'reportid' => $reportid,
'columnid' => $columnid,
] = self::validate_parameters(self::execute_parameters(), [
'reportid' => $reportid,
'columnid' => $columnid,
]);
$report = manager::get_report_from_id($reportid);
self::validate_context($report->get_context());
permission::require_can_edit_report($report->get_report_persistent());
report::delete_report_column($reportid, $columnid);
$exporter = new custom_report_columns_sorting_exporter(null, [
'report' => $report,
]);
return (array) $exporter->export($PAGE->get_renderer('core'));
}
/**
* External method return value
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return custom_report_columns_sorting_exporter::get_read_structure();
}
}
+90
View File
@@ -0,0 +1,90 @@
<?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 core_reportbuilder\external\columns;
use external_api;
use external_function_parameters;
use external_value;
use core_reportbuilder\manager;
use core_reportbuilder\permission;
use core_reportbuilder\local\helpers\report;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->libdir}/externallib.php");
/**
* External method for re-ordering report columns
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class reorder extends external_api {
/**
* External method parameters
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'reportid' => new external_value(PARAM_INT, 'Report ID'),
'columnid' => new external_value(PARAM_INT, 'Column ID'),
'position' => new external_value(PARAM_INT, 'New column position')
]);
}
/**
* External method execution
*
* @param int $reportid
* @param int $columnid
* @param int $position
* @return bool
*/
public static function execute(int $reportid, int $columnid, int $position): bool {
[
'reportid' => $reportid,
'columnid' => $columnid,
'position' => $position,
] = self::validate_parameters(self::execute_parameters(), [
'reportid' => $reportid,
'columnid' => $columnid,
'position' => $position,
]);
$report = manager::get_report_from_id($reportid);
self::validate_context($report->get_context());
permission::require_can_edit_report($report->get_report_persistent());
return report::reorder_report_column($reportid, $columnid, $position);
}
/**
* External method return value
*
* @return external_value
*/
public static function execute_returns(): external_value {
return new external_value(PARAM_BOOL, 'Success');
}
}
@@ -0,0 +1,131 @@
<?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 core_reportbuilder\external;
use pix_icon;
use renderer_base;
use core\external\exporter;
use core_reportbuilder\local\models\column;
use core_reportbuilder\local\report\base;
/**
* Custom report columns sorting exporter class
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_report_columns_sorting_exporter extends exporter {
/**
* Return a list of objects that are related to the exporter
*
* @return array
*/
protected static function define_related(): array {
return [
'report' => base::class,
];
}
/**
* Return the list of additional properties for read structure and export
*
* @return array[]
*/
protected static function define_other_properties(): array {
return [
'hassortablecolumns' => [
'type' => PARAM_BOOL,
],
'sortablecolumns' => [
'type' => [
'id' => ['type' => PARAM_INT],
'title' => ['type' => PARAM_TEXT],
'heading' => ['type' => PARAM_TEXT],
'sortdirection' => ['type' => PARAM_INT],
'sortenabled' => ['type' => PARAM_INT],
'sortorder' => ['type' => PARAM_INT],
'sorticon' => [
'type' => [
'key' => ['type' => PARAM_RAW],
'component' => ['type' => PARAM_COMPONENT],
'title' => ['type' => PARAM_NOTAGS],
],
],
'movetitle' => ['type' => PARAM_TEXT],
'sortenabledtitle' => ['type' => PARAM_TEXT],
],
'optional' => true,
'multiple' => true,
],
'helpicon' => [
'type' => PARAM_RAW,
],
];
}
/**
* Get the additional values to inject while exporting
*
* @param renderer_base $output
* @return array
*/
protected function get_other_values(renderer_base $output): array {
/** @var base $report */
$report = $this->related['report'];
$reportid = $report->get_report_persistent()->get('id');
$activecolumns = column::get_records(['reportid' => $reportid], 'sortorder');
$sortablecolumns = array_filter($activecolumns, function(column $persistent) use($report) {
$column = $report->get_column($persistent->get('uniqueidentifier'));
return $column && $column->get_is_sortable();
});
$sortablecolumns = array_map(function(column $persistent) use ($report) {
$columntitle = $report->get_column($persistent->get('uniqueidentifier'))->get_title();
$columnheading = $persistent->get('heading') ?: $columntitle;
$columnsortascending = ($persistent->get('sortdirection') == SORT_ASC);
$sortenabledtitle = $persistent->get('sortenabled') ? 'columnsortdisable' : 'columnsortenable';
$sortdirectiontitle = $columnsortascending ? 'columnsortdirectiondesc' : 'columnsortdirectionasc';
$icon = $columnsortascending ? 't/uplong' : 't/downlong';
$sorticon = new pix_icon($icon, get_string($sortdirectiontitle, 'core_reportbuilder', $columntitle));
return [
'id' => $persistent->get('id'),
'title' => $columntitle,
'heading' => $columnheading,
'sortdirection' => $persistent->get('sortdirection'),
'sortenabled' => (int)$persistent->get('sortenabled'),
'sortorder' => $persistent->get('sortorder'),
'sorticon' => $sorticon->export_for_pix(),
'movetitle' => get_string('movesorting', 'core_reportbuilder', $columntitle),
'sortenabledtitle' => get_string($sortenabledtitle, 'core_reportbuilder', $columntitle),
];
}, $sortablecolumns);
return [
'hassortablecolumns' => !empty($sortablecolumns),
'sortablecolumns' => array_values($sortablecolumns),
'helpicon' => $output->help_icon('sorting', 'core_reportbuilder'),
];
}
}
@@ -0,0 +1,151 @@
<?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 core_reportbuilder\external;
use renderer_base;
use core\persistent;
use core\external\persistent_exporter;
use core_reportbuilder\manager;
use core_reportbuilder\datasource;
use core_reportbuilder\form\filter as form_filter;
use core_reportbuilder\local\models\report;
use core_reportbuilder\table\custom_report_table;
use core_reportbuilder\table\custom_report_table_filterset;
use core_reportbuilder\table\custom_report_table_view;
use core_reportbuilder\table\custom_report_table_view_filterset;
use core_reportbuilder\local\helpers\report as report_helper;
/**
* Custom report exporter class
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_report_exporter extends persistent_exporter {
/** @var report The persistent object we will export. */
protected $persistent = null;
/** @var bool */
protected $editmode;
/** @var bool $showeditbutton When showing the report on view.php the Edit button has to be hidden */
protected $showeditbutton;
/**
* report_exporter constructor.
*
* @param persistent $persistent
* @param array $related
*/
public function __construct(persistent $persistent, array $related = array(), bool $editmode = true, bool $showeditbutton = true) {
parent::__construct($persistent, $related);
$this->editmode = $editmode;
$this->showeditbutton = $showeditbutton;
}
/**
* Return the name of the class we are exporting
*
* @return string
*/
protected static function define_class(): string {
return report::class;
}
/**
* Return a list of objects that are related to the persistent
*
* @return array
*/
protected static function define_related(): array {
return [
];
}
/**
* Return a list of additional properties used only for display
*
* @return array
*/
protected static function define_other_properties(): array {
return [
'table' => ['type' => PARAM_RAW],
'sidebarmenucards' => ['type' => custom_report_menu_cards_exporter::read_properties_definition()],
'filtersform' => [
'type' => PARAM_RAW,
'optional' => true,
],
'editmode' => ['type' => PARAM_INT],
'showeditbutton' => ['type' => PARAM_BOOL],
'javascript' => ['type' => PARAM_RAW],
];
}
/**
* Get additional values to inject while exporting
*
* @param renderer_base $output
* @return array
*/
protected function get_other_values(renderer_base $output): array {
$filtersform = '';
$menucards = [];
if ($this->editmode) {
$table = custom_report_table::create($this->persistent->get('id'));
$table->set_filterset(new custom_report_table_filterset());
} else {
}
$report = manager::get_report_from_persistent($this->persistent);
if ($this->editmode) {
$menucardexporter = new custom_report_menu_cards_exporter(null, [
'menucards' => report_helper::get_available_columns($report->get_report_persistent())
]);
$menucards = (array) $menucardexporter->export($output);
}
return [
'table' => $output->render($table),
'sidebarmenucards' => $menucards,
'filtersform' => $filtersform,
'editmode' => (int)$this->editmode,
'showeditbutton' => $this->showeditbutton,
'javascript' => '',
];
}
/**
* Generate filters form for the report
*
* @return form_filter
*/
private function generate_filters_form(): form_filter {
$filtersform = new form_filter(null, null, 'post', '', [], true, [
'reportid' => $this->persistent->get('id'),
'parameters' => json_encode([]),
]);
$filtersform->set_data_for_dynamic_submission();
return $filtersform;
}
}
@@ -0,0 +1,97 @@
<?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 core_reportbuilder\external;
use renderer_base;
use core\external\exporter;
/**
* Custom report menu cards exporter class
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_report_menu_cards_exporter extends exporter {
/**
* Return a list of objects that are related to the exporter
*
* @return array
*/
protected static function define_related(): array {
return [
'menucards' => 'array[]',
];
}
/**
* Return the list of additional properties for read structure and export
*
* @return array[]
*/
protected static function define_other_properties(): array {
return [
'menucards' => [
'type' => [
'name' => [
'type' => PARAM_TEXT,
'optional' => true,
],
'key' => [
'type' => PARAM_TEXT,
'optional' => true,
],
'items' => [
'type' => [
'name' => [
'type' => PARAM_TEXT,
],
'identifier' => [
'type' => PARAM_TEXT,
],
'title' => [
'type' => PARAM_TEXT,
],
'action' => [
'type' => PARAM_TEXT,
],
],
'optional' => true,
'multiple' => true,
],
],
'optional' => true,
'multiple' => true,
],
];
}
/**
* Get the additional values to inject while exporting
*
* @param renderer_base $output
* @return array
*/
protected function get_other_values(renderer_base $output): array {
return [
'menucards' => $this->related['menucards']
];
}
}
+84
View File
@@ -0,0 +1,84 @@
<?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 core_reportbuilder\external\reports;
use external_api;
use external_function_parameters;
use external_value;
use core_reportbuilder\permission;
use core_reportbuilder\local\helpers\report;
use core_reportbuilder\local\models\report as report_model;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->libdir}/externallib.php");
/**
* External method for deleting reports
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class delete extends external_api {
/**
* External method parameters
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'reportid' => new external_value(PARAM_INT, 'Report ID'),
]);
}
/**
* External method execution
*
* @param int $reportid
* @return bool
*/
public static function execute(int $reportid): bool {
[
'reportid' => $reportid,
] = self::validate_parameters(self::execute_parameters(), [
'reportid' => $reportid,
]);
// Load the report model for deletion. Note we don't use the manager class because it validates the report source,
// and we want user to be able to delete report, even if it's no longer associated with a valid source.
$report = new report_model($reportid);
self::validate_context($report->get_context());
permission::require_can_edit_report($report);
return report::delete_report($reportid);
}
/**
* External method return value
*
* @return external_value
*/
public static function execute_returns(): external_value {
return new external_value(PARAM_BOOL, 'Success');
}
}
@@ -95,4 +95,141 @@ class report {
return $report->delete();
}
/**
* Add given column to report
*
* @param int $reportid
* @param string $uniqueidentifier
* @return column
* @throws invalid_parameter_exception
*/
public static function add_report_column(int $reportid, string $uniqueidentifier): column {
$report = manager::get_report_from_id($reportid);
if (!array_key_exists($uniqueidentifier, $report->get_columns())) {
throw new invalid_parameter_exception('Invalid column');
}
$column = new column(0, (object) [
'reportid' => $reportid,
'uniqueidentifier' => $uniqueidentifier,
'columnorder' => column::get_max_columnorder($reportid, 'columnorder') + 1,
'sortorder' => column::get_max_columnorder($reportid, 'sortorder') + 1,
]);
return $column->create();
}
/**
* Delete given column from report
*
* @param int $reportid
* @param int $columnid
* @return bool
* @throws invalid_parameter_exception
*/
public static function delete_report_column(int $reportid, int $columnid): bool {
global $DB;
$column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
if ($column === false) {
throw new invalid_parameter_exception('Invalid column');
}
// After deletion, re-index remaining report columns.
if ($result = $column->delete()) {
$sqlupdateorder = '
UPDATE {' . column::TABLE . '}
SET columnorder = columnorder - 1
WHERE reportid = :reportid
AND columnorder > :columnorder';
$DB->execute($sqlupdateorder, ['reportid' => $reportid, 'columnorder' => $column->get('columnorder')]);
}
return $result;
}
/**
* Re-order given column within a report
*
* @param int $reportid
* @param int $columnid
* @param int $position
* @return bool
* @throws invalid_parameter_exception
*/
public static function reorder_report_column(int $reportid, int $columnid, int $position): bool {
$column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
if ($column === false) {
throw new invalid_parameter_exception('Invalid column');
}
// Get the rest of the report columns, excluding the one we are moving.
$columns = column::get_records_select('reportid = :reportid AND id <> :id', [
'reportid' => $reportid,
'id' => $columnid,
], 'columnorder');
return static::reorder_persistents_by_field($column, $columns, $position, 'columnorder');
}
/**
* Get available columns for a given report
*
* @param report_model $persistent
* @return array
*/
public static function get_available_columns(report_model $persistent) : array {
$available = [];
$report = manager::get_report_from_persistent($persistent);
// Get current report columns.
foreach ($report->get_columns() as $column) {
$entityname = $column->get_entity_name();
$entitytitle = $column->get_title();
if (!array_key_exists($entityname, $available)) {
$available[$entityname] = [
'name' => (string) $report->get_entity_title($entityname),
'key' => $entityname,
'items' => [],
];
}
$available[$entityname]['items'][] = [
'name' => $entitytitle,
'identifier' => $column->get_unique_identifier(),
'title' => get_string('addcolumn', 'core_reportbuilder', $entitytitle),
'action' => 'report-add-column'
];
}
return array_values($available);
}
/**
* Helper method for re-ordering given persistents (columns, filters, etc)
*
* @param persistent $persistent The persistent we are moving
* @param persistent[] $persistents The rest of the persistents
* @param string $field The field we need to update
* @return bool
*/
private static function reorder_persistents_by_field(persistent $persistent, array $persistents, int $position,
string $field): bool {
// Splice into new position.
array_splice($persistents, $position - 1, 0, [$persistent]);
$fieldorder = 1;
foreach ($persistents as $persistent) {
$persistent->set($field, $fieldorder++)
->update();
}
return true;
}
}
+24 -1
View File
@@ -281,6 +281,20 @@ abstract class base {
$this->entitytitles[$name] = $title;
}
/**
* Returns title of given report entity
*
* @return lang_string
* @throws coding_exception
*/
final public function get_entity_title(string $name): lang_string {
if (!array_key_exists($name, $this->entitytitles)) {
throw new coding_exception('Invalid entity name', $name);
}
return $this->entitytitles[$name];
}
/**
* Adds a column to the report
*
@@ -431,6 +445,15 @@ abstract class base {
});
}
/**
* Return all active report filters (by default, all available filters)
*
* @return filter[]
*/
public function get_active_filters(): array {
return $this->get_filters();
}
/**
* Return all report filter instances
*
@@ -442,7 +465,7 @@ abstract class base {
$filterclass = $filter->get_filter_class();
return $filterclass::create($filter);
}, $this->get_filters());
}, $this->get_active_filters());
}
/**
@@ -21,6 +21,7 @@ namespace core_reportbuilder\local\report;
use coding_exception;
use lang_string;
use core_reportbuilder\local\helpers\database;
use core_reportbuilder\local\models\column as column_model;
/**
* Class to represent a report column
@@ -85,6 +86,9 @@ final class column {
/** @var bool $available Used to know if column is available to the current user or not */
protected $available = true;
/** @var column_model $persistent */
protected $persistent;
/**
* Column constructor
*
@@ -541,4 +545,25 @@ final class column {
$this->available = $available;
return $this;
}
/**
* Set column persistent
*
* @param column_model $persistent
* @return self
*/
public function set_persistent(column_model $persistent): self {
$this->persistent = $persistent;
return $this;
}
/**
* Return column persistent
*
* @return mixed
*/
public function get_persistent(): column_model {
return $this->persistent;
}
}
@@ -225,6 +225,17 @@ class reports_list extends system_report {
* Add actions to report
*/
protected function add_actions(): void {
// Edit content action.
$this->add_action((new action(
new moodle_url('/reportbuilder/edit.php', ['id' => ':id']),
new pix_icon('t/right', get_string('editreportcontent', 'core_reportbuilder'))
))
->add_callback(function(stdClass $row): bool {
return $this->report_source_valid($row->source) && permission::can_edit_report($this->get_report_from_row($row));
})
);
// Edit details action.
$this->add_action((new action(
new moodle_url('#'),
new pix_icon('t/edit', get_string('editreportdetails', 'core_reportbuilder')),
@@ -235,6 +246,7 @@ class reports_list extends system_report {
})
);
// Delete action.
$this->add_action((new action(
new moodle_url('#'),
new pix_icon('t/delete', get_string('deletereport', 'core_reportbuilder')),
@@ -0,0 +1,81 @@
<?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 core_reportbuilder\output;
use core_external;
use core\output\inplace_editable;
use core_reportbuilder\manager;
use core_reportbuilder\permission;
use core_reportbuilder\local\models\column;
/**
* Column heading editable component
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class column_heading_editable extends inplace_editable {
/**
* Class constructor
*
* @param int $columnid
* @param column|null $column
*/
public function __construct(int $columnid, ?column $column = null) {
if ($column === null) {
$column = new column($columnid);
}
$report = $column->get_report();
$editable = permission::can_edit_report($report);
$columninstance = manager::get_report_from_persistent($report)
->get_column($column->get('uniqueidentifier'));
$displayvalue = $column->get('heading') ?: $columninstance->get_title();
parent::__construct('core_reportbuilder', 'columnheading', $column->get('id'), $editable, $displayvalue, $displayvalue,
get_string('renamecolumn', 'core_reportbuilder', $columninstance->get_title()));
}
/**
* Update column persistent and return self, called from inplace_editable callback
*
* @param int $columnid
* @param string $value
* @return self
*/
public static function update(int $columnid, string $value): self {
$column = new column($columnid);
$report = $column->get_report();
core_external::validate_context($report->get_context());
permission::require_can_edit_report($report);
$value = clean_param($value, PARAM_TEXT);
$column
->set('heading', $value)
->update();
return new self(0, $column);
}
}
@@ -0,0 +1,70 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types=1);
namespace core_reportbuilder\output;
use core_reportbuilder\external\custom_report_exporter;
use core_reportbuilder\local\models\report;
use renderable;
use renderer_base;
use stdClass;
use templatable;
/**
* Custom report output class
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_report implements renderable, templatable {
/** @var report $reportpersistent */
protected $persistent;
/** @var bool $editmode */
protected $editmode;
/** @var bool $showeditbutton */
protected $showeditbutton;
/**
* Class constructor
*
* @param report $reportpersistent
* @param bool $editmode
* @param bool $showeditbutton
*/
public function __construct(report $reportpersistent, bool $editmode = true, bool $showeditbutton = true) {
$this->persistent = $reportpersistent;
$this->editmode = $editmode;
$this->showeditbutton = $showeditbutton;
}
/**
* Export report data suitable for a template
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output): stdClass {
$exporter = new custom_report_exporter($this->persistent, [], $this->editmode, $this->showeditbutton);
return $exporter->export($output);
}
}
+16
View File
@@ -20,6 +20,7 @@ namespace core_reportbuilder\output;
use html_writer;
use plugin_renderer_base;
use core_reportbuilder\table\custom_report_table;
use core_reportbuilder\table\system_report_table;
/**
@@ -58,6 +59,21 @@ class renderer extends plugin_renderer_base {
return $output;
}
/**
* Render a custom report table
*
* @param custom_report_table $table
* @return string
*/
protected function render_custom_report_table(custom_report_table $table): string {
ob_start();
$table->out($table->get_default_per_page(), false);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
/**
* Renders the New report button
*
@@ -0,0 +1,161 @@
<?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 core_reportbuilder\table;
use context;
use moodle_url;
use renderable;
use table_sql;
use core_table\dynamic;
use core_reportbuilder\local\helpers\database;
use core_reportbuilder\local\filters\base;
use core_reportbuilder\local\models\report;
use core_reportbuilder\local\report\base as base_report;
use core_reportbuilder\local\report\filter;
defined('MOODLE_INTERNAL') || die;
require_once("{$CFG->libdir}/tablelib.php");
/**
* Base report dynamic table class
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class base_report_table extends table_sql implements dynamic, renderable {
/** @var report $persistent */
protected $persistent;
/** @var base_report $report */
protected $report;
/** @var string $groupbysql */
protected $groupbysql = '';
/**
* Initialises table SQL properties
*
* @param string $fields
* @param string $from
* @param array $joins
* @param string $where
* @param array $params
* @param array $groupby
*/
protected function init_sql(string $fields, string $from, array $joins, string $where, array $params,
array $groupby = []): void {
$wheres = [];
if ($where !== '') {
$wheres[] = $where;
}
// For each filter, we also need to apply their values (will differ according to user viewing the report).
$filtervalues = $this->report->get_filter_values();
foreach ($this->report->get_active_filters() as $filter) {
[$filtersql, $filterparams] = $this->get_filter_sql($filter, $filtervalues);
if ($filtersql !== '') {
$joins = array_merge($joins, $filter->get_joins());
$wheres[] = "({$filtersql})";
$params = array_merge($params, $filterparams);
}
}
$wheresql = '1=1';
if (!empty($wheres)) {
$wheresql = implode(' AND ', $wheres);
}
if (!empty($groupby)) {
$this->groupbysql = 'GROUP BY ' . implode(', ', $groupby);
}
// Add unique table joins.
$from .= ' ' . implode(' ', array_unique($joins));
$this->set_sql($fields, $from, $wheresql, $params);
$counttablealias = database::generate_alias();
$this->set_count_sql("
SELECT COUNT(1)
FROM (SELECT {$fields}
FROM {$from}
WHERE {$wheresql}
{$this->groupbysql}
) {$counttablealias}", $params);
}
/**
* Return SQL fragments from given filter instance suitable for inclusion in table SQL
*
* @param filter $filter
* @param array $filtervalues
* @return array [$sql, $params]
*/
private function get_filter_sql(filter $filter, array $filtervalues): array {
/** @var base $filterclass */
$filterclass = $filter->get_filter_class();
return $filterclass::create($filter)->get_sql_filter($filtervalues);
}
/**
* Override parent method of the same, to make use of a recordset and avoid issues with duplicate values in the first column
*
* @param int $pagesize
* @param bool $useinitialsbar
*/
public function query_db($pagesize, $useinitialsbar = true) {
global $DB;
$sql = "SELECT {$this->sql->fields} FROM {$this->sql->from} WHERE {$this->sql->where} {$this->groupbysql}";
$sort = $this->get_sql_sort();
if ($sort) {
$sql .= " ORDER BY {$sort}";
}
if (!$this->is_downloading()) {
$this->pagesize($pagesize, $DB->count_records_sql($this->countsql, $this->countparams));
$this->rawdata = $DB->get_recordset_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size());
} else {
$this->rawdata = $DB->get_recordset_sql($sql, $this->sql->params);
}
}
/**
* Get the context for the table (that of the report persistent)
*
* @return context
*/
public function get_context(): context {
return $this->persistent->get_context();
}
/**
* Set the base URL of the table to the current page URL
*/
public function guess_base_url(): void {
$this->baseurl = new moodle_url('/');
}
}
@@ -0,0 +1,262 @@
<?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 core_reportbuilder\table;
use html_writer;
use moodle_exception;
use moodle_url;
use stdClass;
use core_reportbuilder\manager;
use core_reportbuilder\local\models\column as column_model;
use core_reportbuilder\local\models\report;
use core_reportbuilder\local\report\column;
use core_reportbuilder\output\column_aggregation_editable;
use core_reportbuilder\output\column_heading_editable;
/**
* Custom report dynamic table class
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_report_table extends base_report_table {
/** @var string Unique ID prefix for the table */
private const UNIQUEID_PREFIX = 'custom-report-table-';
/**
* Table constructor. Note that the passed unique ID value must match the pattern "custom-report-table-(\d+)" so that
* dynamic updates continue to load the same report
*
* @param string $uniqueid
* @throws moodle_exception For invalid unique ID
*/
public function __construct(string $uniqueid) {
if (!preg_match('/^' . self::UNIQUEID_PREFIX . '(?<id>\d+)$/', $uniqueid, $matches)) {
throw new moodle_exception('invalidcustomreportid', 'core_reportbuilder', '', null, $uniqueid);
}
parent::__construct($uniqueid);
$this->define_baseurl(new moodle_url('/reportbuilder/edit.php', ['id' => $matches['id']]));
// Load the report persistent, and accompanying system report instance.
$this->persistent = new report($matches['id']);
$this->report = manager::get_report_from_persistent($this->persistent);
$fields = $groupby = [];
$maintable = $this->report->get_main_table();
$maintablealias = $this->report->get_main_table_alias();
$joins = $this->report->get_joins();
[$where, $params] = $this->report->get_base_condition();
$this->set_attribute('data-region', 'reportbuilder-table');
$this->set_attribute('class', $this->attributes['class'] . ' reportbuilder-table');
// Retrieve all report columns, exit early if there are none.
$columns = $this->get_active_columns();
if (empty($columns)) {
$this->init_sql('*', "{{$maintable}} {$maintablealias}", [], '1=0', []);
return;
}
$columnheaders = [];
foreach ($columns as $column) {
$columnheaders[$column->get_column_alias()] = $column->get_persistent()->get('heading') ?: $column->get_title();
// Add each columns fields, joins and params to our report.
$fields = array_merge($fields, $column->get_fields());
$joins = array_merge($joins, $column->get_joins());
$params = array_merge($params, $column->get_params());
// Disable sorting for some columns.
if (!$column->get_is_sortable()) {
$this->no_sorting($column->get_column_alias());
}
}
$this->define_columns(array_keys($columnheaders));
$this->define_headers(array_values($columnheaders));
// Table configuration.
$this->initialbars(false);
$this->collapsible(false);
$this->pageable(true);
// Initialise table SQL properties.
$fieldsql = implode(', ', $fields);
$this->init_sql($fieldsql, "{{$maintable}} {$maintablealias}", $joins, $where, $params, $groupby);
}
/**
* Return a new instance of the class for given report ID
*
* @param int $reportid
* @return static
*/
public static function create(int $reportid): self {
return new static(self::UNIQUEID_PREFIX . $reportid);
}
/**
* Get user preferred sort columns, overriding those of parent. If user has no preferences then use report defaults
*
* @return array
*/
public function get_sort_columns() {
$sortcolumns = parent::get_sort_columns();
if (empty($sortcolumns)) {
$columns = $this->get_active_columns();
$instances = column_model::get_records([
'reportid' => $this->report->get_report_persistent()->get('id'),
'sortenabled' => 1,
], 'sortorder');
foreach ($instances as $instance) {
$column = $columns[$instance->get('id')] ?? null;
if ($column !== null && $column->get_is_available()) {
$sortcolumns[$column->get_column_alias()] = $instance->get('sortdirection');
}
}
}
return $sortcolumns;
}
/**
* Format each row of returned data, executing defined callbacks for the row and each column
*
* @param array|stdClass $row
* @return array
*/
public function format_row($row) {
$columns = $this->get_active_columns();
$formattedrow = [];
foreach ($columns as $column) {
$formattedrow[$column->get_column_alias()] = $column->format_value((array) $row);
}
return $formattedrow;
}
/**
* Get the html for the download buttons
*
* @return string
*/
public function download_buttons(): string {
// TODO.
return '';
}
/**
* Get the columns of the custom report, returned instances being valid and available for the user
*
* @return column[] Indexed by column ID
*/
private function get_active_columns(): array {
$columns = [];
$instances = column_model::get_records(['reportid' => $this->report->get_report_persistent()->get('id')], 'columnorder');
foreach ($instances as $index => $instance) {
$column = $this->report->get_column($instance->get('uniqueidentifier'));
if ($column !== null && $column->get_is_available()) {
$column->set_persistent($instance);
// We should clone the report column to ensure if it's added twice to a report, each operates independently.
$columns[$instance->get('id')] = clone $column
->set_index($index);
}
}
return $columns;
}
public function print_headers() {
global $OUTPUT, $PAGE;
$columns = $this->get_active_columns();
if (empty($columns)) {
$this->print_nothing_to_display();
return;
}
$columns = array_values($columns);
$renderer = $PAGE->get_renderer('core');
echo html_writer::start_tag('thead');
echo html_writer::start_tag('tr');
foreach ($this->headers as $index => $title) {
$column = $columns[$index];
$headingeditable = new column_heading_editable(0, $column->get_persistent());
// Render table header cell, with all editing controls.
$headercell = $OUTPUT->render_from_template('core_reportbuilder/table_header_cell', [
'entityname' => $this->report->get_entity_title($column->get_entity_name()),
'name' => $column->get_title(),
'headingeditable' => $headingeditable->render($renderer),
'aggregationeditable' => 'Aggregation',
'movetitle' => get_string('movecolumn', 'core_reportbuilder', $column->get_title()),
]);
echo html_writer::tag('th', $headercell, [
'class' => 'border-right border-left',
'scope' => 'col',
'data-region' => 'column-header',
'data-column-id' => $column->get_persistent()->get('id'),
'data-column-name' => $column->get_title(),
'data-column-position' => $index + 1,
]);
}
echo html_writer::end_tag('tr');
echo html_writer::end_tag('thead');
}
/**
* Override start of HTML to remove top pagination
*/
public function start_html() {
// Render the dynamic table header.
echo $this->get_dynamic_table_html_start();
// Render button to allow user to reset table preferences.
echo $this->render_reset_button();
$this->wrap_html_start();
echo html_writer::start_tag('div', array('class' => 'no-overflow'));
echo html_writer::start_tag('table', $this->attributes);
}
/**
* Override end of HTML to ensure that column headers are always added
*/
public function finish_html() {
if ($this->totalrows == 0 && !$this->started_output) {
$this->start_output();
}
parent::finish_html();
}
}
@@ -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/>.
declare(strict_types=1);
namespace core_reportbuilder\table;
use core_table\local\filter\filterset;
/**
* Custom report dynamic table filterset class
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_report_table_filterset extends filterset {
/**
* Get the required filters
*
* @return array.
*/
public function get_required_filters(): array {
return [];
}
}
@@ -18,26 +18,17 @@ declare(strict_types=1);
namespace core_reportbuilder\table;
use context;
use core_table\local\filter\filterset;
use html_writer;
use moodle_exception;
use moodle_url;
use renderable;
use table_sql;
use stdClass;
use core_table\dynamic;
use core_reportbuilder\manager;
use core_reportbuilder\system_report;
use core_reportbuilder\local\filters\base;
use core_reportbuilder\local\models\report;
use core_reportbuilder\local\report\action;
use core_reportbuilder\local\report\column;
use core_table\local\filter\filterset;
defined('MOODLE_INTERNAL') || die;
require_once("{$CFG->libdir}/tablelib.php");
/**
* System report dynamic table class
*
@@ -45,17 +36,11 @@ require_once("{$CFG->libdir}/tablelib.php");
* @copyright 2020 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class system_report_table extends table_sql implements dynamic, renderable {
class system_report_table extends base_report_table {
/** @var string Unique ID prefix for the table */
private const UNIQUEID_PREFIX = 'system-report-table-';
/** @var report $report */
protected $report;
/** @var system_report $systemreport */
protected $systemreport;
/**
* Table constructor. Note that the passed unique ID value must match the pattern "system-report-table-(\d+)" so that
* dynamic updates continue to load the same report
@@ -72,27 +57,27 @@ class system_report_table extends table_sql implements dynamic, renderable {
parent::__construct($uniqueid);
// Load the report persistent, and accompanying system report instance.
$this->report = new report($matches['id']);
$this->systemreport = manager::get_report_from_persistent($this->report, $parameters);
$this->persistent = new report($matches['id']);
$this->report = manager::get_report_from_persistent($this->persistent, $parameters);
$fields = $this->systemreport->get_base_fields();
$maintable = $this->systemreport->get_main_table();
$maintablealias = $this->systemreport->get_main_table_alias();
$joins = $this->systemreport->get_joins();
[$where, $params] = $this->systemreport->get_base_condition();
$fields = $this->report->get_base_fields();
$maintable = $this->report->get_main_table();
$maintablealias = $this->report->get_main_table_alias();
$joins = $this->report->get_joins();
[$where, $params] = $this->report->get_base_condition();
$this->set_attribute('data-region', 'reportbuilder-table');
$this->set_attribute('class', $this->attributes['class'] . ' reportbuilder-table');
// Download options.
$this->showdownloadbuttonsat = [TABLE_P_BOTTOM];
$this->is_downloading($parameters['download'] ?? null, $this->systemreport->get_downloadfilename());
$this->is_downloading($parameters['download'] ?? null, $this->report->get_downloadfilename());
// Retrieve all report columns. If we are downloading the report, remove as required.
$columns = $this->systemreport->get_columns();
$columns = $this->report->get_columns();
if ($this->is_downloading()) {
$columns = array_diff_key($columns,
array_flip($this->systemreport->get_exclude_columns_for_download()));
array_flip($this->report->get_exclude_columns_for_download()));
}
$columnheaders = [];
@@ -119,7 +104,7 @@ class system_report_table extends table_sql implements dynamic, renderable {
}
// If the report has any actions then append appropriate column, note that actions are excluded during download.
if ($this->systemreport->has_actions() && !$this->is_downloading()) {
if ($this->report->has_actions() && !$this->is_downloading()) {
$columnheaders['actions'] = html_writer::tag('span', get_string('actions', 'core_reportbuilder'), [
'class' => 'sr-only',
]);
@@ -130,15 +115,15 @@ class system_report_table extends table_sql implements dynamic, renderable {
$this->define_headers(array_values($columnheaders));
// Initial table sort column.
if ($sortcolumn = $this->systemreport->get_initial_sort_column()) {
$this->sortable(true, $sortcolumn->get_column_alias(), $this->systemreport->get_initial_sort_direction());
if ($sortcolumn = $this->report->get_initial_sort_column()) {
$this->sortable(true, $sortcolumn->get_column_alias(), $this->report->get_initial_sort_direction());
}
// Table configuration.
$this->initialbars(false);
$this->collapsible(false);
$this->pageable(true);
$this->set_default_per_page($this->systemreport->get_default_per_page());
$this->set_default_per_page($this->report->get_default_per_page());
// Initialise table SQL properties.
$fieldsql = implode(', ', $fields);
@@ -164,78 +149,11 @@ class system_report_table extends table_sql implements dynamic, renderable {
*/
public function set_filterset(filterset $filterset): void {
$parameters = $filterset->get_filter('parameters')->current();
$this->systemreport->set_parameters((array) json_decode($parameters, true));
$this->report->set_parameters((array) json_decode($parameters, true));
parent::set_filterset($filterset);
}
/**
* Initialises table SQL properties
*
* @param string $fields
* @param string $from
* @param array $joins
* @param string $where
* @param array $params
*/
protected function init_sql(string $fields, string $from, array $joins, string $where, array $params): void {
$wheres = [];
if ($where !== '') {
$wheres[] = $where;
}
$filtervalues = $this->systemreport->get_filter_values();
foreach ($this->systemreport->get_filters() as $filter) {
/** @var base $filterclass */
$filterclass = $filter->get_filter_class();
$filterinstance = $filterclass::create($filter);
[$filtersql, $filterparams] = $filterinstance->get_sql_filter($filtervalues);
if ($filtersql !== '') {
$wheres[] = "({$filtersql})";
$params = array_merge($params, $filterparams);
$joins = array_merge($joins, $filter->get_joins());
}
}
$wheresql = '1=1';
if (!empty($wheres)) {
$wheresql = implode(' AND ', $wheres);
}
// Add unique table joins.
$from .= ' ' . implode(' ', array_unique($joins));
$this->set_sql($fields, $from, $wheresql, $params);
$this->set_count_sql("SELECT COUNT(1) FROM {$from} WHERE {$wheresql}", $params);
}
/**
* Override parent method of the same, to make use of a recordset and avoid issues with duplicate values in the first column
*
* @param int $pagesize
* @param bool $useinitialsbar
*/
public function query_db($pagesize, $useinitialsbar = true) {
global $DB;
$sql = "SELECT {$this->sql->fields} FROM {$this->sql->from} WHERE {$this->sql->where}";
$sort = $this->get_sql_sort();
if ($sort) {
$sql .= " ORDER BY {$sort}";
}
if (!$this->is_downloading()) {
$this->pagesize($pagesize, $DB->count_records_sql($this->countsql, $this->countparams));
$this->rawdata = $DB->get_recordset_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size());
} else {
$this->rawdata = $DB->get_recordset_sql($sql, $this->sql->params);
}
}
/**
* Override parent method for retrieving row class with that defined by the system report
*
@@ -243,7 +161,7 @@ class system_report_table extends table_sql implements dynamic, renderable {
* @return string
*/
public function get_row_class($row) {
return $this->systemreport->get_row_class((object) $row);
return $this->report->get_row_class((object) $row);
}
/**
@@ -253,13 +171,13 @@ class system_report_table extends table_sql implements dynamic, renderable {
* @return array
*/
public function format_row($row) {
$this->systemreport->row_callback((object) $row);
$this->report->row_callback((object) $row);
/** @var column[] $columnsbyalias */
$columnsbyalias = [];
// Create a lookup for convenience, indexed by column alias.
$columns = $this->systemreport->get_columns();
$columns = $this->report->get_columns();
foreach ($columns as $column) {
$columnsbyalias[$column->get_column_alias()] = $column;
}
@@ -272,7 +190,7 @@ class system_report_table extends table_sql implements dynamic, renderable {
}
});
if ($this->systemreport->has_actions()) {
if ($this->report->has_actions()) {
$row['actions'] = $this->format_row_actions((object) $row);
}
@@ -288,27 +206,11 @@ class system_report_table extends table_sql implements dynamic, renderable {
private function format_row_actions(stdClass $row): string {
$actions = array_map(static function(action $action) use ($row): string {
return (string) $action->get_action_link($row);
}, $this->systemreport->get_actions());
}, $this->report->get_actions());
return implode('', $actions);
}
/**
* Get the context for the table (that of the report persistent)
*
* @return context
*/
public function get_context(): context {
return $this->report->get_context();
}
/**
* Set the base URL of the table to the current page URL
*/
public function guess_base_url(): void {
$this->baseurl = new moodle_url('/');
}
/**
* Get the html for the download buttons
*
@@ -317,14 +219,14 @@ class system_report_table extends table_sql implements dynamic, renderable {
public function download_buttons(): string {
global $OUTPUT;
if ($this->systemreport->can_be_downloaded() && !$this->is_downloading()) {
if ($this->report->can_be_downloaded() && !$this->is_downloading()) {
return $OUTPUT->download_dataformat_selector(
get_string('downloadas', 'table'),
new \moodle_url('/reportbuilder/download.php'),
'download',
[
'id' => $this->report->get('id'),
'parameters' => json_encode($this->systemreport->get_parameters()),
'id' => $this->persistent->get('id'),
'parameters' => json_encode($this->report->get_parameters()),
]
);
}
+56
View File
@@ -0,0 +1,56 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Edit a custom report
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
use core_reportbuilder\manager;
use core_reportbuilder\output\custom_report;
use core_reportbuilder\permission;
require_once(__DIR__ . '/../config.php');
require_once("{$CFG->libdir}/adminlib.php");
$reportid = required_param('id', PARAM_INT);
admin_externalpage_setup('customreports', null, ['id' => $reportid], new moodle_url('/reportbuilder/edit.php'));
$report = manager::get_report_from_id($reportid);
permission::require_can_edit_report($report->get_report_persistent());
$PAGE->set_context($report->get_context());
/** @var \core_reportbuilder\output\renderer $renderer */
$renderer = $PAGE->get_renderer('core_reportbuilder');
$reportname = $report->get_report_persistent()->get_formatted_name();
$PAGE->navbar->add($reportname);
$PAGE->set_title($reportname);
$PAGE->set_heading($reportname);
echo $OUTPUT->header();
$export = (new custom_report($report->get_report_persistent()))->export_for_template($renderer);
echo $renderer->render_from_template('core_reportbuilder/custom_report', $export);
echo $OUTPUT->footer();
+3
View File
@@ -58,6 +58,9 @@ function core_reportbuilder_inplace_editable($itemtype, $itemid, $newvalue) {
case 'reportname':
return \core_reportbuilder\output\report_name_editable::update($itemid, $newvalue);
case 'columnheading':
return \core_reportbuilder\output\column_heading_editable::update($itemid, $newvalue);
default:
return false;
}
@@ -0,0 +1,95 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core_reportbuilder/custom_report
Template for a custom report
Example context (json):
{
"id": 1,
"source": "some\\class\\name",
"table": "table",
"sidebarmenucards": [{
"name": "General",
"items": [{
"name": "Manually added users",
"identifier": "core_reportbuilder:users"
}]
}]
}
}}
<div data-region="core_reportbuilder/report"
data-report-id="{{id}}"
data-source="{{source}}"
id="core-reportbuilder-{{uniqid}}">
<div class="reportbuilder-wrapper d-flex flex-column flex-md-row">
{{#editmode}}
<!-- Sidebar -->
{{> core_reportbuilder/local/sidebar-menu/area}}
{{/editmode}}
<!-- Report -->
<div class="reportbuilder-report-container">
<div class="{{#editmode}}p-2 border{{/editmode}}">
<div data-region="core_reportbuilder/report-header" class="dropdown d-flex justify-content-end">
<!-- Preview/Edit button -->
{{#showeditbutton}}
<button data-action="toggle-edit-preview" data-edit-mode="{{editmode}}" class="btn btn-secondary mr-2"
title="{{#editmode}}{{#str}} switchpreview, core_reportbuilder {{/str}}{{/editmode}}{{^editmode}}{{#str}} switchedit, core_reportbuilder {{/str}}{{/editmode}}">
{{#editmode}}
{{#pix}} i/preview, core, {{/pix}}
{{#str}} preview, core {{/str}}
{{/editmode}}
{{^editmode}}
{{#pix}} t/editstring, core {{/pix}}
{{#str}} edit, core {{/str}}
{{/editmode}}
</button>
{{/showeditbutton}}
<!-- Settings/Filters button -->
{{#editmode}}
<button id="report-settings-collapse" data-toggle="collapse" data-target="#report-settings" class="btn btn-secondary"
title="{{#str}} reportsettingstoggle, core_reportbuilder {{/str}}">
<i class="icon fa fa-sliders" aria-hidden="true"></i>
{{#str}} settings, moodle {{/str}}
</button>
{{/editmode}}
{{^editmode}}
<!-- Filters -->
{{#filtersform}}
{{>core_reportbuilder/local/filters/area}}
{{/filtersform}}
{{/editmode}}
</div>
<div class="mt-2">
<!-- Table -->
{{{table}}}
</div>
</div>
</div>
</div>
</div>
{{#js}}
require(['core_reportbuilder/editor', 'core_reportbuilder/report'],
function(editor, report) {
editor.init();
report.init();
});
{{/js}}
@@ -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/>.
}}
{{!
@template core_reportbuilder/local/sidebar-menu/area
Template for showing report sidebar menu.
Example context (json):
{
"sidebarmenucards": [{
"name": "General",
"items": [{
"name": "Manually added users",
"identifier": "core_reportbuilder:users"
}]
}]
}
}}
<div class="reportbuilder-sidebar-menu d-flex flex-column mr-md-3" data-region="sidebar-menu" id="region-sidebar-menu">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text pr-0 bg-white">
{{#pix}} a/search, core {{/pix}}
</span>
</div>
<input type="text"
class="form-control border-left-0"
placeholder="{{#str}} search, core_search {{/str}}"
aria-label="{{#str}} search, core_search {{/str}}"
data-action="sidebar-search">
</div>
<div class="reportbuilder-sidebar-menu-cards">
{{#sidebarmenucards}}
{{#menucards}}
{{> core_reportbuilder/local/sidebar-menu/card}}
{{/menucards}}
{{/sidebarmenucards}}
</div>
</div>
{{#js}}
require(['core_reportbuilder/sidebar'], function(sidebar) {
sidebar.init();
});
{{/js}}
@@ -0,0 +1,44 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core_reportbuilder/local/sidebar-menu/card
Template for showing a single report sidebar menu card.
Example context (json):
{
"name": "General",
"items": [{
"name": "Manually added users",
"identifier": "core_reportbuilder:users"
}]
}
}}
<div data-region="sidebar-card">
{{< core_reportbuilder/toggle_card }}
{{$id}}card_{{key}}{{/id}}
{{$header}}{{name}}{{/header}}
{{$body}}
<div class="list-group list-group-flush">
{{#items}}
{{> core_reportbuilder/local/sidebar-menu/card_item}}
{{/items}}
</div>
{{/body}}
{{/ core_reportbuilder/toggle_card }}
</div>
@@ -0,0 +1,39 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core_reportbuilder/local/sidebar-menu/card_item
Template for showing a single element in a sidebar menu card.
Example context (json):
{
"name": "Manually added users",
"identifier": "tool_reportbuilder:users"
}
}}
<a href="#"
title="{{title}}"
role="button"
class="list-group-item list-group-item-action d-flex align-items-start justify-content-between pr-2"
data-action="{{action}}"
data-unique-identifier="{{identifier}}"
data-name="{{name}}">
{{{name}}}
<span class="my-auto ml-1">
{{#pix}} t/add, core {{/pix}}
</span>
</a>
@@ -44,6 +44,6 @@
{{#js}}
require(['core_reportbuilder/report'], function(report) {
report.init('{{id}}');
report.init();
});
{{/js}}
@@ -0,0 +1,46 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core_reportbuilder/table_header_cell
This template renders the header of the columns
Example context (json):
{
"entityname": "Stuff",
"name": "Demo",
"headingeditable": "Demo",
"movetitle": "Move column 'Demo'"
}
}}
<div class="d-flex justify-content-start">
<div class="mr-2">
{{>core/drag_handle}}
</div>
<div class="w-100">
<div class="d-flex justify-content-between">
<small class="text-muted text-nowrap text-uppercase">{{entityname}} &bull; {{name}}</small>
<span class="text-nowrap">
<a data-action="report-remove-column" class="nomargin" href="#">
{{#pix}}e/cancel, core, {{#str}}deletecolumn, core_reportbuilder, {{{name}}}{{/str}}{{/pix}}
</a>
</span>
</div>
<h5 class="text-nowrap">{{{headingeditable}}}</h5>
<small class="text-muted text-nowrap">{{{aggregationeditable}}}</small>
</div>
</div>
@@ -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 core_reportbuilder/toggle_card
Template for empty_message
Example context (json):
{
"collapsed": "collapsed",
"id": "htmlid",
"header": "Toggle card heading",
"buttontitle": "Expand toggle card",
"body": "This is the toggle card content"
}
}}
<div class="card reportbuilder-toggle-card mb-2">
<div class="card-header p-0" id="{{$id}}{{/id}}-heading">
<div class="d-flex align-items-center bg-light mb-0 p-1">
<div class="ml-3">
<span class="mr-1">{{$header}}{{/header}}</span> {{$helpicon}}{{/helpicon}}
</div>
<button class="btn toggle-card-button nomargin ml-auto {{$collapsed}}{{/collapsed}}"
data-toggle="collapse"
data-target="#{{$id}}{{/id}}"
aria-expanded="true"
aria-controls="{{$id}}{{/id}}"
title="{{#str}}showhide, core_reportbuilder, {{$header}}{{/header}}{{/str}}"
>
<span class="collapsed-icon-container"><i class="toggle-card-icon fa fa-lg fa-angle-down"></i></span>
<span class="expanded-icon-container"><i class="toggle-card-icon fa fa-lg fa-angle-up"></i></span>
</button>
</div>
</div>
<div id="{{$id}}{{/id}}" class="notransition collapse {{$collapsed}}show{{/collapsed}}" aria-labelledby="{{$id}}{{/id}}-heading">
<div class="card-body p-0">
{{$body}}{{/body}}
</div>
</div>
</div>
@@ -19,8 +19,10 @@ declare(strict_types=1);
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
use core_reportbuilder\local\models\report;
/**
* Behat step definitions for Reportbuilder
* Behat step definitions for Report builder
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
@@ -28,6 +30,32 @@ require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
*/
class behat_reportbuilder extends behat_base {
/**
* Convert page names to URLs for steps like 'When I am on the "[identifier]" "[page type]" page'.
*
* Recognised page names are:
* | type | identifier | description |
* | Editor | Report name | Custom report editor |
*
* @param string $type
* @param string $identifier
* @return moodle_url
* @throws Exception for unrecognised report or page type
*/
protected function resolve_page_instance_url(string $type, string $identifier): moodle_url {
if (!$report = report::get_record(['name' => $identifier])) {
throw new Exception("Unknown report '{$identifier}'");
}
switch ($type) {
case 'Editor':
return new moodle_url('/reportbuilder/edit.php', ['id' => $report->get('id')]);
default:
throw new Exception("Unrecognised reportbuilder page type '{$type}'");
}
}
/**
* Return the list of partial named selectors
*
@@ -38,6 +66,9 @@ class behat_reportbuilder extends behat_base {
new behat_component_named_selector('Filter', [
".//*[@data-region='filters-form']//*[@data-filter-for=%locator%]",
]),
new behat_component_named_selector('Condition', [
".//*[@data-region='conditions-form']//*[@data-condition-name=%locator%]",
]),
];
}
}
@@ -0,0 +1,54 @@
@core_reportbuilder @javascript
Feature: Manage custom report columns
In order to manage the columns of custom reports
As an admin
I need to add, edit and delete columns in a report
Scenario: Add column to report
Given the following "core_reportbuilder > Reports" exist:
| name | source | default |
| My report | core_user\reportbuilder\datasource\users | 0 |
And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
When I click on "Add column 'Full name'" "link"
Then I should see "Added column 'Full name'"
And I should see "Full name" in the "reportbuilder-table" "table"
Scenario: Rename column in report
Given the following "core_reportbuilder > Reports" exist:
| name | source | default |
| My report | core_user\reportbuilder\datasource\users | 0 |
And the following "core_reportbuilder > Columns" exist:
| report | uniqueidentifier |
| My report | user:fullname |
And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
When I set the field "Rename column 'Full name'" to "My renamed column"
And I reload the page
Then I should see "My renamed column" in the "reportbuilder-table" "table"
Scenario: Move column in report
Given the following "core_reportbuilder > Reports" exist:
| name | source | default |
| My report | core_user\reportbuilder\datasource\users | 0 |
And the following "core_reportbuilder > Columns" exist:
| report | uniqueidentifier |
| My report | user:fullname |
| My report | user:email |
| My report | user:lastaccess |
And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
When I click on "Move column 'Last access'" "button"
And I click on "After \"Full name\"" "link" in the "Move column 'Last access'" "dialogue"
Then I should see "Moved column 'Last access'"
And "Last access" "text" should appear before "Email address" "text"
Scenario: Delete column from report
Given the following "core_reportbuilder > Reports" exist:
| name | source | default |
| My report | core_user\reportbuilder\datasource\users | 0 |
And the following "core_reportbuilder > Columns" exist:
| report | uniqueidentifier |
| My report | user:fullname |
And I am on the "My report" "reportbuilder > Editor" page logged in as "admin"
When I click on "Delete column 'Full name'" "link"
And I click on "Delete" "button" in the "Delete column 'Full name'" "dialogue"
Then I should see "Deleted column 'Full name'"
And I should see "Nothing to display"
+98
View File
@@ -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/>.
declare(strict_types=1);
namespace core_reportbuilder\external\columns;
use core_reportbuilder_generator;
use external_api;
use externallib_advanced_testcase;
use core_reportbuilder\report_access_exception;
use core_reportbuilder\local\models\column;
use core_user\reportbuilder\datasource\users;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->dirroot}/webservice/tests/helpers.php");
/**
* Unit tests of external class for adding report columns
*
* @package core_reportbuilder
* @covers \core_reportbuilder\external\columns\add
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class add_test extends externallib_advanced_testcase {
/**
* Text execute method
*/
public function test_execute(): void {
$this->resetAfterTest();
$this->setAdminUser();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report([
'name' => 'My report',
'source' => users::class,
'default' => false,
]);
// Add column.
$result = add::execute($report->get('id'), 'user:fullname');
$result = external_api::clean_returnvalue(add::execute_returns(), $result);
$this->assertTrue($result['hassortablecolumns']);
$this->assertCount(1, $result['sortablecolumns']);
$sortablecolumn = reset($result['sortablecolumns']);
$this->assertEquals('Full name', $sortablecolumn['title']);
$this->assertEquals(SORT_ASC, $sortablecolumn['sortdirection']);
$this->assertEquals(0, $sortablecolumn['sortenabled']);
$this->assertEquals(1, $sortablecolumn['sortorder']);
$this->assertEquals('t/uplong', $sortablecolumn['sorticon']['key']);
$this->assertEquals('moodle', $sortablecolumn['sorticon']['component']);
$str = get_string('columnsortdirectiondesc', 'core_reportbuilder', 'Full name');
$this->assertEquals($str, $sortablecolumn['sorticon']['title']);
// Assert report columns.
$columns = column::get_records(['reportid' => $report->get('id')]);
$this->assertCount(1, $columns);
$this->assertEquals('user:fullname', reset($columns)->get('uniqueidentifier'));
}
/**
* Test execute method for a user without permission to edit reports
*/
public function test_execute_access_exception(): void {
$this->resetAfterTest();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(report_access_exception::class);
$this->expectExceptionMessage('You can not edit this report');
add::execute($report->get('id'), 'user:fullname');
}
}
+104
View File
@@ -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/>.
declare(strict_types=1);
namespace core_reportbuilder\external\columns;
use core_reportbuilder_generator;
use external_api;
use externallib_advanced_testcase;
use core_reportbuilder\report_access_exception;
use core_reportbuilder\local\models\column;
use core_user\reportbuilder\datasource\users;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->dirroot}/webservice/tests/helpers.php");
/**
* Unit tests of external class for deleting report columns
*
* @package core_reportbuilder
* @covers \core_reportbuilder\external\columns\delete
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class delete_test extends externallib_advanced_testcase {
/**
* Text execute method
*/
public function test_execute(): void {
$this->resetAfterTest();
$this->setAdminUser();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report([
'name' => 'My report',
'source' => users::class,
'default' => false,
]);
// Add two columns.
$columnfullname = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:email']);
// Delete the first column.
$result = delete::execute($report->get('id'), $columnfullname->get('id'));
$result = external_api::clean_returnvalue(delete::execute_returns(), $result);
$this->assertTrue($result['hassortablecolumns']);
$this->assertCount(1, $result['sortablecolumns']);
$sortablecolumn = reset($result['sortablecolumns']);
$this->assertEquals('Email address', $sortablecolumn['title']);
$this->assertEquals(SORT_ASC, $sortablecolumn['sortdirection']);
$this->assertEquals(0, $sortablecolumn['sortenabled']);
$this->assertEquals(2, $sortablecolumn['sortorder']);
$this->assertEquals('t/uplong', $sortablecolumn['sorticon']['key']);
$this->assertEquals('moodle', $sortablecolumn['sorticon']['component']);
$str = get_string('columnsortdirectiondesc', 'core_reportbuilder', 'Email address');
$this->assertEquals($str, $sortablecolumn['sorticon']['title']);
// Assert report columns.
$columns = column::get_records(['reportid' => $report->get('id')]);
$this->assertCount(1, $columns);
$this->assertEquals('user:email', reset($columns)->get('uniqueidentifier'));
}
/**
* Test execute method for a user without permission to edit reports
*/
public function test_execute_access_exception(): void {
$this->resetAfterTest();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
$column = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:email']);
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(report_access_exception::class);
$this->expectExceptionMessage('You can not edit this report');
delete::execute($report->get('id'), $column->get('id'));
}
}
+104
View File
@@ -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/>.
declare(strict_types=1);
namespace core_reportbuilder\external\columns;
use core_reportbuilder_generator;
use external_api;
use externallib_advanced_testcase;
use core_reportbuilder\report_access_exception;
use core_reportbuilder\local\models\column;
use core_user\reportbuilder\datasource\users;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->dirroot}/webservice/tests/helpers.php");
/**
* Unit tests of external class for re-ordering report columns
*
* @package core_reportbuilder
* @covers \core_reportbuilder\external\columns\reorder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class reorder_test extends externallib_advanced_testcase {
/**
* Text execute method
*/
public function test_execute(): void {
$this->resetAfterTest();
$this->setAdminUser();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report([
'name' => 'My report',
'source' => users::class,
'default' => false,
]);
// Add four columns.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:email']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:country']);
$columncity = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:city']);
// Move the city column to second position.
$result = reorder::execute($report->get('id'), $columncity->get('id'), 2);
$result = external_api::clean_returnvalue(reorder::execute_returns(), $result);
$this->assertTrue($result);
// Assert report columns order.
$columns = column::get_records(['reportid' => $report->get('id')], 'columnorder');
$columnidentifiers = array_map(static function(column $column): string {
return $column->get('uniqueidentifier');
}, $columns);
$this->assertEquals([
'user:fullname',
'user:city',
'user:email',
'user:country',
], $columnidentifiers);
}
/**
* Test execute method for a user without permission to edit reports
*/
public function test_execute_access_exception(): void {
$this->resetAfterTest();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
$column = $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:email']);
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(report_access_exception::class);
$this->expectExceptionMessage('You can not edit this report');
reorder::execute($report->get('id'), $column->get('id'), 1);
}
}
+87
View File
@@ -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/>.
declare(strict_types=1);
namespace core_reportbuilder\external\reports;
use core_reportbuilder_generator;
use external_api;
use externallib_advanced_testcase;
use core_reportbuilder\report_access_exception;
use core_reportbuilder\local\models\report;
use core_user\reportbuilder\datasource\users;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->dirroot}/webservice/tests/helpers.php");
/**
* Unit tests of external class for deleting reports
*
* @package core_reportbuilder
* @covers \core_reportbuilder\external\reports\delete
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class delete_test extends externallib_advanced_testcase {
/**
* Text execute method
*/
public function test_execute(): void {
$this->resetAfterTest();
$this->setAdminUser();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report([
'name' => 'My report',
'source' => users::class,
'default' => false,
]);
// Sanity test.
$this->assertCount(1, report::get_records());
$result = delete::execute($report->get('id'));
$result = external_api::clean_returnvalue(delete::execute_returns(), $result);
$this->assertTrue($result);
// Retrieve reports.
$this->assertEmpty(report::get_records());
}
/**
* Test execute method for a user without permission to edit reports
*/
public function test_execute_access_exception(): void {
$this->resetAfterTest();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(report_access_exception::class);
$this->expectExceptionMessage('You can not edit this report');
delete::execute($report->get('id'));
}
}
@@ -0,0 +1,88 @@
<?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);
use core_reportbuilder\local\models\report;
/**
* Behat data generator for Report builder
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_core_reportbuilder_generator extends behat_generator_base {
/**
* Get a list of the entities that can be created for this component
*
* @return array[]
*/
protected function get_creatable_entities(): array {
return [
'Reports' => [
'datagenerator' => 'report',
'required' => [
'name',
'source',
],
],
'Columns' => [
'datagenerator' => 'column',
'required' => [
'report',
'uniqueidentifier',
],
'switchids' => [
'report' => 'reportid',
],
],
'Conditions' => [
'datagenerator' => 'condition',
'required' => [
'report',
'uniqueidentifier',
],
'switchids' => [
'report' => 'reportid',
],
],
'Filters' => [
'datagenerator' => 'filter',
'required' => [
'report',
'uniqueidentifier',
],
'switchids' => [
'report' => 'reportid',
],
],
];
}
/**
* Look up report ID from given name
*
* @param string $name
* @return int
*/
protected function get_report_id(string $name): int {
global $DB;
return (int) $DB->get_field(report::TABLE, 'id', ['name' => $name], MUST_EXIST);
}
}
+115
View File
@@ -0,0 +1,115 @@
<?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);
use core_reportbuilder\local\helpers\report as helper;
use core_reportbuilder\local\models\column;
use core_reportbuilder\local\models\filter;
use core_reportbuilder\local\models\report;
/**
* Report builder test generator
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_reportbuilder_generator extends component_generator_base {
/**
* Create report
*
* @param array|stdClass $record
* @return report
* @throws coding_exception
*/
public function create_report($record): report {
$record = (array) $record;
if (!array_key_exists('name', $record)) {
throw new coding_exception('Record must contain \'name\' property');
}
if (!array_key_exists('source', $record)) {
throw new coding_exception('Record must contain \'source\' property');
}
// Include default setup unless specifically disabled in passed record.
$default = (bool) ($record['default'] ?? true);
return helper::create_report((object) $record, $default);
}
/**
* Create report column
*
* @param array|stdClass $record
* @return column
* @throws coding_exception
*/
public function create_column($record): column {
$record = (array) $record;
if (!array_key_exists('reportid', $record)) {
throw new coding_exception('Record must contain \'reportid\' property');
}
if (!array_key_exists('uniqueidentifier', $record)) {
throw new coding_exception('Record must contain \'uniqueidentifier\' property');
}
return helper::add_report_column($record['reportid'], $record['uniqueidentifier']);
}
/**
* Create report filter
*
* @param array|stdClass $record
* @return filter
* @throws coding_exception
*/
public function create_filter($record): filter {
$record = (array) $record;
if (!array_key_exists('reportid', $record)) {
throw new coding_exception('Record must contain \'reportid\' property');
}
if (!array_key_exists('uniqueidentifier', $record)) {
throw new coding_exception('Record must contain \'uniqueidentifier\' property');
}
return helper::add_report_filter($record['reportid'], $record['uniqueidentifier']);
}
/**
* Create report condition
*
* @param array|stdClass $record
* @return filter
* @throws coding_exception
*/
public function create_condition($record): filter {
$record = (array) $record;
if (!array_key_exists('reportid', $record)) {
throw new coding_exception('Record must contain \'reportid\' property');
}
if (!array_key_exists('uniqueidentifier', $record)) {
throw new coding_exception('Record must contain \'uniqueidentifier\' property');
}
return helper::add_report_condition($record['reportid'], $record['uniqueidentifier']);
}
}
@@ -58,5 +58,17 @@ class users extends datasource {
// Add all columns from entities to be available in custom reports.
$this->add_entity($userentity);
$userentityname = $userentity->get_entity_name();
$this->add_columns_from_entity($userentityname);
}
/**
* Return the columns that will be added to the report once is created
*
* @return string[]
*/
public function get_default_columns(): array {
return ['user:fullname', 'user:username', 'user:email'];
}
}