mirror of
https://github.com/moodle/moodle.git
synced 2025-03-14 12:40:01 +01:00
Merge branch 'MDL-67743-master-final' of git://github.com/andrewnicols/moodle
This commit is contained in:
commit
d5eec2a2fd
@ -22,7 +22,19 @@
|
||||
* @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['countparticipantsfound'] = '{$a} participants found';
|
||||
$string['filtersetmatchdescription'] = 'How multiple filters should be combined';
|
||||
$string['match'] = 'Match';
|
||||
$string['matchofthefollowing'] = 'of the following:';
|
||||
$string['placeholdertypeorselect'] = 'Type or select...';
|
||||
$string['placeholdertype'] = 'Type...';
|
||||
$string['privacy:courserequestpath'] = 'Requested courses';
|
||||
$string['privacy:descriptionpath'] = 'Profile description';
|
||||
$string['privacy:devicespath'] = 'User devices';
|
||||
@ -126,6 +138,8 @@ $string['privacy:passwordresetpath'] = 'Password resets';
|
||||
$string['privacy:profileimagespath'] = 'Profile images';
|
||||
$string['privacy:privatefilespath'] = 'Private files';
|
||||
$string['privacy:sessionpath'] = 'Session data';
|
||||
$string['filterbykeyword'] = 'Keyword';
|
||||
$string['selectfiltertype'] = 'Select';
|
||||
$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.';
|
||||
|
2
lib/amd/build/form-autocomplete.min.js
vendored
2
lib/amd/build/form-autocomplete.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
2
lib/amd/build/templates.min.js
vendored
2
lib/amd/build/templates.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
@ -114,7 +114,7 @@ function($, log, str, templates, notification, LoadingIcon) {
|
||||
});
|
||||
var context = $.extend({items: items}, options, state);
|
||||
// Render the template.
|
||||
return templates.render('core/form_autocomplete_selection_items', context)
|
||||
return templates.render(options.templates.items, context)
|
||||
.then(function(html, js) {
|
||||
// Add it to the page.
|
||||
templates.replaceNodeContents(newSelection, html, js);
|
||||
@ -970,10 +970,11 @@ function($, log, str, templates, notification, LoadingIcon) {
|
||||
* @param {Boolean} showSuggestions - If suggestions should be shown
|
||||
* @param {String} noSelectionString - Text to display when there is no selection
|
||||
* @param {Boolean} closeSuggestionsOnSelect - Whether to close the suggestions immediately after making a selection.
|
||||
* @param {Object} templateOverrides A set of templates to use instead of the standard templates
|
||||
* @return {Promise}
|
||||
*/
|
||||
enhance: function(selector, tags, ajax, placeholder, caseSensitive, showSuggestions, noSelectionString,
|
||||
closeSuggestionsOnSelect) {
|
||||
closeSuggestionsOnSelect, templateOverrides) {
|
||||
// Set some default values.
|
||||
var options = {
|
||||
selector: selector,
|
||||
@ -982,7 +983,14 @@ function($, log, str, templates, notification, LoadingIcon) {
|
||||
placeholder: placeholder,
|
||||
caseSensitive: false,
|
||||
showSuggestions: true,
|
||||
noSelectionString: noSelectionString
|
||||
noSelectionString: noSelectionString,
|
||||
templates: $.extend({
|
||||
input: 'core/form_autocomplete_input',
|
||||
items: 'core/form_autocomplete_selection_items',
|
||||
layout: 'core/form_autocomplete_layout',
|
||||
selection: 'core/form_autocomplete_selection',
|
||||
suggestions: 'core/form_autocomplete_suggestions',
|
||||
}, templateOverrides),
|
||||
};
|
||||
var pendingKey = 'autocomplete-setup-' + selector;
|
||||
M.util.js_pending(pendingKey);
|
||||
@ -1058,27 +1066,35 @@ function($, log, str, templates, notification, LoadingIcon) {
|
||||
// Collect rendered inline JS to be executed once the HTML is shown.
|
||||
var collectedjs = '';
|
||||
|
||||
var renderInput = templates.render('core/form_autocomplete_input', context).then(function(html, js) {
|
||||
collectedjs += js;
|
||||
return html;
|
||||
var renderLayout = templates.render(options.templates.layout, {})
|
||||
.then(function(html) {
|
||||
return $(html);
|
||||
});
|
||||
|
||||
var renderDatalist = templates.render('core/form_autocomplete_suggestions', context).then(function(html, js) {
|
||||
var renderInput = templates.render(options.templates.input, context).then(function(html, js) {
|
||||
collectedjs += js;
|
||||
return html;
|
||||
return $(html);
|
||||
});
|
||||
|
||||
var renderSelection = templates.render('core/form_autocomplete_selection', context).then(function(html, js) {
|
||||
var renderDatalist = templates.render(options.templates.suggestions, context).then(function(html, js) {
|
||||
collectedjs += js;
|
||||
return html;
|
||||
return $(html);
|
||||
});
|
||||
|
||||
return $.when(renderInput, renderDatalist, renderSelection)
|
||||
.then(function(input, suggestions, selection) {
|
||||
var renderSelection = templates.render(options.templates.selection, context).then(function(html, js) {
|
||||
collectedjs += js;
|
||||
return $(html);
|
||||
});
|
||||
|
||||
return $.when(renderLayout, renderInput, renderDatalist, renderSelection)
|
||||
.then(function(layout, input, suggestions, selection) {
|
||||
originalSelect.hide();
|
||||
originalSelect.after(suggestions);
|
||||
originalSelect.after(input);
|
||||
originalSelect.after(selection);
|
||||
var container = originalSelect.parent();
|
||||
|
||||
container.append(layout);
|
||||
container.find('[data-region="form_autocomplete-input"]').replaceWith(input);
|
||||
container.find('[data-region="form_autocomplete-suggestions"]').replaceWith(suggestions);
|
||||
container.find('[data-region="form_autocomplete-selection"]').replaceWith(selection);
|
||||
|
||||
templates.runTemplateJS(collectedjs);
|
||||
|
||||
|
@ -876,6 +876,7 @@ define([
|
||||
* @param {String} newHTML - HTML to insert / replace.
|
||||
* @param {String} newJS - Javascript to run after the insertion.
|
||||
* @param {Boolean} replaceChildNodes - Replace only the childnodes, alternative is to replace the entire node.
|
||||
* @return {Array} The list of new DOM Nodes
|
||||
*/
|
||||
var domReplace = function(element, newHTML, newJS, replaceChildNodes) {
|
||||
var replaceNode = $(element);
|
||||
@ -904,7 +905,11 @@ define([
|
||||
runTemplateJS(newJS);
|
||||
// Notify all filters about the new content.
|
||||
event.notifyFilterContentUpdated(newNodes);
|
||||
|
||||
return newNodes.get();
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1043,17 +1048,23 @@ define([
|
||||
* @param {jQuery|String} element - Element or selector to prepend HTML to
|
||||
* @param {String} html - HTML to prepend
|
||||
* @param {String} js - Javascript to run after we prepend the html
|
||||
* @return {Array} The list of new DOM Nodes
|
||||
*/
|
||||
var domPrepend = function(element, html, js) {
|
||||
var node = $(element);
|
||||
if (node.length) {
|
||||
// Prepend the html.
|
||||
node.prepend(html);
|
||||
var newContent = $(html);
|
||||
node.prepend(newContent);
|
||||
// Run any javascript associated with the new HTML.
|
||||
runTemplateJS(js);
|
||||
// Notify all filters about the new content.
|
||||
event.notifyFilterContentUpdated(node);
|
||||
|
||||
return newContent.get();
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1064,17 +1075,23 @@ define([
|
||||
* @param {jQuery|String} element - Element or selector to append HTML to
|
||||
* @param {String} html - HTML to append
|
||||
* @param {String} js - Javascript to run after we append the html
|
||||
* @return {Array} The list of new DOM Nodes
|
||||
*/
|
||||
var domAppend = function(element, html, js) {
|
||||
var node = $(element);
|
||||
if (node.length) {
|
||||
// Append the html.
|
||||
node.append(html);
|
||||
var newContent = $(html);
|
||||
node.append(newContent);
|
||||
// Run any javascript associated with the new HTML.
|
||||
runTemplateJS(js);
|
||||
// Notify all filters about the new content.
|
||||
event.notifyFilterContentUpdated(node);
|
||||
|
||||
return newContent.get();
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
return /** @alias module:core/templates */ {
|
||||
@ -1175,9 +1192,10 @@ define([
|
||||
* @param {JQuery} element - Element or selector to replace.
|
||||
* @param {String} newHTML - HTML to insert / replace.
|
||||
* @param {String} newJS - Javascript to run after the insertion.
|
||||
* @return {Array} The list of new DOM Nodes
|
||||
*/
|
||||
replaceNodeContents: function(element, newHTML, newJS) {
|
||||
domReplace(element, newHTML, newJS, true);
|
||||
return domReplace(element, newHTML, newJS, true);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1187,9 +1205,10 @@ define([
|
||||
* @param {JQuery} element - Element or selector to replace.
|
||||
* @param {String} newHTML - HTML to insert / replace.
|
||||
* @param {String} newJS - Javascript to run after the insertion.
|
||||
* @return {Array} The list of new DOM Nodes
|
||||
*/
|
||||
replaceNode: function(element, newHTML, newJS) {
|
||||
domReplace(element, newHTML, newJS, false);
|
||||
return domReplace(element, newHTML, newJS, false);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1199,9 +1218,10 @@ define([
|
||||
* @param {jQuery|String} element - Element or selector to prepend HTML to
|
||||
* @param {String} html - HTML to prepend
|
||||
* @param {String} js - Javascript to run after we prepend the html
|
||||
* @return {Array} The list of new DOM Nodes
|
||||
*/
|
||||
prependNodeContents: function(element, html, js) {
|
||||
domPrepend(element, html, js);
|
||||
return domPrepend(element, html, js);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1211,9 +1231,10 @@ define([
|
||||
* @param {jQuery|String} element - Element or selector to append HTML to
|
||||
* @param {String} html - HTML to append
|
||||
* @param {String} js - Javascript to run after we append the html
|
||||
* @return {Array} The list of new DOM Nodes
|
||||
*/
|
||||
appendNodeContents: function(element, html, js) {
|
||||
domAppend(element, html, js);
|
||||
return domAppend(element, html, js);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
1
lib/table/classes/external/dynamic/fetch.php
vendored
1
lib/table/classes/external/dynamic/fetch.php
vendored
@ -217,6 +217,7 @@ class fetch extends external_api {
|
||||
}
|
||||
|
||||
$filterset = new $filtersetclass();
|
||||
$filterset->set_join_type($jointype);
|
||||
foreach ($filters as $rawfilter) {
|
||||
$filterset->add_filter_from_params(
|
||||
$rawfilter['name'],
|
||||
|
@ -1503,7 +1503,7 @@ class flexible_table {
|
||||
$this->sortdata = [];
|
||||
foreach ($sortdata as $sortitem) {
|
||||
if (!array_key_exists($sortitem['sortby'], $this->sortdata)) {
|
||||
$this->sortdata[$sortitem['sortby']] = $sortitem['sortorder'];
|
||||
$this->sortdata[$sortitem['sortby']] = (int) $sortitem['sortorder'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,13 +36,15 @@
|
||||
{ "inputID": 1, "suggestionsId": 2, "selectionId": 3, "downArrowId": 4, "placeholder": "Select something" }
|
||||
}}
|
||||
{{#showSuggestions}}
|
||||
<div class="d-inline-block position-relative">
|
||||
<div class="d-md-inline-block mr-md-2 position-relative">
|
||||
<input type="text" id="{{inputId}}" class="form-control" list="{{suggestionsId}}" placeholder="{{placeholder}}" role="combobox" aria-expanded="false" autocomplete="off" autocorrect="off" autocapitalize="off" aria-autocomplete="list" aria-owns="{{suggestionsId}} {{selectionId}}" {{#tags}}data-tags="1"{{/tags}}/>
|
||||
<span class="form-autocomplete-downarrow position-absolute p-1" id="{{downArrowId}}">▼</span>
|
||||
</div>
|
||||
{{/showSuggestions}}
|
||||
{{^showSuggestions}}
|
||||
<input type="text" id="{{inputId}}" placeholder="{{placeholder}}" role="textbox" aria-owns="{{selectionId}}" {{#tags}}data-tags="1"{{/tags}}/>
|
||||
<div class="d-md-inline-block mr-md-2">
|
||||
<input type="text" id="{{inputId}}" class="form-control" placeholder="{{placeholder}}" role="textbox" aria-owns="{{selectionId}}" {{#tags}}data-tags="1"{{/tags}}/>
|
||||
</div>
|
||||
{{/showSuggestions}}
|
||||
|
||||
{{#js}}
|
||||
|
38
lib/templates/form_autocomplete_layout.mustache
Normal file
38
lib/templates/form_autocomplete_layout.mustache
Normal file
@ -0,0 +1,38 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core/form_autocomplete_layout
|
||||
|
||||
Moodle template for the layout of autocomplete elements.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-region="form_autocomplete-input"
|
||||
* data-region="form_autocomplete-suggestions"
|
||||
* data-region="form_autocomplete-selection"
|
||||
|
||||
Context variables required for this template:
|
||||
* none
|
||||
|
||||
Example context (json):
|
||||
{}
|
||||
}}
|
||||
<div data-region="form_autocomplete-selection"></div>
|
||||
<div data-region="form_autocomplete-input"></div>
|
||||
<div data-region="form_autocomplete-suggestions"></div>
|
@ -2462,6 +2462,12 @@ body.h5p-embed {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.border-radius {
|
||||
@if $enable-rounded {
|
||||
@include border-radius($card-border-radius);
|
||||
}
|
||||
}
|
||||
|
||||
// Emoji picker.
|
||||
$picker-width: 350px !default;
|
||||
$picker-width-xs: 320px !default;
|
||||
|
@ -299,3 +299,14 @@
|
||||
.user-enroller-panel {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
[data-filterverbfor],
|
||||
[data-filterregion="filter"]:last-child [data-filterregion="joinadverb"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-filterverb="0"] [data-filterverbfor="0"],
|
||||
[data-filterverb="1"] [data-filterverbfor="1"],
|
||||
[data-filterverb="2"] [data-filterverbfor="2"] {
|
||||
display: block;
|
||||
}
|
||||
|
@ -16042,6 +16042,15 @@ body.path-question-type .mform fieldset.hidden {
|
||||
.user-enroller-panel {
|
||||
width: 600px; }
|
||||
|
||||
[data-filterverbfor],
|
||||
[data-filterregion="filter"]:last-child [data-filterregion="joinadverb"] {
|
||||
display: none; }
|
||||
|
||||
[data-filterverb="0"] [data-filterverbfor="0"],
|
||||
[data-filterverb="1"] [data-filterverbfor="1"],
|
||||
[data-filterverb="2"] [data-filterverbfor="2"] {
|
||||
display: block; }
|
||||
|
||||
.search-results .result {
|
||||
margin-left: 0;
|
||||
margin-right: 0; }
|
||||
|
@ -11859,6 +11859,9 @@ body.h5p-embed .h5pmessages {
|
||||
color: #343a40;
|
||||
height: 1.5rem; }
|
||||
|
||||
.border-radius {
|
||||
border-radius: 0.25rem; }
|
||||
|
||||
.emoji-picker {
|
||||
width: 350px;
|
||||
height: 400px; }
|
||||
@ -16266,6 +16269,15 @@ body.path-question-type .mform fieldset.hidden {
|
||||
.user-enroller-panel {
|
||||
width: 600px; }
|
||||
|
||||
[data-filterverbfor],
|
||||
[data-filterregion="filter"]:last-child [data-filterregion="joinadverb"] {
|
||||
display: none; }
|
||||
|
||||
[data-filterverb="0"] [data-filterverbfor="0"],
|
||||
[data-filterverb="1"] [data-filterverbfor="1"],
|
||||
[data-filterverb="2"] [data-filterverbfor="2"] {
|
||||
display: block; }
|
||||
|
||||
.search-results .result {
|
||||
margin-left: 0;
|
||||
margin-right: 0; }
|
||||
|
2
user/amd/build/local/participantsfilter/filter.min.js
vendored
Normal file
2
user/amd/build/local/participantsfilter/filter.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
define ("core_user/local/participantsfilter/filter",["exports","core/form-autocomplete","./selectors","core/str"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=e(b);c=e(c);function e(a){return a&&a.__esModule?a:{default:a}}function f(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function g(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function g(a){f(i,d,e,g,h,"next",a)}function h(a){f(i,d,e,g,h,"throw",a)}g(void 0)})}}function h(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function i(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function j(a,b,c){if(b)i(a.prototype,b);if(c)i(a,c);return a}var k=function(a){return a.querySelectorAll(":checked")},l=function(){function a(b,c){h(this,a);this.filterType=b;this.rootNode=c;this.addValueSelector()}j(a,[{key:"tearDown",value:function tearDown(){}},{key:"addValueSelector",value:function(){var a=g(regeneratorRuntime.mark(function a(){var c,d;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:c=this.getFilterValueNode();c.innerHTML=this.getSourceDataForFilter().outerHTML;d=c.querySelector("select");a.t0=b.default;a.t1=d;a.t2="1"==d.dataset.allowCustom;a.next=8;return this.placeholder;case 8:a.t3=a.sent;a.t4=this.showSuggestions;a.t5=!d.multiple;a.t6={items:"core_user/local/participantsfilter/autocomplete_selection_items",layout:"core_user/local/participantsfilter/autocomplete_layout",selection:"core_user/local/participantsfilter/autocomplete_selection"};a.t0.enhance.call(a.t0,a.t1,a.t2,null,a.t3,!1,a.t4,null,a.t5,a.t6);case 13:case"end":return a.stop();}}},a,this)}));return function addValueSelector(){return a.apply(this,arguments)}}()},{key:"getSourceDataForFilter",value:function getSourceDataForFilter(){var a=this.rootNode.querySelector(c.default.filterset.regions.datasource);return a.querySelector(c.default.data.fields.byName(this.filterType))}},{key:"getFilterValueNode",value:function getFilterValueNode(){return this.filterRoot.querySelector(c.default.filter.regions.values)}},{key:"placeholder",get:function get(){return(0,d.get_string)("placeholdertypeorselect","core_user")}},{key:"showSuggestions",get:function get(){return!0}},{key:"filterRoot",get:function get(){return this.rootNode.querySelector(c.default.filter.byName(this.filterType))}},{key:"name",get:function get(){return this.filterType}},{key:"jointype",get:function get(){return this.filterRoot.querySelector(c.default.filter.fields.join).value}},{key:"rawValues",get:function get(){var a=this.getFilterValueNode(),b=a.querySelector("select");return Object.values(k(b)).map(function(a){return a.value})}},{key:"values",get:function get(){return this.rawValues.map(function(a){return parseInt(a,10)})}},{key:"filterValue",get:function get(){return{name:this.name,jointype:this.jointype,values:this.values}}}]);return a}();a.default=l;return a.default});
|
||||
//# sourceMappingURL=filter.min.js.map
|
File diff suppressed because one or more lines are too long
2
user/amd/build/local/participantsfilter/filtertypes/courseid.min.js
vendored
Normal file
2
user/amd/build/local/participantsfilter/filtertypes/courseid.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
define ("core_user/local/participantsfilter/filtertypes/courseid",["exports","../filter"],function(a,b){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function c(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){c=function(a){return typeof a}}else{c=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return c(a)}function d(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function e(a){return function(){var b=this,c=arguments;return new Promise(function(e,f){var i=a.apply(b,c);function g(a){d(i,e,f,g,h,"next",a)}function h(a){d(i,e,f,g,h,"throw",a)}g(void 0)})}}function f(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function g(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function h(a,b,c){if(b)g(a.prototype,b);if(c)g(a,c);return a}function i(a,b){if("function"!=typeof b&&null!==b){throw new TypeError("Super expression must either be null or a function")}a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,writable:!0,configurable:!0}});if(b)j(a,b)}function j(a,b){j=Object.setPrototypeOf||function(a,b){a.__proto__=b;return a};return j(a,b)}function k(a){return function(){var b=p(a),c;if(n()){var d=p(this).constructor;c=Reflect.construct(b,arguments,d)}else{c=b.apply(this,arguments)}return l(this,c)}}function l(a,b){if(b&&("object"===c(b)||"function"==typeof b)){return b}return m(a)}function m(a){if(void 0===a){throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}return a}function n(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return!0}catch(a){return!1}}function p(a){p=Object.setPrototypeOf?Object.getPrototypeOf:function(a){return a.__proto__||Object.getPrototypeOf(a)};return p(a)}var q=function(a){i(b,a);var c=k(b);function b(a,d){f(this,b);return c.call(this,a,d)}h(b,[{key:"addValueSelector",value:function(){var a=e(regeneratorRuntime.mark(function a(){return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:case"end":return a.stop();}}},a)}));return function addValueSelector(){return a.apply(this,arguments)}}()},{key:"filterValue",get:function get(){return{name:this.name,jointype:1,values:[parseInt(this.rootNode.dataset.tableCourseId,10)]}}}]);return b}(b.default);a.default=q;return a.default});
|
||||
//# sourceMappingURL=courseid.min.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../../../src/local/participantsfilter/filtertypes/courseid.js"],"names":["filterType","filterSet","name","jointype","values","parseInt","rootNode","dataset","tableCourseId","Filter"],"mappings":"uLAuBA,uD,+9DAGI,WAAYA,CAAZ,CAAwBC,CAAxB,CAAmC,8BACzBD,CADyB,CACbC,CADa,CAElC,C,4TAWiB,CACd,MAAO,CACHC,IAAI,CAAE,KAAKA,IADR,CAEHC,QAAQ,CAAE,CAFP,CAGHC,MAAM,CAAE,CAACC,QAAQ,CAAC,KAAKC,QAAL,CAAcC,OAAd,CAAsBC,aAAvB,CAAsC,EAAtC,CAAT,CAHL,CAKV,C,cApBwBC,S","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 * @package core_user\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"],"file":"courseid.min.js"}
|
2
user/amd/build/local/participantsfilter/filtertypes/keyword.min.js
vendored
Normal file
2
user/amd/build/local/participantsfilter/filtertypes/keyword.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
define ("core_user/local/participantsfilter/filtertypes/keyword",["exports","../filter","core/str"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;b=function(a){return a&&a.__esModule?a:{default:a}}(b);function d(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){d=function(a){return typeof a}}else{d=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return d(a)}function e(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function f(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function g(a,b,c){if(b)f(a.prototype,b);if(c)f(a,c);return a}function h(a,b){if("function"!=typeof b&&null!==b){throw new TypeError("Super expression must either be null or a function")}a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,writable:!0,configurable:!0}});if(b)i(a,b)}function i(a,b){i=Object.setPrototypeOf||function(a,b){a.__proto__=b;return a};return i(a,b)}function j(a){return function(){var b=n(a),c;if(m()){var d=n(this).constructor;c=Reflect.construct(b,arguments,d)}else{c=b.apply(this,arguments)}return k(this,c)}}function k(a,b){if(b&&("object"===d(b)||"function"==typeof b)){return b}return l(a)}function l(a){if(void 0===a){throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}return a}function m(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return!0}catch(a){return!1}}function n(a){n=Object.setPrototypeOf?Object.getPrototypeOf:function(a){return a.__proto__||Object.getPrototypeOf(a)};return n(a)}var o=function(a){h(b,a);var d=j(b);function b(a,c){e(this,b);return d.call(this,a,c)}g(b,[{key:"values",get:function get(){return this.rawValues}},{key:"placeholder",get:function get(){return(0,c.get_string)("placeholdertype","core_user")}},{key:"showSuggestions",get:function get(){return!1}}]);return b}(b.default);a.default=o;return a.default});
|
||||
//# sourceMappingURL=keyword.min.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../../../src/local/participantsfilter/filtertypes/keyword.js"],"names":["filterType","filterSet","rawValues","Filter"],"mappings":"mMAuBA,uD,gqDAII,WAAYA,CAAZ,CAAwBC,CAAxB,CAAmC,8BACzBD,CADyB,CACbC,CADa,CAElC,C,qCAOY,CACT,MAAO,MAAKC,SACf,C,uCAOiB,CACd,MAAO,iBAAU,iBAAV,CAA6B,WAA7B,CACV,C,2CAOqB,CAClB,QACH,C,cA9BwBC,S","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 * @package core_user\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 constructor(filterType, filterSet) {\n super(filterType, filterSet);\n }\n\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"],"file":"keyword.min.js"}
|
2
user/amd/build/local/participantsfilter/selectors.min.js
vendored
Normal file
2
user/amd/build/local/participantsfilter/selectors.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
define ("core_user/local/participantsfilter/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a){return"[data-filterregion=\"".concat(a,"\"]")},c=function(a){return"[data-filteraction=\"".concat(a,"\"]")},d=function(a){return"[data-filterfield=\"".concat(a,"\"]")},e={filter:{region:b("filter"),actions:{remove:c("remove")},fields:{join:d("join"),type:d("type")},regions:{values:b("value")},byName:function byName(a){return"".concat(b("filter"),"[data-filter-type=\"").concat(a,"\"]")}},filterset:{region:b("actions"),actions:{addRow:c("add"),applyFilters:c("apply"),resetFilters:c("reset")},regions:{filtermatch:b("filtermatch"),filterlist:b("filters"),datasource:b("filtertypedata")},fields:{join:"".concat(b("filtermatch")," ").concat(d("join"))}},data:{fields:{byName:function byName(a){return"[data-field-name=\"".concat(a,"\"]")},all:"".concat(b("filtertypedata")," [data-field-name]")},typeList:b("filtertypelist")}};a.default=e;return a.default});
|
||||
//# sourceMappingURL=selectors.min.js.map
|
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../../src/local/participantsfilter/selectors.js"],"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"],"mappings":"iKAwBMA,CAAAA,CAAe,CAAG,SAAAC,CAAM,uCAA2BA,CAA3B,Q,CACxBC,CAAe,CAAG,SAAAC,CAAM,uCAA2BA,CAA3B,Q,CACxBC,CAAc,CAAG,SAAAC,CAAK,sCAA0BA,CAA1B,Q,GAEb,CACXC,MAAM,CAAE,CACJL,MAAM,CAAED,CAAe,CAAC,QAAD,CADnB,CAEJO,OAAO,CAAE,CACLC,MAAM,CAAEN,CAAe,CAAC,QAAD,CADlB,CAFL,CAKJO,MAAM,CAAE,CACJC,IAAI,CAAEN,CAAc,CAAC,MAAD,CADhB,CAEJO,IAAI,CAAEP,CAAc,CAAC,MAAD,CAFhB,CALJ,CASJQ,OAAO,CAAE,CACLC,MAAM,CAAEb,CAAe,CAAC,OAAD,CADlB,CATL,CAYJc,MAAM,CAAE,gBAAAC,CAAI,kBAAOf,CAAe,CAAC,QAAD,CAAtB,gCAAsDe,CAAtD,QAZR,CADG,CAeXC,SAAS,CAAE,CACPf,MAAM,CAAED,CAAe,CAAC,SAAD,CADhB,CAEPO,OAAO,CAAE,CACLU,MAAM,CAAEf,CAAe,CAAC,KAAD,CADlB,CAELgB,YAAY,CAAEhB,CAAe,CAAC,OAAD,CAFxB,CAGLiB,YAAY,CAAEjB,CAAe,CAAC,OAAD,CAHxB,CAFF,CAOPU,OAAO,CAAE,CACLQ,WAAW,CAAEpB,CAAe,CAAC,aAAD,CADvB,CAELqB,UAAU,CAAErB,CAAe,CAAC,SAAD,CAFtB,CAGLsB,UAAU,CAAEtB,CAAe,CAAC,gBAAD,CAHtB,CAPF,CAYPS,MAAM,CAAE,CACJC,IAAI,WAAKV,CAAe,CAAC,aAAD,CAApB,aAAuCI,CAAc,CAAC,MAAD,CAArD,CADA,CAZD,CAfA,CA+BXmB,IAAI,CAAE,CACFd,MAAM,CAAE,CACJK,MAAM,CAAE,gBAAAC,CAAI,qCAAyBA,CAAzB,QADR,CAEJS,GAAG,WAAKxB,CAAe,CAAC,gBAAD,CAApB,sBAFC,CADN,CAKFyB,QAAQ,CAAEzB,CAAe,CAAC,gBAAD,CALvB,CA/BK,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Module containing the selectors for user filters.\n *\n * @module core_user/local/user_filter/selectors\n * @package core_user\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 },\n};\n"],"file":"selectors.min.js"}
|
2
user/amd/build/participantsfilter.min.js
vendored
Normal file
2
user/amd/build/participantsfilter.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
user/amd/build/participantsfilter.min.js.map
Normal file
1
user/amd/build/participantsfilter.min.js.map
Normal file
File diff suppressed because one or more lines are too long
205
user/amd/src/local/participantsfilter/filter.js
Normal file
205
user/amd/src/local/participantsfilter/filter.js
Normal file
@ -0,0 +1,205 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Base Filter class for a filter type in the participants filter UI.
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filter
|
||||
* @package core_user
|
||||
* @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 {get_string as getString} from 'core/str';
|
||||
|
||||
/**
|
||||
* Fetch all checked options in the select.
|
||||
*
|
||||
* This is a poor-man's polyfill for select.selectedOptions, which is not available in IE11.
|
||||
*
|
||||
* @param {HTMLSelectElement} select
|
||||
* @returns {HTMLOptionElement[]} All selected options
|
||||
*/
|
||||
const getOptionsForSelect = select => {
|
||||
return select.querySelectorAll(':checked');
|
||||
};
|
||||
|
||||
export default class {
|
||||
|
||||
/**
|
||||
* Constructor for a new filter.
|
||||
*
|
||||
* @param {String} filterType The type of filter that this relates to
|
||||
* @param {HTMLElement} rootNode The root node for the participants filterset
|
||||
*/
|
||||
constructor(filterType, rootNode) {
|
||||
this.filterType = filterType;
|
||||
this.rootNode = rootNode;
|
||||
|
||||
this.addValueSelector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any tear-down for this filter type.
|
||||
*/
|
||||
tearDown() {
|
||||
// eslint-disable-line no-empty-function
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the placeholder to use when showing the value selector.
|
||||
*
|
||||
* @return {Promise} Resolving to a String
|
||||
*/
|
||||
get placeholder() {
|
||||
return getString('placeholdertypeorselect', 'core_user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show suggestions in the autocomplete.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
get showSuggestions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the value selector to the filter row.
|
||||
*/
|
||||
async addValueSelector() {
|
||||
const filterValueNode = this.getFilterValueNode();
|
||||
|
||||
// Copy the data in place.
|
||||
filterValueNode.innerHTML = this.getSourceDataForFilter().outerHTML;
|
||||
|
||||
const dataSource = filterValueNode.querySelector('select');
|
||||
|
||||
Autocomplete.enhance(
|
||||
// The source select element.
|
||||
dataSource,
|
||||
|
||||
// Whether to allow 'tags' (custom entries).
|
||||
dataSource.dataset.allowCustom == "1",
|
||||
|
||||
// We do not require AJAX at all as standard.
|
||||
null,
|
||||
|
||||
// The string to use as a placeholder.
|
||||
await this.placeholder,
|
||||
|
||||
// Disable case sensitivity on searches.
|
||||
false,
|
||||
|
||||
// Show suggestions.
|
||||
this.showSuggestions,
|
||||
|
||||
// Do not override the 'no suggestions' string.
|
||||
null,
|
||||
|
||||
// Close the suggestions if this is not a multi-select.
|
||||
!dataSource.multiple,
|
||||
|
||||
// Template overrides.
|
||||
{
|
||||
items: 'core_user/local/participantsfilter/autocomplete_selection_items',
|
||||
layout: 'core_user/local/participantsfilter/autocomplete_layout',
|
||||
selection: 'core_user/local/participantsfilter/autocomplete_selection',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the root node for this filter.
|
||||
*
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
get filterRoot() {
|
||||
return this.rootNode.querySelector(Selectors.filter.byName(this.filterType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the possible data for this filter type.
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
getSourceDataForFilter() {
|
||||
const filterDataNode = this.rootNode.querySelector(Selectors.filterset.regions.datasource);
|
||||
|
||||
return filterDataNode.querySelector(Selectors.data.fields.byName(this.filterType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTMLElement which contains the value selector.
|
||||
*
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
getFilterValueNode() {
|
||||
return this.filterRoot.querySelector(Selectors.filter.regions.values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this filter.
|
||||
*
|
||||
* @returns {String}
|
||||
*/
|
||||
get name() {
|
||||
return this.filterType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of join specified.
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
get jointype() {
|
||||
return this.filterRoot.querySelector(Selectors.filter.fields.join).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of raw values for this filter type.
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
get rawValues() {
|
||||
const filterValueNode = this.getFilterValueNode();
|
||||
const filterValueSelect = filterValueNode.querySelector('select');
|
||||
|
||||
return Object.values(getOptionsForSelect(filterValueSelect)).map(option => option.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of values for this filter type.
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
get values() {
|
||||
return this.rawValues.map(option => parseInt(option, 10));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the composed value for this filter.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
get filterValue() {
|
||||
return {
|
||||
name: this.name,
|
||||
jointype: this.jointype,
|
||||
values: this.values,
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Course ID filter.
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filtertypes/courseid
|
||||
* @package core_user
|
||||
* @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';
|
||||
|
||||
export default class extends Filter {
|
||||
constructor(filterType, filterSet) {
|
||||
super(filterType, filterSet);
|
||||
}
|
||||
|
||||
async addValueSelector() {
|
||||
// eslint-disable-line no-empty-function
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the composed value for this filter.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
get filterValue() {
|
||||
return {
|
||||
name: this.name,
|
||||
jointype: 1,
|
||||
values: [parseInt(this.rootNode.dataset.tableCourseId, 10)],
|
||||
};
|
||||
}
|
||||
}
|
58
user/amd/src/local/participantsfilter/filtertypes/keyword.js
Normal file
58
user/amd/src/local/participantsfilter/filtertypes/keyword.js
Normal file
@ -0,0 +1,58 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Keyword filter.
|
||||
*
|
||||
* @module core_user/local/participantsfilter/filtertypes/keyword
|
||||
* @package core_user
|
||||
* @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 {get_string as getString} from 'core/str';
|
||||
|
||||
export default class extends Filter {
|
||||
constructor(filterType, filterSet) {
|
||||
super(filterType, filterSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* For keywords the final value is an Array of strings.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
get values() {
|
||||
return this.rawValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the placeholder to use when showing the value selector.
|
||||
*
|
||||
* @return {Promise} Resolving to a String
|
||||
*/
|
||||
get placeholder() {
|
||||
return getString('placeholdertype', 'core_user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to show suggestions in the autocomplete.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
get showSuggestions() {
|
||||
return false;
|
||||
}
|
||||
}
|
67
user/amd/src/local/participantsfilter/selectors.js
Normal file
67
user/amd/src/local/participantsfilter/selectors.js
Normal file
@ -0,0 +1,67 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Module containing the selectors for user filters.
|
||||
*
|
||||
* @module core_user/local/user_filter/selectors
|
||||
* @package core_user
|
||||
* @copyright 2020 Michael Hawkins <michaelh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
const getFilterRegion = region => `[data-filterregion="${region}"]`;
|
||||
const getFilterAction = action => `[data-filteraction="${action}"]`;
|
||||
const getFilterField = field => `[data-filterfield="${field}"]`;
|
||||
|
||||
export default {
|
||||
filter: {
|
||||
region: getFilterRegion('filter'),
|
||||
actions: {
|
||||
remove: getFilterAction('remove'),
|
||||
},
|
||||
fields: {
|
||||
join: getFilterField('join'),
|
||||
type: getFilterField('type'),
|
||||
},
|
||||
regions: {
|
||||
values: getFilterRegion('value'),
|
||||
},
|
||||
byName: name => `${getFilterRegion('filter')}[data-filter-type="${name}"]`,
|
||||
},
|
||||
filterset: {
|
||||
region: getFilterRegion('actions'),
|
||||
actions: {
|
||||
addRow: getFilterAction('add'),
|
||||
applyFilters: getFilterAction('apply'),
|
||||
resetFilters: getFilterAction('reset'),
|
||||
},
|
||||
regions: {
|
||||
filtermatch: getFilterRegion('filtermatch'),
|
||||
filterlist: getFilterRegion('filters'),
|
||||
datasource: getFilterRegion('filtertypedata'),
|
||||
},
|
||||
fields: {
|
||||
join: `${getFilterRegion('filtermatch')} ${getFilterField('join')}`,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
fields: {
|
||||
byName: name => `[data-field-name="${name}"]`,
|
||||
all: `${getFilterRegion('filtertypedata')} [data-field-name]`,
|
||||
},
|
||||
typeList: getFilterRegion('filtertypelist'),
|
||||
},
|
||||
};
|
348
user/amd/src/participantsfilter.js
Normal file
348
user/amd/src/participantsfilter.js
Normal file
@ -0,0 +1,348 @@
|
||||
// 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
|
||||
* @package core_user
|
||||
* @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 Notification from 'core/notification';
|
||||
import Selectors from './local/participantsfilter/selectors';
|
||||
import Templates from 'core/templates';
|
||||
|
||||
/**
|
||||
* 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 = () => {
|
||||
return Templates.renderForPromise('core_user/local/participantsfilter/filterrow', {})
|
||||
.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;
|
||||
})
|
||||
.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
|
||||
*/
|
||||
const addFilter = async(filterRow, filterType) => {
|
||||
// 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);
|
||||
|
||||
// Disable the select.
|
||||
const typeField = filterRow.querySelector(Selectors.filter.fields.type);
|
||||
typeField.disabled = 'disabled';
|
||||
|
||||
// Update the list of available filter types.
|
||||
updateFiltersOptions();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
const removeOrReplaceFilterRow = filterRow => {
|
||||
const filterCount = getFilterRegion().querySelectorAll(Selectors.filter.region).length;
|
||||
|
||||
if (filterCount === 1) {
|
||||
replaceFilterRow(filterRow);
|
||||
} else {
|
||||
removeFilterRow(filterRow);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the specified filter row and associated class.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
*/
|
||||
const removeFilterRow = filterRow => {
|
||||
// Remove the filter object.
|
||||
removeFilterObject(filterRow.dataset.filterType);
|
||||
|
||||
// Remove the actual filter HTML.
|
||||
filterRow.remove();
|
||||
|
||||
// Refresh the table.
|
||||
updateTableFromFilter();
|
||||
|
||||
// Update the list of available filter types.
|
||||
updateFiltersOptions();
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace the specified filter row with a new one.
|
||||
*
|
||||
* @param {HTMLElement} filterRow
|
||||
* @return {Promise}
|
||||
*/
|
||||
const replaceFilterRow = filterRow => {
|
||||
// Remove the filter object.
|
||||
removeFilterObject(filterRow.dataset.filterType);
|
||||
|
||||
return Templates.renderForPromise('core_user/local/participantsfilter/filterrow', {})
|
||||
.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.
|
||||
updateTableFromFilter();
|
||||
|
||||
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.
|
||||
*/
|
||||
const removeAllFilters = async() => {
|
||||
const filters = getFilterRegion().querySelectorAll(Selectors.filter.region);
|
||||
filters.forEach((filterRow) => {
|
||||
removeOrReplaceFilterRow(filterRow);
|
||||
});
|
||||
|
||||
// Refresh the table.
|
||||
updateTableFromFilter();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = 1;
|
||||
} else {
|
||||
filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.remove('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the Dynamic table based upon the current filter.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
const updateTableFromFilter = () => {
|
||||
return DynamicTable.setFilters(
|
||||
DynamicTable.getTableFromId(filterSet.dataset.tableRegion),
|
||||
{
|
||||
filters: Object.values(activeFilters).map(filter => filter.filterValue),
|
||||
jointype: filterSet.querySelector(Selectors.filterset.fields.join).value,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// 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));
|
||||
}
|
||||
});
|
||||
|
||||
// Add listeners for the filter type selection.
|
||||
filterSet.querySelector(Selectors.filterset.regions.filterlist).addEventListener('change', 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;
|
||||
});
|
||||
};
|
393
user/classes/output/participants_filter.php
Normal file
393
user/classes/output/participants_filter.php
Normal file
@ -0,0 +1,393 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for rendering user filters on the course participants page.
|
||||
*
|
||||
* @package core_user
|
||||
* @copyright 2020 Michael Hawkins <michaelh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_user\output;
|
||||
|
||||
use context_course;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Class for rendering user filters on the course participants page.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for all filter types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_filtertypes(): array {
|
||||
$filtertypes = [];
|
||||
|
||||
$filtertypes[] = $this->get_keyword_filter();
|
||||
|
||||
if ($filtertype = $this->get_enrolmentstatus_filter()) {
|
||||
$filtertypes[] = $filtertype;
|
||||
}
|
||||
|
||||
if ($filtertype = $this->get_roles_filter()) {
|
||||
$filtertypes[] = $filtertype;
|
||||
}
|
||||
|
||||
if ($filtertype = $this->get_enrolments_filter()) {
|
||||
$filtertypes[] = $filtertype;
|
||||
}
|
||||
|
||||
if ($filtertype = $this->get_groups_filter()) {
|
||||
$filtertypes[] = $filtertype;
|
||||
}
|
||||
|
||||
if ($filtertype = $this->get_accesssince_filter()) {
|
||||
$filtertypes[] = $filtertype;
|
||||
}
|
||||
|
||||
return $filtertypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for the enrolment status filter.
|
||||
*
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function get_enrolmentstatus_filter(): ?stdClass {
|
||||
if (!has_capability('moodle/course:enrolreview', $this->context)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->get_filter_object(
|
||||
'status',
|
||||
get_string('participationstatus', 'core_enrol'),
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
[
|
||||
(object) [
|
||||
'value' => ENROL_USER_ACTIVE,
|
||||
'title' => get_string('active'),
|
||||
],
|
||||
(object) [
|
||||
'value' => ENROL_USER_SUSPENDED,
|
||||
'title' => get_string('inactive'),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for the roles filter.
|
||||
*
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function get_roles_filter(): ?stdClass {
|
||||
$roles = [];
|
||||
$roles += [-1 => get_string('noroles', 'role')];
|
||||
$roles += get_viewable_roles($this->context);
|
||||
|
||||
if (has_capability('moodle/role:assign', $this->context)) {
|
||||
$roles += get_assignable_roles($this->context, ROLENAME_ALIAS);
|
||||
}
|
||||
|
||||
return $this->get_filter_object(
|
||||
'roles',
|
||||
get_string('roles', 'core_role'),
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
array_map(function($id, $title) {
|
||||
return (object) [
|
||||
'value' => $id,
|
||||
'title' => $title,
|
||||
];
|
||||
}, array_keys($roles), array_values($roles))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for the roles filter.
|
||||
*
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function get_enrolments_filter(): ?stdClass {
|
||||
if (!has_capability('moodle/course:enrolreview', $this->context)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->course->id == SITEID) {
|
||||
// No enrolment methods for the site.
|
||||
return null;
|
||||
}
|
||||
|
||||
$instances = enrol_get_instances($this->course->id, true);
|
||||
$plugins = enrol_get_plugins(false);
|
||||
|
||||
return $this->get_filter_object(
|
||||
'enrolments',
|
||||
get_string('enrolmentinstances', 'core_enrol'),
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
array_filter(array_map(function($instance) use ($plugins): ?stdClass {
|
||||
if (!array_key_exists($instance->enrol, $plugins)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (object) [
|
||||
'value' => $instance->id,
|
||||
'title' => $plugins[$instance->enrol]->get_instance_name($instance),
|
||||
];
|
||||
}, array_values($instances)))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for the groups filter.
|
||||
*
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function get_groups_filter(): ?stdClass {
|
||||
global $USER;
|
||||
|
||||
// Filter options for groups, if available.
|
||||
$seeallgroups = has_capability('moodle/site:accessallgroups', $this->context);
|
||||
$seeallgroups = $seeallgroups || ($this->course->groupmode != SEPARATEGROUPS);
|
||||
if ($seeallgroups) {
|
||||
$groups = [];
|
||||
$groups += [USERSWITHOUTGROUP => (object) [
|
||||
'id' => USERSWITHOUTGROUP,
|
||||
'name' => get_string('nogroup', 'group'),
|
||||
]];
|
||||
$groups += groups_get_all_groups($this->course->id);
|
||||
} else {
|
||||
// Otherwise, just list the groups the user belongs to.
|
||||
$groups = groups_get_all_groups($this->course->id, $USER->id);
|
||||
}
|
||||
|
||||
if (empty($groups)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->get_filter_object(
|
||||
'groups',
|
||||
get_string('groups', 'core_group'),
|
||||
false,
|
||||
true,
|
||||
null,
|
||||
array_map(function($group) {
|
||||
return (object) [
|
||||
'value' => $group->id,
|
||||
'title' => $group->name,
|
||||
];
|
||||
}, array_values($groups))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for the accesssince filter.
|
||||
*
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function get_accesssince_filter(): ?stdClass {
|
||||
global $CFG, $DB;
|
||||
|
||||
$hiddenfields = [];
|
||||
if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) {
|
||||
$hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
|
||||
}
|
||||
|
||||
if (array_key_exists('lastaccess', $hiddenfields)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get minimum lastaccess for this course and display a dropbox to filter by lastaccess going back this far.
|
||||
// We need to make it diferently for normal courses and site course.
|
||||
if (!$this->course->id == SITEID) {
|
||||
// Regular course.
|
||||
$params = [
|
||||
'courseid' => $this->course->id,
|
||||
'timeaccess' => 0,
|
||||
];
|
||||
$select = 'courseid = :courseid AND timeaccess != :timeaccess';
|
||||
$minlastaccess = $DB->get_field_select('user_lastaccess', 'MIN(timeaccess)', $select, $params);
|
||||
$lastaccess0exists = $DB->record_exists('user_lastaccess', $params);
|
||||
} else {
|
||||
// Front page.
|
||||
$params = ['lastaccess' => 0];
|
||||
$select = 'lastaccess != :lastaccess';
|
||||
$minlastaccess = $DB->get_field_select('user', 'MIN(lastaccess)', $select, $params);
|
||||
$lastaccess0exists = $DB->record_exists('user', $params);
|
||||
}
|
||||
|
||||
$now = usergetmidnight(time());
|
||||
$timeoptions = [];
|
||||
$criteria = get_string('usersnoaccesssince');
|
||||
|
||||
$getoptions = function(int $count, string $singletype, string $type) use ($now, $minlastaccess): array {
|
||||
$values = [];
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
$timestamp = strtotime("-{$i} {$type}", $now);
|
||||
if ($timestamp < $minlastaccess) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($i === 1) {
|
||||
$title = get_string("num{$singletype}", 'moodle', $i);
|
||||
} else {
|
||||
$title = get_string("num{$type}", 'moodle', $i);
|
||||
}
|
||||
|
||||
$values[] = [
|
||||
'value' => $timestamp,
|
||||
'title' => $title,
|
||||
];
|
||||
}
|
||||
|
||||
return $values;
|
||||
};
|
||||
|
||||
$values = array_merge(
|
||||
$getoptions(6, 'day', 'days'),
|
||||
$getoptions(10, 'week', 'weeks'),
|
||||
$getoptions(11, 'month', 'months'),
|
||||
$getoptions(1, 'year', 'years')
|
||||
);
|
||||
|
||||
if ($lastaccess0exists) {
|
||||
$values[] = [
|
||||
'value' => time(),
|
||||
'title' => get_string('never', 'moodle'),
|
||||
];
|
||||
}
|
||||
|
||||
if (count($values) <= 1) {
|
||||
// Nothing to show.
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->get_filter_object(
|
||||
'accesssince',
|
||||
get_string('usersnoaccesssince'),
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
$values
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for the keywords filter.
|
||||
*
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function get_keyword_filter(): ?stdClass {
|
||||
return $this->get_filter_object(
|
||||
'keywords',
|
||||
get_string('filterbykeyword', 'core_user'),
|
||||
true,
|
||||
true,
|
||||
'core_user/local/participantsfilter/filtertypes/keyword',
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the renderer data in a mustache template friendly format.
|
||||
*
|
||||
* @param renderer_base $output Unused.
|
||||
* @return stdClass Data in a format compatible with a mustache template.
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
return (object) [
|
||||
'tableregionid' => $this->tableregionid,
|
||||
'courseid' => $this->context->instanceid,
|
||||
'filtertypes' => $this->get_filtertypes(),
|
||||
];
|
||||
|
||||
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,
|
||||
];
|
||||
}
|
||||
}
|
@ -143,6 +143,8 @@ $lastaccess = 0;
|
||||
$searchkeywords = [];
|
||||
$enrolid = 0;
|
||||
|
||||
$participanttable = new \core_user\table\participants("user-index-participants-{$course->id}");
|
||||
|
||||
$filterset = new \core_user\table\participants_filterset();
|
||||
$filterset->add_filter(new integer_filter('courseid', filter::JOINTYPE_DEFAULT, [(int)$course->id]));
|
||||
$enrolfilter = new integer_filter('enrolments');
|
||||
@ -240,7 +242,7 @@ $enrolbuttonsout = '';
|
||||
foreach ($enrolbuttons as $enrolbutton) {
|
||||
$enrolbuttonsout .= $enrolrenderer->render($enrolbutton);
|
||||
}
|
||||
echo html_writer::div($enrolbuttonsout, 'float-right', [
|
||||
echo html_writer::div($enrolbuttonsout, 'd-flex justify-content-end', [
|
||||
'data-region' => 'wrapper',
|
||||
'data-table-uniqueid' => $participanttable->uniqueid,
|
||||
]);
|
||||
@ -249,6 +251,10 @@ echo html_writer::div($enrolbuttonsout, 'float-right', [
|
||||
$renderer = $PAGE->get_renderer('core_user');
|
||||
echo $renderer->unified_filter($course, $context, $filtersapplied, $baseurl);
|
||||
|
||||
// Render the user filters.
|
||||
$userrenderer = $PAGE->get_renderer('core_user');
|
||||
echo $userrenderer->participants_filter($context, $participanttable->uniqueid);
|
||||
|
||||
echo '<div class="userlist">';
|
||||
|
||||
// Add filters to the baseurl after creating unified_filter to avoid losing them.
|
||||
|
@ -259,6 +259,20 @@ class core_user_renderer extends plugin_renderer_base {
|
||||
return $this->output->render_from_template('core_user/unified_filter', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return string
|
||||
*/
|
||||
public function participants_filter(context $context, string $tableregionid): string {
|
||||
$renderable = new \core_user\output\participants_filter($context, $tableregionid);
|
||||
$templatecontext = $renderable->export_for_template($this->output);
|
||||
|
||||
return $this->output->render_from_template('core_user/participantsfilter', $templatecontext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted filter option.
|
||||
*
|
||||
|
@ -0,0 +1,38 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_user/local/participantsfilter/autocomplete_layout
|
||||
|
||||
Moodle template for the layout of autocomplete elements.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-region="form_autocomplete-input"
|
||||
* data-region="form_autocomplete-suggestions"
|
||||
* data-region="form_autocomplete-selection"
|
||||
|
||||
Context variables required for this template:
|
||||
* none
|
||||
|
||||
Example context (json):
|
||||
{}
|
||||
}}
|
||||
<div data-region="form_autocomplete-input"></div>
|
||||
<div data-region="form_autocomplete-suggestions"></div>
|
||||
<div data-region="form_autocomplete-selection"></div>
|
@ -0,0 +1,49 @@
|
||||
{{!
|
||||
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_user/local/participantsfilter/autocomplete_selection
|
||||
|
||||
Moodle template for the wrapper of currently selected items in an autocomplate form element.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* multiple True if this field allows multiple selections
|
||||
* selectionId The dom id of the current selection list.
|
||||
* items List of items with label and value fields (used by the partial).
|
||||
* noSelectionString String to use when no items are selected (used by the partial).
|
||||
|
||||
Example context (json):
|
||||
{ "multiple": true, "selectionId": 1, "items": [
|
||||
{ "label": "Item label with <strong>tags</strong>", "value": "5" },
|
||||
{ "label": "Another item label with <strong>tags</strong>", "value": "4" }
|
||||
], "noSelectionString": "No selection" }
|
||||
}}
|
||||
<div{{!
|
||||
}} class="d-inline-block mb-0{{#multiple}} form-autocomplete-multiple h5{{/multiple}}"{{!
|
||||
}} id="{{selectionId}}"{{!
|
||||
}} role="list"{{!
|
||||
}} aria-atomic="true"{{!
|
||||
}}{{#multiple}} tabindex="0" {{/multiple}}{{!
|
||||
}}>
|
||||
<span class="accesshide">{{#str}}selecteditems, form{{/str}}</span>
|
||||
{{> core/form_autocomplete_selection_items }}
|
||||
</div>
|
@ -0,0 +1,51 @@
|
||||
{{!
|
||||
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_user/local/participantsfilter/autocomplete_selection_items
|
||||
|
||||
Moodle template for the currently selected items in an autocomplete form element.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-value
|
||||
|
||||
Context variables required for this template:
|
||||
* items List of items with label and value fields.
|
||||
* - value Value of the selected item.
|
||||
* - label HTML representing the value.
|
||||
* noSelectionString String to use when no items are selected
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"items": [
|
||||
{ "label": "Item label with <strong>tags</strong>", "value": "5" },
|
||||
{ "label": "Another item label with <strong>tags</strong>", "value": "4" }
|
||||
],
|
||||
"noSelectionString": "No selection"
|
||||
}
|
||||
}}
|
||||
{{#items}}
|
||||
<span role="listitem" data-value="{{value}}" aria-selected="true"
|
||||
class="badge badge-secondary clickable text-wrap text-break line-height-4 mr-2 my-1">
|
||||
{{label}}<i class="icon fa fa-times pl-2 mr-0"></i>
|
||||
</span>
|
||||
{{/items}}
|
||||
{{^items}}
|
||||
<span class="mb-3 mr-1">{{noSelectionString}}</span>
|
||||
{{/items}}
|
65
user/templates/local/participantsfilter/filterrow.mustache
Normal file
65
user/templates/local/participantsfilter/filterrow.mustache
Normal file
@ -0,0 +1,65 @@
|
||||
{{!
|
||||
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_user/local/participantsfilter/filterrow
|
||||
|
||||
Template for use by each filter condition.
|
||||
|
||||
Context variables required for this template:
|
||||
* filtertypes - Array of filter types available.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"filtertypes": [
|
||||
{
|
||||
"name": "status",
|
||||
"title": "Status"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div data-filterregion="filter">
|
||||
<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}}">
|
||||
<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}}">filtertype</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>
|
||||
{{#filtertypes}}
|
||||
<option value="{{name}}">{{title}}</option>
|
||||
{{/filtertypes}}
|
||||
</select>
|
||||
|
||||
<div data-filterregion="value" class="d-md-flex 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>
|
||||
</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>
|
||||
</div>
|
61
user/templates/local/participantsfilter/filtertype.mustache
Normal file
61
user/templates/local/participantsfilter/filtertype.mustache
Normal file
@ -0,0 +1,61 @@
|
||||
{{!
|
||||
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_user/local/participantsfilter/filtertype
|
||||
|
||||
Filter type data, not shown to users but used as a source of data for form autocompletion.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* filtertypes
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"name": "status",
|
||||
"title": "Enrolment Status",
|
||||
"allowcustom": "0",
|
||||
"allowmultiple" false,
|
||||
"filtertypeclass": "core_user/local/participantsfilter/filtertypes/courseid",
|
||||
"values": [
|
||||
{
|
||||
"value": "0",
|
||||
"title": "Inactive"
|
||||
},
|
||||
{
|
||||
"value": "1",
|
||||
"title": "Active"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<select {{!
|
||||
}}{{#allowmultiple}}multiple="multiple"{{/allowmultiple}} {{!
|
||||
}}data-field-name="{{name}}" {{!
|
||||
}}data-field-title="{{title}}" {{!
|
||||
}}data-allow-custom="{{allowcustom}}" {{!
|
||||
}}class="hidden" {{!
|
||||
}}{{#filtertypeclass}}data-filter-type-class="{{filtertypeclass}}" {{/filtertypeclass}}{{!
|
||||
}}>
|
||||
{{#values}}
|
||||
<option value="{{value}}">{{title}}</option>
|
||||
{{/values}}
|
||||
</select>
|
64
user/templates/local/participantsfilter/filtertypes.mustache
Normal file
64
user/templates/local/participantsfilter/filtertypes.mustache
Normal file
@ -0,0 +1,64 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_user/local/participantsfilter/filtertypes
|
||||
|
||||
Placeholder to fetch all filter types.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* data-filterregion="filtertypedata"
|
||||
|
||||
Context variables required for this template:
|
||||
* filtertypes
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"filtertypes": [
|
||||
{
|
||||
"name": "status",
|
||||
"title": "Enrolment Status",
|
||||
"allowcustom": "0",
|
||||
"values": [
|
||||
{
|
||||
"value": "0",
|
||||
"title": "Inactive"
|
||||
},
|
||||
{
|
||||
"value": "1",
|
||||
"title": "Active"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div class="hidden" data-filterregion="filtertypedata">
|
||||
{{#filtertypes}}
|
||||
{{> core_user/local/participantsfilter/filtertype}}
|
||||
{{/filtertypes}}
|
||||
</div>
|
||||
<div class="hidden">
|
||||
<select disabled="disabled" data-filterfield="type" data-filterregion="filtertypelist">
|
||||
<option value="">{{#str}}selectfiltertype, core_user{{/str}}</option>
|
||||
{{#filtertypes}}
|
||||
<option value="{{name}}">{{title}}</option>
|
||||
{{/filtertypes}}
|
||||
</select>
|
||||
</div>
|
77
user/templates/participantsfilter.mustache
Normal file
77
user/templates/participantsfilter.mustache
Normal file
@ -0,0 +1,77 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_user/participantsfilter
|
||||
|
||||
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_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="1">
|
||||
<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 selected=selected value="1">{{#str}}any{{/str}}</option>
|
||||
<option value="2">{{#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>
|
||||
|
||||
{{#js}}
|
||||
require(['core_user/participantsfilter'], function(ParticipantsFilter) {
|
||||
ParticipantsFilter.init('core_user-participantsfilter-{{uniqid}}');
|
||||
});
|
||||
{{/js}}
|
Loading…
x
Reference in New Issue
Block a user