mirror of
https://github.com/moodle/moodle.git
synced 2025-04-22 08:55:15 +02:00
MDL-72509 core: De-couple participants filter from core_user to core
This implementation will de-couple the participant filter from core user to core so its easily usable to any api in core or any community plugin. This removes the dependency from the core_user and creates a nice api where it can produce filterable objects. Co-Authored-By: Safat Shahin <safatshahin@catalyst-au.net> Co-Authored-By: Tomo Tsuyuki <tomotsuyuki@catalyst-au.net> AMOS BEGIN MOV [addcondition,core_user], [addcondition,core] MOV [adverbfor_and,core_user], [operator_and,core] MOV [adverbfor_andnot,core_user], [operator_andnot,core] MOV [adverbfor_or,core_user], [operator_or,core] MOV [applyfilters,core_user], [applyfilters,core] MOV [clearfilterrow,core_user], [clearfilterrow,core] MOV [clearfilters,core_user], [clearfilters,core] MOV [filtersetmatchdescription,core_user], [filtersetmatchdescription,core] MOV [filterrowlegend,core_user], [filterrowlegend,core] MOV [filtertype,core_user], [filtertype,core] MOV [match,core_user], [match,core] MOV [matchofthefollowing,core_user], [matchofthefollowing,core] MOV [placeholdertypeorselect,core_user], [placeholdertypeorselect,core] MOV [selectfiltertype,core_user], [selectfiltertype,core] AMOS END
This commit is contained in:
parent
8f492a836a
commit
68a5034b67
@ -57,3 +57,17 @@ notflagged,core_question
|
||||
cannotswitcheditmodeon,core_error
|
||||
multilangforceold,core_admin
|
||||
nopermissionmove,core_question
|
||||
addcondition,core_user
|
||||
adverbfor_and,core_user
|
||||
adverbfor_andnot,core_user
|
||||
adverbfor_or,core_user
|
||||
applyfilters,core_user
|
||||
clearfilterrow,core_user
|
||||
clearfilters,core_user
|
||||
filtersetmatchdescription,core_user
|
||||
filterrowlegend,core_user
|
||||
filtertype,core_user
|
||||
match,core_user
|
||||
matchofthefollowing,core_user
|
||||
placeholdertypeorselect,core_user
|
||||
selectfiltertype,core_user
|
||||
|
@ -52,6 +52,7 @@ $string['addactivitytosection'] = 'Add an activity to section \'{$a}\'';
|
||||
$string['addadmin'] = 'Add admin';
|
||||
$string['addblock'] = 'Add a block';
|
||||
$string['addcomment'] = 'Add a comment...';
|
||||
$string['addcondition'] = 'Add condition';
|
||||
$string['addcountertousername'] = 'Create user by adding number to username';
|
||||
$string['addcreator'] = 'Add course creator';
|
||||
$string['adddots'] = 'Add...';
|
||||
@ -153,6 +154,7 @@ $string['answer'] = 'Answer';
|
||||
$string['any'] = 'Any';
|
||||
$string['approve'] = 'Approve';
|
||||
$string['appearance'] = 'Appearance';
|
||||
$string['applyfilters'] = 'Apply filters';
|
||||
$string['areyousure'] = 'Are you sure?';
|
||||
$string['areyousuretorestorethis'] = 'Do you want to continue?';
|
||||
$string['areyousuretorestorethisinfo'] = 'Later in this process you will have a choice of adding this backup to an existing course or creating a completely new course.';
|
||||
@ -255,6 +257,8 @@ $string['changesmadereallygoaway'] = 'You have made changes. Are you sure you wa
|
||||
$string['city'] = 'City/town';
|
||||
$string['cleaningtempdata'] = 'Cleaning temp data';
|
||||
$string['clear'] = 'Clear';
|
||||
$string['clearfilterrow'] = 'Remove filter row';
|
||||
$string['clearfilters'] = 'Clear filters';
|
||||
$string['clearsearch'] = 'Clear search input';
|
||||
$string['clickhelpiconformoreinfo'] = '... continues ... Click on the help icon to read the full article';
|
||||
$string['clickhere'] = 'Click here ...';
|
||||
@ -880,6 +884,7 @@ $string['filesfolders'] = 'Files/folders';
|
||||
$string['fileuploadwithcontent'] = 'File uploads should not include the content parameter';
|
||||
$string['filloutallfields'] = 'Please fill out all fields in this form';
|
||||
$string['filter'] = 'Filter';
|
||||
$string['filtersetmatchdescription'] = 'How multiple filters should be combined';
|
||||
$string['findmorecourses'] = 'Find more courses...';
|
||||
$string['first'] = 'First';
|
||||
$string['firstaccess'] = 'First access';
|
||||
@ -1070,7 +1075,9 @@ $string['courseenddateenabled_desc'] = 'This setting determines if the course en
|
||||
$string['eventcontentviewed'] = 'Content viewed';
|
||||
$string['filter'] = 'Filter';
|
||||
$string['filteroption'] = '{$a->criteria}: {$a->value}';
|
||||
$string['filterrowlegend'] = 'Filter {$a}';
|
||||
$string['filters'] = 'Filters';
|
||||
$string['filtertype'] = 'Filter type';
|
||||
$string['icon'] = 'Icon';
|
||||
$string['idnumber'] = 'ID number';
|
||||
$string['idnumbercourse'] = 'Course ID number';
|
||||
@ -1244,7 +1251,9 @@ $string['manageroles'] = 'Roles and permissions';
|
||||
$string['markallread'] = 'Mark all as read';
|
||||
$string['markedthistopic'] = 'This topic is highlighted as the current topic';
|
||||
$string['markthistopic'] = 'Highlight this topic as the current topic';
|
||||
$string['match'] = 'Match';
|
||||
$string['matchingsearchandrole'] = 'Matching \'{$a->search}\' and {$a->role}';
|
||||
$string['matchofthefollowing'] = 'of the following:';
|
||||
$string['maxareabytesreached'] = 'The file (or the total size of several files) is larger than the space remaining in this area.';
|
||||
$string['maxsectionslimit'] = 'Cannot create new section as it would exceed the maximum number of sections allowed for this course ({$a}).';
|
||||
$string['maxfilesize'] = 'Maximum size for new files: {$a}';
|
||||
@ -1575,6 +1584,9 @@ $string['olduserdirectory'] = 'This is the OLD users directory, and is no longer
|
||||
$string['opendrawerblocks'] = 'Open block drawer';
|
||||
$string['opendrawerindex'] = 'Open course index';
|
||||
$string['opensinnewwindow'] = 'Opens in new window';
|
||||
$string['operator_and'] = 'and';
|
||||
$string['operator_andnot'] = 'and';
|
||||
$string['operator_or'] = 'or';
|
||||
$string['optional'] = 'optional';
|
||||
$string['options'] = 'options';
|
||||
$string['order'] = 'Order';
|
||||
@ -1639,6 +1651,7 @@ $string['phone2'] = 'Mobile phone';
|
||||
$string['phpinfo'] = 'PHP info';
|
||||
$string['pictureof'] = 'Picture of {$a}';
|
||||
$string['pictureofuser'] = 'User picture';
|
||||
$string['placeholdertypeorselect'] = 'Type or select...';
|
||||
$string['pleaseclose'] = 'Please close this window now.';
|
||||
$string['pleasesearchmore'] = 'Please search some more';
|
||||
$string['pleaseusesearch'] = 'Please use the search';
|
||||
@ -1923,6 +1936,7 @@ $string['selectedfile'] = 'Selected file';
|
||||
$string['selectedcategories'] = 'Selected categories';
|
||||
$string['selectednowmove'] = '{$a} files selected for moving. Now go into the destination folder and press \'Move files to here\'';
|
||||
$string['selectfiles'] = 'Select files';
|
||||
$string['selectfiltertype'] = 'Select';
|
||||
$string['selectitem'] = 'Select \'{$a}\'';
|
||||
$string['selectmoduletoviewhelp'] = 'Select an activity or resource to view its help.
|
||||
|
||||
|
@ -22,23 +22,9 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['addcondition'] = 'Add condition';
|
||||
$string['adverbfor_and'] = 'and';
|
||||
$string['adverbfor_andnot'] = 'and';
|
||||
$string['adverbfor_or'] = 'or';
|
||||
$string['applyfilters'] = 'Apply filters';
|
||||
$string['clearfilterrow'] = 'Remove filter row';
|
||||
$string['clearfilters'] = 'Clear filters';
|
||||
$string['clearfilterselection'] = 'Remove "{$a}" from filter';
|
||||
$string['countparticipantsfound'] = '{$a} participants found';
|
||||
$string['filterrowlegend'] = 'Filter {$a}';
|
||||
$string['filtersetmatchdescription'] = 'How multiple filters should be combined';
|
||||
$string['filtertype'] = 'Filter type';
|
||||
$string['match'] = 'Match';
|
||||
$string['matchofthefollowing'] = 'of the following:';
|
||||
$string['moodlenetprofile'] = 'MoodleNet profile ID';
|
||||
$string['moodlenetprofile_help'] = 'Your MoodleNet profile ID links your MoodleNet profile with this site.';
|
||||
$string['placeholdertypeorselect'] = 'Type or select...';
|
||||
$string['placeholdertype'] = 'Type...';
|
||||
$string['privacy:courserequestpath'] = 'Requested courses';
|
||||
$string['privacy:descriptionpath'] = 'Profile description';
|
||||
@ -146,7 +132,6 @@ $string['privacy:profileimagespath'] = 'Profile images';
|
||||
$string['privacy:privatefilespath'] = 'Private files';
|
||||
$string['privacy:sessionpath'] = 'Session data';
|
||||
$string['filterbykeyword'] = 'Keyword';
|
||||
$string['selectfiltertype'] = 'Select';
|
||||
$string['supportmessagesent'] = 'Your message has been sent.';
|
||||
$string['supportmessagesentforloggedoutuser'] = 'Be careful with this message. The sender was not logged in, so their identity has not been confirmed.';
|
||||
$string['supportmessagenotsent'] = "Unfortunately your message could not be sent.";
|
||||
@ -154,3 +139,20 @@ $string['supportmessagealternative'] = 'Instead you can email {$a}.';
|
||||
$string['target:upcomingactivitiesdue'] = 'Upcoming activities due';
|
||||
$string['target:upcomingactivitiesdue_help'] = 'This target generates reminders for upcoming activities due.';
|
||||
$string['target:upcomingactivitiesdueinfo'] = 'All upcoming activities due insights are listed here. These students have received these insights directly.';
|
||||
|
||||
// Deprecated since Moodle 4.1.
|
||||
$string['addcondition'] = 'Add condition';
|
||||
$string['adverbfor_and'] = 'and';
|
||||
$string['adverbfor_andnot'] = 'and';
|
||||
$string['adverbfor_or'] = 'or';
|
||||
$string['applyfilters'] = 'Apply filters';
|
||||
$string['clearfilterrow'] = 'Remove filter row';
|
||||
$string['clearfilters'] = 'Clear filters';
|
||||
$string['filtersetmatchdescription'] = 'How multiple filters should be combined';
|
||||
$string['filterrowlegend'] = 'Filter {$a}';
|
||||
$string['filtertype'] = 'Filter type';
|
||||
$string['match'] = 'Match';
|
||||
$string['matchofthefollowing'] = 'of the following:';
|
||||
$string['selectfiltertype'] = 'Select';
|
||||
$string['placeholdertypeorselect'] = 'Type or select...';
|
||||
$string['clearfilterselection'] = 'Remove "{$a}" from filter';
|
||||
|
3
lib/amd/build/datafilter.min.js
vendored
Normal file
3
lib/amd/build/datafilter.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
lib/amd/build/datafilter.min.js.map
Normal file
1
lib/amd/build/datafilter.min.js.map
Normal file
File diff suppressed because one or more lines are too long
10
lib/amd/build/datafilter/filtertype.min.js
vendored
Normal file
10
lib/amd/build/datafilter/filtertype.min.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
define("core/datafilter/filtertype",["exports","core/form-autocomplete","core/datafilter/selectors","core/str"],(function(_exports,_formAutocomplete,_selectors,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* Base Filter class for a filter type in the filter UI.
|
||||
*
|
||||
* @module core/datafilter/filtertype
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_formAutocomplete=_interopRequireDefault(_formAutocomplete),_selectors=_interopRequireDefault(_selectors);return _exports.default=class{constructor(filterType,rootNode,initialValues){this.filterType=filterType,this.rootNode=rootNode,this.addValueSelector(initialValues)}tearDown(){}get placeholder(){return(0,_str.get_string)("placeholdertypeorselect","core")}get showSuggestions(){return!0}async addValueSelector(){let initialValues=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];const filterValueNode=this.getFilterValueNode(),sourceDataNode=this.getSourceDataForFilter();if(!sourceDataNode)return;filterValueNode.innerHTML=sourceDataNode.outerHTML;const dataSource=filterValueNode.querySelector("select");dataSource.id="filter-value-"+dataSource.getAttribute("data-field-name");const filterValueLabel=document.createElement("label");filterValueLabel.setAttribute("for",dataSource.id),filterValueLabel.classList.add("sr-only"),filterValueLabel.innerText=dataSource.getAttribute("data-field-title"),filterValueNode.appendChild(filterValueLabel),initialValues.forEach((filterValue=>{let selectedOption=dataSource.querySelector('option[value="'.concat(filterValue,'"]'));selectedOption?selectedOption.selected=!0:this.showSuggestions||(selectedOption=document.createElement("option"),selectedOption.value=filterValue,selectedOption.innerHTML=filterValue,selectedOption.selected=!0,dataSource.append(selectedOption))})),_formAutocomplete.default.enhance(dataSource,"1"==dataSource.dataset.allowCustom,null,await this.placeholder,!1,this.showSuggestions,null,!dataSource.multiple,{items:"core/datafilter/autocomplete_selection_items",layout:"core/datafilter/autocomplete_layout",selection:"core/datafilter/autocomplete_selection"})}get filterRoot(){return this.rootNode.querySelector(_selectors.default.filter.byName(this.filterType))}getSourceDataForFilter(){return this.rootNode.querySelector(_selectors.default.filterset.regions.datasource).querySelector(_selectors.default.data.fields.byName(this.filterType))}getFilterValueNode(){return this.filterRoot.querySelector(_selectors.default.filter.regions.values)}get name(){return this.filterType}get jointype(){return parseInt(this.filterRoot.querySelector(_selectors.default.filter.fields.join).value,10)}get rawValues(){const filterValueSelect=this.getFilterValueNode().querySelector("select");return Object.values((select=filterValueSelect,select.querySelectorAll(":checked"))).map((option=>option.value));var select}get values(){return this.rawValues.map((option=>parseInt(option,10)))}get filterValue(){return{name:this.name,jointype:this.jointype,values:this.values}}},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=filtertype.min.js.map
|
1
lib/amd/build/datafilter/filtertype.min.js.map
Normal file
1
lib/amd/build/datafilter/filtertype.min.js.map
Normal file
File diff suppressed because one or more lines are too long
10
lib/amd/build/datafilter/filtertypes/country.min.js
vendored
Normal file
10
lib/amd/build/datafilter/filtertypes/country.min.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
define("core/datafilter/filtertypes/country",["exports","core/datafilter/filtertype"],(function(_exports,_filtertype){var obj;
|
||||
/**
|
||||
* Country filter
|
||||
*
|
||||
* @module core/datafilter/filtertypes/country
|
||||
* @copyright 2021 Paul Holden <paulh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filtertype=(obj=_filtertype)&&obj.__esModule?obj:{default:obj};class _default extends _filtertype.default{get values(){return this.rawValues}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=country.min.js.map
|
1
lib/amd/build/datafilter/filtertypes/country.min.js.map
Normal file
1
lib/amd/build/datafilter/filtertypes/country.min.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"country.min.js","sources":["../../../src/datafilter/filtertypes/country.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Country filter\n *\n * @module core/datafilter/filtertypes/country\n * @copyright 2021 Paul Holden <paulh@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Filter from 'core/datafilter/filtertype';\n\nexport default class extends Filter {\n\n /**\n * For country the final value is an array of country code strings\n *\n * @return {Object}\n */\n get values() {\n return this.rawValues;\n }\n}\n"],"names":["Filter","values","this","rawValues"],"mappings":";;;;;;;4KAyB6BA,oBAOrBC,oBACOC,KAAKC"}
|
10
lib/amd/build/datafilter/filtertypes/courseid.min.js
vendored
Normal file
10
lib/amd/build/datafilter/filtertypes/courseid.min.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
define("core/datafilter/filtertypes/courseid",["exports","core/datafilter/filtertype"],(function(_exports,_filtertype){var obj;
|
||||
/**
|
||||
* Course ID filter.
|
||||
*
|
||||
* @module core/datafilter/filtertypes/courseid
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filtertype=(obj=_filtertype)&&obj.__esModule?obj:{default:obj};class _default extends _filtertype.default{constructor(filterType,filterSet){super(filterType,filterSet)}async addValueSelector(){}get filterValue(){return{name:this.name,jointype:1,values:[parseInt(this.rootNode.dataset.tableCourseId,10)]}}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=courseid.min.js.map
|
1
lib/amd/build/datafilter/filtertypes/courseid.min.js.map
Normal file
1
lib/amd/build/datafilter/filtertypes/courseid.min.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"courseid.min.js","sources":["../../../src/datafilter/filtertypes/courseid.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Course ID filter.\n *\n * @module core/datafilter/filtertypes/courseid\n * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Filter from 'core/datafilter/filtertype';\n\nexport default class extends Filter {\n constructor(filterType, filterSet) {\n super(filterType, filterSet);\n }\n\n async addValueSelector() {\n // eslint-disable-line no-empty-function\n }\n\n /**\n * Get the composed value for this filter.\n *\n * @returns {Object}\n */\n get filterValue() {\n return {\n name: this.name,\n jointype: 1,\n values: [parseInt(this.rootNode.dataset.tableCourseId, 10)],\n };\n }\n}\n"],"names":["Filter","constructor","filterType","filterSet","filterValue","name","this","jointype","values","parseInt","rootNode","dataset","tableCourseId"],"mappings":";;;;;;;4KAwB6BA,oBACzBC,YAAYC,WAAYC,iBACdD,WAAYC,qCAYlBC,wBACO,CACHC,KAAMC,KAAKD,KACXE,SAAU,EACVC,OAAQ,CAACC,SAASH,KAAKI,SAASC,QAAQC,cAAe"}
|
10
lib/amd/build/datafilter/filtertypes/keyword.min.js
vendored
Normal file
10
lib/amd/build/datafilter/filtertypes/keyword.min.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
define("core/datafilter/filtertypes/keyword",["exports","core/datafilter/filtertype","core/str"],(function(_exports,_filtertype,_str){var obj;
|
||||
/**
|
||||
* Keyword filter.
|
||||
*
|
||||
* @module core/datafilter/filtertypes/keyword
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filtertype=(obj=_filtertype)&&obj.__esModule?obj:{default:obj};class _default extends _filtertype.default{get values(){return this.rawValues}get placeholder(){return(0,_str.get_string)("placeholdertype","core_user")}get showSuggestions(){return!1}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=keyword.min.js.map
|
1
lib/amd/build/datafilter/filtertypes/keyword.min.js.map
Normal file
1
lib/amd/build/datafilter/filtertypes/keyword.min.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"keyword.min.js","sources":["../../../src/datafilter/filtertypes/keyword.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Keyword filter.\n *\n * @module core/datafilter/filtertypes/keyword\n * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Filter from 'core/datafilter/filtertype';\nimport {get_string as getString} from 'core/str';\n\nexport default class extends Filter {\n /**\n * For keywords the final value is an Array of strings.\n *\n * @returns {Object}\n */\n get values() {\n return this.rawValues;\n }\n\n /**\n * Get the placeholder to use when showing the value selector.\n *\n * @return {Promise} Resolving to a String\n */\n get placeholder() {\n return getString('placeholdertype', 'core_user');\n }\n\n /**\n * Whether to show suggestions in the autocomplete.\n *\n * @return {Boolean}\n */\n get showSuggestions() {\n return false;\n }\n}\n"],"names":["Filter","values","this","rawValues","placeholder","showSuggestions"],"mappings":";;;;;;;4KAyB6BA,oBAMrBC,oBACOC,KAAKC,UAQZC,yBACO,mBAAU,kBAAmB,aAQpCC,6BACO"}
|
@ -1,8 +1,8 @@
|
||||
define("core_user/local/participantsfilter/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;
|
||||
define("core/datafilter/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;
|
||||
/**
|
||||
* Module containing the selectors for user filters.
|
||||
*
|
||||
* @module core_user/local/user_filter/selectors
|
||||
* @module core/datafilter/selectors
|
||||
* @copyright 2020 Michael Hawkins <michaelh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
1
lib/amd/build/datafilter/selectors.min.js.map
Normal file
1
lib/amd/build/datafilter/selectors.min.js.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"selectors.min.js","sources":["../../src/datafilter/selectors.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Module containing the selectors for user filters.\n *\n * @module core/datafilter/selectors\n * @copyright 2020 Michael Hawkins <michaelh@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst getFilterRegion = region => `[data-filterregion=\"${region}\"]`;\nconst getFilterAction = action => `[data-filteraction=\"${action}\"]`;\nconst getFilterField = field => `[data-filterfield=\"${field}\"]`;\n\nexport default {\n filter: {\n region: getFilterRegion('filter'),\n actions: {\n remove: getFilterAction('remove'),\n },\n fields: {\n join: getFilterField('join'),\n type: getFilterField('type'),\n },\n regions: {\n values: getFilterRegion('value'),\n },\n byName: name => `${getFilterRegion('filter')}[data-filter-type=\"${name}\"]`,\n },\n filterset: {\n region: getFilterRegion('actions'),\n actions: {\n addRow: getFilterAction('add'),\n applyFilters: getFilterAction('apply'),\n resetFilters: getFilterAction('reset'),\n },\n regions: {\n filtermatch: getFilterRegion('filtermatch'),\n filterlist: getFilterRegion('filters'),\n datasource: getFilterRegion('filtertypedata'),\n },\n fields: {\n join: `${getFilterRegion('filtermatch')} ${getFilterField('join')}`,\n },\n },\n data: {\n fields: {\n byName: name => `[data-field-name=\"${name}\"]`,\n all: `${getFilterRegion('filtertypedata')} [data-field-name]`,\n },\n typeList: getFilterRegion('filtertypelist'),\n typeListSelect: `select${getFilterRegion('filtertypelist')}`,\n },\n};\n"],"names":["getFilterRegion","region","getFilterAction","action","getFilterField","field","filter","actions","remove","fields","join","type","regions","values","byName","name","filterset","addRow","applyFilters","resetFilters","filtermatch","filterlist","datasource","data","all","typeList","typeListSelect"],"mappings":";;;;;;;;MAuBMA,gBAAkBC,sCAAiCA,aACnDC,gBAAkBC,sCAAiCA,aACnDC,eAAiBC,oCAA+BA,yBAEvC,CACXC,OAAQ,CACJL,OAAQD,gBAAgB,UACxBO,QAAS,CACLC,OAAQN,gBAAgB,WAE5BO,OAAQ,CACJC,KAAMN,eAAe,QACrBO,KAAMP,eAAe,SAEzBQ,QAAS,CACLC,OAAQb,gBAAgB,UAE5Bc,OAAQC,gBAAWf,gBAAgB,wCAA+Be,YAEtEC,UAAW,CACPf,OAAQD,gBAAgB,WACxBO,QAAS,CACLU,OAAQf,gBAAgB,OACxBgB,aAAchB,gBAAgB,SAC9BiB,aAAcjB,gBAAgB,UAElCU,QAAS,CACLQ,YAAapB,gBAAgB,eAC7BqB,WAAYrB,gBAAgB,WAC5BsB,WAAYtB,gBAAgB,mBAEhCS,OAAQ,CACJC,eAASV,gBAAgB,2BAAkBI,eAAe,WAGlEmB,KAAM,CACFd,OAAQ,CACJK,OAAQC,kCAA6BA,WACrCS,cAAQxB,gBAAgB,yCAE5ByB,SAAUzB,gBAAgB,kBAC1B0B,+BAAyB1B,gBAAgB"}
|
436
lib/amd/src/datafilter.js
Normal file
436
lib/amd/src/datafilter.js
Normal file
@ -0,0 +1,436 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Data filter management.
|
||||
*
|
||||
* @module core/datafilter
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import CourseFilter from 'core/datafilter/filtertypes/courseid';
|
||||
import GenericFilter from 'core/datafilter/filtertype';
|
||||
import {get_strings as getStrings} from 'core/str';
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
import Selectors from 'core/datafilter/selectors';
|
||||
import Templates from 'core/templates';
|
||||
import CustomEvents from 'core/custom_interaction_events';
|
||||
import jQuery from 'jquery';
|
||||
|
||||
export default class {
|
||||
|
||||
/**
|
||||
* Initialise the filter on the element with the given filterSet and callback.
|
||||
*
|
||||
* @param {HTMLElement} filterSet The filter element.
|
||||
* @param {Function} applyCallback Callback function when updateTableFromFilter
|
||||
*/
|
||||
constructor(filterSet, applyCallback) {
|
||||
|
||||
this.filterSet = filterSet;
|
||||
this.applyCallback = applyCallback;
|
||||
// Keep a reference to all of the active filters.
|
||||
this.activeFilters = {
|
||||
courseid: new CourseFilter('courseid', filterSet),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise event listeners to the filter.
|
||||
*/
|
||||
init() {
|
||||
// Add listeners for the main actions.
|
||||
this.filterSet.querySelector(Selectors.filterset.region).addEventListener('click', e => {
|
||||
if (e.target.closest(Selectors.filterset.actions.addRow)) {
|
||||
e.preventDefault();
|
||||
|
||||
this.addFilterRow();
|
||||
}
|
||||
|
||||
if (e.target.closest(Selectors.filterset.actions.applyFilters)) {
|
||||
e.preventDefault();
|
||||
|
||||
this.updateTableFromFilter();
|
||||
}
|
||||
|
||||
if (e.target.closest(Selectors.filterset.actions.resetFilters)) {
|
||||
e.preventDefault();
|
||||
|
||||
this.removeAllFilters();
|
||||
}
|
||||
});
|
||||
|
||||
// Add the listener to remove a single filter.
|
||||
this.filterSet.querySelector(Selectors.filterset.regions.filterlist).addEventListener('click', e => {
|
||||
if (e.target.closest(Selectors.filter.actions.remove)) {
|
||||
e.preventDefault();
|
||||
|
||||
this.removeOrReplaceFilterRow(e.target.closest(Selectors.filter.region), true);
|
||||
}
|
||||
});
|
||||
|
||||
// Add listeners for the filter type selection.
|
||||
let filterRegion = jQuery(this.getFilterRegion());
|
||||
CustomEvents.define(filterRegion, [CustomEvents.events.accessibleChange]);
|
||||
filterRegion.on(CustomEvents.events.accessibleChange, e => {
|
||||
const typeField = e.target.closest(Selectors.filter.fields.type);
|
||||
if (typeField && typeField.value) {
|
||||
const filter = e.target.closest(Selectors.filter.region);
|
||||
|
||||
this.addFilter(filter, typeField.value);
|
||||
}
|
||||
});
|
||||
|
||||
this.filterSet.querySelector(Selectors.filterset.fields.join).addEventListener('change', e => {
|
||||
this.filterSet.dataset.filterverb = e.target.value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filter list region.
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
getFilterRegion() {
|
||||
return this.filterSet.querySelector(Selectors.filterset.regions.filterlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an unselected filter row.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
addFilterRow() {
|
||||
const pendingPromise = new Pending('core/datafilter:addFilterRow');
|
||||
const rownum = 1 + this.getFilterRegion().querySelectorAll(Selectors.filter.region).length;
|
||||
return Templates.renderForPromise('core/datafilter/filter_row', {"rownumber": rownum})
|
||||
.then(({html, js}) => {
|
||||
const newContentNodes = Templates.appendNodeContents(this.getFilterRegion(), html, js);
|
||||
|
||||
return newContentNodes;
|
||||
})
|
||||
.then(filterRow => {
|
||||
// Note: This is a nasty hack.
|
||||
// We should try to find a better way of doing this.
|
||||
// We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy
|
||||
// it in place.
|
||||
const typeList = this.filterSet.querySelector(Selectors.data.typeList);
|
||||
|
||||
filterRow.forEach(contentNode => {
|
||||
const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);
|
||||
|
||||
if (contentTypeList) {
|
||||
contentTypeList.innerHTML = typeList.innerHTML;
|
||||
}
|
||||
});
|
||||
|
||||
return filterRow;
|
||||
})
|
||||
.then(filterRow => {
|
||||
this.updateFiltersOptions();
|
||||
|
||||
return filterRow;
|
||||
})
|
||||
.then(result => {
|
||||
pendingPromise.resolve();
|
||||
|
||||
return result;
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filter data source node fro the specified filter type.
|
||||
*
|
||||
* @param {String} filterType
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
getFilterDataSource(filterType) {
|
||||
const filterDataNode = this.filterSet.querySelector(Selectors.filterset.regions.datasource);
|
||||
|
||||
return filterDataNode.querySelector(Selectors.data.fields.byName(filterType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a filter to the list of active filters, performing any necessary setup.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
* @param {String} filterType
|
||||
* @param {Array} initialFilterValues The initially selected values for the filter
|
||||
* @returns {Filter}
|
||||
*/
|
||||
async addFilter(filterRow, filterType, initialFilterValues) {
|
||||
// Name the filter on the filter row.
|
||||
filterRow.dataset.filterType = filterType;
|
||||
|
||||
const filterDataNode = this.getFilterDataSource(filterType);
|
||||
|
||||
// Instantiate the Filter class.
|
||||
let Filter = GenericFilter;
|
||||
if (filterDataNode.dataset.filterTypeClass) {
|
||||
Filter = await import(filterDataNode.dataset.filterTypeClass);
|
||||
}
|
||||
this.activeFilters[filterType] = new Filter(filterType, this.filterSet, initialFilterValues);
|
||||
|
||||
// Disable the select.
|
||||
const typeField = filterRow.querySelector(Selectors.filter.fields.type);
|
||||
typeField.value = filterType;
|
||||
typeField.disabled = 'disabled';
|
||||
|
||||
// Update the list of available filter types.
|
||||
this.updateFiltersOptions();
|
||||
|
||||
return this.activeFilters[filterType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered filter class for the named filter.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Object} See the Filter class.
|
||||
*/
|
||||
getFilterObject(name) {
|
||||
return this.activeFilters[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove or replace the specified filter row and associated class, ensuring that if there is only one filter row,
|
||||
* that it is replaced instead of being removed.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
* @param {Bool} refreshContent Whether to refresh the table content when removing
|
||||
*/
|
||||
removeOrReplaceFilterRow(filterRow, refreshContent) {
|
||||
const filterCount = this.getFilterRegion().querySelectorAll(Selectors.filter.region).length;
|
||||
if (filterCount === 1) {
|
||||
this.replaceFilterRow(filterRow, refreshContent);
|
||||
} else {
|
||||
this.removeFilterRow(filterRow, refreshContent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified filter row and associated class.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
* @param {Bool} refreshContent Whether to refresh the table content when removing
|
||||
*/
|
||||
async removeFilterRow(filterRow, refreshContent = true) {
|
||||
const filterType = filterRow.querySelector(Selectors.filter.fields.type);
|
||||
const hasFilterValue = !!filterType.value;
|
||||
|
||||
// Remove the filter object.
|
||||
this.removeFilterObject(filterRow.dataset.filterType);
|
||||
|
||||
// Remove the actual filter HTML.
|
||||
filterRow.remove();
|
||||
|
||||
// Update the list of available filter types.
|
||||
this.updateFiltersOptions();
|
||||
|
||||
if (hasFilterValue && refreshContent) {
|
||||
// Refresh the table if there was any content in this row.
|
||||
this.updateTableFromFilter();
|
||||
}
|
||||
|
||||
// Update filter fieldset legends.
|
||||
const filterLegends = await this.getAvailableFilterLegends();
|
||||
|
||||
this.getFilterRegion().querySelectorAll(Selectors.filter.region).forEach((filterRow, index) => {
|
||||
filterRow.querySelector('legend').innerText = filterLegends[index];
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the specified filter row with a new one.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
* @param {Bool} refreshContent Whether to refresh the table content when removing
|
||||
* @param {Number} rowNum The number used to label the filter fieldset legend (eg Row 1). Defaults to 1 (the first filter).
|
||||
* @return {Promise}
|
||||
*/
|
||||
replaceFilterRow(filterRow, refreshContent = true, rowNum = 1) {
|
||||
// Remove the filter object.
|
||||
this.removeFilterObject(filterRow.dataset.filterType);
|
||||
|
||||
return Templates.renderForPromise('core/datafilter/filter_row', {"rownumber": rowNum})
|
||||
.then(({html, js}) => {
|
||||
const newContentNodes = Templates.replaceNode(filterRow, html, js);
|
||||
|
||||
return newContentNodes;
|
||||
})
|
||||
.then(filterRow => {
|
||||
// Note: This is a nasty hack.
|
||||
// We should try to find a better way of doing this.
|
||||
// We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy
|
||||
// it in place.
|
||||
const typeList = this.filterSet.querySelector(Selectors.data.typeList);
|
||||
|
||||
filterRow.forEach(contentNode => {
|
||||
const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);
|
||||
|
||||
if (contentTypeList) {
|
||||
contentTypeList.innerHTML = typeList.innerHTML;
|
||||
}
|
||||
});
|
||||
|
||||
return filterRow;
|
||||
})
|
||||
.then(filterRow => {
|
||||
this.updateFiltersOptions();
|
||||
|
||||
return filterRow;
|
||||
})
|
||||
.then(filterRow => {
|
||||
// Refresh the table.
|
||||
if (refreshContent) {
|
||||
return this.updateTableFromFilter();
|
||||
} else {
|
||||
return filterRow;
|
||||
}
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the Filter Object from the register.
|
||||
*
|
||||
* @param {string} filterName The name of the filter to be removed
|
||||
*/
|
||||
removeFilterObject(filterName) {
|
||||
if (filterName) {
|
||||
const filter = this.getFilterObject(filterName);
|
||||
if (filter) {
|
||||
filter.tearDown();
|
||||
|
||||
// Remove from the list of active filters.
|
||||
delete this.activeFilters[filterName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all filters.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
removeAllFilters() {
|
||||
const filters = this.getFilterRegion().querySelectorAll(Selectors.filter.region);
|
||||
filters.forEach(filterRow => this.removeOrReplaceFilterRow(filterRow, false));
|
||||
|
||||
// Refresh the table.
|
||||
return this.updateTableFromFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any empty filters.
|
||||
*/
|
||||
removeEmptyFilters() {
|
||||
const filters = this.getFilterRegion().querySelectorAll(Selectors.filter.region);
|
||||
filters.forEach(filterRow => {
|
||||
const filterType = filterRow.querySelector(Selectors.filter.fields.type);
|
||||
if (!filterType.value) {
|
||||
this.removeOrReplaceFilterRow(filterRow, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the list of filter types to filter out those already selected.
|
||||
*/
|
||||
updateFiltersOptions() {
|
||||
const filters = this.getFilterRegion().querySelectorAll(Selectors.filter.region);
|
||||
filters.forEach(filterRow => {
|
||||
const options = filterRow.querySelectorAll(Selectors.filter.fields.type + ' option');
|
||||
options.forEach(option => {
|
||||
if (option.value === filterRow.dataset.filterType) {
|
||||
option.classList.remove('hidden');
|
||||
option.disabled = false;
|
||||
} else if (this.activeFilters[option.value]) {
|
||||
option.classList.add('hidden');
|
||||
option.disabled = true;
|
||||
} else {
|
||||
option.classList.remove('hidden');
|
||||
option.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Configure the state of the "Add row" button.
|
||||
// This button is disabled when there is a filter row available for each condition.
|
||||
const addRowButton = this.filterSet.querySelector(Selectors.filterset.actions.addRow);
|
||||
const filterDataNode = this.filterSet.querySelectorAll(Selectors.data.fields.all);
|
||||
if (filterDataNode.length <= filters.length) {
|
||||
addRowButton.setAttribute('disabled', 'disabled');
|
||||
} else {
|
||||
addRowButton.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
if (filters.length === 1) {
|
||||
this.filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.add('hidden');
|
||||
this.filterSet.querySelector(Selectors.filterset.fields.join).value = 2;
|
||||
this.filterSet.dataset.filterverb = 2;
|
||||
} else {
|
||||
this.filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Dynamic table based upon the current filter.
|
||||
*/
|
||||
updateTableFromFilter() {
|
||||
const pendingPromise = new Pending('core/datafilter:updateTableFromFilter');
|
||||
|
||||
const filters = {};
|
||||
Object.values(this.activeFilters).forEach(filter => {
|
||||
filters[filter.filterValue.name] = filter.filterValue;
|
||||
});
|
||||
|
||||
if (this.applyCallback) {
|
||||
this.applyCallback(filters, pendingPromise);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the strings used to populate the fieldset legends for the maximum number of filters possible.
|
||||
*
|
||||
* @return {array}
|
||||
*/
|
||||
async getAvailableFilterLegends() {
|
||||
const maxFilters = document.querySelector(Selectors.data.typeListSelect).length - 1;
|
||||
let requests = [];
|
||||
|
||||
[...Array(maxFilters)].forEach((_, rowIndex) => {
|
||||
requests.push({
|
||||
"key": "filterrowlegend",
|
||||
"component": "core_user",
|
||||
// Add 1 since rows begin at 1 (index begins at zero).
|
||||
"param": rowIndex + 1
|
||||
});
|
||||
});
|
||||
|
||||
const legendStrings = await getStrings(requests)
|
||||
.then(fetchedStrings => {
|
||||
return fetchedStrings;
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
|
||||
return legendStrings;
|
||||
}
|
||||
|
||||
}
|
@ -14,14 +14,14 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Base Filter class for a filter type in the participants filter UI.
|
||||
* Base Filter class for a filter type in the filter UI.
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filter
|
||||
* @module core/datafilter/filtertype
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
import Autocomplete from 'core/form-autocomplete';
|
||||
import Selectors from './selectors';
|
||||
import Selectors from 'core/datafilter/selectors';
|
||||
import {get_string as getString} from 'core/str';
|
||||
|
||||
/**
|
||||
@ -65,7 +65,7 @@ export default class {
|
||||
* @return {Promise} Resolving to a String
|
||||
*/
|
||||
get placeholder() {
|
||||
return getString('placeholdertypeorselect', 'core_user');
|
||||
return getString('placeholdertypeorselect', 'core');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,9 +148,9 @@ export default class {
|
||||
|
||||
// Template overrides.
|
||||
{
|
||||
items: 'core_user/local/participantsfilter/autocomplete_selection_items',
|
||||
layout: 'core_user/local/participantsfilter/autocomplete_layout',
|
||||
selection: 'core_user/local/participantsfilter/autocomplete_selection',
|
||||
items: 'core/datafilter/autocomplete_selection_items',
|
||||
layout: 'core/datafilter/autocomplete_layout',
|
||||
selection: 'core/datafilter/autocomplete_selection',
|
||||
}
|
||||
);
|
||||
}
|
@ -16,12 +16,12 @@
|
||||
/**
|
||||
* Country filter
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filtertypes/country
|
||||
* @module core/datafilter/filtertypes/country
|
||||
* @copyright 2021 Paul Holden <paulh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Filter from '../filter';
|
||||
import Filter from 'core/datafilter/filtertype';
|
||||
|
||||
export default class extends Filter {
|
||||
|
@ -16,11 +16,11 @@
|
||||
/**
|
||||
* Course ID filter.
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filtertypes/courseid
|
||||
* @module core/datafilter/filtertypes/courseid
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
import Filter from '../filter';
|
||||
import Filter from 'core/datafilter/filtertype';
|
||||
|
||||
export default class extends Filter {
|
||||
constructor(filterType, filterSet) {
|
@ -16,11 +16,11 @@
|
||||
/**
|
||||
* Keyword filter.
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filtertypes/keyword
|
||||
* @module core/datafilter/filtertypes/keyword
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
import Filter from '../filter';
|
||||
import Filter from 'core/datafilter/filtertype';
|
||||
import {get_string as getString} from 'core/str';
|
||||
|
||||
export default class extends Filter {
|
@ -16,7 +16,7 @@
|
||||
/**
|
||||
* Module containing the selectors for user filters.
|
||||
*
|
||||
* @module core_user/local/user_filter/selectors
|
||||
* @module core/datafilter/selectors
|
||||
* @copyright 2020 Michael Hawkins <michaelh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
101
lib/classes/output/datafilter.php
Normal file
101
lib/classes/output/datafilter.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\output;
|
||||
|
||||
use context;
|
||||
use renderable;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* The filter renderable class.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2021 Catalyst IT Australia Pty Ltd
|
||||
* @author Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class datafilter implements renderable, templatable {
|
||||
|
||||
/** @var context $context The context where the filters are being rendered. */
|
||||
protected $context;
|
||||
|
||||
/** @var string $tableregionid Container of the table to be updated by this filter, is used to retrieve the table */
|
||||
protected $tableregionid;
|
||||
|
||||
/** @var stdClass $course The course shown */
|
||||
protected $course;
|
||||
|
||||
/**
|
||||
* Filter constructor.
|
||||
*
|
||||
* @param context $context The context where the filters are being rendered
|
||||
* @param string|null $tableregionid Container of the table which will be updated by this filter
|
||||
*/
|
||||
public function __construct(context $context, ?string $tableregionid = null) {
|
||||
$this->context = $context;
|
||||
$this->tableregionid = $tableregionid;
|
||||
|
||||
if ($context instanceof \context_course) {
|
||||
$this->course = get_course($context->instanceid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for all filter types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function get_filtertypes(): array;
|
||||
|
||||
/**
|
||||
* Get a standardised filter object.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $title
|
||||
* @param bool $custom
|
||||
* @param bool $multiple
|
||||
* @param string|null $filterclass
|
||||
* @param array $values
|
||||
* @param bool $allowempty
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function get_filter_object(
|
||||
string $name,
|
||||
string $title,
|
||||
bool $custom,
|
||||
bool $multiple,
|
||||
?string $filterclass,
|
||||
array $values,
|
||||
bool $allowempty = false
|
||||
): ?stdClass {
|
||||
|
||||
if (!$allowempty && empty($values)) {
|
||||
// Do not show empty filters.
|
||||
return null;
|
||||
}
|
||||
|
||||
return (object) [
|
||||
'name' => $name,
|
||||
'title' => $title,
|
||||
'allowcustom' => $custom,
|
||||
'allowmultiple' => $multiple,
|
||||
'filtertypeclass' => $filterclass,
|
||||
'values' => $values,
|
||||
];
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_user/local/participantsfilter/autocomplete_layout
|
||||
@template core/datafilter/autocomplete_layout
|
||||
|
||||
Moodle template for the layout of autocomplete elements.
|
||||
|
@ -15,7 +15,7 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_user/local/participantsfilter/autocomplete_selection
|
||||
@template core/datafilter/autocomplete_selection
|
||||
|
||||
Moodle template for the wrapper of currently selected items in an autocomplate form element.
|
||||
|
@ -15,7 +15,7 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_user/local/participantsfilter/autocomplete_selection_items
|
||||
@template core/datafilter/autocomplete_selection_items
|
||||
|
||||
Moodle template for the currently selected items in an autocomplete form element.
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
{{#items}}
|
||||
<span role="option" data-value="{{value}}" aria-selected="true"
|
||||
class="badge badge-secondary clickable text-wrap text-break line-height-4 m-1">
|
||||
{{label}}<i class="icon fa fa-times pl-2 mr-0"></i>
|
||||
{{{label}}}<i class="icon fa fa-times pl-2 mr-0"></i>
|
||||
</span>
|
||||
{{/items}}
|
||||
{{^items}}
|
63
lib/templates/datafilter/filter.mustache
Normal file
63
lib/templates/datafilter/filter.mustache
Normal file
@ -0,0 +1,63 @@
|
||||
{{!
|
||||
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/datafilter/filter
|
||||
|
||||
Template for the form containing one or more filter rows.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"filtertypes": [
|
||||
{
|
||||
"name": "status",
|
||||
"title": "Status",
|
||||
"values": [
|
||||
{
|
||||
"value": 1,
|
||||
"title": "Active"
|
||||
},
|
||||
{
|
||||
"value": 0,
|
||||
"title": "Suspended"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
|
||||
<div id="core-filter-{{uniqid}}" class="filter-group my-2 p-2 bg-light border-radius border" data-table-region="{{tableregionid}}" data-table-course-id="{{courseid}}" data-filterverb="2">
|
||||
{{> core/datafilter/filter_match }}
|
||||
|
||||
<div data-filterregion="filters">
|
||||
{{> core/datafilter/filter_row }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex" data-filterregion="actions">
|
||||
|
||||
<button type="button" class="btn btn-link text-reset" data-filteraction="add">
|
||||
{{#pix}}t/add{{/pix}}<span class="pl-3">{{#str}}addcondition{{/str}}</span>
|
||||
</button>
|
||||
<button data-filteraction="reset" type="button" class="btn btn-secondary ml-auto mr-2">{{#str}}clearfilters{{/str}}</button>
|
||||
<button data-filteraction="apply" type="button" class="btn btn-primary">{{#str}}applyfilters{{/str}}</button>
|
||||
</div>
|
||||
|
||||
{{> core/datafilter/filter_types }}
|
||||
|
||||
</div>
|
||||
|
||||
|
55
lib/templates/datafilter/filter_match.mustache
Normal file
55
lib/templates/datafilter/filter_match.mustache
Normal file
@ -0,0 +1,55 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core/datafilter/filter_match
|
||||
|
||||
Template for the form containing one or more filter rows.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"filtertypes": [
|
||||
{
|
||||
"name": "status",
|
||||
"title": "Status",
|
||||
"values": [
|
||||
{
|
||||
"value": 1,
|
||||
"title": "Active"
|
||||
},
|
||||
{
|
||||
"value": 0,
|
||||
"title": "Suspended"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
|
||||
<div data-filterregion="filtermatch" class="hidden">
|
||||
<label for="core-filter-jointype-{{uniqid}}" class="my-0" aria-hidden="true">
|
||||
{{#str}}match{{/str}}
|
||||
</label>
|
||||
<select class="custom-select" data-filterfield="join" id="core-filter-jointype-{{uniqid}}"
|
||||
aria-label="{{#str}}filtersetmatchdescription{{/str}}">
|
||||
<option value="0">{{#str}}none{{/str}}</option>
|
||||
<option value="1">{{#str}}any{{/str}}</option>
|
||||
<option value="2" selected>{{#str}}all{{/str}}</option>
|
||||
</select>
|
||||
<span aria-hidden="true">{{#str}}matchofthefollowing{{/str}}</span>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_user/local/participantsfilter/filterrow
|
||||
@template core/datafilter/filter_row
|
||||
|
||||
Template for use by each filter condition.
|
||||
|
||||
@ -35,20 +35,20 @@
|
||||
}}
|
||||
<div data-filterregion="filter">
|
||||
<fieldset>
|
||||
<legend class="sr-only">{{#str}}filterrowlegend, core_user, {{rownumber}}{{/str}}</legend>
|
||||
<legend class="sr-only">{{#str}}filterrowlegend, core, {{rownumber}}{{/str}}</legend>
|
||||
<div class="border-radius my-2 p-2 bg-white border d-flex flex-column flex-md-row align-items-md-start">
|
||||
<div class="d-flex flex-column flex-md-row align-items-md-center">
|
||||
<label for="core_user-local-participantsfilter-filterrow-jointype-{{uniqid}}" class="mr-md-2 mb-md-0">{{#str}}match, core_user{{/str}}</label>
|
||||
<select class="custom-select mb-1 mb-md-0 mr-md-2" data-filterfield="join" id="core_user-local-participantsfilter-filterrow-jointype-{{uniqid}}">
|
||||
<label for="core-filter_row-jointype-{{uniqid}}" class="mr-md-2 mb-md-0">{{#str}}match{{/str}}</label>
|
||||
<select class="custom-select mb-1 mb-md-0 mr-md-2" data-filterfield="join" id="core-filter_row-jointype-{{uniqid}}">
|
||||
<option value="0">{{#str}}none{{/str}}</option>
|
||||
<option selected=selected value="1">{{#str}}any{{/str}}</option>
|
||||
<option value="2">{{#str}}all{{/str}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<label class="sr-only pt-2" for="core_user-local-participantsfilter-filterrow-filtertype-{{uniqid}}">{{#str}}filtertype, core_user{{/str}}</label>
|
||||
<select class="custom-select mb-1 mb-md-0 mr-md-2" data-filterfield="type" id="core_user-local-participantsfilter-filterrow-filtertype-{{uniqid}}">
|
||||
<option value="">{{#str}}selectfiltertype, core_user{{/str}}</option>
|
||||
<label class="sr-only pt-2" for="core-filter_row-filtertype-{{uniqid}}">{{#str}}filtertype{{/str}}</label>
|
||||
<select class="custom-select mb-1 mb-md-0 mr-md-2" data-filterfield="type" id="core-filter_row-filtertype-{{uniqid}}">
|
||||
<option value="">{{#str}}selectfiltertype{{/str}}</option>
|
||||
{{#filtertypes}}
|
||||
<option value="{{name}}">{{title}}</option>
|
||||
{{/filtertypes}}
|
||||
@ -56,14 +56,14 @@
|
||||
|
||||
<div data-filterregion="value" class="d-md-block flex-column align-items-start flex-lg-row"></div>
|
||||
|
||||
<button data-filteraction="remove" class="ml-auto icon-no-margin icon-size-4 btn text-reset" aria-label="{{#str}}clearfilterrow, core_user{{/str}}">
|
||||
<i class="icon fa fa-times-circle"></i>
|
||||
<button data-filteraction="remove" class="ml-auto icon-no-margin icon-size-4 btn text-reset" aria-label="{{#str}}clearfilterrow{{/str}}">
|
||||
{{#pix}}e/cancel_solid_circle{{/pix}}
|
||||
</button>
|
||||
</div>
|
||||
<div data-filterregion="joinadverb" class="pl-1 text-uppercase font-weight-bold">
|
||||
<div data-filterverbfor="0">{{#str}}adverbfor_andnot, core_user{{/str}}</div>
|
||||
<div data-filterverbfor="1">{{#str}}adverbfor_or, core_user{{/str}}</div>
|
||||
<div data-filterverbfor="2">{{#str}}adverbfor_and, core_user{{/str}}</div>
|
||||
<div data-filterverbfor="0">{{#str}}operator_andnot{{/str}}</div>
|
||||
<div data-filterverbfor="1">{{#str}}operator_or{{/str}}</div>
|
||||
<div data-filterverbfor="2">{{#str}}operator_and{{/str}}</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
@ -15,7 +15,7 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_user/local/participantsfilter/filtertype
|
||||
@template core/datafilter/filter_type
|
||||
|
||||
Filter type data, not shown to users but used as a source of data for form autocompletion.
|
||||
|
||||
@ -33,8 +33,8 @@
|
||||
"name": "status",
|
||||
"title": "Enrolment Status",
|
||||
"allowcustom": "0",
|
||||
"allowmultiple" false,
|
||||
"filtertypeclass": "core_user/local/participantsfilter/filtertypes/courseid",
|
||||
"allowmultiple": false,
|
||||
"filtertypeclass": "core/filter_types/courseid",
|
||||
"values": [
|
||||
{
|
||||
"value": "0",
|
@ -15,7 +15,7 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_user/local/participantsfilter/filtertypes
|
||||
@template core/datafilter/filter_types
|
||||
|
||||
Placeholder to fetch all filter types.
|
||||
|
||||
@ -51,12 +51,12 @@
|
||||
}}
|
||||
<div class="hidden" data-filterregion="filtertypedata">
|
||||
{{#filtertypes}}
|
||||
{{> core_user/local/participantsfilter/filtertype}}
|
||||
{{> core/datafilter/filter_type}}
|
||||
{{/filtertypes}}
|
||||
</div>
|
||||
<div class="hidden">
|
||||
<select disabled="disabled" data-filterfield="type" data-filterregion="filtertypelist">
|
||||
<option value="">{{#str}}selectfiltertype, core_user{{/str}}</option>
|
||||
<option value="">{{#str}}selectfiltertype{{/str}}</option>
|
||||
{{#filtertypes}}
|
||||
<option value="{{name}}">{{title}}</option>
|
||||
{{/filtertypes}}
|
@ -1,10 +0,0 @@
|
||||
define("core_user/local/participantsfilter/filter",["exports","core/form-autocomplete","./selectors","core/str"],(function(_exports,_formAutocomplete,_selectors,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* Base Filter class for a filter type in the participants filter UI.
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filter
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_formAutocomplete=_interopRequireDefault(_formAutocomplete),_selectors=_interopRequireDefault(_selectors);return _exports.default=class{constructor(filterType,rootNode,initialValues){this.filterType=filterType,this.rootNode=rootNode,this.addValueSelector(initialValues)}tearDown(){}get placeholder(){return(0,_str.get_string)("placeholdertypeorselect","core_user")}get showSuggestions(){return!0}async addValueSelector(){let initialValues=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];const filterValueNode=this.getFilterValueNode(),sourceDataNode=this.getSourceDataForFilter();if(!sourceDataNode)return;filterValueNode.innerHTML=sourceDataNode.outerHTML;const dataSource=filterValueNode.querySelector("select");dataSource.id="filter-value-"+dataSource.getAttribute("data-field-name");const filterValueLabel=document.createElement("label");filterValueLabel.setAttribute("for",dataSource.id),filterValueLabel.classList.add("sr-only"),filterValueLabel.innerText=dataSource.getAttribute("data-field-title"),filterValueNode.appendChild(filterValueLabel),initialValues.forEach((filterValue=>{let selectedOption=dataSource.querySelector('option[value="'.concat(filterValue,'"]'));selectedOption?selectedOption.selected=!0:this.showSuggestions||(selectedOption=document.createElement("option"),selectedOption.value=filterValue,selectedOption.innerHTML=filterValue,selectedOption.selected=!0,dataSource.append(selectedOption))})),_formAutocomplete.default.enhance(dataSource,"1"==dataSource.dataset.allowCustom,null,await this.placeholder,!1,this.showSuggestions,null,!dataSource.multiple,{items:"core_user/local/participantsfilter/autocomplete_selection_items",layout:"core_user/local/participantsfilter/autocomplete_layout",selection:"core_user/local/participantsfilter/autocomplete_selection"})}get filterRoot(){return this.rootNode.querySelector(_selectors.default.filter.byName(this.filterType))}getSourceDataForFilter(){return this.rootNode.querySelector(_selectors.default.filterset.regions.datasource).querySelector(_selectors.default.data.fields.byName(this.filterType))}getFilterValueNode(){return this.filterRoot.querySelector(_selectors.default.filter.regions.values)}get name(){return this.filterType}get jointype(){return parseInt(this.filterRoot.querySelector(_selectors.default.filter.fields.join).value,10)}get rawValues(){const filterValueSelect=this.getFilterValueNode().querySelector("select");return Object.values((select=filterValueSelect,select.querySelectorAll(":checked"))).map((option=>option.value));var select}get values(){return this.rawValues.map((option=>parseInt(option,10)))}get filterValue(){return{name:this.name,jointype:this.jointype,values:this.values}}},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=filter.min.js.map
|
File diff suppressed because one or more lines are too long
@ -1,10 +0,0 @@
|
||||
define("core_user/local/participantsfilter/filtertypes/country",["exports","../filter"],(function(_exports,_filter){var obj;
|
||||
/**
|
||||
* Country filter
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filtertypes/country
|
||||
* @copyright 2021 Paul Holden <paulh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filter=(obj=_filter)&&obj.__esModule?obj:{default:obj};class _default extends _filter.default{get values(){return this.rawValues}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=country.min.js.map
|
@ -1 +0,0 @@
|
||||
{"version":3,"file":"country.min.js","sources":["../../../../src/local/participantsfilter/filtertypes/country.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Country filter\n *\n * @module core_user/local/participantsfilter/filtertypes/country\n * @copyright 2021 Paul Holden <paulh@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Filter from '../filter';\n\nexport default class extends Filter {\n\n /**\n * For country the final value is an array of country code strings\n *\n * @return {Object}\n */\n get values() {\n return this.rawValues;\n }\n}\n"],"names":["Filter","values","this","rawValues"],"mappings":";;;;;;;oKAyB6BA,gBAOrBC,oBACOC,KAAKC"}
|
@ -1,10 +0,0 @@
|
||||
define("core_user/local/participantsfilter/filtertypes/courseid",["exports","../filter"],(function(_exports,_filter){var obj;
|
||||
/**
|
||||
* Course ID filter.
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filtertypes/courseid
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filter=(obj=_filter)&&obj.__esModule?obj:{default:obj};class _default extends _filter.default{constructor(filterType,filterSet){super(filterType,filterSet)}async addValueSelector(){}get filterValue(){return{name:this.name,jointype:1,values:[parseInt(this.rootNode.dataset.tableCourseId,10)]}}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=courseid.min.js.map
|
@ -1 +0,0 @@
|
||||
{"version":3,"file":"courseid.min.js","sources":["../../../../src/local/participantsfilter/filtertypes/courseid.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Course ID filter.\n *\n * @module core_user/local/participantsfilter/filtertypes/courseid\n * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Filter from '../filter';\n\nexport default class extends Filter {\n constructor(filterType, filterSet) {\n super(filterType, filterSet);\n }\n\n async addValueSelector() {\n // eslint-disable-line no-empty-function\n }\n\n /**\n * Get the composed value for this filter.\n *\n * @returns {Object}\n */\n get filterValue() {\n return {\n name: this.name,\n jointype: 1,\n values: [parseInt(this.rootNode.dataset.tableCourseId, 10)],\n };\n }\n}\n"],"names":["Filter","constructor","filterType","filterSet","filterValue","name","this","jointype","values","parseInt","rootNode","dataset","tableCourseId"],"mappings":";;;;;;;oKAwB6BA,gBACzBC,YAAYC,WAAYC,iBACdD,WAAYC,qCAYlBC,wBACO,CACHC,KAAMC,KAAKD,KACXE,SAAU,EACVC,OAAQ,CAACC,SAASH,KAAKI,SAASC,QAAQC,cAAe"}
|
@ -1,10 +0,0 @@
|
||||
define("core_user/local/participantsfilter/filtertypes/keyword",["exports","../filter","core/str"],(function(_exports,_filter,_str){var obj;
|
||||
/**
|
||||
* Keyword filter.
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filtertypes/keyword
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_filter=(obj=_filter)&&obj.__esModule?obj:{default:obj};class _default extends _filter.default{get values(){return this.rawValues}get placeholder(){return(0,_str.get_string)("placeholdertype","core_user")}get showSuggestions(){return!1}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=keyword.min.js.map
|
@ -1 +0,0 @@
|
||||
{"version":3,"file":"keyword.min.js","sources":["../../../../src/local/participantsfilter/filtertypes/keyword.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Keyword filter.\n *\n * @module core_user/local/participantsfilter/filtertypes/keyword\n * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Filter from '../filter';\nimport {get_string as getString} from 'core/str';\n\nexport default class extends Filter {\n /**\n * For keywords the final value is an Array of strings.\n *\n * @returns {Object}\n */\n get values() {\n return this.rawValues;\n }\n\n /**\n * Get the placeholder to use when showing the value selector.\n *\n * @return {Promise} Resolving to a String\n */\n get placeholder() {\n return getString('placeholdertype', 'core_user');\n }\n\n /**\n * Whether to show suggestions in the autocomplete.\n *\n * @return {Boolean}\n */\n get showSuggestions() {\n return false;\n }\n}\n"],"names":["Filter","values","this","rawValues","placeholder","showSuggestions"],"mappings":";;;;;;;oKAyB6BA,gBAMrBC,oBACOC,KAAKC,UAQZC,yBACO,mBAAU,kBAAmB,aAQpCC,6BACO"}
|
@ -1 +0,0 @@
|
||||
{"version":3,"file":"selectors.min.js","sources":["../../../src/local/participantsfilter/selectors.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Module containing the selectors for user filters.\n *\n * @module core_user/local/user_filter/selectors\n * @copyright 2020 Michael Hawkins <michaelh@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst getFilterRegion = region => `[data-filterregion=\"${region}\"]`;\nconst getFilterAction = action => `[data-filteraction=\"${action}\"]`;\nconst getFilterField = field => `[data-filterfield=\"${field}\"]`;\n\nexport default {\n filter: {\n region: getFilterRegion('filter'),\n actions: {\n remove: getFilterAction('remove'),\n },\n fields: {\n join: getFilterField('join'),\n type: getFilterField('type'),\n },\n regions: {\n values: getFilterRegion('value'),\n },\n byName: name => `${getFilterRegion('filter')}[data-filter-type=\"${name}\"]`,\n },\n filterset: {\n region: getFilterRegion('actions'),\n actions: {\n addRow: getFilterAction('add'),\n applyFilters: getFilterAction('apply'),\n resetFilters: getFilterAction('reset'),\n },\n regions: {\n filtermatch: getFilterRegion('filtermatch'),\n filterlist: getFilterRegion('filters'),\n datasource: getFilterRegion('filtertypedata'),\n },\n fields: {\n join: `${getFilterRegion('filtermatch')} ${getFilterField('join')}`,\n },\n },\n data: {\n fields: {\n byName: name => `[data-field-name=\"${name}\"]`,\n all: `${getFilterRegion('filtertypedata')} [data-field-name]`,\n },\n typeList: getFilterRegion('filtertypelist'),\n typeListSelect: `select${getFilterRegion('filtertypelist')}`,\n },\n};\n"],"names":["getFilterRegion","region","getFilterAction","action","getFilterField","field","filter","actions","remove","fields","join","type","regions","values","byName","name","filterset","addRow","applyFilters","resetFilters","filtermatch","filterlist","datasource","data","all","typeList","typeListSelect"],"mappings":";;;;;;;;MAuBMA,gBAAkBC,sCAAiCA,aACnDC,gBAAkBC,sCAAiCA,aACnDC,eAAiBC,oCAA+BA,yBAEvC,CACXC,OAAQ,CACJL,OAAQD,gBAAgB,UACxBO,QAAS,CACLC,OAAQN,gBAAgB,WAE5BO,OAAQ,CACJC,KAAMN,eAAe,QACrBO,KAAMP,eAAe,SAEzBQ,QAAS,CACLC,OAAQb,gBAAgB,UAE5Bc,OAAQC,gBAAWf,gBAAgB,wCAA+Be,YAEtEC,UAAW,CACPf,OAAQD,gBAAgB,WACxBO,QAAS,CACLU,OAAQf,gBAAgB,OACxBgB,aAAchB,gBAAgB,SAC9BiB,aAAcjB,gBAAgB,UAElCU,QAAS,CACLQ,YAAapB,gBAAgB,eAC7BqB,WAAYrB,gBAAgB,WAC5BsB,WAAYtB,gBAAgB,mBAEhCS,OAAQ,CACJC,eAASV,gBAAgB,2BAAkBI,eAAe,WAGlEmB,KAAM,CACFd,OAAQ,CACJK,OAAQC,kCAA6BA,WACrCS,cAAQxB,gBAAgB,yCAE5ByB,SAAUzB,gBAAgB,kBAC1B0B,+BAAyB1B,gBAAgB"}
|
10
user/amd/build/participants_filter.min.js
vendored
Normal file
10
user/amd/build/participants_filter.min.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
define("core_user/participants_filter",["exports","core/datafilter","core_table/dynamic","core/datafilter/selectors","core/notification","core/pending"],(function(_exports,_datafilter,DynamicTable,_selectors,_notification,_pending){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* Participants filter management.
|
||||
*
|
||||
* @module core_user/participants_filter
|
||||
* @copyright 2021 Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_datafilter=_interopRequireDefault(_datafilter),DynamicTable=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(DynamicTable),_selectors=_interopRequireDefault(_selectors),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);_exports.init=filterRegionId=>{const filterSet=document.getElementById(filterRegionId),coreFilter=new _datafilter.default(filterSet,(function(filters,pendingPromise){DynamicTable.setFilters(DynamicTable.getTableFromId(filterSet.dataset.tableRegion),{jointype:parseInt(filterSet.querySelector(_selectors.default.filterset.fields.join).value,10),filters:filters}).then((result=>(pendingPromise.resolve(),result))).catch(_notification.default.exception)}));coreFilter.init();const tableRoot=DynamicTable.getTableFromId(filterSet.dataset.tableRegion),initialFilters=DynamicTable.getFilters(tableRoot);if(initialFilters){const initialFilterPromise=new _pending.default("core/filter:setFilterFromConfig");(config=>{const filterConfig=Object.entries(config.filters);if(!filterConfig.length)return Promise.resolve();filterSet.querySelector(_selectors.default.filterset.fields.join).value=config.jointype;const filterPromises=filterConfig.map((_ref=>{let[filterType,filterData]=_ref;if("courseid"===filterType)return!1;const filterValues=filterData.values;return!!filterValues.length&&coreFilter.addFilterRow().then((_ref2=>{let[filterRow]=_ref2;coreFilter.addFilter(filterRow,filterType,filterValues)}))})).filter((promise=>promise));return filterPromises.length?Promise.all(filterPromises).then((()=>coreFilter.removeEmptyFilters())).then((()=>{coreFilter.updateFiltersOptions()})).then((()=>{coreFilter.updateTableFromFilter()})):Promise.resolve()})(initialFilters).then((()=>initialFilterPromise.resolve())).catch()}}}));
|
||||
|
||||
//# sourceMappingURL=participants_filter.min.js.map
|
1
user/amd/build/participants_filter.min.js.map
Normal file
1
user/amd/build/participants_filter.min.js.map
Normal file
File diff suppressed because one or more lines are too long
3
user/amd/build/participantsfilter.min.js
vendored
3
user/amd/build/participantsfilter.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
125
user/amd/src/participants_filter.js
Normal file
125
user/amd/src/participants_filter.js
Normal file
@ -0,0 +1,125 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Participants filter management.
|
||||
*
|
||||
* @module core_user/participants_filter
|
||||
* @copyright 2021 Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import CoreFilter from 'core/datafilter';
|
||||
import * as DynamicTable from 'core_table/dynamic';
|
||||
import Selectors from 'core/datafilter/selectors';
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
|
||||
/**
|
||||
* Initialise the participants filter on the element with the given id.
|
||||
*
|
||||
* @param {String} filterRegionId The id for the filter element.
|
||||
*/
|
||||
export const init = filterRegionId => {
|
||||
|
||||
const filterSet = document.getElementById(filterRegionId);
|
||||
|
||||
// Create and initialize filter.
|
||||
const coreFilter = new CoreFilter(filterSet, function(filters, pendingPromise) {
|
||||
DynamicTable.setFilters(
|
||||
DynamicTable.getTableFromId(filterSet.dataset.tableRegion),
|
||||
{
|
||||
jointype: parseInt(filterSet.querySelector(Selectors.filterset.fields.join).value, 10),
|
||||
filters,
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
pendingPromise.resolve();
|
||||
|
||||
return result;
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
});
|
||||
coreFilter.init();
|
||||
|
||||
/**
|
||||
* Set the current filter options based on a provided configuration.
|
||||
*
|
||||
* @param {Object} config
|
||||
* @param {Number} config.jointype
|
||||
* @param {Object} config.filters
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const setFilterFromConfig = config => {
|
||||
const filterConfig = Object.entries(config.filters);
|
||||
|
||||
if (!filterConfig.length) {
|
||||
// There are no filters to set from.
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Set the main join type.
|
||||
filterSet.querySelector(Selectors.filterset.fields.join).value = config.jointype;
|
||||
|
||||
const filterPromises = filterConfig.map(([filterType, filterData]) => {
|
||||
if (filterType === 'courseid') {
|
||||
// The courseid is a special case.
|
||||
return false;
|
||||
}
|
||||
|
||||
const filterValues = filterData.values;
|
||||
|
||||
if (!filterValues.length) {
|
||||
// There are no values for this filter.
|
||||
// Skip it.
|
||||
return false;
|
||||
}
|
||||
return coreFilter.addFilterRow()
|
||||
.then(([filterRow]) => {
|
||||
coreFilter.addFilter(filterRow, filterType, filterValues);
|
||||
return;
|
||||
});
|
||||
}).filter(promise => promise);
|
||||
|
||||
if (!filterPromises.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.all(filterPromises)
|
||||
.then(() => {
|
||||
return coreFilter.removeEmptyFilters();
|
||||
})
|
||||
.then(() => {
|
||||
coreFilter.updateFiltersOptions();
|
||||
return;
|
||||
})
|
||||
.then(() => {
|
||||
coreFilter.updateTableFromFilter();
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
// Initialize DynamicTable for showing result.
|
||||
const tableRoot = DynamicTable.getTableFromId(filterSet.dataset.tableRegion);
|
||||
const initialFilters = DynamicTable.getFilters(tableRoot);
|
||||
if (initialFilters) {
|
||||
const initialFilterPromise = new Pending('core/filter:setFilterFromConfig');
|
||||
// Apply the initial filter configuration.
|
||||
setFilterFromConfig(initialFilters)
|
||||
.then(() => initialFilterPromise.resolve())
|
||||
.catch();
|
||||
}
|
||||
};
|
||||
|
@ -1,503 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Participants filter managemnet.
|
||||
*
|
||||
* @module core_user/participants_filter
|
||||
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import CourseFilter from './local/participantsfilter/filtertypes/courseid';
|
||||
import * as DynamicTable from 'core_table/dynamic';
|
||||
import GenericFilter from './local/participantsfilter/filter';
|
||||
import {get_strings as getStrings} from 'core/str';
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
import Selectors from './local/participantsfilter/selectors';
|
||||
import Templates from 'core/templates';
|
||||
import CustomEvents from 'core/custom_interaction_events';
|
||||
import jQuery from 'jquery';
|
||||
|
||||
/**
|
||||
* Initialise the participants filter on the element with the given id.
|
||||
*
|
||||
* @param {String} participantsRegionId
|
||||
*/
|
||||
export const init = participantsRegionId => {
|
||||
// Keep a reference to the filterset.
|
||||
const filterSet = document.querySelector(`#${participantsRegionId}`);
|
||||
|
||||
// Keep a reference to all of the active filters.
|
||||
const activeFilters = {
|
||||
courseid: new CourseFilter('courseid', filterSet),
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the filter list region.
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
const getFilterRegion = () => filterSet.querySelector(Selectors.filterset.regions.filterlist);
|
||||
|
||||
/**
|
||||
* Add an unselected filter row.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
const addFilterRow = () => {
|
||||
const pendingPromise = new Pending('core_user/participantsfilter:addFilterRow');
|
||||
|
||||
const rownum = 1 + getFilterRegion().querySelectorAll(Selectors.filter.region).length;
|
||||
return Templates.renderForPromise('core_user/local/participantsfilter/filterrow', {"rownumber": rownum})
|
||||
.then(({html, js}) => {
|
||||
const newContentNodes = Templates.appendNodeContents(getFilterRegion(), html, js);
|
||||
|
||||
return newContentNodes;
|
||||
})
|
||||
.then(filterRow => {
|
||||
// Note: This is a nasty hack.
|
||||
// We should try to find a better way of doing this.
|
||||
// We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy
|
||||
// it in place.
|
||||
const typeList = filterSet.querySelector(Selectors.data.typeList);
|
||||
|
||||
filterRow.forEach(contentNode => {
|
||||
const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);
|
||||
|
||||
if (contentTypeList) {
|
||||
contentTypeList.innerHTML = typeList.innerHTML;
|
||||
}
|
||||
});
|
||||
|
||||
return filterRow;
|
||||
})
|
||||
.then(filterRow => {
|
||||
updateFiltersOptions();
|
||||
|
||||
return filterRow;
|
||||
})
|
||||
.then(result => {
|
||||
pendingPromise.resolve();
|
||||
|
||||
return result;
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the filter data source node fro the specified filter type.
|
||||
*
|
||||
* @param {String} filterType
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
const getFilterDataSource = filterType => {
|
||||
const filterDataNode = filterSet.querySelector(Selectors.filterset.regions.datasource);
|
||||
|
||||
return filterDataNode.querySelector(Selectors.data.fields.byName(filterType));
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a filter to the list of active filters, performing any necessary setup.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
* @param {String} filterType
|
||||
* @param {Array} initialFilterValues The initially selected values for the filter
|
||||
* @returns {Filter}
|
||||
*/
|
||||
const addFilter = async(filterRow, filterType, initialFilterValues) => {
|
||||
// Name the filter on the filter row.
|
||||
filterRow.dataset.filterType = filterType;
|
||||
|
||||
const filterDataNode = getFilterDataSource(filterType);
|
||||
|
||||
// Instantiate the Filter class.
|
||||
let Filter = GenericFilter;
|
||||
if (filterDataNode?.dataset.filterTypeClass) {
|
||||
Filter = await import(filterDataNode.dataset.filterTypeClass);
|
||||
}
|
||||
activeFilters[filterType] = new Filter(filterType, filterSet, initialFilterValues);
|
||||
|
||||
// Disable the select.
|
||||
const typeField = filterRow.querySelector(Selectors.filter.fields.type);
|
||||
typeField.value = filterType;
|
||||
typeField.disabled = 'disabled';
|
||||
|
||||
// Update the list of available filter types.
|
||||
updateFiltersOptions();
|
||||
|
||||
return activeFilters[filterType];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the registered filter class for the named filter.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Object} See the Filter class.
|
||||
*/
|
||||
const getFilterObject = name => {
|
||||
return activeFilters[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove or replace the specified filter row and associated class, ensuring that if there is only one filter row,
|
||||
* that it is replaced instead of being removed.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
* @param {Bool} refreshContent Whether to refresh the table content when removing
|
||||
*/
|
||||
const removeOrReplaceFilterRow = (filterRow, refreshContent) => {
|
||||
const filterCount = getFilterRegion().querySelectorAll(Selectors.filter.region).length;
|
||||
|
||||
if (filterCount === 1) {
|
||||
replaceFilterRow(filterRow, refreshContent);
|
||||
} else {
|
||||
removeFilterRow(filterRow, refreshContent);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the specified filter row and associated class.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
* @param {Bool} refreshContent Whether to refresh the table content when removing
|
||||
*/
|
||||
const removeFilterRow = async(filterRow, refreshContent = true) => {
|
||||
const filterType = filterRow.querySelector(Selectors.filter.fields.type);
|
||||
const hasFilterValue = !!filterType.value;
|
||||
|
||||
// Remove the filter object.
|
||||
removeFilterObject(filterRow.dataset.filterType);
|
||||
|
||||
// Remove the actual filter HTML.
|
||||
filterRow.remove();
|
||||
|
||||
// Update the list of available filter types.
|
||||
updateFiltersOptions();
|
||||
|
||||
if (hasFilterValue && refreshContent) {
|
||||
// Refresh the table if there was any content in this row.
|
||||
updateTableFromFilter();
|
||||
}
|
||||
|
||||
// Update filter fieldset legends.
|
||||
const filterLegends = await getAvailableFilterLegends();
|
||||
|
||||
getFilterRegion().querySelectorAll(Selectors.filter.region).forEach((filterRow, index) => {
|
||||
filterRow.querySelector('legend').innerText = filterLegends[index];
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace the specified filter row with a new one.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
* @param {Bool} refreshContent Whether to refresh the table content when removing
|
||||
* @param {Number} rowNum The number used to label the filter fieldset legend (eg Row 1). Defaults to 1 (the first filter).
|
||||
* @return {Promise}
|
||||
*/
|
||||
const replaceFilterRow = (filterRow, refreshContent = true, rowNum = 1) => {
|
||||
// Remove the filter object.
|
||||
removeFilterObject(filterRow.dataset.filterType);
|
||||
|
||||
return Templates.renderForPromise('core_user/local/participantsfilter/filterrow', {"rownumber": rowNum})
|
||||
.then(({html, js}) => {
|
||||
const newContentNodes = Templates.replaceNode(filterRow, html, js);
|
||||
|
||||
return newContentNodes;
|
||||
})
|
||||
.then(filterRow => {
|
||||
// Note: This is a nasty hack.
|
||||
// We should try to find a better way of doing this.
|
||||
// We do not have the list of types in a readily consumable format, so we take the pre-rendered one and copy
|
||||
// it in place.
|
||||
const typeList = filterSet.querySelector(Selectors.data.typeList);
|
||||
|
||||
filterRow.forEach(contentNode => {
|
||||
const contentTypeList = contentNode.querySelector(Selectors.filter.fields.type);
|
||||
|
||||
if (contentTypeList) {
|
||||
contentTypeList.innerHTML = typeList.innerHTML;
|
||||
}
|
||||
});
|
||||
|
||||
return filterRow;
|
||||
})
|
||||
.then(filterRow => {
|
||||
updateFiltersOptions();
|
||||
|
||||
return filterRow;
|
||||
})
|
||||
.then(filterRow => {
|
||||
// Refresh the table.
|
||||
if (refreshContent) {
|
||||
return updateTableFromFilter();
|
||||
} else {
|
||||
return filterRow;
|
||||
}
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the Filter Object from the register.
|
||||
*
|
||||
* @param {string} filterName The name of the filter to be removed
|
||||
*/
|
||||
const removeFilterObject = filterName => {
|
||||
if (filterName) {
|
||||
const filter = getFilterObject(filterName);
|
||||
if (filter) {
|
||||
filter.tearDown();
|
||||
|
||||
// Remove from the list of active filters.
|
||||
delete activeFilters[filterName];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove all filters.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const removeAllFilters = () => {
|
||||
const pendingPromise = new Pending('core_user/participantsfilter:setFilterFromConfig');
|
||||
|
||||
const filters = getFilterRegion().querySelectorAll(Selectors.filter.region);
|
||||
filters.forEach(filterRow => removeOrReplaceFilterRow(filterRow, false));
|
||||
|
||||
// Refresh the table.
|
||||
return updateTableFromFilter()
|
||||
.then(result => {
|
||||
pendingPromise.resolve();
|
||||
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove any empty filters.
|
||||
*/
|
||||
const removeEmptyFilters = () => {
|
||||
const filters = getFilterRegion().querySelectorAll(Selectors.filter.region);
|
||||
filters.forEach(filterRow => {
|
||||
const filterType = filterRow.querySelector(Selectors.filter.fields.type);
|
||||
if (!filterType.value) {
|
||||
removeOrReplaceFilterRow(filterRow, false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the list of filter types to filter out those already selected.
|
||||
*/
|
||||
const updateFiltersOptions = () => {
|
||||
const filters = getFilterRegion().querySelectorAll(Selectors.filter.region);
|
||||
filters.forEach(filterRow => {
|
||||
const options = filterRow.querySelectorAll(Selectors.filter.fields.type + ' option');
|
||||
options.forEach(option => {
|
||||
if (option.value === filterRow.dataset.filterType) {
|
||||
option.classList.remove('hidden');
|
||||
option.disabled = false;
|
||||
} else if (activeFilters[option.value]) {
|
||||
option.classList.add('hidden');
|
||||
option.disabled = true;
|
||||
} else {
|
||||
option.classList.remove('hidden');
|
||||
option.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Configure the state of the "Add row" button.
|
||||
// This button is disabled when there is a filter row available for each condition.
|
||||
const addRowButton = filterSet.querySelector(Selectors.filterset.actions.addRow);
|
||||
const filterDataNode = filterSet.querySelectorAll(Selectors.data.fields.all);
|
||||
if (filterDataNode.length <= filters.length) {
|
||||
addRowButton.setAttribute('disabled', 'disabled');
|
||||
} else {
|
||||
addRowButton.removeAttribute('disabled');
|
||||
}
|
||||
|
||||
if (filters.length === 1) {
|
||||
filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.add('hidden');
|
||||
filterSet.querySelector(Selectors.filterset.fields.join).value = 2;
|
||||
filterSet.dataset.filterverb = 2;
|
||||
} else {
|
||||
filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.remove('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the current filter options based on a provided configuration.
|
||||
*
|
||||
* @param {Object} config
|
||||
* @param {Number} config.jointype
|
||||
* @param {Object} config.filters
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const setFilterFromConfig = config => {
|
||||
const filterConfig = Object.entries(config.filters);
|
||||
|
||||
if (!filterConfig.length) {
|
||||
// There are no filters to set from.
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Set the main join type.
|
||||
filterSet.querySelector(Selectors.filterset.fields.join).value = config.jointype;
|
||||
|
||||
const filterPromises = filterConfig.map(([filterType, filterData]) => {
|
||||
if (filterType === 'courseid') {
|
||||
// The courseid is a special case.
|
||||
return false;
|
||||
}
|
||||
|
||||
const filterValues = filterData.values;
|
||||
|
||||
if (!filterValues.length) {
|
||||
// There are no values for this filter.
|
||||
// Skip it.
|
||||
return false;
|
||||
}
|
||||
|
||||
return addFilterRow().then(([filterRow]) => addFilter(filterRow, filterType, filterValues));
|
||||
}).filter(promise => promise);
|
||||
|
||||
if (!filterPromises.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.all(filterPromises).then(() => {
|
||||
return removeEmptyFilters();
|
||||
})
|
||||
.then(updateFiltersOptions)
|
||||
.then(updateTableFromFilter);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the Dynamic table based upon the current filter.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
const updateTableFromFilter = () => {
|
||||
const pendingPromise = new Pending('core_user/participantsfilter:updateTableFromFilter');
|
||||
|
||||
const filters = {};
|
||||
Object.values(activeFilters).forEach(filter => {
|
||||
filters[filter.filterValue.name] = filter.filterValue;
|
||||
});
|
||||
|
||||
return DynamicTable.setFilters(
|
||||
DynamicTable.getTableFromId(filterSet.dataset.tableRegion),
|
||||
{
|
||||
jointype: parseInt(filterSet.querySelector(Selectors.filterset.fields.join).value, 10),
|
||||
filters,
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
pendingPromise.resolve();
|
||||
|
||||
return result;
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the strings used to populate the fieldset legends for the maximum number of filters possible.
|
||||
*
|
||||
* @return {array}
|
||||
*/
|
||||
const getAvailableFilterLegends = async() => {
|
||||
const maxFilters = document.querySelector(Selectors.data.typeListSelect).length - 1;
|
||||
let requests = [];
|
||||
|
||||
[...Array(maxFilters)].forEach((_, rowIndex) => {
|
||||
requests.push({
|
||||
"key": "filterrowlegend",
|
||||
"component": "core_user",
|
||||
// Add 1 since rows begin at 1 (index begins at zero).
|
||||
"param": rowIndex + 1
|
||||
});
|
||||
});
|
||||
|
||||
const legendStrings = await getStrings(requests)
|
||||
.then(fetchedStrings => {
|
||||
return fetchedStrings;
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
|
||||
return legendStrings;
|
||||
};
|
||||
|
||||
// Add listeners for the main actions.
|
||||
filterSet.querySelector(Selectors.filterset.region).addEventListener('click', e => {
|
||||
if (e.target.closest(Selectors.filterset.actions.addRow)) {
|
||||
e.preventDefault();
|
||||
|
||||
addFilterRow();
|
||||
}
|
||||
|
||||
if (e.target.closest(Selectors.filterset.actions.applyFilters)) {
|
||||
e.preventDefault();
|
||||
|
||||
updateTableFromFilter();
|
||||
}
|
||||
|
||||
if (e.target.closest(Selectors.filterset.actions.resetFilters)) {
|
||||
e.preventDefault();
|
||||
|
||||
removeAllFilters();
|
||||
}
|
||||
});
|
||||
|
||||
// Add the listener to remove a single filter.
|
||||
filterSet.querySelector(Selectors.filterset.regions.filterlist).addEventListener('click', e => {
|
||||
if (e.target.closest(Selectors.filter.actions.remove)) {
|
||||
e.preventDefault();
|
||||
|
||||
removeOrReplaceFilterRow(e.target.closest(Selectors.filter.region), true);
|
||||
}
|
||||
});
|
||||
|
||||
// Add listeners for the filter type selection.
|
||||
let filterRegion = jQuery(getFilterRegion());
|
||||
CustomEvents.define(filterRegion, [CustomEvents.events.accessibleChange]);
|
||||
filterRegion.on(CustomEvents.events.accessibleChange, e => {
|
||||
const typeField = e.target.closest(Selectors.filter.fields.type);
|
||||
if (typeField && typeField.value) {
|
||||
const filter = e.target.closest(Selectors.filter.region);
|
||||
|
||||
addFilter(filter, typeField.value);
|
||||
}
|
||||
});
|
||||
|
||||
filterSet.querySelector(Selectors.filterset.fields.join).addEventListener('change', e => {
|
||||
filterSet.dataset.filterverb = e.target.value;
|
||||
});
|
||||
|
||||
const tableRoot = DynamicTable.getTableFromId(filterSet.dataset.tableRegion);
|
||||
const initialFilters = DynamicTable.getFilters(tableRoot);
|
||||
if (initialFilters) {
|
||||
const initialFilterPromise = new Pending('core_user/participantsfilter:setFilterFromConfig');
|
||||
// Apply the initial filter configuration.
|
||||
setFilterFromConfig(initialFilters)
|
||||
.then(() => initialFilterPromise.resolve())
|
||||
.catch();
|
||||
}
|
||||
};
|
@ -23,12 +23,9 @@
|
||||
*/
|
||||
namespace core_user\output;
|
||||
|
||||
use context_course;
|
||||
use core_user\fields;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Class for rendering user filters on the course participants page.
|
||||
@ -36,29 +33,7 @@ use templatable;
|
||||
* @copyright 2020 Michael Hawkins <michaelh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class participants_filter implements renderable, templatable {
|
||||
|
||||
/** @var context_course $context The context where the filters are being rendered. */
|
||||
protected $context;
|
||||
|
||||
/** @var string $tableregionid The table to be updated by this filter */
|
||||
protected $tableregionid;
|
||||
|
||||
/** @var stdClass $course The course shown */
|
||||
protected $course;
|
||||
|
||||
/**
|
||||
* Participants filter constructor.
|
||||
*
|
||||
* @param context_course $context The context where the filters are being rendered.
|
||||
* @param string $tableregionid The table to be updated by this filter
|
||||
*/
|
||||
public function __construct(context_course $context, string $tableregionid) {
|
||||
$this->context = $context;
|
||||
$this->tableregionid = $tableregionid;
|
||||
|
||||
$this->course = get_course($context->instanceid);
|
||||
}
|
||||
class participants_filter extends \core\output\datafilter {
|
||||
|
||||
/**
|
||||
* Get data for all filter types.
|
||||
@ -345,7 +320,7 @@ class participants_filter implements renderable, templatable {
|
||||
get_string('country'),
|
||||
false,
|
||||
true,
|
||||
'core_user/local/participantsfilter/filtertypes/country',
|
||||
'core/datafilter/filtertypes/country',
|
||||
array_map(function(string $code, string $name): stdClass {
|
||||
return (object) [
|
||||
'value' => $code,
|
||||
@ -366,7 +341,7 @@ class participants_filter implements renderable, templatable {
|
||||
get_string('filterbykeyword', 'core_user'),
|
||||
true,
|
||||
true,
|
||||
'core_user/local/participantsfilter/filtertypes/keyword',
|
||||
'core/datafilter/filtertypes/keyword',
|
||||
[],
|
||||
true
|
||||
);
|
||||
@ -388,41 +363,4 @@ class participants_filter implements renderable, templatable {
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a standardised filter object.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $title
|
||||
* @param bool $custom
|
||||
* @param bool $multiple
|
||||
* @param string|null $filterclass
|
||||
* @param array $values
|
||||
* @param bool $allowempty
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function get_filter_object(
|
||||
string $name,
|
||||
string $title,
|
||||
bool $custom,
|
||||
bool $multiple,
|
||||
?string $filterclass,
|
||||
array $values,
|
||||
bool $allowempty = false
|
||||
): ?stdClass {
|
||||
|
||||
if (!$allowempty && empty($values)) {
|
||||
// Do not show empty filters.
|
||||
return null;
|
||||
}
|
||||
|
||||
return (object) [
|
||||
'name' => $name,
|
||||
'title' => $title,
|
||||
'allowcustom' => $custom,
|
||||
'allowmultiple' => $multiple,
|
||||
'filtertypeclass' => $filterclass,
|
||||
'values' => $values,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ class core_user_renderer extends plugin_renderer_base {
|
||||
* Render the data required for the participants filter on the course participants page.
|
||||
*
|
||||
* @param context $context The context of the course being displayed
|
||||
* @param string $tableregionid The table to be updated by this filter
|
||||
* @param string $tableregionid Container of the table to be updated by this filter, is used to retrieve the table
|
||||
* @return string
|
||||
*/
|
||||
public function participants_filter(context $context, string $tableregionid): string {
|
||||
|
@ -39,39 +39,10 @@
|
||||
]
|
||||
}
|
||||
}}
|
||||
|
||||
<div id="core_user-participantsfilter-{{uniqid}}" class="filter-group my-2 p-2 bg-light border-radius border" data-table-region="{{tableregionid}}" data-table-course-id="{{courseid}}" data-filterverb="2">
|
||||
<div data-filterregion="filtermatch" class="hidden">
|
||||
<label for="core_user-local-participantsfilter-jointype-{{uniqid}}" class="my-0" aria-hidden="true">
|
||||
{{#str}}match, core_user{{/str}}
|
||||
</label>
|
||||
<select class="custom-select" data-filterfield="join" id="core_user-local-participantsfilter-jointype-{{uniqid}}"
|
||||
aria-label="{{#str}}filtersetmatchdescription, core_user{{/str}}">
|
||||
<option value="0">{{#str}}none{{/str}}</option>
|
||||
<option value="1">{{#str}}any{{/str}}</option>
|
||||
<option value="2" selected>{{#str}}all{{/str}}</option>
|
||||
</select>
|
||||
<span aria-hidden="true">{{#str}}matchofthefollowing, core_user{{/str}}</span>
|
||||
</div>
|
||||
|
||||
<div data-filterregion="filters">
|
||||
{{> core_user/local/participantsfilter/filterrow }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex" data-filterregion="actions">
|
||||
|
||||
<button type="button" class="btn btn-link text-reset" data-filteraction="add">
|
||||
<i class="fa fa-plus"></i><span class="pl-3">{{#str}}addcondition, core_user{{/str}}</span>
|
||||
</button>
|
||||
<button data-filteraction="reset" type="button" class="btn btn-secondary ml-auto mr-2">{{#str}}clearfilters, core_user{{/str}}</button>
|
||||
<button data-filteraction="apply" type="button" class="btn btn-primary">{{#str}}applyfilters, core_user{{/str}}</button>
|
||||
</div>
|
||||
|
||||
{{> core_user/local/participantsfilter/filtertypes}}
|
||||
</div>
|
||||
{{> core/datafilter/filter }}
|
||||
|
||||
{{#js}}
|
||||
require(['core_user/participantsfilter'], function(ParticipantsFilter) {
|
||||
ParticipantsFilter.init('core_user-participantsfilter-{{uniqid}}');
|
||||
});
|
||||
require(['core_user/participants_filter'], function(ParticipantsFilter) {
|
||||
ParticipantsFilter.init('core-filter-{{uniqid}}');
|
||||
});
|
||||
{{/js}}
|
||||
|
@ -9,6 +9,23 @@ This files describes API changes for code that uses the user API.
|
||||
* deleted
|
||||
* all potential fullname fields
|
||||
|
||||
* Participant filter is moved to core as an API which can be used in different areas of core by implementing the API
|
||||
and filterable objects. As a part of making the API mature as a core one, these are the js files moved from core
|
||||
user to core library:
|
||||
* user/amd/src/local/participantsfilter/filter.js → lib/amd/src/datafilter/filtertype.js
|
||||
* user/amd/src/local/participantsfilter/filtertypes/country.js → lib/amd/src/datafilter/filtertypes/country.js
|
||||
* user/amd/src/local/participantsfilter/filtertypes/courseid.js → lib/amd/src/datafilter/filtertypes/courseid.js
|
||||
* user/amd/src/local/participantsfilter/filtertypes/keyword.js → lib/amd/src/datafilter/filtertypes/keyword.js
|
||||
* user/amd/src/local/participantsfilter/selectors.js → lib/amd/src/datafilter/selectors.js
|
||||
The following mustache have been moved from core user to core library:
|
||||
* user/templates/local/participantsfilter/filterrow.mustache → lib/templates/datafilter/filter_row.mustache
|
||||
* user/templates/local/participantsfilter/filtertype.mustache → lib/templates/datafilter/filter_type.mustache
|
||||
* user/templates/local/participantsfilter/filtertypes.mustache → lib/templates/datafilter/filter_types.mustache
|
||||
* user/templates/local/participantsfilter/autocomplete_layout.mustache → lib/templates/datafilter/autocomplete_layout.mustache
|
||||
* user/templates/local/participantsfilter/autocomplete_selection.mustache → lib/templates/datafilter/autocomplete_selection.mustache
|
||||
* user/templates/local/participantsfilter/autocomplete_selection_items.mustache → lib/templates/datafilter/autocomplete_selection_items.mustache
|
||||
Class participant_filter now extends core filter api in core user.
|
||||
|
||||
=== 4.0 ===
|
||||
|
||||
* External function core_user_external::update_users() will now fail on a per user basis. Previously if one user
|
||||
|
Loading…
x
Reference in New Issue
Block a user