Merge branch 'MDL-78554-401' of https://github.com/andrewnicols/moodle into MOODLE_401_STABLE

This commit is contained in:
Sara Arjona 2023-06-29 06:37:36 +02:00 committed by Ilya Tregubov
commit 9137b58fca
No known key found for this signature in database
GPG Key ID: 0F58186F748E55C1
36 changed files with 1263 additions and 0 deletions

View File

@ -2035,6 +2035,7 @@ class core_plugin_manager {
'h5p',
'media',
'recordrtc',
'link'
],
'tinymce' => array(

View File

@ -499,6 +499,9 @@ class manager {
// Disable the preview plugin as it does not support Moodle filters.
'preview',
// Use the Moodle link plugin instead.
'link',
];
}

View File

@ -0,0 +1,3 @@
define("tiny_link/commands",["exports","core/str","tiny_link/common","tiny_link/ui","tiny_link/link"],(function(_exports,_str,_common,_ui,_link){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0;_exports.getSetup=async()=>{const[linkButtonText,unlinkButtonText]=await Promise.all([(0,_str.get_string)("link",_common.component),(0,_str.get_string)("unlink",_common.component)]);return editor=>{editor.ui.registry.addToggleButton(_common.linkButtonShortName,{icon:"link",tooltip:linkButtonText,onAction:()=>{(0,_ui.handleAction)(editor)},onSetup:(0,_link.toggleActiveState)(editor)}),editor.ui.registry.addMenuItem(_common.linkButtonShortName,{icon:"link",shortcut:"Meta+K",text:linkButtonText,onAction:()=>{(0,_ui.handleAction)(editor)}}),editor.ui.registry.addToggleButton(_common.unlinkButtonShortName,{icon:"unlink",tooltip:unlinkButtonText,onAction:()=>{(0,_ui.handleAction)(editor,!0)},onSetup:(0,_link.toggleActiveState)(editor)}),editor.shortcuts.add("Meta+K","Shortcut for create link",(()=>{(0,_ui.handleAction)(editor)}))}}}));
//# sourceMappingURL=commands.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"commands.min.js","sources":["../src/commands.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\nimport {get_string as getString} from 'core/str';\nimport {component, linkButtonShortName, unlinkButtonShortName} from 'tiny_link/common';\nimport {handleAction} from 'tiny_link/ui';\nimport {toggleActiveState} from 'tiny_link/link';\n\n/**\n * Tiny Link commands.\n *\n * @module tiny_link/commands\n * @copyright 2023 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport const getSetup = async() => {\n const [\n linkButtonText,\n unlinkButtonText,\n ] = await Promise.all([\n getString('link', component),\n getString('unlink', component),\n ]);\n\n return (editor) => {\n // Register Link button.\n editor.ui.registry.addToggleButton(linkButtonShortName, {\n icon: 'link',\n tooltip: linkButtonText,\n onAction: () => {\n handleAction(editor);\n },\n onSetup: toggleActiveState(editor),\n });\n\n // Register the Link menu item.\n editor.ui.registry.addMenuItem(linkButtonShortName, {\n icon: 'link',\n shortcut: 'Meta+K',\n text: linkButtonText,\n onAction: () => {\n handleAction(editor);\n },\n });\n\n // Register Unlink button.\n editor.ui.registry.addToggleButton(unlinkButtonShortName, {\n icon: 'unlink',\n tooltip: unlinkButtonText,\n onAction: () => {\n handleAction(editor, true);\n },\n onSetup: toggleActiveState(editor),\n });\n\n // Register shortcut.\n editor.shortcuts.add('Meta+K', 'Shortcut for create link', () => {\n handleAction(editor);\n });\n };\n};\n"],"names":["async","linkButtonText","unlinkButtonText","Promise","all","component","editor","ui","registry","addToggleButton","linkButtonShortName","icon","tooltip","onAction","onSetup","addMenuItem","shortcut","text","unlinkButtonShortName","shortcuts","add"],"mappings":"oPA4BwBA,gBAEhBC,eACAC,wBACMC,QAAQC,IAAI,EAClB,mBAAU,OAAQC,oBAClB,mBAAU,SAAUA,4BAGhBC,SAEJA,OAAOC,GAAGC,SAASC,gBAAgBC,4BAAqB,CACpDC,KAAM,OACNC,QAASX,eACTY,SAAU,0BACOP,SAEjBQ,SAAS,2BAAkBR,UAI/BA,OAAOC,GAAGC,SAASO,YAAYL,4BAAqB,CAChDC,KAAM,OACNK,SAAU,SACVC,KAAMhB,eACNY,SAAU,0BACOP,WAKrBA,OAAOC,GAAGC,SAASC,gBAAgBS,8BAAuB,CACtDP,KAAM,SACNC,QAASV,iBACTW,SAAU,0BACOP,QAAQ,IAEzBQ,SAAS,2BAAkBR,UAI/BA,OAAOa,UAAUC,IAAI,SAAU,4BAA4B,0BAC1Cd"}

View File

@ -0,0 +1,3 @@
define("tiny_link/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={pluginName:"tiny_link/plugin",component:"tiny_link",linkButtonName:"link",linkButtonShortName:"tiny_link_link",unlinkButtonName:"unlink",unlinkButtonShortName:"tiny_link_unlink"},_exports.default}));
//# sourceMappingURL=common.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"common.min.js","sources":["../src/common.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Link common values.\n *\n * @module tiny_link/common\n * @copyright 2023 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n pluginName: 'tiny_link/plugin',\n component: 'tiny_link',\n linkButtonName: 'link',\n linkButtonShortName: 'tiny_link_link',\n unlinkButtonName: 'unlink',\n unlinkButtonShortName: 'tiny_link_unlink',\n};\n"],"names":["pluginName","component","linkButtonName","linkButtonShortName","unlinkButtonName","unlinkButtonShortName"],"mappings":"kKAuBe,CACXA,WAAY,mBACZC,UAAW,YACXC,eAAgB,OAChBC,oBAAqB,iBACrBC,iBAAkB,SAClBC,sBAAuB"}

View File

@ -0,0 +1,3 @@
define("tiny_link/configuration",["exports","tiny_link/common","editor_tiny/utils"],(function(_exports,_common,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;_exports.configure=instanceConfig=>{return{menu:(menu=instanceConfig.menu,menu.insert.items.match(/\blink\b/)?menu.insert.items=menu.insert.items.replace(/\blink\b/,_common.linkButtonShortName):menu.insert.items="".concat(_common.linkButtonShortName," ").concat(menu.insert.items),menu),toolbar:(0,_utils.addToolbarButtons)(instanceConfig.toolbar,"content",[_common.linkButtonShortName,_common.unlinkButtonShortName])};var menu}}));
//# sourceMappingURL=configuration.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"configuration.min.js","sources":["../src/configuration.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Link configuration.\n *\n * @module tiny_link/configuration\n * @copyright 2023 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {linkButtonShortName, unlinkButtonShortName} from 'tiny_link/common';\nimport {addToolbarButtons} from 'editor_tiny/utils';\n\nconst configureMenu = (menu) => {\n // Replace the standard Link plugin with the Moodle link.\n if (menu.insert.items.match(/\\blink\\b/)) {\n menu.insert.items = menu.insert.items.replace(/\\blink\\b/, linkButtonShortName);\n } else {\n menu.insert.items = `${linkButtonShortName} ${menu.insert.items}`;\n }\n\n return menu;\n};\n\nexport const configure = (instanceConfig) => {\n // Update the instance configuration to add the Link option to the menus and toolbars.\n return {\n menu: configureMenu(instanceConfig.menu),\n toolbar: addToolbarButtons(instanceConfig.toolbar, 'content', [linkButtonShortName, unlinkButtonShortName]),\n };\n};\n"],"names":["instanceConfig","menu","insert","items","match","replace","linkButtonShortName","toolbar","unlinkButtonShortName"],"mappings":"4NAqC0BA,uBAEf,CACHC,MAdeA,KAcKD,eAAeC,KAZnCA,KAAKC,OAAOC,MAAMC,MAAM,YACxBH,KAAKC,OAAOC,MAAQF,KAAKC,OAAOC,MAAME,QAAQ,WAAYC,6BAE1DL,KAAKC,OAAOC,gBAAWG,wCAAuBL,KAAKC,OAAOC,OAGvDF,MAOHM,SAAS,4BAAkBP,eAAeO,QAAS,UAAW,CAACD,4BAAqBE,iCAfrEP,IAAAA"}

View File

@ -0,0 +1,10 @@
define("tiny_link/link",["exports","core/templates","core/pending","tiny_link/selectors"],(function(_exports,_templates,_pending,_selectors){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Link helper for Tiny Link plugin.
*
* @module tiny_link/link
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.unSetLink=_exports.toggleActiveState=_exports.setLink=_exports.getCurrentLinkData=void 0,_templates=_interopRequireDefault(_templates),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors);_exports.setLink=(currentForm,editor)=>{let value=currentForm.querySelector(_selectors.default.elements.urlEntry).value;if(""!==value){const pendingPromise=new _pending.default("tiny_link/setLink");value=value.trim();new RegExp(/^[a-zA-Z]*\.*\/|^#|^[a-zA-Z]*:/).test(value)||(value="http://"+value),setLinkOnSelection(currentForm,editor,value).then(pendingPromise.resolve)}};_exports.unSetLink=editor=>{if(editor.hasPlugin("rtc",!0))editor.execCommand("unlink");else{const dom=editor.dom,selection=editor.selection,bookmark=selection.getBookmark(),rng=selection.getRng().cloneRange(),startAnchorElm=dom.getParent(rng.startContainer,"a[href]",editor.getBody()),endAnchorElm=dom.getParent(rng.endContainer,"a[href]",editor.getBody());startAnchorElm&&rng.setStartBefore(startAnchorElm),endAnchorElm&&rng.setEndAfter(endAnchorElm),selection.setRng(rng),editor.execCommand("unlink"),selection.moveToBookmark(bookmark)}};const setLinkOnSelection=async(currentForm,editor,url)=>{const urlText=currentForm.querySelector(_selectors.default.elements.urlText),target=currentForm.querySelector(_selectors.default.elements.openInNewWindow);let textToDisplay=urlText.value.replace(/(<([^>]+)>)/gi,"").trim();""===textToDisplay&&(textToDisplay=url);const context={url:url,newwindow:target.checked};urlText.getAttribute("data-link-on-element")?(context.title=textToDisplay,context.name=editor.selection.getNode().outerHTML):context.name=textToDisplay;const{html:html}=await _templates.default.renderForPromise("tiny_link/embed_link",context),currentLink=getSelectedLink(editor);currentLink?currentLink.outerHTML=html:editor.insertContent(html)};_exports.getCurrentLinkData=editor=>{let properties={};const link=getSelectedLink(editor);if(link){const url=link.getAttribute("href"),target=link.getAttribute("target"),textToDisplay=link.innerText,title=link.getAttribute("title");""!==url&&(properties.url=url),"_blank"===target&&(properties.newwindow=!0),title&&""!==title?properties.urltext=title.trim():""!==textToDisplay&&(properties.urltext=textToDisplay.trim())}else{const selectedNode=editor.selection.getNode();if(selectedNode){const textToDisplay=getTextSelection(editor);""!==textToDisplay?(properties.urltext=textToDisplay.trim(),properties.hasTextToDisplay=!0,properties.hasPlainTextSelected=!0):selectedNode.getAttribute("data-mce-selected")&&(properties.setLinkOnElement=!0)}}return properties};const getSelectedLink=editor=>getAnchorElement(editor),getAnchorElement=(editor,selectedElm)=>(selectedElm=selectedElm||editor.selection.getNode(),editor.dom.getParent(selectedElm,"a[href]")),getTextSelection=editor=>{let selText="";const sel=editor.selection.getSel(),rangeCount=sel.rangeCount;if(rangeCount){let rangeTexts=[];for(let i=0;i<rangeCount;++i)rangeTexts.push(""+sel.getRangeAt(i));selText=rangeTexts.join("")}return selText};_exports.toggleActiveState=editor=>api=>{const updateState=()=>api.setActive(!editor.mode.isReadOnly()&&((editor,selectedElm)=>null!==getAnchorElement(editor,selectedElm))(editor,editor.selection.getNode()));return updateState(),((editor,toggler)=>(editor.on("NodeChange",toggler),()=>editor.off("NodeChange",toggler)))(editor,updateState)}}));
//# sourceMappingURL=link.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
define("tiny_link/modal",["exports","core/modal","core/modal_registry"],(function(_exports,_modal,_modal_registry){var _class;function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=_interopRequireDefault(_modal),_modal_registry=_interopRequireDefault(_modal_registry);const LinkModal=(_defineProperty(_class=class extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}},"TYPE","tiny_link/modal"),_defineProperty(_class,"TEMPLATE","tiny_link/modal"),_class);_modal_registry.default.register(LinkModal.TYPE,LinkModal,LinkModal.TEMPLATE);var _default=LinkModal;return _exports.default=_default,_exports.default}));
//# sourceMappingURL=modal.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"modal.min.js","sources":["../src/modal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Link Modal for Tiny.\n *\n * @module tiny_link/modal\n * @copyright 2023 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport ModalRegistry from 'core/modal_registry';\n\nconst LinkModal = class extends Modal {\n static TYPE = 'tiny_link/modal';\n static TEMPLATE = 'tiny_link/modal';\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n};\n\nModalRegistry.register(LinkModal.TYPE, LinkModal, LinkModal.TEMPLATE);\n\nexport default LinkModal;\n"],"names":["LinkModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","register","TYPE","TEMPLATE"],"mappings":"wiBA0BMA,kCAAY,cAAcC,eAI5BC,+BAEUA,8BAGDC,2BACAC,iCATK,qDACI,mDAYRC,SAASL,UAAUM,KAAMN,UAAWA,UAAUO,uBAE7CP"}

View File

@ -0,0 +1,11 @@
define("tiny_link/options",["exports","editor_tiny/options","tiny_link/common"],(function(_exports,_options,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.getPermissions=void 0;
/**
* Options helper for Tiny Link plugin.
*
* @module tiny_link/options
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const dataName=(0,_options.getPluginOptionName)(_common.pluginName,"data"),permissionsName=(0,_options.getPluginOptionName)(_common.pluginName,"permissions");_exports.register=editor=>{const registerOption=editor.options.register;registerOption(permissionsName,{processor:"object",default:{filepicker:!1}}),registerOption(dataName,{processor:"object"})};_exports.getPermissions=editor=>editor.options.get(permissionsName)}));
//# sourceMappingURL=options.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"options.min.js","sources":["../src/options.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Options helper for Tiny Link plugin.\n *\n * @module tiny_link/options\n * @copyright 2023 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getPluginOptionName} from 'editor_tiny/options';\nimport {pluginName} from 'tiny_link/common';\n\nconst dataName = getPluginOptionName(pluginName, 'data');\nconst permissionsName = getPluginOptionName(pluginName, 'permissions');\n\n/**\n * Register the options for the Tiny Link plugin.\n *\n * @param {TinyMCE} editor\n */\nexport const register = (editor) => {\n const registerOption = editor.options.register;\n\n registerOption(permissionsName, {\n processor: 'object',\n \"default\": {\n filepicker: false,\n },\n });\n\n registerOption(dataName, {\n processor: 'object',\n });\n};\n\n/**\n * Get the permissions configuration for the Tiny Link plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getPermissions = (editor) => editor.options.get(permissionsName);\n"],"names":["dataName","pluginName","permissionsName","editor","registerOption","options","register","processor","filepicker","get"],"mappings":";;;;;;;;MA0BMA,UAAW,gCAAoBC,mBAAY,QAC3CC,iBAAkB,gCAAoBD,mBAAY,iCAO/BE,eACfC,eAAiBD,OAAOE,QAAQC,SAEtCF,eAAeF,gBAAiB,CAC5BK,UAAW,iBACA,CACPC,YAAY,KAIpBJ,eAAeJ,SAAU,CACrBO,UAAW,oCAUYJ,QAAWA,OAAOE,QAAQI,IAAIP"}

View File

@ -0,0 +1,10 @@
define("tiny_link/plugin",["exports","editor_tiny/loader","editor_tiny/utils","tiny_link/common","tiny_link/commands","tiny_link/configuration","tiny_link/options"],(function(_exports,_loader,_utils,_common,Commands,Configuration,Options){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
/**
* Tiny Link plugin for Moodle.
*
* @module tiny_link/plugin
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Commands=_interopRequireWildcard(Commands),Configuration=_interopRequireWildcard(Configuration),Options=_interopRequireWildcard(Options);var _default=new Promise((async resolve=>{const[tinyMCE,setupCommands,pluginMetadata]=await Promise.all([(0,_loader.getTinyMCE)(),Commands.getSetup(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName)]);tinyMCE.PluginManager.add("".concat(_common.component,"/plugin"),(editor=>(Options.register(editor),setupCommands(editor),pluginMetadata))),resolve(["".concat(_common.component,"/plugin"),Configuration])}));return _exports.default=_default,_exports.default}));
//# sourceMappingURL=plugin.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"plugin.min.js","sources":["../src/plugin.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\nimport {getTinyMCE} from 'editor_tiny/loader';\nimport {getPluginMetadata} from 'editor_tiny/utils';\n\nimport {component, pluginName} from 'tiny_link/common';\nimport * as Commands from 'tiny_link/commands';\nimport * as Configuration from 'tiny_link/configuration';\nimport * as Options from 'tiny_link/options';\n\n/**\n * Tiny Link plugin for Moodle.\n *\n * @module tiny_link/plugin\n * @copyright 2023 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default new Promise(async(resolve) => {\n const [\n tinyMCE,\n setupCommands,\n pluginMetadata,\n ] = await Promise.all([\n getTinyMCE(),\n Commands.getSetup(),\n getPluginMetadata(component, pluginName),\n ]);\n\n tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {\n // Register options.\n Options.register(editor);\n // Setup the Commands (buttons, menu items, and so on).\n setupCommands(editor);\n\n return pluginMetadata;\n });\n\n // Resolve the Link Plugin and include configuration.\n resolve([`${component}/plugin`, Configuration]);\n});\n"],"names":["Promise","async","tinyMCE","setupCommands","pluginMetadata","all","Commands","getSetup","component","pluginName","PluginManager","add","editor","Options","register","resolve","Configuration"],"mappings":";;;;;;;2OA+Be,IAAIA,SAAQC,MAAAA,gBAEnBC,QACAC,cACAC,sBACMJ,QAAQK,IAAI,EAClB,wBACAC,SAASC,YACT,4BAAkBC,kBAAWC,sBAGjCP,QAAQQ,cAAcC,cAAOH,8BAAqBI,SAE9CC,QAAQC,SAASF,QAEjBT,cAAcS,QAEPR,kBAIXW,QAAQ,WAAIP,6BAAoBQ"}

View File

@ -0,0 +1,3 @@
define("tiny_link/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={actions:{submit:'[data-action="save"]',linkBrowser:".openlinkbrowser"},elements:{urlEntry:".tiny_link_urlentry",urlText:".tiny_link_urltext",openInNewWindow:".tiny_link_newwindow"}},_exports.default}));
//# sourceMappingURL=selectors.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Link plugin helper function to build queryable data selectors.\n *\n * @module tiny_link/selectors\n * @copyright 2023 Huong Nguyen <huongnv13@gmail.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n actions: {\n submit: '[data-action=\"save\"]',\n linkBrowser: '.openlinkbrowser',\n },\n elements: {\n urlEntry: '.tiny_link_urlentry',\n urlText: '.tiny_link_urltext',\n openInNewWindow: '.tiny_link_newwindow',\n }\n};\n"],"names":["actions","submit","linkBrowser","elements","urlEntry","urlText","openInNewWindow"],"mappings":"qKAuBe,CACXA,QAAS,CACLC,OAAQ,uBACRC,YAAa,oBAEjBC,SAAU,CACNC,SAAU,sBACVC,QAAS,qBACTC,gBAAiB"}

View File

@ -0,0 +1,10 @@
define("tiny_link/ui",["exports","core/modal_factory","core/modal_events","editor_tiny/utils","tiny_link/modal","tiny_link/options","tiny_link/link","tiny_link/selectors"],(function(_exports,_modal_factory,_modal_events,_utils,_modal,_options,_link,_selectors){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Tiny Link UI.
*
* @module tiny_link/ui
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.handleAction=void 0,_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_modal=_interopRequireDefault(_modal),_selectors=_interopRequireDefault(_selectors);_exports.handleAction=function(editor){let unlink=arguments.length>1&&void 0!==arguments[1]&&arguments[1];unlink?(0,_link.unSetLink)(editor):displayDialogue(editor)};const displayDialogue=async editor=>{const modalPromises=await _modal_factory.default.create({type:_modal.default.TYPE,templateContext:getTemplateContext(editor),large:!1});modalPromises.show();const $root=await modalPromises.getRoot(),root=$root[0],currentForm=root.querySelector("form");$root.on(_modal_events.default.hidden,(()=>{modalPromises.destroy()})),root.addEventListener("click",(e=>{const submitAction=e.target.closest(_selectors.default.actions.submit),linkBrowserAction=e.target.closest(_selectors.default.actions.linkBrowser);submitAction&&(e.preventDefault(),(0,_link.setLink)(currentForm,editor),modalPromises.destroy()),linkBrowserAction&&(e.preventDefault(),(0,_utils.displayFilepicker)(editor,"link").then((params=>(filePickerCallback(params,currentForm,editor),modalPromises.destroy()))).catch())}));const linkTitle=root.querySelector(_selectors.default.elements.urlText),linkUrl=root.querySelector(_selectors.default.elements.urlEntry);linkTitle.addEventListener("change",(()=>{linkTitle.value.length>0?linkTitle.dataset.useLinkAsText="false":(linkTitle.dataset.useLinkAsText="true",linkTitle.value=linkUrl.value)})),linkUrl.addEventListener("keyup",(()=>{updateTextToDisplay(currentForm)}))},getTemplateContext=editor=>{const data=(0,_link.getCurrentLinkData)(editor);return Object.assign({},{elementid:editor.id,showfilepicker:(0,_options.getPermissions)(editor).filepicker,isupdating:Object.keys(data).length>0},data)},filePickerCallback=(params,currentForm,editor)=>{if(params.url){currentForm.querySelector(_selectors.default.elements.urlEntry).value=params.url,(0,_link.setLink)(currentForm,editor)}},updateTextToDisplay=currentForm=>{const urlEntry=currentForm.querySelector(_selectors.default.elements.urlEntry),urlText=currentForm.querySelector(_selectors.default.elements.urlText);"true"===urlText.dataset.useLinkAsText&&(urlText.value=urlEntry.value)}}));
//# sourceMappingURL=ui.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -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/>.
import {get_string as getString} from 'core/str';
import {component, linkButtonShortName, unlinkButtonShortName} from 'tiny_link/common';
import {handleAction} from 'tiny_link/ui';
import {toggleActiveState} from 'tiny_link/link';
/**
* Tiny Link commands.
*
* @module tiny_link/commands
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export const getSetup = async() => {
const [
linkButtonText,
unlinkButtonText,
] = await Promise.all([
getString('link', component),
getString('unlink', component),
]);
return (editor) => {
// Register Link button.
editor.ui.registry.addToggleButton(linkButtonShortName, {
icon: 'link',
tooltip: linkButtonText,
onAction: () => {
handleAction(editor);
},
onSetup: toggleActiveState(editor),
});
// Register the Link menu item.
editor.ui.registry.addMenuItem(linkButtonShortName, {
icon: 'link',
shortcut: 'Meta+K',
text: linkButtonText,
onAction: () => {
handleAction(editor);
},
});
// Register Unlink button.
editor.ui.registry.addToggleButton(unlinkButtonShortName, {
icon: 'unlink',
tooltip: unlinkButtonText,
onAction: () => {
handleAction(editor, true);
},
onSetup: toggleActiveState(editor),
});
// Register shortcut.
editor.shortcuts.add('Meta+K', 'Shortcut for create link', () => {
handleAction(editor);
});
};
};

View File

@ -0,0 +1,31 @@
// 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/>.
/**
* Tiny Link common values.
*
* @module tiny_link/common
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export default {
pluginName: 'tiny_link/plugin',
component: 'tiny_link',
linkButtonName: 'link',
linkButtonShortName: 'tiny_link_link',
unlinkButtonName: 'unlink',
unlinkButtonShortName: 'tiny_link_unlink',
};

View File

@ -0,0 +1,44 @@
// 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/>.
/**
* Tiny Link configuration.
*
* @module tiny_link/configuration
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {linkButtonShortName, unlinkButtonShortName} from 'tiny_link/common';
import {addToolbarButtons} from 'editor_tiny/utils';
const configureMenu = (menu) => {
// Replace the standard Link plugin with the Moodle link.
if (menu.insert.items.match(/\blink\b/)) {
menu.insert.items = menu.insert.items.replace(/\blink\b/, linkButtonShortName);
} else {
menu.insert.items = `${linkButtonShortName} ${menu.insert.items}`;
}
return menu;
};
export const configure = (instanceConfig) => {
// Update the instance configuration to add the Link option to the menus and toolbars.
return {
menu: configureMenu(instanceConfig.menu),
toolbar: addToolbarButtons(instanceConfig.toolbar, 'content', [linkButtonShortName, unlinkButtonShortName]),
};
};

View File

@ -0,0 +1,237 @@
// 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/>.
/**
* Link helper for Tiny Link plugin.
*
* @module tiny_link/link
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Templates from 'core/templates';
import Pending from 'core/pending';
import Selectors from 'tiny_link/selectors';
/**
* Handle insertion of a new link, or update of an existing one.
*
* @param {Element} currentForm
* @param {TinyMCE} editor
*/
export const setLink = (currentForm, editor) => {
const input = currentForm.querySelector(Selectors.elements.urlEntry);
let value = input.value;
if (value !== '') {
const pendingPromise = new Pending('tiny_link/setLink');
// We add a prefix if it is not already prefixed.
value = value.trim();
const expr = new RegExp(/^[a-zA-Z]*\.*\/|^#|^[a-zA-Z]*:/);
if (!expr.test(value)) {
value = 'http://' + value;
}
// Add the link.
setLinkOnSelection(currentForm, editor, value).then(pendingPromise.resolve);
}
};
/**
* Handle unlink of a link
*
* @param {TinyMCE} editor
*/
export const unSetLink = (editor) => {
if (editor.hasPlugin('rtc', true)) {
editor.execCommand('unlink');
} else {
const dom = editor.dom;
const selection = editor.selection;
const bookmark = selection.getBookmark();
const rng = selection.getRng().cloneRange();
const startAnchorElm = dom.getParent(rng.startContainer, 'a[href]', editor.getBody());
const endAnchorElm = dom.getParent(rng.endContainer, 'a[href]', editor.getBody());
if (startAnchorElm) {
rng.setStartBefore(startAnchorElm);
}
if (endAnchorElm) {
rng.setEndAfter(endAnchorElm);
}
selection.setRng(rng);
editor.execCommand('unlink');
selection.moveToBookmark(bookmark);
}
};
/**
* Final step setting the anchor on the selection.
*
* @param {Element} currentForm
* @param {TinyMCE} editor
* @param {String} url URL the link will point to.
*/
const setLinkOnSelection = async(currentForm, editor, url) => {
const urlText = currentForm.querySelector(Selectors.elements.urlText);
const target = currentForm.querySelector(Selectors.elements.openInNewWindow);
let textToDisplay = urlText.value.replace(/(<([^>]+)>)/gi, "").trim();
if (textToDisplay === '') {
textToDisplay = url;
}
const context = {
url: url,
newwindow: target.checked,
};
if (urlText.getAttribute('data-link-on-element')) {
context.title = textToDisplay;
context.name = editor.selection.getNode().outerHTML;
} else {
context.name = textToDisplay;
}
const {html} = await Templates.renderForPromise('tiny_link/embed_link', context);
const currentLink = getSelectedLink(editor);
if (currentLink) {
currentLink.outerHTML = html;
} else {
editor.insertContent(html);
}
};
/**
* Get current link data.
*
* @param {TinyMCE} editor
* @returns {{}}
*/
export const getCurrentLinkData = (editor) => {
let properties = {};
const link = getSelectedLink(editor);
if (link) {
const url = link.getAttribute('href');
const target = link.getAttribute('target');
const textToDisplay = link.innerText;
const title = link.getAttribute('title');
if (url !== '') {
properties.url = url;
}
if (target === '_blank') {
properties.newwindow = true;
}
if (title && title !== '') {
properties.urltext = title.trim();
} else if (textToDisplay !== '') {
properties.urltext = textToDisplay.trim();
}
} else {
// Check if the user is selecting some text before clicking on the Link button.
const selectedNode = editor.selection.getNode();
if (selectedNode) {
const textToDisplay = getTextSelection(editor);
if (textToDisplay !== '') {
properties.urltext = textToDisplay.trim();
properties.hasTextToDisplay = true;
properties.hasPlainTextSelected = true;
} else {
if (selectedNode.getAttribute('data-mce-selected')) {
properties.setLinkOnElement = true;
}
}
}
}
return properties;
};
/**
* Get selected link.
*
* @param {TinyMCE} editor
* @returns {Element}
*/
const getSelectedLink = (editor) => {
return getAnchorElement(editor);
};
/**
* Get anchor element.
*
* @param {TinyMCE} editor
* @param {Element} selectedElm
* @returns {Element}
*/
const getAnchorElement = (editor, selectedElm) => {
selectedElm = selectedElm || editor.selection.getNode();
return editor.dom.getParent(selectedElm, 'a[href]');
};
/**
* Get only the selected text.
* In some cases, window.getSelection() is not run as expected. We should only get the text value
* For ex: <img src="" alt="XYZ">Some text here
* window.getSelection() will return XYZSome text here
*
* @param {TinyMCE} editor
* @return {string} Selected text
*/
const getTextSelection = (editor) => {
let selText = '';
const sel = editor.selection.getSel();
const rangeCount = sel.rangeCount;
if (rangeCount) {
let rangeTexts = [];
for (let i = 0; i < rangeCount; ++i) {
rangeTexts.push('' + sel.getRangeAt(i));
}
selText = rangeTexts.join('');
}
return selText;
};
/**
* Check the current selected element is an anchor or not.
*
* @param {TinyMCE} editor
* @param {Element} selectedElm
* @returns {boolean}
*/
const isInAnchor = (editor, selectedElm) => getAnchorElement(editor, selectedElm) !== null;
/**
* Change state of button.
*
* @param {TinyMCE} editor
* @param {function()} toggler
* @returns {function()}
*/
const toggleState = (editor, toggler) => {
editor.on('NodeChange', toggler);
return () => editor.off('NodeChange', toggler);
};
/**
* Change the active state of button.
*
* @param {TinyMCE} editor
* @returns {function(*): function(): *}
*/
export const toggleActiveState = (editor) => (api) => {
const updateState = () => api.setActive(!editor.mode.isReadOnly() && isInAnchor(editor, editor.selection.getNode()));
updateState();
return toggleState(editor, updateState);
};

View File

@ -0,0 +1,43 @@
// 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/>.
/**
* Link Modal for Tiny.
*
* @module tiny_link/modal
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Modal from 'core/modal';
import ModalRegistry from 'core/modal_registry';
const LinkModal = class extends Modal {
static TYPE = 'tiny_link/modal';
static TEMPLATE = 'tiny_link/modal';
registerEventListeners() {
// Call the parent registration.
super.registerEventListeners();
// Register to close on save/cancel.
this.registerCloseOnSave();
this.registerCloseOnCancel();
}
};
ModalRegistry.register(LinkModal.TYPE, LinkModal, LinkModal.TEMPLATE);
export default LinkModal;

View File

@ -0,0 +1,56 @@
// 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/>.
/**
* Options helper for Tiny Link plugin.
*
* @module tiny_link/options
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {getPluginOptionName} from 'editor_tiny/options';
import {pluginName} from 'tiny_link/common';
const dataName = getPluginOptionName(pluginName, 'data');
const permissionsName = getPluginOptionName(pluginName, 'permissions');
/**
* Register the options for the Tiny Link plugin.
*
* @param {TinyMCE} editor
*/
export const register = (editor) => {
const registerOption = editor.options.register;
registerOption(permissionsName, {
processor: 'object',
"default": {
filepicker: false,
},
});
registerOption(dataName, {
processor: 'object',
});
};
/**
* Get the permissions configuration for the Tiny Link plugin.
*
* @param {TinyMCE} editor
* @returns {object}
*/
export const getPermissions = (editor) => editor.options.get(permissionsName);

View File

@ -0,0 +1,54 @@
// 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/>.
import {getTinyMCE} from 'editor_tiny/loader';
import {getPluginMetadata} from 'editor_tiny/utils';
import {component, pluginName} from 'tiny_link/common';
import * as Commands from 'tiny_link/commands';
import * as Configuration from 'tiny_link/configuration';
import * as Options from 'tiny_link/options';
/**
* Tiny Link plugin for Moodle.
*
* @module tiny_link/plugin
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export default new Promise(async(resolve) => {
const [
tinyMCE,
setupCommands,
pluginMetadata,
] = await Promise.all([
getTinyMCE(),
Commands.getSetup(),
getPluginMetadata(component, pluginName),
]);
tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {
// Register options.
Options.register(editor);
// Setup the Commands (buttons, menu items, and so on).
setupCommands(editor);
return pluginMetadata;
});
// Resolve the Link Plugin and include configuration.
resolve([`${component}/plugin`, Configuration]);
});

View File

@ -0,0 +1,34 @@
// 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/>.
/**
* Tiny Link plugin helper function to build queryable data selectors.
*
* @module tiny_link/selectors
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export default {
actions: {
submit: '[data-action="save"]',
linkBrowser: '.openlinkbrowser',
},
elements: {
urlEntry: '.tiny_link_urlentry',
urlText: '.tiny_link_urltext',
openInNewWindow: '.tiny_link_newwindow',
}
};

View File

@ -0,0 +1,143 @@
// 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/>.
/**
* Tiny Link UI.
*
* @module tiny_link/ui
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import ModalFactory from 'core/modal_factory';
import ModalEvents from 'core/modal_events';
import {displayFilepicker} from 'editor_tiny/utils';
import LinkModal from 'tiny_link/modal';
import {getPermissions} from "tiny_link/options";
import {setLink, getCurrentLinkData, unSetLink} from "tiny_link/link";
import Selectors from 'tiny_link/selectors';
/**
* Handle action.
*
* @param {TinyMCE} editor
* @param {boolean} unlink
*/
export const handleAction = (editor, unlink = false) => {
if (!unlink) {
displayDialogue(editor);
} else {
unSetLink(editor);
}
};
/**
* Display the link dialogue.
*
* @param {TinyMCE} editor
* @returns {Promise<void>}
*/
const displayDialogue = async(editor) => {
const modalPromises = await ModalFactory.create({
type: LinkModal.TYPE,
templateContext: getTemplateContext(editor),
large: false,
});
modalPromises.show();
const $root = await modalPromises.getRoot();
const root = $root[0];
const currentForm = root.querySelector('form');
$root.on(ModalEvents.hidden, () => {
modalPromises.destroy();
});
root.addEventListener('click', (e) => {
const submitAction = e.target.closest(Selectors.actions.submit);
const linkBrowserAction = e.target.closest(Selectors.actions.linkBrowser);
if (submitAction) {
e.preventDefault();
setLink(currentForm, editor);
modalPromises.destroy();
}
if (linkBrowserAction) {
e.preventDefault();
displayFilepicker(editor, 'link').then((params) => {
filePickerCallback(params, currentForm, editor);
return modalPromises.destroy();
}).catch();
}
});
const linkTitle = root.querySelector(Selectors.elements.urlText);
const linkUrl = root.querySelector(Selectors.elements.urlEntry);
linkTitle.addEventListener('change', () => {
if (linkTitle.value.length > 0) {
linkTitle.dataset.useLinkAsText = 'false';
} else {
linkTitle.dataset.useLinkAsText = 'true';
linkTitle.value = linkUrl.value;
}
});
linkUrl.addEventListener('keyup', () => {
updateTextToDisplay(currentForm);
});
};
/**
* Get template context.
*
* @param {TinyMCE} editor
* @returns {Object}
*/
const getTemplateContext = (editor) => {
const data = getCurrentLinkData(editor);
return Object.assign({}, {
elementid: editor.id,
showfilepicker: getPermissions(editor).filepicker,
isupdating: Object.keys(data).length > 0,
}, data);
};
/**
* Update the dialogue after a link was selected in the File Picker.
*
* @param {Object} params
* @param {Element} currentForm
* @param {TinyMCE} editor
*/
const filePickerCallback = (params, currentForm, editor) => {
if (params.url) {
const inputUrl = currentForm.querySelector(Selectors.elements.urlEntry);
inputUrl.value = params.url;
setLink(currentForm, editor);
}
};
/**
* Update the text to display if the user does not provide the custom text.
*
* @param {Element} currentForm
*/
const updateTextToDisplay = (currentForm) => {
const urlEntry = currentForm.querySelector(Selectors.elements.urlEntry);
const urlText = currentForm.querySelector(Selectors.elements.urlText);
if (urlText.dataset.useLinkAsText === 'true') {
urlText.value = urlEntry.value;
}
};

View File

@ -0,0 +1,89 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny Link plugin.
*
* @package tiny_link
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tiny_link;
use context;
use context_system;
use editor_tiny\editor;
use editor_tiny\plugin;
use editor_tiny\plugin_with_buttons;
use editor_tiny\plugin_with_configuration;
use editor_tiny\plugin_with_menuitems;
/**
* Tiny link plugin.
*
* @package tiny_link
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menuitems, plugin_with_configuration {
/**
* Get a list of the buttons provided by this plugin.
*
* @return string[]
*/
public static function get_available_buttons(): array {
return [
'tiny_link/tiny_link_link',
'tiny_link/tiny_link_unlink',
];
}
/**
* Get a list of the menu items provided by this plugin.
*
* @return string[]
*/
public static function get_available_menuitems(): array {
return [
'tiny_link/tiny_link_link',
];
}
/**
* Get a list of the menu items provided by this plugin.
*
* @param context $context The context that the editor is used within
* @param array $options The options passed in when requesting the editor
* @param array $fpoptions The filepicker options passed in when requesting the editor
* @param editor $editor The editor instance in which the plugin is initialised
* @return array
*/
public static function get_plugin_configuration_for_context(
context $context,
array $options,
array $fpoptions,
?editor $editor = null
): array {
// TODO Fetch the actual permissions.
$permissions['filepicker'] = true;
return [
'permissions' => $permissions,
];
}
}

View File

@ -0,0 +1,45 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for the Link plugin for TinyMCE.
*
* @package tiny_link
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tiny_link\privacy;
/**
* Privacy Subsystem implementation for the Link plugin for TinyMCE.
*
* @package tiny_link
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}

View File

@ -0,0 +1,35 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'tiny_link', language 'en'.
*
* @package tiny_link
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['browserepositories'] = 'Browse repositories...';
$string['createlink'] = 'Create link';
$string['enterurl'] = 'Enter a URL';
$string['helplinktext'] = 'Link';
$string['openinnewwindow'] = 'Open in new window';
$string['pluginname'] = 'Tiny link';
$string['link'] = 'Link';
$string['unlink'] = 'Unlink';
$string['updatelink'] = 'Update link';
$string['privacy:metadata'] = 'The link plugin for TinyMCE does not store any personal data.';
$string['texttodisplay'] = 'Text to display';

View File

@ -0,0 +1,33 @@
{{!
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 tiny_link/embed_link
Embed media link template.
Example context (json):
{
}
}}
<a href="{{url}}"{{!
}}{{#newwindow}} target="_blank"{{/newwindow}}{{!
}}{{#title}} title="{{.}}"{{/title}}{{!
}}>{{!
}}{{#name}}{{{.}}}{{/name}}{{!
}}{{^name}}{{url}}{{/name}}{{!
}}</a>

View File

@ -0,0 +1,89 @@
{{!
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 tiny_link/modal
Modal to manage a link within the Tiny Editor.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
Example context (json):
{
"elementid": "exampleId",
"setLinkOnElement": false,
"showfilepicker": true,
"urltext": "Abc",
"url": "https://moodle.org/",
"newwindow": false
}
}}
{{< core/modal }}
{{$title}}
{{#str}} createlink, tiny_link {{/str}}
{{/title}}
{{$body}}
<form class="mform" id="{{uniqid}}">
<div class="mb-1">
<label for="{{elementid}}_tiny_link_urltext">{{#str}} texttodisplay, tiny_link {{/str}}</label>
<input
class="form-control fullwidth text-ltr w-100 tiny_link_urltext"
id="{{elementid}}_tiny_link_urltext" type="text"
data-use-link-as-text="{{#urltext}}false{{/urltext}}{{^urltext}}true{{/urltext}}"
{{#setLinkOnElement}}data-link-on-element="1"{{/setLinkOnElement}}
value="{{urltext}}"/>
</div>
<div class="mb-1">
<label for="{{elementid}}_tiny_link_urlentry">{{#str}} enterurl, tiny_link {{/str}}</label>
{{#showfilepicker}}
<div class="input-group input-append w-100">
<input class="form-control fullwidth text-ltr tiny_link_urlentry" id="{{elementid}}_tiny_link_urlentry" type="text" value="{{url}}"/>
<span class="input-group-append">
<button class="btn btn-secondary openlinkbrowser" type="button">{{#str}} browserepositories, tiny_link {{/str}}</button>
</span>
</div>
{{/showfilepicker}}
{{^showfilepicker}}
<input class="form-control fullwidth text-ltr tiny_link_urlentry" id="{{elementid}}_tiny_link_urlentry" type="text" value="{{url}}"/>
{{/showfilepicker}}
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input tiny_link_newwindow" id="{{elementid}}_tiny_link_newwindow" {{#newwindow}}checked{{/newwindow}}/>
<label class="form-check-label" for="{{elementid}}_tiny_link_newwindow">{{#str}} openinnewwindow, tiny_link {{/str}}</label>
</div>
</form>
{{/body}}
{{$footer}}
<button type="button" class="btn btn-primary" data-action="save">
{{#isupdating}}
{{#str}} updatelink, tiny_link {{/str}}
{{/isupdating}}
{{^isupdating}}
{{#str}} createlink, tiny_link {{/str}}
{{/isupdating}}
</button>
{{/footer}}
{{/ core/modal }}

View File

@ -0,0 +1,158 @@
@editor @editor_tiny @tiny_link @_file_upload
Feature: Add links to TinyMCE
To write rich text - I need to add links.
@javascript
Scenario: Insert a link
Given the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| private_files | System | 1 | my-index | side-post |
And I log in as "admin"
And I follow "Manage private files..."
And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" file to "Files" filemanager
And I click on "Save changes" "button"
And I open my profile in edit mode
And I set the field "Description" to "Super cool"
When I select the "p" element in position "0" of the "Description" TinyMCE editor
And I click on the "Link" button for the "Description" TinyMCE editor
Then the field "Text to display" matches value "Super cool"
And I click on "Browse repositories..." "button" in the "Create link" "dialogue"
And I select "Private files" repository in file picker
And I click on "moodle-logo.png" "link"
And I click on "Select this file" "button"
And I click on "Update profile" "button"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
And I should see "Super cool</a>"
@javascript
Scenario: Insert a link without providing text to display
Given I log in as "admin"
When I open my profile in edit mode
And I click on the "Link" button for the "Description" TinyMCE editor
And I set the field "URL" to "https://moodle.org/"
Then the field "Text to display" matches value "https://moodle.org/"
And I click on "Create link" "button" in the "Create link" "dialogue"
And the field "Description" matches value "<p><a href=\"https://moodle.org/\">https://moodle.org/</a></p>"
And I select the "a" element in position "0" of the "Description" TinyMCE editor
And I click on the "Link" button for the "Description" TinyMCE editor
And the field "Text to display" matches value "https://moodle.org/"
And the field "URL" matches value "https://moodle.org/"
And I click on "Close" "button" in the "Create link" "dialogue"
@javascript
Scenario: Insert a link with providing text to display
Given I log in as "admin"
When I open my profile in edit mode
And I click on "Link" "button"
And I set the field "Text to display" to "Moodle - Open-source learning platform"
And I set the field "Enter a URL" to "https://moodle.org/"
And I click on "Create link" "button" in the "Create link" "dialogue"
Then the field "Description" matches value "<p><a href=\"https://moodle.org/\">Moodle - Open-source learning platform</a></p>"
And I select the "a" element in position "0" of the "Description" TinyMCE editor
And I click on the "Link" button for the "Description" TinyMCE editor
And the field "Text to display" matches value "Moodle - Open-source learning platform"
And the field "Enter a URL" matches value "https://moodle.org/"
And I click on "Close" "button" in the "Create link" "dialogue"
@javascript
Scenario: Edit a link that already had a custom text to display
Given I log in as "admin"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
And I set the field "Description" to "<a href=\"https://moodle.org/\">Moodle - Open-source learning platform</a>"
And I click on "Update profile" "button"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "TinyMCE editor"
And I press "Save changes"
When I click on "Edit profile" "link" in the "region-main" "region"
Then the field "Description" matches value "<p><a href=\"https://moodle.org/\">Moodle - Open-source learning platform</a></p>"
And I select the "a" element in position "0" of the "Description" TinyMCE editor
And I click on the "Link" button for the "Description" TinyMCE editor
And the field "Text to display" matches value "Moodle - Open-source learning platform"
And the field "Enter a URL" matches value "https://moodle.org/"
@javascript
Scenario: Insert and update link in the TinyMCE editor
Given I log in as "admin"
When I open my profile in edit mode
And I click on "Link" "button"
And I set the field "Text to display" to "Moodle - Open-source learning platform"
And I set the field "Enter a URL" to "https://moodle.org/"
And I click on "Create link" "button" in the "Create link" "dialogue"
Then the field "Description" matches value "<p><a href=\"https://moodle.org/\">Moodle - Open-source learning platform</a></p>"
And I select the "a" element in position "0" of the "Description" TinyMCE editor
And I click on the "Link" button for the "Description" TinyMCE editor
And the field "Text to display" matches value "Moodle - Open-source learning platform"
And the field "Enter a URL" matches value "https://moodle.org/"
And I set the field "Enter a URL" to "https://moodle.com/"
And "Create link" "button" should not exist in the "Create link" "dialogue"
And "Update link" "button" should exist in the "Create link" "dialogue"
And I click on "Update link" "button" in the "Create link" "dialogue"
And the field "Description" matches value "<p><a href=\"https://moodle.com/\">Moodle - Open-source learning platform</a></p>"
@javascript
Scenario: Insert a link for an image using TinyMCE editor
Given I log in as "admin"
And I follow "Private files" in the user menu
And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" file to "Files" filemanager
And I click on "Save changes" "button"
And I open my profile in edit mode
And I click on the "Image" button for the "Description" TinyMCE editor
And I click on "Browse repositories..." "button" in the "Image properties" "dialogue"
And I select "Private files" repository in file picker
And I click on "moodle-logo.png" "link"
And I click on "Select this file" "button"
And I set the field "Describe this image for someone who cannot see it" to "It's the Moodle"
And I click on "Save image" "button" in the "Image properties" "dialogue"
And I select the "img" element in position "0" of the "Description" TinyMCE editor
And I click on the "Link" button for the "Description" TinyMCE editor
And I set the field "Enter a URL" to "https://moodle.org/"
And I set the field "Text to display" to "Moodle - Open-source learning platform"
And I click on "Update link" "button" in the "Create link" "dialogue"
# TODO: Verify the HTML by the improved code plugin in MDL-75265
And I click on "Update profile" "button"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
When I click on "Edit profile" "link" in the "region-main" "region"
Then I should see "<a title=\"Moodle - Open-source learning platform\" href=\"https://moodle.org/\"><img"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "TinyMCE editor"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
And I select the "img" element in position "0" of the "Description" TinyMCE editor
And I click on the "Image" button for the "Description" TinyMCE editor
And the field "Describe this image for someone who cannot see it" matches value "It's the Moodle"
And I click on "Close" "button" in the "Image properties" "dialogue"
And I click on the "Link" button for the "Description" TinyMCE editor
And the field "Text to display" matches value "Moodle - Open-source learning platform"
And the field "Enter a URL" matches value "https://moodle.org/"
@javascript
Scenario: Unset a link
Given I log in as "admin"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "Plain text area"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
And I set the field "Description" to "<a href=\"https://moodle.org/\">Moodle - Open-source learning platform</a>"
And I click on "Update profile" "button"
And I follow "Preferences" in the user menu
And I follow "Editor preferences"
And I set the field "Text editor" to "TinyMCE editor"
And I press "Save changes"
And I click on "Edit profile" "link" in the "region-main" "region"
And I select the "a" element in position "0" of the "Description" TinyMCE editor
When I click on the "Unlink" button for the "Description" TinyMCE editor
Then the field "Description" matches value "<p>Moodle - Open-source learning platform</p>"

View File

@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Tiny Link plugin version details.
*
* @package tiny_link
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2022112800;
$plugin->requires = 2022111800;
$plugin->component = 'tiny_link';