diff --git a/lib/table/amd/build/dynamic.min.js b/lib/table/amd/build/dynamic.min.js
index d00b78f31dc..2a30ca5dabc 100644
--- a/lib/table/amd/build/dynamic.min.js
+++ b/lib/table/amd/build/dynamic.min.js
@@ -1,2 +1,2 @@
-define ("core_table/dynamic",["exports","core_table/local/dynamic/repository","core_table/local/dynamic/selectors"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=a.setSortOrder=a.setFilters=a.updateTable=a.refreshTableContent=void 0;c=function(a){if(a&&a.__esModule){return a}else{var b={};if(null!=a){for(var c in a){if(Object.prototype.hasOwnProperty.call(a,c)){var d=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(a,c):{};if(d.get||d.set){Object.defineProperty(b,c,d)}else{b[c]=a[c]}}}}b.default=a;return b}}(c);function d(a){return g(a)||f(a)||e()}function e(){throw new TypeError("Invalid attempt to spread non-iterable instance")}function f(a){if(Symbol.iterator in Object(a)||"[object Arguments]"===Object.prototype.toString.call(a))return Array.from(a)}function g(a){if(Array.isArray(a)){for(var b=0,c=Array(a.length);b.\n\n/**\n * Module to handle dynamic table features.\n *\n * @module core_table/dynamic\n * @package core_table\n * @copyright 2020 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {fetch as fetchTableData} from 'core_table/local/dynamic/repository';\nimport * as Selectors from 'core_table/local/dynamic/selectors';\n\nlet watching = false;\n\n/**\n * Ensure that a table is a dynamic table.\n *\n * @param {HTMLElement} tableRoot\n * @returns {Bool}\n */\nconst checkTableIsDynamic = tableRoot => {\n if (!tableRoot) {\n // The table is not a dynamic table.\n throw new Error(\"The table specified is not a dynamic table and cannot be updated\");\n }\n\n if (!tableRoot.matches(Selectors.table.region)) {\n // The table is not a dynamic table.\n throw new Error(\"The table specified is not a dynamic table and cannot be updated\");\n }\n\n return true;\n};\n\n/**\n * Get the filterset data from a known dynamic table.\n *\n * @param {HTMLElement} tableRoot\n * @returns {Object}\n */\nconst getFiltersetFromTable = tableRoot => {\n return JSON.parse(tableRoot.dataset.tableFilters);\n};\n\n/**\n * Update the specified table based on its current values.\n *\n * @param {HTMLElement} tableRoot\n * @returns {Promise}\n */\nexport const refreshTableContent = tableRoot => {\n const filterset = getFiltersetFromTable(tableRoot);\n\n return fetchTableData(\n tableRoot.dataset.tableHandler,\n tableRoot.dataset.tableUniqueid,\n {\n sortBy: tableRoot.dataset.tableSortBy,\n sortOrder: tableRoot.dataset.tableSortOrder,\n joinType: filterset.jointype,\n filters: filterset.filters,\n }\n )\n .then(data => {\n const placeholder = document.createElement('div');\n placeholder.innerHTML = data.html;\n tableRoot.replaceWith(...placeholder.childNodes);\n\n return data;\n });\n};\n\nexport const updateTable = (tableRoot, {\n sortBy = null,\n sortOrder = null,\n filters = null,\n} = {}, refreshContent = true) => {\n checkTableIsDynamic(tableRoot);\n\n // Update sort fields.\n if (sortBy && sortOrder) {\n tableRoot.dataset.tableSortBy = sortBy;\n tableRoot.dataset.tableSortOrder = sortOrder;\n }\n\n // Update filters.\n if (filters) {\n tableRoot.dataset.tableFilters = JSON.stringify(filters);\n }\n\n // Refresh.\n if (refreshContent) {\n return refreshTableContent(tableRoot);\n } else {\n return Promise.resolve();\n }\n};\n\n/**\n * Update the specified table using the new filters.\n *\n * @param {HTMLElement} tableRoot\n * @param {Object} filters\n * @param {Bool} refreshContent\n * @returns {Promise}\n */\nexport const setFilters = (tableRoot, filters, refreshContent = true) =>\n updateTable(tableRoot, {filters}, refreshContent);\n\n/**\n * Update the sort order.\n *\n * @param {HTMLElement} tableRoot\n * @param {String} sortBy\n * @param {Number} sortOrder\n * @param {Bool} refreshContent\n * @returns {Promise}\n */\nexport const setSortOrder = (tableRoot, sortBy, sortOrder, refreshContent = true) =>\n updateTable(tableRoot, {sortBy, sortOrder}, refreshContent);\n\n/**\n * Set up listeners to handle table updates.\n */\nexport const init = () => {\n if (watching) {\n // Already watching.\n return;\n }\n watching = true;\n\n document.addEventListener('click', e => {\n const tableRoot = e.target.closest(Selectors.table.region);\n\n if (!tableRoot) {\n return;\n }\n\n const sortableLink = e.target.closest(Selectors.table.links.sortableColumn);\n if (sortableLink) {\n e.preventDefault();\n\n setSortOrder(tableRoot, sortableLink.dataset.sortby, sortableLink.dataset.sortorder);\n }\n });\n};\n"],"file":"dynamic.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/dynamic.js"],"names":["watching","checkTableIsDynamic","tableRoot","Error","matches","Selectors","main","region","getFiltersetFromTable","JSON","parse","dataset","tableFilters","refreshTableContent","filterset","tableHandler","tableUniqueid","sortBy","tableSortBy","sortOrder","tableSortOrder","joinType","jointype","filters","firstinitial","tableFirstInitial","lastinitial","tableLastInitial","then","data","placeholder","document","createElement","innerHTML","html","replaceWith","childNodes","updateTable","firstInitial","lastInitial","refreshContent","stringify","Promise","resolve","setFilters","setSortOrder","setFirstInitial","setLastInitial","init","addEventListener","e","target","closest","sortableLink","table","links","sortableColumn","preventDefault","sortby","sortorder","firstInitialLink","initialsBar","initial","lastInitialLink"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module to handle dynamic table features.\n *\n * @module core_table/dynamic\n * @package core_table\n * @copyright 2020 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {fetch as fetchTableData} from 'core_table/local/dynamic/repository';\nimport * as Selectors from 'core_table/local/dynamic/selectors';\n\nlet watching = false;\n\n/**\n * Ensure that a table is a dynamic table.\n *\n * @param {HTMLElement} tableRoot\n * @returns {Bool}\n */\nconst checkTableIsDynamic = tableRoot => {\n if (!tableRoot) {\n // The table is not a dynamic table.\n throw new Error(\"The table specified is not a dynamic table and cannot be updated\");\n }\n\n if (!tableRoot.matches(Selectors.main.region)) {\n // The table is not a dynamic table.\n throw new Error(\"The table specified is not a dynamic table and cannot be updated\");\n }\n\n return true;\n};\n\n/**\n * Get the filterset data from a known dynamic table.\n *\n * @param {HTMLElement} tableRoot\n * @returns {Object}\n */\nconst getFiltersetFromTable = tableRoot => {\n return JSON.parse(tableRoot.dataset.tableFilters);\n};\n\n/**\n * Update the specified table based on its current values.\n *\n * @param {HTMLElement} tableRoot\n * @returns {Promise}\n */\nexport const refreshTableContent = tableRoot => {\n const filterset = getFiltersetFromTable(tableRoot);\n\n return fetchTableData(\n tableRoot.dataset.tableHandler,\n tableRoot.dataset.tableUniqueid,\n {\n sortBy: tableRoot.dataset.tableSortBy,\n sortOrder: tableRoot.dataset.tableSortOrder,\n joinType: filterset.jointype,\n filters: filterset.filters,\n firstinitial: tableRoot.dataset.tableFirstInitial,\n lastinitial: tableRoot.dataset.tableLastInitial,\n }\n )\n .then(data => {\n const placeholder = document.createElement('div');\n placeholder.innerHTML = data.html;\n tableRoot.replaceWith(...placeholder.childNodes);\n\n return data;\n });\n};\n\nexport const updateTable = (tableRoot, {\n sortBy = null,\n sortOrder = null,\n filters = null,\n firstInitial = null,\n lastInitial = null,\n} = {}, refreshContent = true) => {\n checkTableIsDynamic(tableRoot);\n\n // Update sort fields.\n if (sortBy && sortOrder) {\n tableRoot.dataset.tableSortBy = sortBy;\n tableRoot.dataset.tableSortOrder = sortOrder;\n }\n\n // Update initials.\n if (firstInitial !== null) {\n tableRoot.dataset.tableFirstInitial = firstInitial;\n }\n\n if (lastInitial !== null) {\n tableRoot.dataset.tableLastInitial = lastInitial;\n }\n\n // Update filters.\n if (filters) {\n tableRoot.dataset.tableFilters = JSON.stringify(filters);\n }\n\n // Refresh.\n if (refreshContent) {\n return refreshTableContent(tableRoot);\n } else {\n return Promise.resolve();\n }\n};\n\n/**\n * Update the specified table using the new filters.\n *\n * @param {HTMLElement} tableRoot\n * @param {Object} filters\n * @param {Bool} refreshContent\n * @returns {Promise}\n */\nexport const setFilters = (tableRoot, filters, refreshContent = true) =>\n updateTable(tableRoot, {filters}, refreshContent);\n\n/**\n * Update the sort order.\n *\n * @param {HTMLElement} tableRoot\n * @param {String} sortBy\n * @param {Number} sortOrder\n * @param {Bool} refreshContent\n * @returns {Promise}\n */\nexport const setSortOrder = (tableRoot, sortBy, sortOrder, refreshContent = true) =>\n updateTable(tableRoot, {sortBy, sortOrder}, refreshContent);\n\n/**\n * Update the first initial to show.\n *\n * @param {HTMLElement} tableRoot\n * @param {String} firstInitial\n * @param {Bool} refreshContent\n * @returns {Promise}\n */\nexport const setFirstInitial = (tableRoot, firstInitial, refreshContent = true) =>\n updateTable(tableRoot, {firstInitial}, refreshContent);\n\n/**\n * Update the last initial to show.\n *\n * @param {HTMLElement} tableRoot\n * @param {String} lastInitial\n * @param {Bool} refreshContent\n * @returns {Promise}\n */\nexport const setLastInitial = (tableRoot, lastInitial, refreshContent = true) =>\n updateTable(tableRoot, {lastInitial}, refreshContent);\n\n/**\n * Set up listeners to handle table updates.\n */\nexport const init = () => {\n if (watching) {\n // Already watching.\n return;\n }\n watching = true;\n\n document.addEventListener('click', e => {\n const tableRoot = e.target.closest(Selectors.main.region);\n\n if (!tableRoot) {\n return;\n }\n\n const sortableLink = e.target.closest(Selectors.table.links.sortableColumn);\n if (sortableLink) {\n e.preventDefault();\n\n setSortOrder(tableRoot, sortableLink.dataset.sortby, sortableLink.dataset.sortorder);\n }\n\n const firstInitialLink = e.target.closest(Selectors.initialsBar.links.firstInitial);\n if (firstInitialLink !== null) {\n e.preventDefault();\n\n setFirstInitial(tableRoot, firstInitialLink.dataset.initial);\n }\n\n const lastInitialLink = e.target.closest(Selectors.initialsBar.links.lastInitial);\n if (lastInitialLink !== null) {\n e.preventDefault();\n\n setLastInitial(tableRoot, lastInitialLink.dataset.initial);\n }\n });\n};\n"],"file":"dynamic.min.js"}
\ No newline at end of file
diff --git a/lib/table/amd/build/local/dynamic/repository.min.js b/lib/table/amd/build/local/dynamic/repository.min.js
index d07cd466158..849507d094a 100644
--- a/lib/table/amd/build/local/dynamic/repository.min.js
+++ b/lib/table/amd/build/local/dynamic/repository.min.js
@@ -1,2 +1,2 @@
-define ("core_table/local/dynamic/repository",["exports","core/ajax"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.fetch=void 0;a.fetch=function fetch(a,c){var d=2.\n\n/**\n * A javascript module to handle calendar ajax actions.\n *\n * @module core_calendar/repository\n * @class repository\n * @package core_calendar\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {call as fetchMany} from 'core/ajax';\n\n/**\n * Fetch table view.\n *\n * @method fetch\n * @param {String} handler The name of the handler\n * @param {String} uniqueid The unique id of the table\n * @param {Number} params parameters to request table\n * @return {Promise} Resolved with requested table view\n */\nexport const fetch = (handler, uniqueid, {\n sortBy = null,\n sortOrder = null,\n joinType = null,\n filters = {}\n } = {}\n) => {\n return fetchMany([{\n methodname: `core_table_dynamic_fetch`,\n args: {\n handler,\n uniqueid,\n sortby: sortBy,\n sortorder: sortOrder,\n jointype: joinType,\n filters,\n },\n }])[0];\n};\n"],"file":"repository.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../../../src/local/dynamic/repository.js"],"names":["fetch","handler","uniqueid","sortBy","sortOrder","joinType","filters","firstinitial","lastinitial","methodname","args","sortby","sortorder","jointype"],"mappings":"yKAsCqB,QAARA,CAAAA,KAAQ,CAACC,CAAD,CAAUC,CAAV,CAQhB,8DADG,EACH,KAPGC,MAOH,CAPGA,CAOH,YAPY,IAOZ,OANGC,SAMH,CANGA,CAMH,YANe,IAMf,OALGC,QAKH,CALGA,CAKH,YALc,IAKd,OAJGC,OAIH,CAJGA,CAIH,YAJa,EAIb,OAHGC,YAGH,CAHGA,CAGH,YAHkB,IAGlB,OAFGC,WAEH,CAFGA,CAEH,YAFiB,IAEjB,GACD,MAAO,WAAU,CAAC,CACdC,UAAU,2BADI,CAEdC,IAAI,CAAE,CACFT,OAAO,CAAPA,CADE,CAEFC,QAAQ,CAARA,CAFE,CAGFS,MAAM,CAAER,CAHN,CAIFS,SAAS,CAAER,CAJT,CAKFS,QAAQ,CAAER,CALR,CAMFC,OAAO,CAAPA,CANE,CAOFC,YAAY,CAAZA,CAPE,CAQFC,WAAW,CAAXA,CARE,CAFQ,CAAD,CAAV,EAYH,CAZG,CAaV,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A javascript module to handle calendar ajax actions.\n *\n * @module core_calendar/repository\n * @class repository\n * @package core_calendar\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {call as fetchMany} from 'core/ajax';\n\n/**\n * Fetch table view.\n *\n * @method fetch\n * @param {String} handler The name of the handler\n * @param {String} uniqueid The unique id of the table\n * @param {Object} filters The filters to apply when searching\n * @param {String} firstinitial The first name initial to filter on\n * @param {String} lastinitial The last name initial to filter on\n * @param {Number} params parameters to request table\n * @return {Promise} Resolved with requested table view\n */\nexport const fetch = (handler, uniqueid, {\n sortBy = null,\n sortOrder = null,\n joinType = null,\n filters = {},\n firstinitial = null,\n lastinitial = null,\n } = {}\n) => {\n return fetchMany([{\n methodname: `core_table_dynamic_fetch`,\n args: {\n handler,\n uniqueid,\n sortby: sortBy,\n sortorder: sortOrder,\n jointype: joinType,\n filters,\n firstinitial,\n lastinitial,\n },\n }])[0];\n};\n"],"file":"repository.min.js"}
\ No newline at end of file
diff --git a/lib/table/amd/build/local/dynamic/selectors.min.js b/lib/table/amd/build/local/dynamic/selectors.min.js
index e45e5ff4dcd..7e047e0e0a8 100644
--- a/lib/table/amd/build/local/dynamic/selectors.min.js
+++ b/lib/table/amd/build/local/dynamic/selectors.min.js
@@ -1,2 +1,2 @@
-define ("core_table/local/dynamic/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;a.default={table:{region:"[data-region=\"core_table/dynamic\"]",links:{sortableColumn:"a[data-sortable=\"1\"]"}}};return a.default});
+define ("core_table/local/dynamic/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;a.default={main:{region:"[data-region=\"core_table/dynamic\"]"},table:{links:{sortableColumn:"a[data-sortable=\"1\"]"}},initialsBar:{links:{firstInitial:".firstinitial [data-initial]",lastInitial:".lastinitial [data-initial]"}}};return a.default});
//# sourceMappingURL=selectors.min.js.map
diff --git a/lib/table/amd/build/local/dynamic/selectors.min.js.map b/lib/table/amd/build/local/dynamic/selectors.min.js.map
index 6af6cf153bd..525cf305c74 100644
--- a/lib/table/amd/build/local/dynamic/selectors.min.js.map
+++ b/lib/table/amd/build/local/dynamic/selectors.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../../../src/local/dynamic/selectors.js"],"names":["table","region","links","sortableColumn"],"mappings":"8JAuBe,CACXA,KAAK,CAAE,CACHC,MAAM,CAAE,sCADL,CAEHC,KAAK,CAAE,CACHC,cAAc,CAAE,wBADb,CAFJ,CADI,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Dynamic table selectors.\n *\n * @module core_table/selectors\n * @package core_table\n * @copyright 2020 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default {\n table: {\n region: '[data-region=\"core_table/dynamic\"]',\n links: {\n sortableColumn: 'a[data-sortable=\"1\"]',\n },\n },\n};\n"],"file":"selectors.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../../../src/local/dynamic/selectors.js"],"names":["main","region","table","links","sortableColumn","initialsBar","firstInitial","lastInitial"],"mappings":"8JAuBe,CACXA,IAAI,CAAE,CACFC,MAAM,CAAE,sCADN,CADK,CAIXC,KAAK,CAAE,CACHC,KAAK,CAAE,CACHC,cAAc,CAAE,wBADb,CADJ,CAJI,CASXC,WAAW,CAAE,CACTF,KAAK,CAAE,CACHG,YAAY,CAAE,8BADX,CAEHC,WAAW,CAAE,6BAFV,CADE,CATF,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Dynamic table selectors.\n *\n * @module core_table/selectors\n * @package core_table\n * @copyright 2020 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default {\n main: {\n region: '[data-region=\"core_table/dynamic\"]',\n },\n table: {\n links: {\n sortableColumn: 'a[data-sortable=\"1\"]',\n },\n },\n initialsBar: {\n links: {\n firstInitial: '.firstinitial [data-initial]',\n lastInitial: '.lastinitial [data-initial]',\n },\n },\n};\n"],"file":"selectors.min.js"}
\ No newline at end of file
diff --git a/lib/table/amd/src/dynamic.js b/lib/table/amd/src/dynamic.js
index d3b9d06b888..4cddc4d0bd5 100644
--- a/lib/table/amd/src/dynamic.js
+++ b/lib/table/amd/src/dynamic.js
@@ -38,7 +38,7 @@ const checkTableIsDynamic = tableRoot => {
throw new Error("The table specified is not a dynamic table and cannot be updated");
}
- if (!tableRoot.matches(Selectors.table.region)) {
+ if (!tableRoot.matches(Selectors.main.region)) {
// The table is not a dynamic table.
throw new Error("The table specified is not a dynamic table and cannot be updated");
}
@@ -73,6 +73,8 @@ export const refreshTableContent = tableRoot => {
sortOrder: tableRoot.dataset.tableSortOrder,
joinType: filterset.jointype,
filters: filterset.filters,
+ firstinitial: tableRoot.dataset.tableFirstInitial,
+ lastinitial: tableRoot.dataset.tableLastInitial,
}
)
.then(data => {
@@ -88,6 +90,8 @@ export const updateTable = (tableRoot, {
sortBy = null,
sortOrder = null,
filters = null,
+ firstInitial = null,
+ lastInitial = null,
} = {}, refreshContent = true) => {
checkTableIsDynamic(tableRoot);
@@ -97,6 +101,15 @@ export const updateTable = (tableRoot, {
tableRoot.dataset.tableSortOrder = sortOrder;
}
+ // Update initials.
+ if (firstInitial !== null) {
+ tableRoot.dataset.tableFirstInitial = firstInitial;
+ }
+
+ if (lastInitial !== null) {
+ tableRoot.dataset.tableLastInitial = lastInitial;
+ }
+
// Update filters.
if (filters) {
tableRoot.dataset.tableFilters = JSON.stringify(filters);
@@ -133,6 +146,28 @@ export const setFilters = (tableRoot, filters, refreshContent = true) =>
export const setSortOrder = (tableRoot, sortBy, sortOrder, refreshContent = true) =>
updateTable(tableRoot, {sortBy, sortOrder}, refreshContent);
+/**
+ * Update the first initial to show.
+ *
+ * @param {HTMLElement} tableRoot
+ * @param {String} firstInitial
+ * @param {Bool} refreshContent
+ * @returns {Promise}
+ */
+export const setFirstInitial = (tableRoot, firstInitial, refreshContent = true) =>
+ updateTable(tableRoot, {firstInitial}, refreshContent);
+
+/**
+ * Update the last initial to show.
+ *
+ * @param {HTMLElement} tableRoot
+ * @param {String} lastInitial
+ * @param {Bool} refreshContent
+ * @returns {Promise}
+ */
+export const setLastInitial = (tableRoot, lastInitial, refreshContent = true) =>
+ updateTable(tableRoot, {lastInitial}, refreshContent);
+
/**
* Set up listeners to handle table updates.
*/
@@ -144,7 +179,7 @@ export const init = () => {
watching = true;
document.addEventListener('click', e => {
- const tableRoot = e.target.closest(Selectors.table.region);
+ const tableRoot = e.target.closest(Selectors.main.region);
if (!tableRoot) {
return;
@@ -156,5 +191,19 @@ export const init = () => {
setSortOrder(tableRoot, sortableLink.dataset.sortby, sortableLink.dataset.sortorder);
}
+
+ const firstInitialLink = e.target.closest(Selectors.initialsBar.links.firstInitial);
+ if (firstInitialLink !== null) {
+ e.preventDefault();
+
+ setFirstInitial(tableRoot, firstInitialLink.dataset.initial);
+ }
+
+ const lastInitialLink = e.target.closest(Selectors.initialsBar.links.lastInitial);
+ if (lastInitialLink !== null) {
+ e.preventDefault();
+
+ setLastInitial(tableRoot, lastInitialLink.dataset.initial);
+ }
});
};
diff --git a/lib/table/amd/src/local/dynamic/repository.js b/lib/table/amd/src/local/dynamic/repository.js
index 4fe7219bb90..67437751f45 100644
--- a/lib/table/amd/src/local/dynamic/repository.js
+++ b/lib/table/amd/src/local/dynamic/repository.js
@@ -30,6 +30,9 @@ import {call as fetchMany} from 'core/ajax';
* @method fetch
* @param {String} handler The name of the handler
* @param {String} uniqueid The unique id of the table
+ * @param {Object} filters The filters to apply when searching
+ * @param {String} firstinitial The first name initial to filter on
+ * @param {String} lastinitial The last name initial to filter on
* @param {Number} params parameters to request table
* @return {Promise} Resolved with requested table view
*/
@@ -37,7 +40,9 @@ export const fetch = (handler, uniqueid, {
sortBy = null,
sortOrder = null,
joinType = null,
- filters = {}
+ filters = {},
+ firstinitial = null,
+ lastinitial = null,
} = {}
) => {
return fetchMany([{
@@ -49,6 +54,8 @@ export const fetch = (handler, uniqueid, {
sortorder: sortOrder,
jointype: joinType,
filters,
+ firstinitial,
+ lastinitial,
},
}])[0];
};
diff --git a/lib/table/amd/src/local/dynamic/selectors.js b/lib/table/amd/src/local/dynamic/selectors.js
index 68a0a3ba93b..6803571d4e5 100644
--- a/lib/table/amd/src/local/dynamic/selectors.js
+++ b/lib/table/amd/src/local/dynamic/selectors.js
@@ -22,10 +22,18 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export default {
- table: {
+ main: {
region: '[data-region="core_table/dynamic"]',
+ },
+ table: {
links: {
sortableColumn: 'a[data-sortable="1"]',
},
},
+ initialsBar: {
+ links: {
+ firstInitial: '.firstinitial [data-initial]',
+ lastInitial: '.lastinitial [data-initial]',
+ },
+ },
};
diff --git a/lib/table/classes/external/dynamic/fetch.php b/lib/table/classes/external/dynamic/fetch.php
index 8cef49c056d..b029ff56554 100644
--- a/lib/table/classes/external/dynamic/fetch.php
+++ b/lib/table/classes/external/dynamic/fetch.php
@@ -86,6 +86,18 @@ class fetch extends external_api {
VALUE_OPTIONAL
),
'jointype' => new external_value(PARAM_INT, 'Type of join to join all filters together', VALUE_REQUIRED),
+ 'firstinitial' => new external_value(
+ PARAM_ALPHANUMEXT,
+ 'The first initial to sort filter on',
+ VALUE_REQUIRED,
+ null
+ ),
+ 'lastinitial' => new external_value(
+ PARAM_ALPHANUMEXT,
+ 'The last initial to sort filter on',
+ VALUE_REQUIRED,
+ null
+ ),
]);
}
@@ -98,11 +110,21 @@ class fetch extends external_api {
* @param string $sortorder The sort order.
* @param array $filters The filters that will be applied in the request.
* @param string $jointype The join type.
+ * @param string $firstinitial The first name initial to filter on
+ * @param string $lastinitial The last name initial to filter on
*
* @return array
*/
- public static function execute(string $handler, string $uniqueid, string $sortby, string $sortorder,
- array $filters = [], string $jointype = null) {
+ public static function execute(
+ string $handler,
+ string $uniqueid,
+ string $sortby,
+ string $sortorder,
+ ?array $filters = null,
+ ?string $jointype = null,
+ ?string $firstinitial = null,
+ ?string $lastinitial = null
+ ) {
global $PAGE;
@@ -117,6 +139,8 @@ class fetch extends external_api {
'sortorder' => $sortorder,
'filters' => $filters,
'jointype' => $jointype,
+ 'firstinitial' => $firstinitial,
+ 'lastinitial' => $lastinitial,
] = self::validate_parameters(self::execute_parameters(), [
'handler' => $handler,
'uniqueid' => $uniqueid,
@@ -124,6 +148,8 @@ class fetch extends external_api {
'sortorder' => $sortorder,
'filters' => $filters,
'jointype' => $jointype,
+ 'firstinitial' => $firstinitial,
+ 'lastinitial' => $lastinitial,
]);
$filterset = new \core_user\table\participants_filterset();
@@ -139,6 +165,14 @@ class fetch extends external_api {
$instance->set_filterset($filterset);
$instance->set_sorting($sortby, $sortorder);
+ if ($firstinitial !== null) {
+ $instance->set_first_initial($firstinitial);
+ }
+
+ if ($lastinitial !== null) {
+ $instance->set_last_initial($lastinitial);
+ }
+
$context = $instance->get_context();
self::validate_context($context);
diff --git a/lib/tablelib.php b/lib/tablelib.php
index 4e0c648bf1a..a3c9298d0a3 100644
--- a/lib/tablelib.php
+++ b/lib/tablelib.php
@@ -95,6 +95,12 @@ class flexible_table {
*/
protected $sortorder;
+ /** @var string The manually set first name initial preference */
+ protected $ifirst;
+
+ /** @var string The manually set last name initial preference */
+ protected $ilast;
+
var $use_pages = false;
var $use_initials = false;
@@ -525,16 +531,7 @@ class flexible_table {
}
$this->set_sorting_preferences();
-
- $ilast = optional_param($this->request[TABLE_VAR_ILAST], null, PARAM_RAW);
- if (!is_null($ilast) && ($ilast ==='' || strpos(get_string('alphabet', 'langconfig'), $ilast) !== false)) {
- $this->prefs['i_last'] = $ilast;
- }
-
- $ifirst = optional_param($this->request[TABLE_VAR_IFIRST], null, PARAM_RAW);
- if (!is_null($ifirst) && ($ifirst === '' || strpos(get_string('alphabet', 'langconfig'), $ifirst) !== false)) {
- $this->prefs['i_first'] = $ifirst;
- }
+ $this->set_initials_preferences();
// Save user preferences if they have changed.
if ($this->prefs != $oldprefs) {
@@ -1355,6 +1352,31 @@ class flexible_table {
}
}
+ /**
+ * Fill in the preferences for the initials bar.
+ */
+ protected function set_initials_preferences(): void {
+ $ifirst = $this->ifirst;
+ $ilast = $this->ilast;
+
+ if ($ifirst === null) {
+ $ifirst = optional_param($this->request[TABLE_VAR_IFIRST], null, PARAM_RAW);
+ }
+
+ if ($ilast === null) {
+ $ilast = optional_param($this->request[TABLE_VAR_ILAST], null, PARAM_RAW);
+ }
+
+ if (!is_null($ifirst) && ($ifirst === '' || strpos(get_string('alphabet', 'langconfig'), $ifirst) !== false)) {
+ $this->prefs['i_first'] = $ifirst;
+ }
+
+ if (!is_null($ilast) && ($ilast === '' || strpos(get_string('alphabet', 'langconfig'), $ilast) !== false)) {
+ $this->prefs['i_last'] = $ilast;
+ }
+
+ }
+
/**
* Set the preferred table sorting attributes.
*
@@ -1366,6 +1388,24 @@ class flexible_table {
$this->sortorder = $sortorder;
}
+ /**
+ * Set the preferred first name initial in an initials bar.
+ *
+ * @param string $initial The character to set
+ */
+ public function set_first_initial(string $initial): void {
+ $this->ifirst = $initial;
+ }
+
+ /**
+ * Set the preferred last name initial in an initials bar.
+ *
+ * @param string $initial The character to set
+ */
+ public function set_last_initial(string $initial): void {
+ $this->ilast = $initial;
+ }
+
/**
* Generate the HTML for the sort icon. This is a helper method used by {@link sort_link()}.
* @param bool $isprimary whether an icon is needed (it is only needed for the primary sort column.)
@@ -1463,6 +1503,8 @@ class flexible_table {
'data-table-filters' => json_encode($this->get_filterset()),
'data-table-sort-by' => $sortdata['sortby'],
'data-table-sort-order' => $sortdata['sortorder'],
+ 'data-table-first-initial' => $this->prefs['i_first'],
+ 'data-table-last-initial' => $this->prefs['i_last'],
]);
}
diff --git a/lib/templates/initials_bar.mustache b/lib/templates/initials_bar.mustache
index 7f02e93d3ee..ab87624b664 100644
--- a/lib/templates/initials_bar.mustache
+++ b/lib/templates/initials_bar.mustache
@@ -80,12 +80,12 @@
@@ -93,10 +93,10 @@
diff --git a/lib/tests/tablelib_test.php b/lib/tests/tablelib_test.php
index 25bf48ae22d..0e41c873c61 100644
--- a/lib/tests/tablelib_test.php
+++ b/lib/tests/tablelib_test.php
@@ -701,4 +701,91 @@ class core_tablelib_testcase extends advanced_testcase {
$this->assertEquals("Col1,Col2,Col3\na,b,c\n", substr($output, 3));
}
+ /**
+ * Test the initials functionality.
+ *
+ * @dataProvider initials_provider
+ * @param string|null $getvalue
+ * @param string|null $setvalue
+ * @param string|null $finalvalue
+ */
+ public function test_initials_first_set(?string $getvalue, ?string $setvalue, ?string $finalvalue): void {
+ global $_GET;
+
+ $this->resetAfterTest(true);
+
+ $table = new flexible_table('tablelib_test');
+
+ $user = $this->getDataGenerator()->create_user();
+
+ $table->define_columns(['fullname']);
+ $table->define_headers(['Fullname']);
+ $table->define_baseurl('/invalid.php');
+ $table->initialbars(true);
+
+ if ($getvalue !== null) {
+ $_GET['tifirst'] = $getvalue;
+ }
+
+ if ($setvalue !== null) {
+ $table->set_first_initial($setvalue);
+ }
+
+ $table->setup();
+
+ $this->assertEquals($finalvalue, $table->get_initial_first());
+ }
+
+ /**
+ * Test the initials functionality.
+ *
+ * @dataProvider initials_provider
+ * @param string|null $getvalue
+ * @param string|null $setvalue
+ * @param string|null $finalvalue
+ */
+ public function test_initials_last_set(?string $getvalue, ?string $setvalue, ?string $finalvalue): void {
+ global $_GET;
+
+ $this->resetAfterTest(true);
+
+ $table = new flexible_table('tablelib_test');
+
+ $user = $this->getDataGenerator()->create_user();
+
+ $table->define_columns(['fullname']);
+ $table->define_headers(['Fullname']);
+ $table->define_baseurl('/invalid.php');
+ $table->initialbars(true);
+
+ if ($getvalue !== null) {
+ $_GET['tilast'] = $getvalue;
+ }
+
+ if ($setvalue !== null) {
+ $table->set_last_initial($setvalue);
+ }
+
+ $table->setup();
+
+ $this->assertEquals($finalvalue, $table->get_initial_last());
+ }
+
+ /**
+ * Data for testing initials providers.
+ *
+ * @return array
+ */
+ public function initials_provider(): array {
+ return [
+ [null, null, null],
+ ['A', null, 'A'],
+ ['Z', null, 'Z'],
+ [null, 'A', 'A'],
+ [null, 'Z', 'Z'],
+ ['A', 'Z', 'Z'],
+ ['Z', 'A', 'A'],
+ ];
+ }
+
}