Merge branch 'MDL-78294-master' of https://github.com/mihailges/moodle
This commit is contained in:
+10
@@ -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 <mihail@moodle.com>
|
||||
* @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<tags.length;i++)tags[i].overlimit=!0;return{tags:tags,tagscount:tags.length,overflow:hasOverflow,label:await(0,_str.getString)("tags")}};_exports.init=()=>{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
|
||||
File diff suppressed because one or more lines are too long
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Module for auto-linking glossary entries.
|
||||
*
|
||||
* @module filter_glossary/autolinker
|
||||
* @copyright 2023 Mihail Geshoski <[email protected]>
|
||||
* @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();
|
||||
};
|
||||
@@ -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()
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
|
||||
<p>{{{definition}}}</p>
|
||||
{{#taglistdata}}
|
||||
<div class="mb-2">
|
||||
{{> core_tag/taglist }}
|
||||
</div>
|
||||
{{/taglistdata}}
|
||||
{{#hasattachments}}
|
||||
<b>{{#str}} attachments, filter_glossary {{/str}}:</b>
|
||||
<ul>
|
||||
{{#attachments}}
|
||||
<li>
|
||||
{{#pix}} {{icon}}, core {{/pix}}
|
||||
<a href="{{{fileurl}}}">
|
||||
{{filename}}
|
||||
</a>
|
||||
</li>
|
||||
{{/attachments}}
|
||||
</ul>
|
||||
{{/hasattachments}}
|
||||
-193
@@ -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('<div id="glossaryfilteroverlayprogress">' +
|
||||
'</div>'),
|
||||
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"
|
||||
]
|
||||
});
|
||||
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js
Vendored
-1
@@ -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('<div id="glossaryfilteroverlayprogress"></div>'),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"]});
|
||||
Vendored
-193
@@ -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('<div id="glossaryfilteroverlayprogress">' +
|
||||
'</div>'),
|
||||
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"
|
||||
]
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "moodle-filter_glossary-autolinker",
|
||||
"builds": {
|
||||
"moodle-filter_glossary-autolinker": {
|
||||
"jsfiles": [
|
||||
"autolinker.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
-175
@@ -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('<div id="glossaryfilteroverlayprogress">' +
|
||||
'</div>'),
|
||||
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);
|
||||
};
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
+8
@@ -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,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+22
-8
@@ -146,14 +146,28 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
Vendored
+1
@@ -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.
|
||||
|
||||
@@ -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 ===
|
||||
|
||||
|
||||
@@ -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)
|
||||
),
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
<?php
|
||||
define('AJAX_SCRIPT', true);
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once('lib.php');
|
||||
require_once($CFG->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 = "<p style=\"text-align:right\">» <a href=\"$CFG->wwwroot/mod/glossary/view.php?g=$entry->glossaryid\">".format_string($entry->glossaryname,true)."</a></p>";
|
||||
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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
+30
@@ -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),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user