Merge branch 'MDL-78325-main' of https://github.com/meirzamoodle/moodle
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
issueNumber: MDL-78325
|
||||
notes:
|
||||
core_grades:
|
||||
- message: >-
|
||||
In Moodle 4.2, the legacy Gradebook base widget from 4.1 has been removed and
|
||||
replaced with a simpler class-based system due to a breaking change and
|
||||
excessive complexity in the old pattern. The files `core/grades/basewidget.js` and
|
||||
templates in `grade/templates/searchwidget/` have been deleted,
|
||||
with minimal expected third-party impact.
|
||||
type: removed
|
||||
@@ -1,10 +0,0 @@
|
||||
define("core_grades/searchwidget/basewidget",["exports","core/utils","core/templates","core_grades/searchwidget/selectors","core/notification","core/log"],(function(_exports,_utils,Templates,Selectors,_notification,_log){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}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 _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
|
||||
/**
|
||||
* A widget to search users or grade items within the gradebook.
|
||||
*
|
||||
* @module core_grades/searchwidget/basewidget
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showLoader=_exports.registerListenerEvents=_exports.promisesAndResolvers=_exports.init=void 0,Templates=_interopRequireWildcard(Templates),Selectors=_interopRequireWildcard(Selectors),_notification=_interopRequireDefault(_notification),_log=_interopRequireDefault(_log);_exports.init=async function(widgetContentContainer,bodyPromise,data,searchFunc){let unsearchableContent=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null,afterSelect=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null;_log.default.debug("The core_grades/searchwidget/basewidget component is deprecated. Please refer to core/search_combobox() instead."),bodyPromise.then((async bodyContent=>{if(widgetContentContainer.innerHTML=bodyContent,unsearchableContent){widgetContentContainer.querySelector(Selectors.regions.unsearchableContent).innerHTML+=unsearchableContent}const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults);await showLoader(searchResultsContainer),await renderSearchResults(searchResultsContainer,data),registerListenerEvents(widgetContentContainer,data,searchFunc,afterSelect)})).catch(_notification.default.exception)};const registerListenerEvents=function(widgetContentContainer,data,searchFunc){let afterSelect=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults),searchInput=widgetContentContainer.querySelector(Selectors.actions.search);if(!searchInput)return;searchInput.focus();const clearSearchButton=widgetContentContainer.querySelector(Selectors.actions.clearSearch);searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{searchInput.value.length>0?clearSearchButton.classList.remove("d-none"):clearSearchButton.classList.add("d-none"),searchInput.removeAttribute("aria-activedescendant"),await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}),300)),clearSearchButton.addEventListener("click",(async e=>{e.stopPropagation(),searchInput.value="",searchInput.focus(),clearSearchButton.classList.add("d-none"),searchInput.removeAttribute("aria-activedescendant"),await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}));const inputElement=document.getElementById(searchInput.dataset.inputElement);inputElement&&afterSelect&&inputElement.addEventListener("change",(e=>{widgetContentContainer.querySelector(Selectors.elements.getSearchWidgetSelectOption(searchInput))&&afterSelect(e.target.value)})),widgetContentContainer.addEventListener("click",(e=>{const deprecatedOption=e.target.closest('a.dropdown-item[role="menuitem"]:not([href]), .dropdown-item[role="option"]:not([href])');if(deprecatedOption)if(inputElement&&afterSelect)afterSelect(deprecatedOption.dataset.value);else{const url=(data.find((object=>object.id==deprecatedOption.dataset.value))||{url:""}).url;location.href=url}})),widgetContentContainer.addEventListener("keydown",(e=>{const deprecatedOption=e.target.closest('a.dropdown-item[role="menuitem"]:not([href]), .dropdown-item[role="option"]:not([href])');if(deprecatedOption&&(" "===e.key||"Enter"===e.key))if(e.preventDefault(),inputElement&&afterSelect)afterSelect(deprecatedOption.dataset.value);else{const url=(data.find((object=>object.id==deprecatedOption.dataset.value))||{url:""}).url;location.href=url}}))};_exports.registerListenerEvents=registerListenerEvents;const showLoader=async container=>{container.innerHTML="";const{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/loading",{});Templates.replaceNodeContents(container,html,js)};_exports.showLoader=showLoader;const debounceCallee=(searchValue,data,searchFunction)=>searchValue.length>0?searchFunction(data,searchValue):data,renderSearchResults=async(searchResultsContainer,searchResultsData)=>{const templateData={searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/searchresults",templateData);if(await Templates.replaceNodeContents(searchResultsContainer,html,js),"listbox"!==searchResultsContainer.getAttribute("role")){const deprecatedOptions=searchResultsContainer.querySelectorAll('a.dropdown-item[role="menuitem"][href=""], .dropdown-item[role="option"]:not([href])');for(const option of deprecatedOptions)option.tabIndex=0,option.removeAttribute("href")}};_exports.promisesAndResolvers=()=>{let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve}));return{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}}}));
|
||||
|
||||
//# sourceMappingURL=basewidget.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,261 +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/>.
|
||||
|
||||
/**
|
||||
* A widget to search users or grade items within the gradebook.
|
||||
*
|
||||
* @module core_grades/searchwidget/basewidget
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
import {debounce} from 'core/utils';
|
||||
import * as Templates from 'core/templates';
|
||||
import * as Selectors from 'core_grades/searchwidget/selectors';
|
||||
import Notification from 'core/notification';
|
||||
import Log from 'core/log';
|
||||
|
||||
/**
|
||||
* Build the base searching widget.
|
||||
*
|
||||
* @method init
|
||||
* @param {HTMLElement} widgetContentContainer The selector for the widget container element.
|
||||
* @param {Promise} bodyPromise The promise from the callee of the contents to place in the widget container.
|
||||
* @param {Array} data An array of all the data generated by the callee.
|
||||
* @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.
|
||||
* @param {string|null} unsearchableContent The content rendered in a non-searchable area.
|
||||
* @param {Function|null} afterSelect Callback executed after an item is selected.
|
||||
*/
|
||||
export const init = async(
|
||||
widgetContentContainer,
|
||||
bodyPromise,
|
||||
data,
|
||||
searchFunc,
|
||||
unsearchableContent = null,
|
||||
afterSelect = null,
|
||||
) => {
|
||||
Log.debug('The core_grades/searchwidget/basewidget component is deprecated. Please refer to core/search_combobox() instead.');
|
||||
bodyPromise.then(async(bodyContent) => {
|
||||
// Render the body content.
|
||||
widgetContentContainer.innerHTML = bodyContent;
|
||||
|
||||
// Render the unsearchable content if defined.
|
||||
if (unsearchableContent) {
|
||||
const unsearchableContentContainer = widgetContentContainer.querySelector(Selectors.regions.unsearchableContent);
|
||||
unsearchableContentContainer.innerHTML += unsearchableContent;
|
||||
}
|
||||
|
||||
const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);
|
||||
// Display a loader until the search results are rendered.
|
||||
await showLoader(searchResultsContainer);
|
||||
// Render the search results.
|
||||
await renderSearchResults(searchResultsContainer, data);
|
||||
|
||||
registerListenerEvents(widgetContentContainer, data, searchFunc, afterSelect);
|
||||
|
||||
}).catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the event listeners for the search widget.
|
||||
*
|
||||
* @method registerListenerEvents
|
||||
* @param {HTMLElement} widgetContentContainer The selector for the widget container element.
|
||||
* @param {Array} data An array of all the data generated by the callee.
|
||||
* @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.
|
||||
* @param {Function|null} afterSelect Callback executed after an item is selected.
|
||||
*/
|
||||
export const registerListenerEvents = (widgetContentContainer, data, searchFunc, afterSelect = null) => {
|
||||
const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);
|
||||
const searchInput = widgetContentContainer.querySelector(Selectors.actions.search);
|
||||
|
||||
if (!searchInput) {
|
||||
// Too late. The widget is already closed and its content is empty.
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to focus on the first known user interable element within the dropdown.
|
||||
searchInput.focus();
|
||||
const clearSearchButton = widgetContentContainer.querySelector(Selectors.actions.clearSearch);
|
||||
|
||||
// The search input is triggered.
|
||||
searchInput.addEventListener('input', debounce(async() => {
|
||||
// If search query is present display the 'clear search' button, otherwise hide it.
|
||||
if (searchInput.value.length > 0) {
|
||||
clearSearchButton.classList.remove('d-none');
|
||||
} else {
|
||||
clearSearchButton.classList.add('d-none');
|
||||
}
|
||||
// Remove aria-activedescendant when the available options change.
|
||||
searchInput.removeAttribute('aria-activedescendant');
|
||||
// Display the search results.
|
||||
await renderSearchResults(
|
||||
searchResultsContainer,
|
||||
debounceCallee(
|
||||
searchInput.value,
|
||||
data,
|
||||
searchFunc()
|
||||
)
|
||||
);
|
||||
}, 300));
|
||||
|
||||
// Clear search is triggered.
|
||||
clearSearchButton.addEventListener('click', async(e) => {
|
||||
e.stopPropagation();
|
||||
// Clear the entered search query in the search bar.
|
||||
searchInput.value = "";
|
||||
searchInput.focus();
|
||||
clearSearchButton.classList.add('d-none');
|
||||
|
||||
// Remove aria-activedescendant when the available options change.
|
||||
searchInput.removeAttribute('aria-activedescendant');
|
||||
|
||||
// Display all results.
|
||||
await renderSearchResults(
|
||||
searchResultsContainer,
|
||||
debounceCallee(
|
||||
searchInput.value,
|
||||
data,
|
||||
searchFunc()
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const inputElement = document.getElementById(searchInput.dataset.inputElement);
|
||||
if (inputElement && afterSelect) {
|
||||
inputElement.addEventListener('change', e => {
|
||||
const selectedOption = widgetContentContainer.querySelector(
|
||||
Selectors.elements.getSearchWidgetSelectOption(searchInput),
|
||||
);
|
||||
|
||||
if (selectedOption) {
|
||||
afterSelect(e.target.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Backward compatibility. Handle the click event for the following cases:
|
||||
// - When we have <li> tags without an afterSelect callback function being provided (old js).
|
||||
// - When we have <a> tags without href (old template).
|
||||
widgetContentContainer.addEventListener('click', e => {
|
||||
const deprecatedOption = e.target.closest(
|
||||
'a.dropdown-item[role="menuitem"]:not([href]), .dropdown-item[role="option"]:not([href])'
|
||||
);
|
||||
if (deprecatedOption) {
|
||||
// We are in one of these situations:
|
||||
// - We have <li> tags without an afterSelect callback function being provided.
|
||||
// - We have <a> tags without href.
|
||||
if (inputElement && afterSelect) {
|
||||
afterSelect(deprecatedOption.dataset.value);
|
||||
} else {
|
||||
const url = (data.find(object => object.id == deprecatedOption.dataset.value) || {url: ''}).url;
|
||||
location.href = url;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Backward compatibility. Handle the keydown event for the following cases:
|
||||
// - When we have <li> tags without an afterSelect callback function being provided (old js).
|
||||
// - When we have <a> tags without href (old template).
|
||||
widgetContentContainer.addEventListener('keydown', e => {
|
||||
const deprecatedOption = e.target.closest(
|
||||
'a.dropdown-item[role="menuitem"]:not([href]), .dropdown-item[role="option"]:not([href])'
|
||||
);
|
||||
if (deprecatedOption && (e.key === ' ' || e.key === 'Enter')) {
|
||||
// We are in one of these situations:
|
||||
// - We have <li> tags without an afterSelect callback function being provided.
|
||||
// - We have <a> tags without href.
|
||||
e.preventDefault();
|
||||
if (inputElement && afterSelect) {
|
||||
afterSelect(deprecatedOption.dataset.value);
|
||||
} else {
|
||||
const url = (data.find(object => object.id == deprecatedOption.dataset.value) || {url: ''}).url;
|
||||
location.href = url;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the loading placeholder for the search widget.
|
||||
*
|
||||
* @method showLoader
|
||||
* @param {HTMLElement} container The DOM node where we'll render the loading placeholder.
|
||||
*/
|
||||
export const showLoader = async(container) => {
|
||||
container.innerHTML = '';
|
||||
const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/loading', {});
|
||||
Templates.replaceNodeContents(container, html, js);
|
||||
};
|
||||
|
||||
/**
|
||||
* We have a small helper that'll call the curried search function allowing callers to filter
|
||||
* the data set however we want rather than defining how data must be filtered.
|
||||
*
|
||||
* @method debounceCallee
|
||||
* @param {String} searchValue The input from the user that we'll search against.
|
||||
* @param {Array} data An array of all the data generated by the callee.
|
||||
* @param {Function} searchFunction Partially applied function we need to manage search the passed dataset.
|
||||
* @return {Array} The filtered subset of the provided data that we'll then render into the results.
|
||||
*/
|
||||
const debounceCallee = (searchValue, data, searchFunction) => {
|
||||
if (searchValue.length > 0) { // Search query is present.
|
||||
return searchFunction(data, searchValue);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given the output of the callers' search function, render out the results into the search results container.
|
||||
*
|
||||
* @method renderSearchResults
|
||||
* @param {HTMLElement} searchResultsContainer The DOM node of the widget where we'll render the provided results.
|
||||
* @param {Array} searchResultsData The filtered subset of the provided data that we'll then render into the results.
|
||||
*/
|
||||
const renderSearchResults = async(searchResultsContainer, searchResultsData) => {
|
||||
const templateData = {
|
||||
'searchresults': searchResultsData,
|
||||
};
|
||||
// Build up the html & js ready to place into the help section.
|
||||
const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/searchresults', templateData);
|
||||
await Templates.replaceNodeContents(searchResultsContainer, html, js);
|
||||
|
||||
// Backward compatibility.
|
||||
if (searchResultsContainer.getAttribute('role') !== 'listbox') {
|
||||
const deprecatedOptions = searchResultsContainer.querySelectorAll(
|
||||
'a.dropdown-item[role="menuitem"][href=""], .dropdown-item[role="option"]:not([href])'
|
||||
);
|
||||
for (const option of deprecatedOptions) {
|
||||
option.tabIndex = 0;
|
||||
option.removeAttribute('href');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* We want to create the basic promises and hooks that the caller will implement, so we can build the search widget
|
||||
* ahead of time and allow the caller to resolve their promises once complete.
|
||||
*
|
||||
* @method promisesAndResolvers
|
||||
* @returns {{bodyPromise: Promise, bodyPromiseResolver}}
|
||||
*/
|
||||
export const promisesAndResolvers = () => {
|
||||
// We want to show the widget instantly but loading whilst waiting for our data.
|
||||
let bodyPromiseResolver;
|
||||
const bodyPromise = new Promise(resolve => {
|
||||
bodyPromiseResolver = resolve;
|
||||
});
|
||||
|
||||
return {bodyPromiseResolver, bodyPromise};
|
||||
};
|
||||
@@ -1,39 +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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_grades/searchwidget/error
|
||||
|
||||
Chooser error template.
|
||||
|
||||
Variables required for this template:
|
||||
* errormessage - The error message
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"errormessage": "Error"
|
||||
}
|
||||
}}
|
||||
<div class="p-2 px-sm-5 py-sm-4">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h5 class="alert-heading">
|
||||
<i class="fa fa-exclamation-circle fa-fw text-danger"></i>
|
||||
{{#str}} error, error {{/str}}
|
||||
</h5>
|
||||
<hr>
|
||||
<p class="text-break">{{{errormessage}}}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,27 +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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_grades/searchwidget/loading
|
||||
|
||||
This template renders the loading placeholder for the search widget.
|
||||
|
||||
Example context (json):
|
||||
{}
|
||||
}}
|
||||
<div class="d-flex w-100 h-100 justify-content-center align-items-center">
|
||||
{{> core/loading }}
|
||||
</div>
|
||||
@@ -1,7 +0,0 @@
|
||||
NOTE: Templates inside this directory have been deprecated since Moodle 4.3 and will be removed in 4.7.
|
||||
|
||||
They are only retained so that plugins using the now-deprecated searchwidget located in:
|
||||
|
||||
'grade/amd/src/searchwidget/basewidget.js'
|
||||
|
||||
can continue to use this widget in a working state during the deprecation period.
|
||||
@@ -1,59 +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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_grades/searchwidget/searchitem
|
||||
|
||||
Search result line items.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Quiz 1",
|
||||
"fullname": "Cameron Greeve"
|
||||
}
|
||||
}}
|
||||
<li
|
||||
role="option"
|
||||
class="dropdown-item d-flex px-2 py-1 {{^name}}align-items-center{{/name}}"
|
||||
{{#name}}id="item-{{id}}"{{/name}}
|
||||
{{^name}}id="user-{{id}}"{{/name}}
|
||||
data-value="{{id}}"
|
||||
>
|
||||
{{#name}}
|
||||
<span class="pull-left result-cell text-truncate me-2">
|
||||
{{name}}
|
||||
</span>
|
||||
{{/name}}
|
||||
{{^name}}
|
||||
<div class="d-block pe-2 w-25">
|
||||
{{#profileimage}}
|
||||
<img class="userpicture w-75 mx-auto d-block" src="{{profileimage}}" alt="" />
|
||||
{{/profileimage}}
|
||||
{{^profileimage}}
|
||||
<span class="userinitials"></span>
|
||||
{{/profileimage}}
|
||||
</div>
|
||||
<div class="d-block pe-3 w-75">
|
||||
<span class="w-100 p-0 fw-bold">
|
||||
{{fullname}}
|
||||
</span>
|
||||
<span class="w-100 pull-left text-truncate small">
|
||||
{{email}}
|
||||
</span>
|
||||
</div>
|
||||
{{/name}}
|
||||
</li>
|
||||
@@ -1,45 +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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_grades/searchwidget/searchresults
|
||||
|
||||
The wrapper in which our search results will be rendered
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"searchresults": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Quiz 1",
|
||||
"fullname": "Cameron Greeve",
|
||||
"sendmessage": "http://foo.bar/message/index.php?id=25",
|
||||
"addcontact": "http://foo.bar/message/index.php?user1=2&user2=14&addcontact=14&sesskey=XXXXX",
|
||||
"currentuser": "2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div class="searchresultitemscontainer-wrapper dropdown">
|
||||
<ul class="searchresultitemscontainer d-flex flex-column mw-100 position-relative py-2 list-group rounded-0" role="none" data-region="search-result-items-container">
|
||||
{{#searchresults}}
|
||||
{{>core_grades/searchwidget/searchitem}}
|
||||
{{/searchresults}}
|
||||
{{^searchresults}}
|
||||
<p>{{#str}} resultsfound, core, 0 {{/str}}</p>
|
||||
{{/searchresults}}
|
||||
</ul>
|
||||
</div>
|
||||
Reference in New Issue
Block a user