From 80d82635dcf0483c9a470f4aa851b576d9bcc12f Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Tue, 17 Oct 2023 22:41:15 +0800 Subject: [PATCH 1/5] MDL-78294 core_tag: New 'viewurl' property in tag_item_exporter New additional property 'viewurl' has been added to the pre-defined structure in tag_item_exporter. This property represents the URL to view a given tag. --- tag/classes/external/tag_item_exporter.php | 30 ++++++++++++++++++++++ tag/upgrade.txt | 5 ++++ 2 files changed, 35 insertions(+) diff --git a/tag/classes/external/tag_item_exporter.php b/tag/classes/external/tag_item_exporter.php index bbbe67e4b7a..2946161fed9 100644 --- a/tag/classes/external/tag_item_exporter.php +++ b/tag/classes/external/tag_item_exporter.php @@ -27,6 +27,7 @@ namespace core_tag\external; defined('MOODLE_INTERNAL') || die(); use core\external\exporter; +use renderer_base; /** * Contains related class for displaying information of a tag item. @@ -89,4 +90,33 @@ class tag_item_exporter extends exporter { ], ]; } + + /** + * Return the list of additional properties used only for display. + * + * @return array + */ + protected static function define_other_properties() { + return [ + 'viewurl' => [ + 'type' => PARAM_URL, + 'description' => 'The url to view the tag.', + 'optional' => true, + 'default' => null, + 'null' => NULL_ALLOWED, + ], + ]; + } + + /** + * Get the additional values to inject while exporting. + * + * @param renderer_base $output The renderer. + * @return array Keys are the property names, values are their values. + */ + protected function get_other_values(renderer_base $output) { + return [ + 'viewurl' => \core_tag_tag::make_url($this->data->tagcollid, $this->data->rawname)->out(false), + ]; + } } diff --git a/tag/upgrade.txt b/tag/upgrade.txt index 47b385ab613..9ff30c0e2b8 100644 --- a/tag/upgrade.txt +++ b/tag/upgrade.txt @@ -1,6 +1,11 @@ This files describes API changes in tagging, information provided here is intended especially for developers. +=== 4.4 === + +* New additional property 'viewurl' has been added to the pre-defined structure in tag_item_exporter. This property + represents the URL to view a given tag. + === 3.6 === * The following functions have been finally deprecated and can not be used anymore: From 5f10f82301021db50d6e0b3a8164a0c88e77560e Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Tue, 17 Oct 2023 22:48:40 +0800 Subject: [PATCH 2/5] MDL-78294 core_external: New return value 'icon' in external_files New return value 'icon' has been added to the webservice structure external_files. This return value represents the relative icon path for a given file. Also, get_area_files() from the core external utility class now includes this information in its return. --- lib/external/classes/external_files.php | 8 ++++++++ lib/external/classes/util.php | 1 + lib/external/tests/external_files_test.php | 2 +- lib/external/tests/util_test.php | 1 + lib/upgrade.txt | 2 ++ mod/wiki/tests/externallib_test.php | 2 ++ 6 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/external/classes/external_files.php b/lib/external/classes/external_files.php index c8f16363b64..6560e3653c0 100644 --- a/lib/external/classes/external_files.php +++ b/lib/external/classes/external_files.php @@ -40,6 +40,8 @@ class external_files extends external_multiple_structure { 'mimetype' => new external_value(PARAM_RAW, 'File mime type.', VALUE_OPTIONAL), 'isexternalfile' => new external_value(PARAM_BOOL, 'Whether is an external file.', VALUE_OPTIONAL), 'repositorytype' => new external_value(PARAM_PLUGIN, 'The repository type for external files.', VALUE_OPTIONAL), + 'icon' => new external_value(PARAM_RAW, + 'The relative path to the relevant file type icon based on the file\'s mime type.', VALUE_OPTIONAL), ], 'File.'), $desc, $required, @@ -102,6 +104,12 @@ class external_files extends external_multiple_structure { 'optional' => true, 'null' => NULL_ALLOWED, ], + 'icon' => [ + 'type' => PARAM_RAW, + 'description' => 'Relative path to the relevant file type icon based on the file\'s mime type.', + 'optional' => true, + 'null' => NULL_ALLOWED, + ], ]; } } diff --git a/lib/external/classes/util.php b/lib/external/classes/util.php index ab4d3385368..1b4bcd1fe0e 100644 --- a/lib/external/classes/util.php +++ b/lib/external/classes/util.php @@ -154,6 +154,7 @@ class util { $areafile->get_filepath(), $areafile->get_filename() )->out(false); + $file['icon'] = file_file_icon($areafile); $files[] = $file; } } diff --git a/lib/external/tests/external_files_test.php b/lib/external/tests/external_files_test.php index 479ec6dc041..4eb7dd5aa8d 100644 --- a/lib/external/tests/external_files_test.php +++ b/lib/external/tests/external_files_test.php @@ -37,7 +37,7 @@ class external_files_test extends \advanced_testcase { // First check that the expected default values and keys are returned. $expectedkeys = array_flip([ 'filename', 'filepath', 'filesize', 'fileurl', 'timemodified', 'mimetype', - 'isexternalfile', 'repositorytype', + 'isexternalfile', 'repositorytype', 'icon', ]); $returnedkeys = array_flip(array_keys($description->content->keys)); $this->assertEquals($expectedkeys, $returnedkeys); diff --git a/lib/external/tests/util_test.php b/lib/external/tests/util_test.php index 797fc1e2366..218a46b2912 100644 --- a/lib/external/tests/util_test.php +++ b/lib/external/tests/util_test.php @@ -199,6 +199,7 @@ class util_test extends \advanced_testcase { 'filesize' => $filesize, 'mimetype' => 'text/plain', 'isexternalfile' => false, + 'icon' => 'f/text', ], ]; // Get all the files for the area. diff --git a/lib/upgrade.txt b/lib/upgrade.txt index f1a7babd311..e74082227d4 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -11,6 +11,8 @@ information provided here is intended especially for developers. * course_modinfo now has a purge_course_modules_cache() method, which takes a list of cmids and purges them all in a single cache set. * Behat generators can now implement the function finish_generate_ to detect when the whole list of elements have been generated. +* New return value 'icon' has been added to the 'external_files' webservice structure. This return value represents the + relative path to the relevant file type icon based on the file's mime type. === 4.3 === diff --git a/mod/wiki/tests/externallib_test.php b/mod/wiki/tests/externallib_test.php index e8d0775a5ad..7a5dd1cea4d 100644 --- a/mod/wiki/tests/externallib_test.php +++ b/mod/wiki/tests/externallib_test.php @@ -1195,6 +1195,7 @@ class externallib_test extends externallib_advanced_testcase { 'timemodified' => $file['timemodified'], 'fileurl' => \moodle_url::make_webservice_pluginfile_url($file['contextid'], $file['component'], $file['filearea'], $file['itemid'], $file['filepath'], $file['filename']), + 'icon' => 'f/image', ); // Call the WS and check that it returns this file. @@ -1250,6 +1251,7 @@ class externallib_test extends externallib_advanced_testcase { 'timemodified' => $file['timemodified'], 'fileurl' => \moodle_url::make_webservice_pluginfile_url($file['contextid'], $file['component'], $file['filearea'], $file['itemid'], $file['filepath'], $file['filename']), + 'icon' => 'f/image', ); // Call the WS and check that it returns this file. From df1cc4ac932cf252fbaaeecbc50c8bfa69ec1e4c Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Tue, 17 Oct 2023 22:56:45 +0800 Subject: [PATCH 3/5] MDL-78294 filter_glossary: Replace YUI with an AMD module Replaces the existing YUI module in the glossary filter with an AMD module. The auto-linked glossary entries are now obtained through the existing webservices in mod_glossary and displayed within a modal utilizing the core/modal js module. --- filter/glossary/amd/build/autolinker.min.js | 10 + .../glossary/amd/build/autolinker.min.js.map | 1 + filter/glossary/amd/src/autolinker.js | 150 ++++++++++++++ filter/glossary/filter.php | 10 +- filter/glossary/lang/en/filter_glossary.php | 1 + .../templates/linked_glossary_entry.mustache | 74 +++++++ ...moodle-filter_glossary-autolinker-debug.js | 193 ------------------ .../moodle-filter_glossary-autolinker-min.js | 1 - .../moodle-filter_glossary-autolinker.js | 193 ------------------ filter/glossary/yui/src/autolinker/build.json | 10 - .../yui/src/autolinker/js/autolinker.js | 175 ---------------- .../yui/src/autolinker/meta/autolinker.json | 16 -- mod/glossary/db/services.php | 2 + 13 files changed, 242 insertions(+), 594 deletions(-) create mode 100644 filter/glossary/amd/build/autolinker.min.js create mode 100644 filter/glossary/amd/build/autolinker.min.js.map create mode 100644 filter/glossary/amd/src/autolinker.js create mode 100644 filter/glossary/templates/linked_glossary_entry.mustache delete mode 100644 filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js delete mode 100644 filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js delete mode 100644 filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js delete mode 100644 filter/glossary/yui/src/autolinker/build.json delete mode 100644 filter/glossary/yui/src/autolinker/js/autolinker.js delete mode 100644 filter/glossary/yui/src/autolinker/meta/autolinker.json diff --git a/filter/glossary/amd/build/autolinker.min.js b/filter/glossary/amd/build/autolinker.min.js new file mode 100644 index 00000000000..7876a798a7f --- /dev/null +++ b/filter/glossary/amd/build/autolinker.min.js @@ -0,0 +1,10 @@ +define("filter_glossary/autolinker",["exports","core/ajax","core/modal_cancel","core/templates","core/str"],(function(_exports,_ajax,_modal_cancel,_templates,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} +/** + * Module for auto-linking glossary entries. + * + * @module filter_glossary/autolinker + * @copyright 2023 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_modal_cancel=_interopRequireDefault(_modal_cancel),_templates=_interopRequireDefault(_templates);const Selectors_glossaryEntryAutoLink="a.glossary.autolink.concept",showGlossaryEntry=async entryId=>{const entryData=await fetchGlossaryEntry(entryId),{html:html,js:js}=await _templates.default.renderForPromise("filter_glossary/linked_glossary_entry",{definition:entryData.entry.definition,taglistdata:await generateTagListData(entryData.entry.tags),hasattachments:Boolean(entryData.entry.attachment),attachments:entryData.entry.attachments}),modal=await _modal_cancel.default.create({title:entryData.entry.concept,body:html,isVerticallyCentered:!0,buttons:{cancel:await(0,_str.getString)("ok")}});return _templates.default.runTemplateJS(js),modal.show(),modal},fetchGlossaryEntry=entryId=>{const request={methodname:"mod_glossary_get_entry_by_id",args:{id:entryId}};return _ajax.default.call([request])[0]},glossaryEntryViewed=entryId=>{const request={methodname:"mod_glossary_view_entry",args:{id:entryId}};return _ajax.default.call([request])[0]},generateTagListData=async tags=>{const hasOverflow=tags.length>10;if(hasOverflow)for(let i=10;i{document.addEventListener("click",(async e=>{const glossaryEntryAutoLink=e.target.closest(Selectors_glossaryEntryAutoLink);if(glossaryEntryAutoLink){e.preventDefault();const entryId=glossaryEntryAutoLink.dataset.entryid;await showGlossaryEntry(entryId),await glossaryEntryViewed(entryId)}}))}})); + +//# sourceMappingURL=autolinker.min.js.map \ No newline at end of file diff --git a/filter/glossary/amd/build/autolinker.min.js.map b/filter/glossary/amd/build/autolinker.min.js.map new file mode 100644 index 00000000000..16cba74f989 --- /dev/null +++ b/filter/glossary/amd/build/autolinker.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"autolinker.min.js","sources":["../src/autolinker.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Module for auto-linking glossary entries.\n *\n * @module filter_glossary/autolinker\n * @copyright 2023 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport ModalCancel from \"core/modal_cancel\";\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n glossaryEntryAutoLink: 'a.glossary.autolink.concept',\n};\n\n/**\n * Register the event listeners for the glossary entry auto-linker.\n *\n * @return {void}\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', async(e) => {\n const glossaryEntryAutoLink = e.target.closest(Selectors.glossaryEntryAutoLink);\n if (glossaryEntryAutoLink) {\n e.preventDefault();\n const entryId = glossaryEntryAutoLink.dataset.entryid;\n await showGlossaryEntry(entryId);\n await glossaryEntryViewed(entryId);\n }\n });\n};\n\n/**\n * Show the linked glossary entry in a modal.\n *\n * @method showGlossaryEntry\n * @param {int} entryId The id of the linked glossary entry.\n * @returns {Promise} The modal promise.\n */\nconst showGlossaryEntry = async(entryId) => {\n const entryData = await fetchGlossaryEntry(entryId);\n // Obtain the HTML and JS used for rendering the auto-linked glossary entry.\n const {html, js} = await Templates.renderForPromise('filter_glossary/linked_glossary_entry', {\n definition: entryData.entry.definition,\n taglistdata: await generateTagListData(entryData.entry.tags),\n hasattachments: Boolean(entryData.entry.attachment),\n attachments: entryData.entry.attachments\n });\n // Create the modal.\n const modal = await ModalCancel.create({\n title: entryData.entry.concept,\n body: html,\n isVerticallyCentered: true,\n buttons: {\n cancel: await getString('ok')\n }\n });\n // Execute the JS code returned from the template once the modal is created.\n Templates.runTemplateJS(js);\n // Display the modal.\n modal.show();\n\n return modal;\n};\n\n/**\n * Fetch the linked glossary entry.\n *\n * @method fetchGlossaryEntry\n * @param {int} entryId The id of the linked glossary entry.\n * @returns {Promise} The glossary entry promise.\n */\nconst fetchGlossaryEntry = (entryId) => {\n const request = {\n methodname: 'mod_glossary_get_entry_by_id',\n args: {\n id: entryId,\n },\n };\n return Ajax.call([request])[0];\n};\n\n/**\n * Notify that the linked glossary entry was viewed.\n *\n * @method glossaryEntryViewed\n * @param {int} entryId The id of the linked glossary entry.\n * @returns {Promise} The promise object.\n */\nconst glossaryEntryViewed = (entryId) => {\n const request = {\n methodname: 'mod_glossary_view_entry',\n args: {\n id: entryId,\n },\n };\n return Ajax.call([request])[0];\n};\n\n/**\n * Generates an object that contains the data required to render a tag list.\n *\n * @method generateTagListData\n * @param {array} tags The array containing the tags related to the linked glossary entry.\n * @returns {Object} The data required to render a tag list.\n */\nconst generateTagListData = async(tags) => {\n // Define the number of initially displayed tags.\n const limit = 10;\n const hasOverflow = tags.length > limit;\n // If the total number of tags exceeds the defined limit, then we need to mark all the excess tags as over the limit.\n // By specifying this, these tags will be initially hidden.\n if (hasOverflow) {\n for (let i = limit; i < tags.length; i++) {\n tags[i].overlimit = true;\n }\n }\n\n return {\n tags: tags,\n tagscount: tags.length,\n overflow: hasOverflow,\n label: await getString('tags')\n };\n};\n\n/**\n * Initialize the module.\n */\nexport const init = () => {\n registerEventListeners();\n};\n"],"names":["Selectors","showGlossaryEntry","async","entryData","fetchGlossaryEntry","entryId","html","js","Templates","renderForPromise","definition","entry","taglistdata","generateTagListData","tags","hasattachments","Boolean","attachment","attachments","modal","ModalCancel","create","title","concept","body","isVerticallyCentered","buttons","cancel","runTemplateJS","show","request","methodname","args","id","Ajax","call","glossaryEntryViewed","hasOverflow","length","i","overlimit","tagscount","overflow","label","document","addEventListener","glossaryEntryAutoLink","e","target","closest","preventDefault","dataset","entryid"],"mappings":";;;;;;;8NA6BMA,gCACqB,8BA2BrBC,kBAAoBC,MAAAA,gBAChBC,gBAAkBC,mBAAmBC,UAErCC,KAACA,KAADC,GAAOA,UAAYC,mBAAUC,iBAAiB,wCAAyC,CACzFC,WAAYP,UAAUQ,MAAMD,WAC5BE,kBAAmBC,oBAAoBV,UAAUQ,MAAMG,MACvDC,eAAgBC,QAAQb,UAAUQ,MAAMM,YACxCC,YAAaf,UAAUQ,MAAMO,cAG3BC,YAAcC,sBAAYC,OAAO,CACnCC,MAAOnB,UAAUQ,MAAMY,QACvBC,KAAMlB,KACNmB,sBAAsB,EACtBC,QAAS,CACLC,aAAc,kBAAU,mCAItBC,cAAcrB,IAExBY,MAAMU,OAECV,OAULf,mBAAsBC,gBAClByB,QAAU,CACZC,WAAY,+BACZC,KAAM,CACFC,GAAI5B,iBAGL6B,cAAKC,KAAK,CAACL,UAAU,IAU1BM,oBAAuB/B,gBACnByB,QAAU,CACZC,WAAY,0BACZC,KAAM,CACFC,GAAI5B,iBAGL6B,cAAKC,KAAK,CAACL,UAAU,IAU1BjB,oBAAsBX,MAAAA,aAGlBmC,YAAcvB,KAAKwB,OADX,MAIVD,gBACK,IAAIE,EALC,GAKUA,EAAIzB,KAAKwB,OAAQC,IACjCzB,KAAKyB,GAAGC,WAAY,QAIrB,CACH1B,KAAMA,KACN2B,UAAW3B,KAAKwB,OAChBI,SAAUL,YACVM,YAAa,kBAAU,wBAOX,KA5GhBC,SAASC,iBAAiB,SAAS3C,MAAAA,UACzB4C,sBAAwBC,EAAEC,OAAOC,QAAQjD,oCAC3C8C,sBAAuB,CACvBC,EAAEG,uBACI7C,QAAUyC,sBAAsBK,QAAQC,cACxCnD,kBAAkBI,eAClB+B,oBAAoB/B"} \ No newline at end of file diff --git a/filter/glossary/amd/src/autolinker.js b/filter/glossary/amd/src/autolinker.js new file mode 100644 index 00000000000..83913571e07 --- /dev/null +++ b/filter/glossary/amd/src/autolinker.js @@ -0,0 +1,150 @@ +// 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 . + +/** + * Module for auto-linking glossary entries. + * + * @module filter_glossary/autolinker + * @copyright 2023 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +import Ajax from 'core/ajax'; +import ModalCancel from "core/modal_cancel"; +import Templates from 'core/templates'; +import {getString} from 'core/str'; + +/** @constant {Object} The object containing the relevant selectors. */ +const Selectors = { + glossaryEntryAutoLink: 'a.glossary.autolink.concept', +}; + +/** + * Register the event listeners for the glossary entry auto-linker. + * + * @return {void} + */ +const registerEventListeners = () => { + document.addEventListener('click', async(e) => { + const glossaryEntryAutoLink = e.target.closest(Selectors.glossaryEntryAutoLink); + if (glossaryEntryAutoLink) { + e.preventDefault(); + const entryId = glossaryEntryAutoLink.dataset.entryid; + await showGlossaryEntry(entryId); + await glossaryEntryViewed(entryId); + } + }); +}; + +/** + * Show the linked glossary entry in a modal. + * + * @method showGlossaryEntry + * @param {int} entryId The id of the linked glossary entry. + * @returns {Promise} The modal promise. + */ +const showGlossaryEntry = async(entryId) => { + const entryData = await fetchGlossaryEntry(entryId); + // Obtain the HTML and JS used for rendering the auto-linked glossary entry. + const {html, js} = await Templates.renderForPromise('filter_glossary/linked_glossary_entry', { + definition: entryData.entry.definition, + taglistdata: await generateTagListData(entryData.entry.tags), + hasattachments: Boolean(entryData.entry.attachment), + attachments: entryData.entry.attachments + }); + // Create the modal. + const modal = await ModalCancel.create({ + title: entryData.entry.concept, + body: html, + isVerticallyCentered: true, + buttons: { + cancel: await getString('ok') + } + }); + // Execute the JS code returned from the template once the modal is created. + Templates.runTemplateJS(js); + // Display the modal. + modal.show(); + + return modal; +}; + +/** + * Fetch the linked glossary entry. + * + * @method fetchGlossaryEntry + * @param {int} entryId The id of the linked glossary entry. + * @returns {Promise} The glossary entry promise. + */ +const fetchGlossaryEntry = (entryId) => { + const request = { + methodname: 'mod_glossary_get_entry_by_id', + args: { + id: entryId, + }, + }; + return Ajax.call([request])[0]; +}; + +/** + * Notify that the linked glossary entry was viewed. + * + * @method glossaryEntryViewed + * @param {int} entryId The id of the linked glossary entry. + * @returns {Promise} The promise object. + */ +const glossaryEntryViewed = (entryId) => { + const request = { + methodname: 'mod_glossary_view_entry', + args: { + id: entryId, + }, + }; + return Ajax.call([request])[0]; +}; + +/** + * Generates an object that contains the data required to render a tag list. + * + * @method generateTagListData + * @param {array} tags The array containing the tags related to the linked glossary entry. + * @returns {Object} The data required to render a tag list. + */ +const generateTagListData = async(tags) => { + // Define the number of initially displayed tags. + const limit = 10; + const hasOverflow = tags.length > limit; + // If the total number of tags exceeds the defined limit, then we need to mark all the excess tags as over the limit. + // By specifying this, these tags will be initially hidden. + if (hasOverflow) { + for (let i = limit; i < tags.length; i++) { + tags[i].overlimit = true; + } + } + + return { + tags: tags, + tagscount: tags.length, + overflow: hasOverflow, + label: await getString('tags') + }; +}; + +/** + * Initialize the module. + */ +export const init = () => { + registerEventListeners(); +}; diff --git a/filter/glossary/filter.php b/filter/glossary/filter.php index 1f2591836a1..65f9186e8a4 100644 --- a/filter/glossary/filter.php +++ b/filter/glossary/filter.php @@ -38,11 +38,7 @@ class filter_glossary extends moodle_text_filter { public function setup($page, $context) { if ($page->requires->should_create_one_time_item_now('filter_glossary_autolinker')) { - $page->requires->yui_module( - 'moodle-filter_glossary-autolinker', - 'M.filter_glossary.init_filter_autolinking', - array(array('courseid' => 0))); - $page->requires->strings_for_js(array('ok'), 'moodle'); + $page->requires->js_call_amd('filter_glossary/autolinker', 'init', []); } } @@ -145,7 +141,9 @@ class filter_glossary extends moodle_text_filter { $attributes = array( 'href' => $link, 'title' => str_replace('&', '&', $title), // Undo the s() mangling. - 'class' => 'glossary autolink concept glossaryid' . $concept->glossaryid); + 'class' => 'glossary autolink concept glossaryid' . $concept->glossaryid, + 'data-entryid' => $concept->id, + ); } // This flag is optionally set by resource_pluginfile() diff --git a/filter/glossary/lang/en/filter_glossary.php b/filter/glossary/lang/en/filter_glossary.php index d7917afb25c..ee75e57f570 100644 --- a/filter/glossary/lang/en/filter_glossary.php +++ b/filter/glossary/lang/en/filter_glossary.php @@ -29,3 +29,4 @@ $string['glossarycategory'] = '{$a->glossary}: Category {$a->category}'; $string['glossaryconcept'] = '{$a->glossary}: {$a->concept}'; $string['filtername'] = 'Glossary auto-linking'; $string['privacy:metadata'] = 'The Glossary auto-linking plugin does not store any personal data.'; +$string['attachments'] = 'Attachments'; diff --git a/filter/glossary/templates/linked_glossary_entry.mustache b/filter/glossary/templates/linked_glossary_entry.mustache new file mode 100644 index 00000000000..a96e25ad546 --- /dev/null +++ b/filter/glossary/templates/linked_glossary_entry.mustache @@ -0,0 +1,74 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template filter_glossary/linked_glossary_entry + + Renders the linked glossary entry. + + Context variables required for this template: + * definition - The definition of the linked glossary entry. + * taglistdata - The data for the tag list related to the linked glossary entry. + * hasattachments - Whether the linked glossary entry has attachments. + * attachments - The data for the attachments related to the linked glossary entry. + + Example context (json): + { + "definition": 1, + "taglistdata": { + "tags": [ + { + "id": 1, + "name": "Cats", + "viewurl": "http://moodle.org/tag/index.php?tag=Cats", + "isstandard": true, + "flag": 0 + } + ], + "label": "Tags", + "tagscount": 1, + "overflow": false + }, + "hasattachments": true, + "attachments": [ + { + "filename": "example.jpg", + "fileurl": "www.example.com/example.jpg", + "icon": "f/image" + } + ] + } +}} + +

{{{definition}}}

+{{#taglistdata}} +
+ {{> core_tag/taglist }} +
+{{/taglistdata}} +{{#hasattachments}} + {{#str}} attachments, filter_glossary {{/str}}: +
    + {{#attachments}} +
  • + {{#pix}} {{icon}}, core {{/pix}} + + {{filename}} + +
  • + {{/attachments}} +
+{{/hasattachments}} diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js deleted file mode 100644 index 81f03933b08..00000000000 --- a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js +++ /dev/null @@ -1,193 +0,0 @@ -YUI.add('moodle-filter_glossary-autolinker', function (Y, NAME) { - -var AUTOLINKERNAME = 'Glossary filter autolinker', - WIDTH = 'width', - HEIGHT = 'height', - MENUBAR = 'menubar', - LOCATION = 'location', - SCROLLBARS = 'scrollbars', - RESIZEABLE = 'resizable', - TOOLBAR = 'toolbar', - STATUS = 'status', - DIRECTORIES = 'directories', - FULLSCREEN = 'fullscreen', - DEPENDENT = 'dependent', - AUTOLINKER; - -AUTOLINKER = function() { - AUTOLINKER.superclass.constructor.apply(this, arguments); -}; -Y.extend(AUTOLINKER, Y.Base, { - overlay: null, - alertpanels: {}, - initializer: function() { - var self = this; - require(['core/event'], function(event) { - Y.delegate('click', function(e) { - e.preventDefault(); - - // display a progress indicator - var title = '', - content = Y.Node.create('
' + - '
'), - o = new Y.Overlay({ - headerContent: title, - bodyContent: content - }), - fullurl, - cfg; - - window.require(['core/templates'], function(Templates) { - Templates.renderPix('i/loading', 'core').then(function(html) { - content.append(html); - }); - }); - - self.overlay = o; - o.render(Y.one(document.body)); - - // Switch over to the ajax url and fetch the glossary item - fullurl = this.getAttribute('href').replace('showentry.php', 'showentry_ajax.php'); - cfg = { - method: 'get', - context: self, - on: { - success: function(id, o) { - this.display_callback(o.responseText, event); - }, - failure: function(id, o) { - var debuginfo = o.statusText; - if (M.cfg.developerdebug) { - o.statusText += ' (' + fullurl + ')'; - } - new M.core.exception({message: debuginfo}); - } - } - }; - Y.io(fullurl, cfg); - - }, Y.one(document.body), 'a.glossary.autolink.concept'); - }); - }, - /** - * @method display_callback - * @param {String} content - Content to display - * @param {Object} event The amd event module used to fire events for jquery and yui. - */ - display_callback: function(content, event) { - var data, - key, - alertpanel, - alertpanelid, - definition, - position; - try { - data = Y.JSON.parse(content); - if (data.success) { - this.overlay.hide(); // hide progress indicator - - for (key in data.entries) { - definition = data.entries[key].definition + data.entries[key].attachments; - alertpanel = new M.core.alert({title: data.entries[key].concept, draggable: true, - message: definition, modal: false, yesLabel: M.util.get_string('ok', 'moodle')}); - // Notify the filters about the modified nodes. - event.notifyFilterContentUpdated(alertpanel.get('boundingBox').getDOMNode()); - Y.Node.one('#id_yuialertconfirm-' + alertpanel.get('COUNT')).focus(); - - // Register alertpanel for stacking. - alertpanelid = '#moodle-dialogue-' + alertpanel.get('COUNT'); - alertpanel.on('complete', this._deletealertpanel, this, alertpanelid); - - // We already have some windows opened, so set the right position... - if (!Y.Object.isEmpty(this.alertpanels)) { - position = this._getLatestWindowPosition(); - Y.Node.one(alertpanelid).setXY([position[0] + 10, position[1] + 10]); - } - - this.alertpanels[alertpanelid] = Y.Node.one(alertpanelid).getXY(); - } - - return true; - } else if (data.error) { - new M.core.ajaxException(data); - } - } catch (e) { - new M.core.exception(e); - } - return false; - }, - _getLatestWindowPosition: function() { - var lastPosition = [0, 0]; - Y.Object.each(this.alertpanels, function(position) { - if (position[0] > lastPosition[0]) { - lastPosition = position; - } - }); - return lastPosition; - }, - _deletealertpanel: function(ev, alertpanelid) { - delete this.alertpanels[alertpanelid]; - } -}, { - NAME: AUTOLINKERNAME, - ATTRS: { - url: { - validator: Y.Lang.isString, - value: M.cfg.wwwroot + '/mod/glossary/showentry.php' - }, - name: { - validator: Y.Lang.isString, - value: 'glossaryconcept' - }, - options: { - getter: function() { - return { - width: this.get(WIDTH), - height: this.get(HEIGHT), - menubar: this.get(MENUBAR), - location: this.get(LOCATION), - scrollbars: this.get(SCROLLBARS), - resizable: this.get(RESIZEABLE), - toolbar: this.get(TOOLBAR), - status: this.get(STATUS), - directories: this.get(DIRECTORIES), - fullscreen: this.get(FULLSCREEN), - dependent: this.get(DEPENDENT) - }; - }, - readOnly: true - }, - width: {value: 600}, - height: {value: 450}, - menubar: {value: false}, - location: {value: false}, - scrollbars: {value: true}, - resizable: {value: true}, - toolbar: {value: true}, - status: {value: true}, - directories: {value: false}, - fullscreen: {value: false}, - dependent: {value: true} - } -}); - -M.filter_glossary = M.filter_glossary || {}; -M.filter_glossary.init_filter_autolinking = function(config) { - return new AUTOLINKER(config); -}; - - -}, '@VERSION@', { - "requires": [ - "base", - "node", - "io-base", - "json-parse", - "event-delegate", - "overlay", - "moodle-core-event", - "moodle-core-notification-alert", - "moodle-core-notification-exception", - "moodle-core-notification-ajaxexception" - ] -}); diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js deleted file mode 100644 index 20d23ce17fb..00000000000 --- a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js +++ /dev/null @@ -1 +0,0 @@ -YUI.add("moodle-filter_glossary-autolinker",function(c,e){var t=function(){t.superclass.constructor.apply(this,arguments)};c.extend(t,c.Base,{overlay:null,alertpanels:{},initializer:function(){var i=this;require(["core/event"],function(o){c.delegate("click",function(e){e.preventDefault();var n,t=c.Node.create('
'),e=new c.Overlay({headerContent:"",bodyContent:t});window.require(["core/templates"],function(e){e.renderPix("i/loading","core").then(function(e){t.append(e)})}),(i.overlay=e).render(c.one(document.body)),n=this.getAttribute("href").replace("showentry.php","showentry_ajax.php"),c.io(n,{method:"get",context:i,on:{success:function(e,t){this.display_callback(t.responseText,o)},failure:function(e,t){var o=t.statusText;M.cfg.developerdebug&&(t.statusText+=" ("+n+")"),new M.core.exception({message:o})}}})},c.one(document.body),"a.glossary.autolink.concept")})},display_callback:function(e,t){var o,n,i,r,a,l;try{if((o=c.JSON.parse(e)).success){for(n in this.overlay.hide(),o.entries)a=o.entries[n].definition+o.entries[n].attachments,i=new M.core.alert({title:o.entries[n].concept,draggable:!0,message:a,modal:!1,yesLabel:M.util.get_string("ok","moodle")}),t.notifyFilterContentUpdated(i.get("boundingBox").getDOMNode()),c.Node.one("#id_yuialertconfirm-"+i.get("COUNT")).focus(),r="#moodle-dialogue-"+i.get("COUNT"),i.on("complete",this._deletealertpanel,this,r),c.Object.isEmpty(this.alertpanels)||(l=this._getLatestWindowPosition(),c.Node.one(r).setXY([l[0]+10,l[1]+10])),this.alertpanels[r]=c.Node.one(r).getXY();return!0}o.error&&new M.core.ajaxException(o)}catch(s){new M.core.exception(s)}return!1},_getLatestWindowPosition:function(){var t=[0,0];return c.Object.each(this.alertpanels,function(e){e[0]>t[0]&&(t=e)}),t},_deletealertpanel:function(e,t){delete this.alertpanels[t]}},{NAME:"Glossary filter autolinker",ATTRS:{url:{validator:c.Lang.isString,value:M.cfg.wwwroot+"/mod/glossary/showentry.php"},name:{validator:c.Lang.isString,value:"glossaryconcept"},options:{getter:function(){return{width:this.get("width"),height:this.get("height"),menubar:this.get("menubar"),location:this.get("location"),scrollbars:this.get("scrollbars"),resizable:this.get("resizable"),toolbar:this.get("toolbar"),status:this.get("status"),directories:this.get("directories"),fullscreen:this.get("fullscreen"),dependent:this.get("dependent")}},readOnly:!0},width:{value:600},height:{value:450},menubar:{value:!1},location:{value:!1},scrollbars:{value:!0},resizable:{value:!0},toolbar:{value:!0},status:{value:!0},directories:{value:!1},fullscreen:{value:!1},dependent:{value:!0}}}),M.filter_glossary=M.filter_glossary||{},M.filter_glossary.init_filter_autolinking=function(e){return new t(e)}},"@VERSION@",{requires:["base","node","io-base","json-parse","event-delegate","overlay","moodle-core-event","moodle-core-notification-alert","moodle-core-notification-exception","moodle-core-notification-ajaxexception"]}); \ No newline at end of file diff --git a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js b/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js deleted file mode 100644 index 81f03933b08..00000000000 --- a/filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js +++ /dev/null @@ -1,193 +0,0 @@ -YUI.add('moodle-filter_glossary-autolinker', function (Y, NAME) { - -var AUTOLINKERNAME = 'Glossary filter autolinker', - WIDTH = 'width', - HEIGHT = 'height', - MENUBAR = 'menubar', - LOCATION = 'location', - SCROLLBARS = 'scrollbars', - RESIZEABLE = 'resizable', - TOOLBAR = 'toolbar', - STATUS = 'status', - DIRECTORIES = 'directories', - FULLSCREEN = 'fullscreen', - DEPENDENT = 'dependent', - AUTOLINKER; - -AUTOLINKER = function() { - AUTOLINKER.superclass.constructor.apply(this, arguments); -}; -Y.extend(AUTOLINKER, Y.Base, { - overlay: null, - alertpanels: {}, - initializer: function() { - var self = this; - require(['core/event'], function(event) { - Y.delegate('click', function(e) { - e.preventDefault(); - - // display a progress indicator - var title = '', - content = Y.Node.create('
' + - '
'), - o = new Y.Overlay({ - headerContent: title, - bodyContent: content - }), - fullurl, - cfg; - - window.require(['core/templates'], function(Templates) { - Templates.renderPix('i/loading', 'core').then(function(html) { - content.append(html); - }); - }); - - self.overlay = o; - o.render(Y.one(document.body)); - - // Switch over to the ajax url and fetch the glossary item - fullurl = this.getAttribute('href').replace('showentry.php', 'showentry_ajax.php'); - cfg = { - method: 'get', - context: self, - on: { - success: function(id, o) { - this.display_callback(o.responseText, event); - }, - failure: function(id, o) { - var debuginfo = o.statusText; - if (M.cfg.developerdebug) { - o.statusText += ' (' + fullurl + ')'; - } - new M.core.exception({message: debuginfo}); - } - } - }; - Y.io(fullurl, cfg); - - }, Y.one(document.body), 'a.glossary.autolink.concept'); - }); - }, - /** - * @method display_callback - * @param {String} content - Content to display - * @param {Object} event The amd event module used to fire events for jquery and yui. - */ - display_callback: function(content, event) { - var data, - key, - alertpanel, - alertpanelid, - definition, - position; - try { - data = Y.JSON.parse(content); - if (data.success) { - this.overlay.hide(); // hide progress indicator - - for (key in data.entries) { - definition = data.entries[key].definition + data.entries[key].attachments; - alertpanel = new M.core.alert({title: data.entries[key].concept, draggable: true, - message: definition, modal: false, yesLabel: M.util.get_string('ok', 'moodle')}); - // Notify the filters about the modified nodes. - event.notifyFilterContentUpdated(alertpanel.get('boundingBox').getDOMNode()); - Y.Node.one('#id_yuialertconfirm-' + alertpanel.get('COUNT')).focus(); - - // Register alertpanel for stacking. - alertpanelid = '#moodle-dialogue-' + alertpanel.get('COUNT'); - alertpanel.on('complete', this._deletealertpanel, this, alertpanelid); - - // We already have some windows opened, so set the right position... - if (!Y.Object.isEmpty(this.alertpanels)) { - position = this._getLatestWindowPosition(); - Y.Node.one(alertpanelid).setXY([position[0] + 10, position[1] + 10]); - } - - this.alertpanels[alertpanelid] = Y.Node.one(alertpanelid).getXY(); - } - - return true; - } else if (data.error) { - new M.core.ajaxException(data); - } - } catch (e) { - new M.core.exception(e); - } - return false; - }, - _getLatestWindowPosition: function() { - var lastPosition = [0, 0]; - Y.Object.each(this.alertpanels, function(position) { - if (position[0] > lastPosition[0]) { - lastPosition = position; - } - }); - return lastPosition; - }, - _deletealertpanel: function(ev, alertpanelid) { - delete this.alertpanels[alertpanelid]; - } -}, { - NAME: AUTOLINKERNAME, - ATTRS: { - url: { - validator: Y.Lang.isString, - value: M.cfg.wwwroot + '/mod/glossary/showentry.php' - }, - name: { - validator: Y.Lang.isString, - value: 'glossaryconcept' - }, - options: { - getter: function() { - return { - width: this.get(WIDTH), - height: this.get(HEIGHT), - menubar: this.get(MENUBAR), - location: this.get(LOCATION), - scrollbars: this.get(SCROLLBARS), - resizable: this.get(RESIZEABLE), - toolbar: this.get(TOOLBAR), - status: this.get(STATUS), - directories: this.get(DIRECTORIES), - fullscreen: this.get(FULLSCREEN), - dependent: this.get(DEPENDENT) - }; - }, - readOnly: true - }, - width: {value: 600}, - height: {value: 450}, - menubar: {value: false}, - location: {value: false}, - scrollbars: {value: true}, - resizable: {value: true}, - toolbar: {value: true}, - status: {value: true}, - directories: {value: false}, - fullscreen: {value: false}, - dependent: {value: true} - } -}); - -M.filter_glossary = M.filter_glossary || {}; -M.filter_glossary.init_filter_autolinking = function(config) { - return new AUTOLINKER(config); -}; - - -}, '@VERSION@', { - "requires": [ - "base", - "node", - "io-base", - "json-parse", - "event-delegate", - "overlay", - "moodle-core-event", - "moodle-core-notification-alert", - "moodle-core-notification-exception", - "moodle-core-notification-ajaxexception" - ] -}); diff --git a/filter/glossary/yui/src/autolinker/build.json b/filter/glossary/yui/src/autolinker/build.json deleted file mode 100644 index e6e0e90ad47..00000000000 --- a/filter/glossary/yui/src/autolinker/build.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "moodle-filter_glossary-autolinker", - "builds": { - "moodle-filter_glossary-autolinker": { - "jsfiles": [ - "autolinker.js" - ] - } - } -} diff --git a/filter/glossary/yui/src/autolinker/js/autolinker.js b/filter/glossary/yui/src/autolinker/js/autolinker.js deleted file mode 100644 index 8aea8fdfa41..00000000000 --- a/filter/glossary/yui/src/autolinker/js/autolinker.js +++ /dev/null @@ -1,175 +0,0 @@ -var AUTOLINKERNAME = 'Glossary filter autolinker', - WIDTH = 'width', - HEIGHT = 'height', - MENUBAR = 'menubar', - LOCATION = 'location', - SCROLLBARS = 'scrollbars', - RESIZEABLE = 'resizable', - TOOLBAR = 'toolbar', - STATUS = 'status', - DIRECTORIES = 'directories', - FULLSCREEN = 'fullscreen', - DEPENDENT = 'dependent', - AUTOLINKER; - -AUTOLINKER = function() { - AUTOLINKER.superclass.constructor.apply(this, arguments); -}; -Y.extend(AUTOLINKER, Y.Base, { - overlay: null, - alertpanels: {}, - initializer: function() { - var self = this; - require(['core/event'], function(event) { - Y.delegate('click', function(e) { - e.preventDefault(); - - // display a progress indicator - var title = '', - content = Y.Node.create('
' + - '
'), - o = new Y.Overlay({ - headerContent: title, - bodyContent: content - }), - fullurl, - cfg; - - window.require(['core/templates'], function(Templates) { - Templates.renderPix('i/loading', 'core').then(function(html) { - content.append(html); - }); - }); - - self.overlay = o; - o.render(Y.one(document.body)); - - // Switch over to the ajax url and fetch the glossary item - fullurl = this.getAttribute('href').replace('showentry.php', 'showentry_ajax.php'); - cfg = { - method: 'get', - context: self, - on: { - success: function(id, o) { - this.display_callback(o.responseText, event); - }, - failure: function(id, o) { - var debuginfo = o.statusText; - if (M.cfg.developerdebug) { - o.statusText += ' (' + fullurl + ')'; - } - new M.core.exception({message: debuginfo}); - } - } - }; - Y.io(fullurl, cfg); - - }, Y.one(document.body), 'a.glossary.autolink.concept'); - }); - }, - /** - * @method display_callback - * @param {String} content - Content to display - * @param {Object} event The amd event module used to fire events for jquery and yui. - */ - display_callback: function(content, event) { - var data, - key, - alertpanel, - alertpanelid, - definition, - position; - try { - data = Y.JSON.parse(content); - if (data.success) { - this.overlay.hide(); // hide progress indicator - - for (key in data.entries) { - definition = data.entries[key].definition + data.entries[key].attachments; - alertpanel = new M.core.alert({title: data.entries[key].concept, draggable: true, - message: definition, modal: false, yesLabel: M.util.get_string('ok', 'moodle')}); - // Notify the filters about the modified nodes. - event.notifyFilterContentUpdated(alertpanel.get('boundingBox').getDOMNode()); - Y.Node.one('#id_yuialertconfirm-' + alertpanel.get('COUNT')).focus(); - - // Register alertpanel for stacking. - alertpanelid = '#moodle-dialogue-' + alertpanel.get('COUNT'); - alertpanel.on('complete', this._deletealertpanel, this, alertpanelid); - - // We already have some windows opened, so set the right position... - if (!Y.Object.isEmpty(this.alertpanels)) { - position = this._getLatestWindowPosition(); - Y.Node.one(alertpanelid).setXY([position[0] + 10, position[1] + 10]); - } - - this.alertpanels[alertpanelid] = Y.Node.one(alertpanelid).getXY(); - } - - return true; - } else if (data.error) { - new M.core.ajaxException(data); - } - } catch (e) { - new M.core.exception(e); - } - return false; - }, - _getLatestWindowPosition: function() { - var lastPosition = [0, 0]; - Y.Object.each(this.alertpanels, function(position) { - if (position[0] > lastPosition[0]) { - lastPosition = position; - } - }); - return lastPosition; - }, - _deletealertpanel: function(ev, alertpanelid) { - delete this.alertpanels[alertpanelid]; - } -}, { - NAME: AUTOLINKERNAME, - ATTRS: { - url: { - validator: Y.Lang.isString, - value: M.cfg.wwwroot + '/mod/glossary/showentry.php' - }, - name: { - validator: Y.Lang.isString, - value: 'glossaryconcept' - }, - options: { - getter: function() { - return { - width: this.get(WIDTH), - height: this.get(HEIGHT), - menubar: this.get(MENUBAR), - location: this.get(LOCATION), - scrollbars: this.get(SCROLLBARS), - resizable: this.get(RESIZEABLE), - toolbar: this.get(TOOLBAR), - status: this.get(STATUS), - directories: this.get(DIRECTORIES), - fullscreen: this.get(FULLSCREEN), - dependent: this.get(DEPENDENT) - }; - }, - readOnly: true - }, - width: {value: 600}, - height: {value: 450}, - menubar: {value: false}, - location: {value: false}, - scrollbars: {value: true}, - resizable: {value: true}, - toolbar: {value: true}, - status: {value: true}, - directories: {value: false}, - fullscreen: {value: false}, - dependent: {value: true} - } -}); - -M.filter_glossary = M.filter_glossary || {}; -M.filter_glossary.init_filter_autolinking = function(config) { - return new AUTOLINKER(config); -}; diff --git a/filter/glossary/yui/src/autolinker/meta/autolinker.json b/filter/glossary/yui/src/autolinker/meta/autolinker.json deleted file mode 100644 index 36f0fea5ef9..00000000000 --- a/filter/glossary/yui/src/autolinker/meta/autolinker.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "moodle-filter_glossary-autolinker": { - "requires": [ - "base", - "node", - "io-base", - "json-parse", - "event-delegate", - "overlay", - "moodle-core-event", - "moodle-core-notification-alert", - "moodle-core-notification-exception", - "moodle-core-notification-ajaxexception" - ] - } -} diff --git a/mod/glossary/db/services.php b/mod/glossary/db/services.php index 2614b9143c5..5ddce771e64 100644 --- a/mod/glossary/db/services.php +++ b/mod/glossary/db/services.php @@ -50,6 +50,7 @@ $functions = array( 'methodname' => 'view_entry', 'description' => 'Notify a glossary entry as being viewed.', 'type' => 'write', + 'ajax' => true, 'capabilities' => 'mod/glossary:view', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) ), @@ -149,6 +150,7 @@ $functions = array( 'methodname' => 'get_entry_by_id', 'description' => 'Get an entry by ID', 'type' => 'read', + 'ajax' => true, 'capabilities' => 'mod/glossary:view', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE) ), From 0cf04d9caa09d842370a2a3d5a51d57cd3a59c8c Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Wed, 18 Oct 2023 10:40:56 +0800 Subject: [PATCH 4/5] MDL-78294 mod_glossary: Remove the redundant showentry_ajax.php Removes the redundant ajax script showentry_ajax.php that was only used by the old YUI module in the glossary filter. The new AMD module now uses web services, and therefore, this script is no longer used nor required. --- mod/glossary/showentry_ajax.php | 100 -------------------------------- 1 file changed, 100 deletions(-) delete mode 100644 mod/glossary/showentry_ajax.php diff --git a/mod/glossary/showentry_ajax.php b/mod/glossary/showentry_ajax.php deleted file mode 100644 index df7cf58ce85..00000000000 --- a/mod/glossary/showentry_ajax.php +++ /dev/null @@ -1,100 +0,0 @@ -libdir . '/filelib.php'); - -$concept = optional_param('concept', '', PARAM_CLEAN); -$courseid = optional_param('courseid', 0, PARAM_INT); -$eid = optional_param('eid', 0, PARAM_INT); // glossary entry id -$displayformat = optional_param('displayformat',-1, PARAM_SAFEDIR); - -$url = new moodle_url('/mod/glossary/showentry.php'); -$url->param('concept', $concept); -$url->param('courseid', $courseid); -$url->param('eid', $eid); -$url->param('displayformat', $displayformat); -$PAGE->set_url($url); - -if ($CFG->forcelogin) { - require_login(); -} - -if ($eid) { - $entry = $DB->get_record('glossary_entries', array('id'=>$eid), '*', MUST_EXIST); - $glossary = $DB->get_record('glossary', array('id'=>$entry->glossaryid), '*', MUST_EXIST); - $cm = get_coursemodule_from_instance('glossary', $glossary->id, 0, false, MUST_EXIST); - $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); - require_course_login($course, true, $cm); - $entry->glossaryname = $glossary->name; - $entry->cmid = $cm->id; - $entry->courseid = $cm->course; - $entries = array($entry); - -} else if ($concept) { - $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST); - require_course_login($course); - $entries = glossary_get_entries_search($concept, $courseid); - -} else { - throw new \moodle_exception('invalidelementid'); -} - -if ($entries) { - foreach ($entries as $key => $entry) { - // Need to get the course where the entry is, - // in order to check for visibility/approve permissions there - $entrycourse = $DB->get_record('course', array('id' => $entry->courseid), '*', MUST_EXIST); - $modinfo = get_fast_modinfo($entrycourse); - // make sure the entry is visible - if (empty($modinfo->cms[$entry->cmid]->uservisible)) { - unset($entries[$key]); - continue; - } - // make sure the entry is approved (or approvable by current user) - if (!$entry->approved and ($USER->id != $entry->userid)) { - $context = context_module::instance($entry->cmid); - if (!has_capability('mod/glossary:approve', $context)) { - unset($entries[$key]); - continue; - } - } - - // Make sure entry is not autolinking itself. - $GLOSSARY_EXCLUDEENTRY = $entry->id; - - $context = context_module::instance($entry->cmid); - $definition = file_rewrite_pluginfile_urls($entry->definition, 'pluginfile.php', $context->id, 'mod_glossary', 'entry', $entry->id); - - $options = new stdClass(); - $options->para = false; - $options->trusted = $entry->definitiontrust; - $options->context = $context; - $entries[$key]->definition = format_text($definition, $entry->definitionformat, $options); - - if (core_tag_tag::is_enabled('mod_glossary', 'glossary_entries')) { - $entries[$key]->definition .= $OUTPUT->tag_list( - core_tag_tag::get_item_tags('mod_glossary', 'glossary_entries', $entry->id), null, 'glossary-tags'); - } - - $entries[$key]->attachments = ''; - if (!empty($entries[$key]->attachment)) { - $attachments = glossary_print_attachments($entry, $cm, 'html'); - $entries[$key]->attachments = html_writer::tag('p', $attachments); - } - - $entries[$key]->footer = "

» wwwroot/mod/glossary/view.php?g=$entry->glossaryid\">".format_string($entry->glossaryname,true)."

"; - glossary_entry_view($entry, $modinfo->cms[$entry->cmid]->context); - } -} - -echo $OUTPUT->header(); - -$result = new stdClass; -$result->success = true; -$result->entries = $entries; -echo json_encode($result); - -echo $OUTPUT->footer(); - From 8958667c9ae571fbc672afa91e314fe8c683a53c Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Mon, 30 Oct 2023 10:16:09 +0800 Subject: [PATCH 5/5] MDL-78294 core_external: Generate the file url based on the request The file url for each returned area file in get_area_files() is now generated based on the request type. If AJAX request, a standard file plugin url will be created. Otherwise, a webservice plugin file will be created. --- lib/external/classes/util.php | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/external/classes/util.php b/lib/external/classes/util.php index 1b4bcd1fe0e..e4083f3b544 100644 --- a/lib/external/classes/util.php +++ b/lib/external/classes/util.php @@ -146,14 +146,27 @@ class util { $file['repositorytype'] = $areafile->get_repository_type(); } $fileitemid = $useitemidinurl ? $areafile->get_itemid() : null; - $file['fileurl'] = moodle_url::make_webservice_pluginfile_url( - $contextid, - $component, - $filearea, - $fileitemid, - $areafile->get_filepath(), - $areafile->get_filename() - )->out(false); + // If AJAX request, generate a standard plugin file url. + if (AJAX_SCRIPT) { + $fileurl = moodle_url::make_pluginfile_url( + $contextid, + $component, + $filearea, + $fileitemid, + $areafile->get_filepath(), + $areafile->get_filename() + ); + } else { // Otherwise, generate a webservice plugin file url. + $fileurl = moodle_url::make_webservice_pluginfile_url( + $contextid, + $component, + $filearea, + $fileitemid, + $areafile->get_filepath(), + $areafile->get_filename() + ); + } + $file['fileurl'] = $fileurl->out(false); $file['icon'] = file_file_icon($areafile); $files[] = $file; }