From ed42b2e92bd545b6aff72bf63f59b9b587c3e154 Mon Sep 17 00:00:00 2001 From: David Woloszyn Date: Tue, 30 Apr 2024 09:40:15 +1000 Subject: [PATCH] MDL-80571 tiny_premium: Premium plugins can be individually enabled --- .../premium/amd/build/configuration.min.js | 2 +- .../amd/build/configuration.min.js.map | 2 +- .../plugins/premium/amd/src/configuration.js | 131 ++++++++------- .../admin_setting_tiny_premium_plugins.php | 151 ++++++++++++++++++ .../tiny/plugins/premium/classes/manager.php | 109 +++++++++++++ .../plugins/premium/classes/plugininfo.php | 24 ++- .../tiny/plugins/premium/db/upgrade.php | 55 +++++++ .../plugins/premium/lang/en/tiny_premium.php | 25 ++- .../tiny/plugins/premium/pluginsettings.php | 74 +++++++++ .../tiny/plugins/premium/readme_moodle.txt | 9 +- lib/editor/tiny/plugins/premium/settings.php | 4 + .../tests/behat/tiny_premium_settings.feature | 23 +++ .../plugins/premium/tests/manager_test.php | 73 +++++++++ lib/editor/tiny/plugins/premium/version.php | 2 +- 14 files changed, 620 insertions(+), 64 deletions(-) create mode 100644 lib/editor/tiny/plugins/premium/classes/local/admin_setting_tiny_premium_plugins.php create mode 100644 lib/editor/tiny/plugins/premium/classes/manager.php create mode 100644 lib/editor/tiny/plugins/premium/db/upgrade.php create mode 100644 lib/editor/tiny/plugins/premium/pluginsettings.php create mode 100644 lib/editor/tiny/plugins/premium/tests/behat/tiny_premium_settings.feature create mode 100644 lib/editor/tiny/plugins/premium/tests/manager_test.php diff --git a/lib/editor/tiny/plugins/premium/amd/build/configuration.min.js b/lib/editor/tiny/plugins/premium/amd/build/configuration.min.js index 621fad27a73..c084ef3e635 100644 --- a/lib/editor/tiny/plugins/premium/amd/build/configuration.min.js +++ b/lib/editor/tiny/plugins/premium/amd/build/configuration.min.js @@ -1,3 +1,3 @@ -define("tiny_premium/configuration",["exports","editor_tiny/utils"],(function(_exports,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;_exports.configure=instanceConfig=>{let plugins=instanceConfig.plugins,menu=instanceConfig.menu,toolbar=(toolbar=>(toolbar=(0,_utils.addToolbarSection)(toolbar,"premium_a","advanced",!0),(0,_utils.addToolbarSection)(toolbar,"premium_b","formatting",!0)))(instanceConfig.toolbar),contextmenu=instanceConfig.contextmenu;return plugins+=" advtable",menu=(0,_utils.addMenubarItem)(menu,"table","| advtablerownumbering","advtablesort"),plugins+=" editimage",toolbar=(0,_utils.addToolbarButton)(toolbar,"content","editimage","tiny_media_image"),instanceConfig.editimage_toolbar="rotateleft rotateright flipv fliph editimage",plugins+=" export",menu=(0,_utils.addMenubarItem)(menu,"tools","| export"),plugins+=" pageembed",toolbar=(0,_utils.addToolbarButton)(toolbar,"content","pageembed","tiny_media_video"),plugins+=" typography",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_b","typography"),plugins+=" casechange",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","casechange"),plugins+=" checklist",toolbar=(0,_utils.addToolbarButton)(toolbar,"lists","checklist"),plugins+=" tinymcespellchecker",menu=(0,_utils.addMenubarItem)(menu,"tools","spellcheckdialog","spellcheckerlanguage"),contextmenu=(0,_utils.addContextmenuItem)(contextmenu,"spellchecker"),toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","spellcheckdialog"),plugins+=" autocorrect",menu=(0,_utils.addMenubarItem)(menu,"tools","| autocorrect capitalization","spellcheckdialog"),plugins+=" permanentpen",menu=(0,_utils.addMenubarItem)(menu,"format","| permanentpen configurepermanentpen"),toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","permanentpen"),contextmenu=(0,_utils.addContextmenuItem)(contextmenu,"configurepermanentpen"),plugins+=" formatpainter",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","formatpainter"),plugins+=" linkchecker",contextmenu=(0,_utils.addContextmenuItem)(contextmenu,"linkchecker"),plugins+=" tableofcontents",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","tableofcontents"),plugins+=" footnotes",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","footnotes"),menu=(0,_utils.addMenubarItem)(menu,"insert","footnotes","tableofcontents"),plugins+=" powerpaste",{plugins:plugins,toolbar:toolbar,menu:menu,contextmenu:contextmenu}}})); +define("tiny_premium/configuration",["exports","editor_tiny/utils","editor_tiny/options"],(function(_exports,_utils,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;_exports.configure=(instanceConfig,options)=>{const enabledPremiumPlugins=(0,_options.getInitialPluginConfiguration)(options)[(0,_options.getPluginOptionName)("tiny_premium/plugin","premiumplugins")].split(",");let plugins=instanceConfig.plugins,menu=instanceConfig.menu,toolbar=(toolbar=>(toolbar=(0,_utils.addToolbarSection)(toolbar,"premium_a","advanced",!0),(0,_utils.addToolbarSection)(toolbar,"premium_b","formatting",!0)))(instanceConfig.toolbar),contextmenu=instanceConfig.contextmenu;return-1!==enabledPremiumPlugins.indexOf("advtable")&&(plugins+=" advtable",menu=(0,_utils.addMenubarItem)(menu,"table","| advtablerownumbering","advtablesort")),-1!==enabledPremiumPlugins.indexOf("editimage")&&(plugins+=" editimage",toolbar=(0,_utils.addToolbarButton)(toolbar,"content","editimage","tiny_media_image"),instanceConfig.editimage_toolbar="rotateleft rotateright flipv fliph editimage"),-1!==enabledPremiumPlugins.indexOf("export")&&(plugins+=" export",menu=(0,_utils.addMenubarItem)(menu,"tools","| export")),-1!==enabledPremiumPlugins.indexOf("pageembed")&&(plugins+=" pageembed",toolbar=(0,_utils.addToolbarButton)(toolbar,"content","pageembed","tiny_media_video")),-1!==enabledPremiumPlugins.indexOf("typography")&&(plugins+=" typography",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_b","typography")),-1!==enabledPremiumPlugins.indexOf("casechange")&&(plugins+=" casechange",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","casechange")),-1!==enabledPremiumPlugins.indexOf("checklist")&&(plugins+=" checklist",toolbar=(0,_utils.addToolbarButton)(toolbar,"lists","checklist")),-1!==enabledPremiumPlugins.indexOf("tinymcespellchecker")&&(plugins+=" tinymcespellchecker",menu=(0,_utils.addMenubarItem)(menu,"tools","spellcheckdialog","spellcheckerlanguage"),contextmenu=(0,_utils.addContextmenuItem)(contextmenu,"spellchecker"),toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","spellcheckdialog")),-1!==enabledPremiumPlugins.indexOf("autocorrect")&&(plugins+=" autocorrect",menu=(0,_utils.addMenubarItem)(menu,"tools","| autocorrect capitalization","spellcheckdialog")),-1!==enabledPremiumPlugins.indexOf("permanentpen")&&(plugins+=" permanentpen",menu=(0,_utils.addMenubarItem)(menu,"format","| permanentpen configurepermanentpen"),toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","permanentpen"),contextmenu=(0,_utils.addContextmenuItem)(contextmenu,"configurepermanentpen")),-1!==enabledPremiumPlugins.indexOf("formatpainter")&&(plugins+=" formatpainter",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","formatpainter")),-1!==enabledPremiumPlugins.indexOf("linkchecker")&&(plugins+=" linkchecker",contextmenu=(0,_utils.addContextmenuItem)(contextmenu,"linkchecker")),-1!==enabledPremiumPlugins.indexOf("tableofcontents")&&(plugins+=" tableofcontents",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","tableofcontents")),-1!==enabledPremiumPlugins.indexOf("footnotes")&&(plugins+=" footnotes",toolbar=(0,_utils.addToolbarButton)(toolbar,"premium_a","footnotes"),menu=(0,_utils.addMenubarItem)(menu,"insert","footnotes","tableofcontents")),-1!==enabledPremiumPlugins.indexOf("powerpaste")&&(plugins+=" powerpaste"),{plugins:plugins,toolbar:toolbar,menu:menu,contextmenu:contextmenu}}})); //# sourceMappingURL=configuration.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/premium/amd/build/configuration.min.js.map b/lib/editor/tiny/plugins/premium/amd/build/configuration.min.js.map index ca798b5a1b9..404d85db577 100644 --- a/lib/editor/tiny/plugins/premium/amd/build/configuration.min.js.map +++ b/lib/editor/tiny/plugins/premium/amd/build/configuration.min.js.map @@ -1 +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 .\n\n/**\n * Tiny Premium configuration.\n *\n * @module tiny_premium/configuration\n * @copyright 2023 David Woloszyn \n * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {\n addToolbarButton,\n addMenubarItem,\n addToolbarSection,\n addContextmenuItem\n} from 'editor_tiny/utils';\n\nconst configureToolbar = (toolbar) => {\n // Add premium toolbar sections to house all the plugins with no natural home.\n toolbar = addToolbarSection(toolbar, 'premium_a', 'advanced', true);\n toolbar = addToolbarSection(toolbar, 'premium_b', 'formatting', true);\n return toolbar;\n};\n\nexport const configure = (instanceConfig) => {\n // There is some manipulating of the plugin menu, toolbar, context and quickbar items.\n // This was necessary to enhance user experience and closer align to the Tiny demo site.\n let plugins = instanceConfig.plugins;\n let menu = instanceConfig.menu;\n let toolbar = configureToolbar(instanceConfig.toolbar);\n let contextmenu = instanceConfig.contextmenu;\n let pluginsettings = {};\n\n // Advanced Table.\n plugins += ` advtable`;\n menu = addMenubarItem(menu, 'table', '| advtablerownumbering', 'advtablesort');\n\n // Enhanced Image Editing.\n plugins += ` editimage`;\n toolbar = addToolbarButton(toolbar, 'content', 'editimage', 'tiny_media_image');\n // Remove the duplicate image button from the quickbar toolbar by redefining the values without 'imageoptions'.\n // eslint-disable-next-line camelcase\n instanceConfig.editimage_toolbar = 'rotateleft rotateright flipv fliph editimage';\n\n // Export.\n plugins += ` export`;\n menu = addMenubarItem(menu, 'tools', '| export');\n\n // Page Embed.\n plugins += ` pageembed`;\n toolbar = addToolbarButton(toolbar, 'content', 'pageembed', 'tiny_media_video');\n\n // Advanced Typography.\n plugins += ` typography`;\n toolbar = addToolbarButton(toolbar, 'premium_b', 'typography');\n\n // Case Change.\n plugins += ` casechange`;\n toolbar = addToolbarButton(toolbar, 'premium_a', 'casechange');\n\n // Checklist.\n plugins += ` checklist`;\n toolbar = addToolbarButton(toolbar, 'lists', 'checklist');\n\n // Spell Checker Pro.\n plugins += ` tinymcespellchecker`;\n menu = addMenubarItem(menu, 'tools', 'spellcheckdialog', 'spellcheckerlanguage');\n contextmenu = addContextmenuItem(contextmenu, 'spellchecker');\n toolbar = addToolbarButton(toolbar, 'premium_a', 'spellcheckdialog');\n\n // Spelling Autocorrect.\n plugins += ` autocorrect`;\n menu = addMenubarItem(menu, 'tools', '| autocorrect capitalization', 'spellcheckdialog');\n\n // Permanent Pen.\n plugins += ` permanentpen`;\n menu = addMenubarItem(menu, 'format', '| permanentpen configurepermanentpen');\n toolbar = addToolbarButton(toolbar, 'premium_a', 'permanentpen');\n contextmenu = addContextmenuItem(contextmenu, 'configurepermanentpen');\n\n // Format Painter.\n plugins += ` formatpainter`;\n toolbar = addToolbarButton(toolbar, 'premium_a', 'formatpainter');\n\n // Link Checker.\n plugins += ` linkchecker`;\n contextmenu = addContextmenuItem(contextmenu, 'linkchecker');\n\n // Table of Contents.\n plugins += ` tableofcontents`;\n toolbar = addToolbarButton(toolbar, 'premium_a', 'tableofcontents');\n\n // Footnotes.\n plugins += ` footnotes`;\n toolbar = addToolbarButton(toolbar, 'premium_a', 'footnotes');\n menu = addMenubarItem(menu, 'insert', 'footnotes', 'tableofcontents');\n\n // Powerpaste.\n plugins += ` powerpaste`;\n\n return {\n plugins,\n toolbar,\n menu,\n contextmenu,\n ...pluginsettings\n };\n};\n"],"names":["instanceConfig","plugins","menu","toolbar","configureToolbar","contextmenu","editimage_toolbar"],"mappings":"oMAqC0BA,qBAGlBC,QAAUD,eAAeC,QACzBC,KAAOF,eAAeE,KACtBC,QAZkBA,CAAAA,UAEtBA,SAAU,4BAAkBA,QAAS,YAAa,YAAY,IACpD,4BAAkBA,QAAS,YAAa,cAAc,IASlDC,CAAiBJ,eAAeG,SAC1CE,YAAcL,eAAeK,mBAIjCJ,qBACAC,MAAO,yBAAeA,KAAM,QAAS,yBAA0B,gBAG/DD,sBACAE,SAAU,2BAAiBA,QAAS,UAAW,YAAa,oBAG5DH,eAAeM,kBAAoB,+CAGnCL,mBACAC,MAAO,yBAAeA,KAAM,QAAS,YAGrCD,sBACAE,SAAU,2BAAiBA,QAAS,UAAW,YAAa,oBAG5DF,uBACAE,SAAU,2BAAiBA,QAAS,YAAa,cAGjDF,uBACAE,SAAU,2BAAiBA,QAAS,YAAa,cAGjDF,sBACAE,SAAU,2BAAiBA,QAAS,QAAS,aAG7CF,gCACAC,MAAO,yBAAeA,KAAM,QAAS,mBAAoB,wBACzDG,aAAc,6BAAmBA,YAAa,gBAC9CF,SAAU,2BAAiBA,QAAS,YAAa,oBAGjDF,wBACAC,MAAO,yBAAeA,KAAM,QAAS,+BAAgC,oBAGrED,yBACAC,MAAO,yBAAeA,KAAM,SAAU,wCACtCC,SAAU,2BAAiBA,QAAS,YAAa,gBACjDE,aAAc,6BAAmBA,YAAa,yBAG9CJ,0BACAE,SAAU,2BAAiBA,QAAS,YAAa,iBAGjDF,wBACAI,aAAc,6BAAmBA,YAAa,eAG9CJ,4BACAE,SAAU,2BAAiBA,QAAS,YAAa,mBAGjDF,sBACAE,SAAU,2BAAiBA,QAAS,YAAa,aACjDD,MAAO,yBAAeA,KAAM,SAAU,YAAa,mBAGnDD,uBAEO,CACHA,QAAAA,QACAE,QAAAA,QACAD,KAAAA,KACAG,YAAAA"} \ No newline at end of file +{"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 .\n\n/**\n * Tiny Premium configuration.\n *\n * @module tiny_premium/configuration\n * @copyright 2023 David Woloszyn \n * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {\n addToolbarButton,\n addMenubarItem,\n addToolbarSection,\n addContextmenuItem\n} from 'editor_tiny/utils';\nimport {\n getInitialPluginConfiguration,\n getPluginOptionName\n} from 'editor_tiny/options';\n\nconst configureToolbar = (toolbar) => {\n // Add premium toolbar sections to house all the plugins with no natural home.\n toolbar = addToolbarSection(toolbar, 'premium_a', 'advanced', true);\n toolbar = addToolbarSection(toolbar, 'premium_b', 'formatting', true);\n return toolbar;\n};\n\nexport const configure = (instanceConfig, options) => {\n // Get the namespaced options for Tiny Premium before they are officially initialised.\n // Due to the timing the plugin options are available, we need to get at the options in this slightly unconventional way.\n const pluginOptions = getInitialPluginConfiguration(options);\n const enabledPremiumPlugins = pluginOptions[getPluginOptionName('tiny_premium/plugin', 'premiumplugins')].split(',');\n\n let plugins = instanceConfig.plugins;\n let menu = instanceConfig.menu;\n let toolbar = configureToolbar(instanceConfig.toolbar);\n let contextmenu = instanceConfig.contextmenu;\n let pluginsettings = {};\n\n // Advanced Table.\n if (enabledPremiumPlugins.indexOf('advtable') !== -1) {\n plugins += ` advtable`;\n menu = addMenubarItem(menu, 'table', '| advtablerownumbering', 'advtablesort');\n }\n // Enhanced Image Editing.\n if (enabledPremiumPlugins.indexOf('editimage') !== -1) {\n plugins += ` editimage`;\n toolbar = addToolbarButton(toolbar, 'content', 'editimage', 'tiny_media_image');\n // Remove the duplicate image button from the quickbar toolbar by redefining the values without 'imageoptions'.\n // eslint-disable-next-line camelcase\n instanceConfig.editimage_toolbar = 'rotateleft rotateright flipv fliph editimage';\n }\n // Export.\n if (enabledPremiumPlugins.indexOf('export') !== -1) {\n plugins += ` export`;\n menu = addMenubarItem(menu, 'tools', '| export');\n }\n // Page Embed.\n if (enabledPremiumPlugins.indexOf('pageembed') !== -1) {\n plugins += ` pageembed`;\n toolbar = addToolbarButton(toolbar, 'content', 'pageembed', 'tiny_media_video');\n }\n // Advanced Typography.\n if (enabledPremiumPlugins.indexOf('typography') !== -1) {\n plugins += ` typography`;\n toolbar = addToolbarButton(toolbar, 'premium_b', 'typography');\n }\n // Case Change.\n if (enabledPremiumPlugins.indexOf('casechange') !== -1) {\n plugins += ` casechange`;\n toolbar = addToolbarButton(toolbar, 'premium_a', 'casechange');\n }\n // Checklist.\n if (enabledPremiumPlugins.indexOf('checklist') !== -1) {\n plugins += ` checklist`;\n toolbar = addToolbarButton(toolbar, 'lists', 'checklist');\n }\n // Spell Checker Pro.\n if (enabledPremiumPlugins.indexOf('tinymcespellchecker') !== -1) {\n plugins += ` tinymcespellchecker`;\n menu = addMenubarItem(menu, 'tools', 'spellcheckdialog', 'spellcheckerlanguage');\n contextmenu = addContextmenuItem(contextmenu, 'spellchecker');\n toolbar = addToolbarButton(toolbar, 'premium_a', 'spellcheckdialog');\n }\n // Spelling Autocorrect.\n if (enabledPremiumPlugins.indexOf('autocorrect') !== -1) {\n plugins += ` autocorrect`;\n menu = addMenubarItem(menu, 'tools', '| autocorrect capitalization', 'spellcheckdialog');\n }\n // Permanent Pen.\n if (enabledPremiumPlugins.indexOf('permanentpen') !== -1) {\n plugins += ` permanentpen`;\n menu = addMenubarItem(menu, 'format', '| permanentpen configurepermanentpen');\n toolbar = addToolbarButton(toolbar, 'premium_a', 'permanentpen');\n contextmenu = addContextmenuItem(contextmenu, 'configurepermanentpen');\n }\n // Format Painter.\n if (enabledPremiumPlugins.indexOf('formatpainter') !== -1) {\n plugins += ` formatpainter`;\n toolbar = addToolbarButton(toolbar, 'premium_a', 'formatpainter');\n }\n // Link Checker.\n if (enabledPremiumPlugins.indexOf('linkchecker') !== -1) {\n plugins += ` linkchecker`;\n contextmenu = addContextmenuItem(contextmenu, 'linkchecker');\n }\n // Table of Contents.\n if (enabledPremiumPlugins.indexOf('tableofcontents') !== -1) {\n plugins += ` tableofcontents`;\n toolbar = addToolbarButton(toolbar, 'premium_a', 'tableofcontents');\n }\n // Footnotes.\n if (enabledPremiumPlugins.indexOf('footnotes') !== -1) {\n plugins += ` footnotes`;\n toolbar = addToolbarButton(toolbar, 'premium_a', 'footnotes');\n menu = addMenubarItem(menu, 'insert', 'footnotes', 'tableofcontents');\n }\n // Powerpaste.\n if (enabledPremiumPlugins.indexOf('powerpaste') !== -1) {\n plugins += ` powerpaste`;\n }\n\n return {\n plugins,\n toolbar,\n menu,\n contextmenu,\n ...pluginsettings\n };\n};\n"],"names":["instanceConfig","options","enabledPremiumPlugins","split","plugins","menu","toolbar","configureToolbar","contextmenu","indexOf","editimage_toolbar"],"mappings":"mOAyCyB,CAACA,eAAgBC,iBAIhCC,uBADgB,0CAA8BD,UACR,gCAAoB,sBAAuB,mBAAmBE,MAAM,SAE5GC,QAAUJ,eAAeI,QACzBC,KAAOL,eAAeK,KACtBC,QAfkBA,CAAAA,UAEtBA,SAAU,4BAAkBA,QAAS,YAAa,YAAY,IACpD,4BAAkBA,QAAS,YAAa,cAAc,IAYlDC,CAAiBP,eAAeM,SAC1CE,YAAcR,eAAeQ,mBAIkB,IAA/CN,sBAAsBO,QAAQ,cAC9BL,qBACAC,MAAO,yBAAeA,KAAM,QAAS,yBAA0B,kBAGf,IAAhDH,sBAAsBO,QAAQ,eAC9BL,sBACAE,SAAU,2BAAiBA,QAAS,UAAW,YAAa,oBAG5DN,eAAeU,kBAAoB,iDAGU,IAA7CR,sBAAsBO,QAAQ,YAC9BL,mBACAC,MAAO,yBAAeA,KAAM,QAAS,cAGW,IAAhDH,sBAAsBO,QAAQ,eAC9BL,sBACAE,SAAU,2BAAiBA,QAAS,UAAW,YAAa,sBAGX,IAAjDJ,sBAAsBO,QAAQ,gBAC9BL,uBACAE,SAAU,2BAAiBA,QAAS,YAAa,gBAGA,IAAjDJ,sBAAsBO,QAAQ,gBAC9BL,uBACAE,SAAU,2BAAiBA,QAAS,YAAa,gBAGD,IAAhDJ,sBAAsBO,QAAQ,eAC9BL,sBACAE,SAAU,2BAAiBA,QAAS,QAAS,eAGa,IAA1DJ,sBAAsBO,QAAQ,yBAC9BL,gCACAC,MAAO,yBAAeA,KAAM,QAAS,mBAAoB,wBACzDG,aAAc,6BAAmBA,YAAa,gBAC9CF,SAAU,2BAAiBA,QAAS,YAAa,sBAGC,IAAlDJ,sBAAsBO,QAAQ,iBAC9BL,wBACAC,MAAO,yBAAeA,KAAM,QAAS,+BAAgC,sBAGlB,IAAnDH,sBAAsBO,QAAQ,kBAC9BL,yBACAC,MAAO,yBAAeA,KAAM,SAAU,wCACtCC,SAAU,2BAAiBA,QAAS,YAAa,gBACjDE,aAAc,6BAAmBA,YAAa,2BAGM,IAApDN,sBAAsBO,QAAQ,mBAC9BL,0BACAE,SAAU,2BAAiBA,QAAS,YAAa,mBAGC,IAAlDJ,sBAAsBO,QAAQ,iBAC9BL,wBACAI,aAAc,6BAAmBA,YAAa,iBAGQ,IAAtDN,sBAAsBO,QAAQ,qBAC9BL,4BACAE,SAAU,2BAAiBA,QAAS,YAAa,qBAGD,IAAhDJ,sBAAsBO,QAAQ,eAC9BL,sBACAE,SAAU,2BAAiBA,QAAS,YAAa,aACjDD,MAAO,yBAAeA,KAAM,SAAU,YAAa,qBAGF,IAAjDH,sBAAsBO,QAAQ,gBAC9BL,wBAGG,CACHA,QAAAA,QACAE,QAAAA,QACAD,KAAAA,KACAG,YAAAA"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/premium/amd/src/configuration.js b/lib/editor/tiny/plugins/premium/amd/src/configuration.js index 1b2cfa7e14c..6b5f9460aca 100644 --- a/lib/editor/tiny/plugins/premium/amd/src/configuration.js +++ b/lib/editor/tiny/plugins/premium/amd/src/configuration.js @@ -27,6 +27,10 @@ import { addToolbarSection, addContextmenuItem } from 'editor_tiny/utils'; +import { + getInitialPluginConfiguration, + getPluginOptionName +} from 'editor_tiny/options'; const configureToolbar = (toolbar) => { // Add premium toolbar sections to house all the plugins with no natural home. @@ -35,9 +39,12 @@ const configureToolbar = (toolbar) => { return toolbar; }; -export const configure = (instanceConfig) => { - // There is some manipulating of the plugin menu, toolbar, context and quickbar items. - // This was necessary to enhance user experience and closer align to the Tiny demo site. +export const configure = (instanceConfig, options) => { + // Get the namespaced options for Tiny Premium before they are officially initialised. + // Due to the timing the plugin options are available, we need to get at the options in this slightly unconventional way. + const pluginOptions = getInitialPluginConfiguration(options); + const enabledPremiumPlugins = pluginOptions[getPluginOptionName('tiny_premium/plugin', 'premiumplugins')].split(','); + let plugins = instanceConfig.plugins; let menu = instanceConfig.menu; let toolbar = configureToolbar(instanceConfig.toolbar); @@ -45,71 +52,87 @@ export const configure = (instanceConfig) => { let pluginsettings = {}; // Advanced Table. - plugins += ` advtable`; - menu = addMenubarItem(menu, 'table', '| advtablerownumbering', 'advtablesort'); - + if (enabledPremiumPlugins.indexOf('advtable') !== -1) { + plugins += ` advtable`; + menu = addMenubarItem(menu, 'table', '| advtablerownumbering', 'advtablesort'); + } // Enhanced Image Editing. - plugins += ` editimage`; - toolbar = addToolbarButton(toolbar, 'content', 'editimage', 'tiny_media_image'); - // Remove the duplicate image button from the quickbar toolbar by redefining the values without 'imageoptions'. - // eslint-disable-next-line camelcase - instanceConfig.editimage_toolbar = 'rotateleft rotateright flipv fliph editimage'; - + if (enabledPremiumPlugins.indexOf('editimage') !== -1) { + plugins += ` editimage`; + toolbar = addToolbarButton(toolbar, 'content', 'editimage', 'tiny_media_image'); + // Remove the duplicate image button from the quickbar toolbar by redefining the values without 'imageoptions'. + // eslint-disable-next-line camelcase + instanceConfig.editimage_toolbar = 'rotateleft rotateright flipv fliph editimage'; + } // Export. - plugins += ` export`; - menu = addMenubarItem(menu, 'tools', '| export'); - + if (enabledPremiumPlugins.indexOf('export') !== -1) { + plugins += ` export`; + menu = addMenubarItem(menu, 'tools', '| export'); + } // Page Embed. - plugins += ` pageembed`; - toolbar = addToolbarButton(toolbar, 'content', 'pageembed', 'tiny_media_video'); - + if (enabledPremiumPlugins.indexOf('pageembed') !== -1) { + plugins += ` pageembed`; + toolbar = addToolbarButton(toolbar, 'content', 'pageembed', 'tiny_media_video'); + } // Advanced Typography. - plugins += ` typography`; - toolbar = addToolbarButton(toolbar, 'premium_b', 'typography'); - + if (enabledPremiumPlugins.indexOf('typography') !== -1) { + plugins += ` typography`; + toolbar = addToolbarButton(toolbar, 'premium_b', 'typography'); + } // Case Change. - plugins += ` casechange`; - toolbar = addToolbarButton(toolbar, 'premium_a', 'casechange'); - + if (enabledPremiumPlugins.indexOf('casechange') !== -1) { + plugins += ` casechange`; + toolbar = addToolbarButton(toolbar, 'premium_a', 'casechange'); + } // Checklist. - plugins += ` checklist`; - toolbar = addToolbarButton(toolbar, 'lists', 'checklist'); - + if (enabledPremiumPlugins.indexOf('checklist') !== -1) { + plugins += ` checklist`; + toolbar = addToolbarButton(toolbar, 'lists', 'checklist'); + } // Spell Checker Pro. - plugins += ` tinymcespellchecker`; - menu = addMenubarItem(menu, 'tools', 'spellcheckdialog', 'spellcheckerlanguage'); - contextmenu = addContextmenuItem(contextmenu, 'spellchecker'); - toolbar = addToolbarButton(toolbar, 'premium_a', 'spellcheckdialog'); - + if (enabledPremiumPlugins.indexOf('tinymcespellchecker') !== -1) { + plugins += ` tinymcespellchecker`; + menu = addMenubarItem(menu, 'tools', 'spellcheckdialog', 'spellcheckerlanguage'); + contextmenu = addContextmenuItem(contextmenu, 'spellchecker'); + toolbar = addToolbarButton(toolbar, 'premium_a', 'spellcheckdialog'); + } // Spelling Autocorrect. - plugins += ` autocorrect`; - menu = addMenubarItem(menu, 'tools', '| autocorrect capitalization', 'spellcheckdialog'); - + if (enabledPremiumPlugins.indexOf('autocorrect') !== -1) { + plugins += ` autocorrect`; + menu = addMenubarItem(menu, 'tools', '| autocorrect capitalization', 'spellcheckdialog'); + } // Permanent Pen. - plugins += ` permanentpen`; - menu = addMenubarItem(menu, 'format', '| permanentpen configurepermanentpen'); - toolbar = addToolbarButton(toolbar, 'premium_a', 'permanentpen'); - contextmenu = addContextmenuItem(contextmenu, 'configurepermanentpen'); - + if (enabledPremiumPlugins.indexOf('permanentpen') !== -1) { + plugins += ` permanentpen`; + menu = addMenubarItem(menu, 'format', '| permanentpen configurepermanentpen'); + toolbar = addToolbarButton(toolbar, 'premium_a', 'permanentpen'); + contextmenu = addContextmenuItem(contextmenu, 'configurepermanentpen'); + } // Format Painter. - plugins += ` formatpainter`; - toolbar = addToolbarButton(toolbar, 'premium_a', 'formatpainter'); - + if (enabledPremiumPlugins.indexOf('formatpainter') !== -1) { + plugins += ` formatpainter`; + toolbar = addToolbarButton(toolbar, 'premium_a', 'formatpainter'); + } // Link Checker. - plugins += ` linkchecker`; - contextmenu = addContextmenuItem(contextmenu, 'linkchecker'); - + if (enabledPremiumPlugins.indexOf('linkchecker') !== -1) { + plugins += ` linkchecker`; + contextmenu = addContextmenuItem(contextmenu, 'linkchecker'); + } // Table of Contents. - plugins += ` tableofcontents`; - toolbar = addToolbarButton(toolbar, 'premium_a', 'tableofcontents'); - + if (enabledPremiumPlugins.indexOf('tableofcontents') !== -1) { + plugins += ` tableofcontents`; + toolbar = addToolbarButton(toolbar, 'premium_a', 'tableofcontents'); + } // Footnotes. - plugins += ` footnotes`; - toolbar = addToolbarButton(toolbar, 'premium_a', 'footnotes'); - menu = addMenubarItem(menu, 'insert', 'footnotes', 'tableofcontents'); - + if (enabledPremiumPlugins.indexOf('footnotes') !== -1) { + plugins += ` footnotes`; + toolbar = addToolbarButton(toolbar, 'premium_a', 'footnotes'); + menu = addMenubarItem(menu, 'insert', 'footnotes', 'tableofcontents'); + } // Powerpaste. - plugins += ` powerpaste`; + if (enabledPremiumPlugins.indexOf('powerpaste') !== -1) { + plugins += ` powerpaste`; + } return { plugins, diff --git a/lib/editor/tiny/plugins/premium/classes/local/admin_setting_tiny_premium_plugins.php b/lib/editor/tiny/plugins/premium/classes/local/admin_setting_tiny_premium_plugins.php new file mode 100644 index 00000000000..0a1c78af700 --- /dev/null +++ b/lib/editor/tiny/plugins/premium/classes/local/admin_setting_tiny_premium_plugins.php @@ -0,0 +1,151 @@ +. + +namespace tiny_premium\local; + +use tiny_premium\manager; + +/** + * Admin setting for managing Tiny Premium plugins. + * + * @package tiny_premium + * @copyright 2024 David Woloszyn + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class admin_setting_tiny_premium_plugins extends \admin_setting { + + /** + * Calls parent::__construct with specific arguments. + */ + public function __construct() { + $this->nosave = true; + parent::__construct( + name: 'tiny_premium/premiumplugins', + visiblename: new \lang_string('premiumplugins', 'tiny_premium'), + description: new \lang_string('premiumplugins_desc', 'tiny_premium'), + defaultsetting: '', + ); + } + + /** + * Always returns true. + * + * @return bool + */ + public function get_setting(): bool { + return true; + } + + /** + * Always returns '' and doesn't write anything. + * + * @param mixed $data + * @return string Always returns '' + */ + public function write_setting($data): string { + return ''; + } + + /** + * Builds the HTML to display the Tiny Premium plugins table. + * + * @param mixed $data Unused + * @param string $query + * @return string highlight + */ + public function output_html($data, $query=''): string { + global $OUTPUT; + + $return = ''; + + // Warn users about an empty API key when displaying enabled plugins. + if (empty(get_config('tiny_premium', 'apikey')) && !empty(manager::get_enabled_plugins())) { + $return .= \core\notification::warning(get_string('emptyapikeywarning', 'tiny_premium')); + } + + $return .= $OUTPUT->box_start('generalbox'); + $return .= $OUTPUT->heading(get_string('premiumplugins', 'tiny_premium'), 3); + $return .= \html_writer::tag('p', get_string('premiumplugins_desc', 'tiny_premium')); + $return .= $this->define_manage_tiny_premium_plugins_table(); + $return .= $OUTPUT->box_end(); + + return highlight($query, $return); + } + + /** + * Defines table for managing Tiny Premium plugins. + * + * @return string HTML for table + */ + public function define_manage_tiny_premium_plugins_table(): string { + global $OUTPUT; + $sesskey = sesskey(); + + // Set up table. + $table = new \html_table(); + $table->id = 'managetinypremiumpluginstable'; + $table->attributes['class'] = 'admintable generaltable'; + $table->head = [ + get_string('name'), + get_string('enable'), + ]; + $table->colclasses = [ + 'leftalign', + 'centeralign', + ]; + $table->data = []; + + // Keep enabled plugins on top. + $plugins = manager::get_plugins(); + $enabledplugins = manager::get_enabled_plugins(); + $disabledplugins = array_diff($plugins, $enabledplugins); + $plugins = array_merge($enabledplugins, $disabledplugins); + + foreach ($plugins as $plugin) { + + $pluginname = get_string('premiumplugin:' . $plugin, 'tiny_premium'); + + // Determine plugin actions. + if (manager::is_plugin_enabled($plugin)) { + $action = 'disable'; + $icon = $OUTPUT->pix_icon('t/hide', get_string('disableplugin', 'core_admin', $pluginname)); + $class = ''; + } else { + $action = 'enable'; + $icon = $OUTPUT->pix_icon('t/show', get_string('enableplugin', 'core_admin', $pluginname)); + $class = 'dimmed_text'; + } + + // Prepare a link to perform the action. + $hideshowurl = new \moodle_url('/lib/editor/tiny/plugins/premium/pluginsettings.php', [ + 'action' => $action, + 'plugin' => $plugin, + 'sesskey' => $sesskey, + ]); + $hideshowlink = \html_writer::link($hideshowurl, $icon); + + // Populate table row. + $row = new \html_table_row([ + $pluginname, + $hideshowlink, + ]); + $row->attributes['class'] = $class; + $table->data[] = $row; + } + + return \html_writer::table($table); + } +} diff --git a/lib/editor/tiny/plugins/premium/classes/manager.php b/lib/editor/tiny/plugins/premium/classes/manager.php new file mode 100644 index 00000000000..03f8b0664f1 --- /dev/null +++ b/lib/editor/tiny/plugins/premium/classes/manager.php @@ -0,0 +1,109 @@ +. + +namespace tiny_premium; + +/** + * Tiny Premium manager. + * + * @package tiny_premium + * @copyright 2024 David Woloszyn + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class manager { + + /** + * Get all Tiny Premium plugins currently supported. + * + * The plugin identifiers are taken from Tiny Cloud (https://www.tiny.cloud/docs/tinymce/6/plugins/#premium-plugins). + * + * @return array The array of plugins. + */ + public static function get_plugins(): array { + return [ + 'advtable', + 'typography', + 'casechange', + 'checklist', + 'editimage', + 'export', + 'footnotes', + 'formatpainter', + 'linkchecker', + 'pageembed', + 'permanentpen', + 'powerpaste', + 'tinymcespellchecker', + 'autocorrect', + 'tableofcontents', + ]; + } + + /** + * Get enabled Tiny Premium plugins. + * + * @return array The array of enabled plugins. + */ + public static function get_enabled_plugins(): array { + $plugins = self::get_plugins(); + $enabledplugins = []; + foreach ($plugins as $plugin) { + if (self::is_plugin_enabled($plugin)) { + $enabledplugins[] = $plugin; + } + } + return $enabledplugins; + } + + /** + * Check if a Tiny Premium plugin is enabled in config. + * + * @param string $plugin The plugin to check. + * @return bool Return true if enabled. + */ + public static function is_plugin_enabled(string $plugin): bool { + $config = get_config('tiny_premium_' . $plugin, 'enabled'); + return ($config == 1); + } + + /** + * Set a new value for a Tiny Premium plugin config. + * + * @param array $data The data to set. + * @param string $plugin The plugin to use. + */ + public static function set_plugin_config(array $data, string $plugin): void { + // Check this is a valid premium plugin. + if (!in_array($plugin, self::get_plugins())) { + return; + } + + $plugin = 'tiny_premium_' . $plugin; + + foreach ($data as $key => $newvalue) { + // Get the old value for the log. + $oldvalue = get_config($plugin, $key) ?? null; + add_to_config_log($key, $oldvalue, $newvalue, $plugin); + + // If we are disabling the plugin, remove it, otherwise, set the new value. + if ($key === 'enabled' && $newvalue == 0) { + unset_config($key, $plugin); + } else { + set_config($key, $newvalue, $plugin); + } + } + } +} diff --git a/lib/editor/tiny/plugins/premium/classes/plugininfo.php b/lib/editor/tiny/plugins/premium/classes/plugininfo.php index 9c7ccdea441..17634a81df8 100644 --- a/lib/editor/tiny/plugins/premium/classes/plugininfo.php +++ b/lib/editor/tiny/plugins/premium/classes/plugininfo.php @@ -19,6 +19,8 @@ namespace tiny_premium; use context; use editor_tiny\editor; use editor_tiny\plugin; +use editor_tiny\plugin_with_configuration; +use tiny_premium\manager; /** * Tiny Premium plugin. @@ -27,7 +29,7 @@ use editor_tiny\plugin; * @copyright 2023 David Woloszyn * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class plugininfo extends plugin { +class plugininfo extends plugin implements plugin_with_configuration { /** * Determine if the plugin should be enabled by checking the capability and if the Tiny Premium API key is set. @@ -46,4 +48,24 @@ class plugininfo extends plugin { ): bool { return has_capability('tiny/premium:accesspremium', $context) && (get_config('tiny_premium', 'apikey') != false); } + + /** + * Get a list of enabled Tiny Premium plugins set by the admin. + * + * @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|null $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 { + return [ + 'premiumplugins' => implode(',', manager::get_enabled_plugins()), + ]; + } } diff --git a/lib/editor/tiny/plugins/premium/db/upgrade.php b/lib/editor/tiny/plugins/premium/db/upgrade.php new file mode 100644 index 00000000000..a9f2cc95d0b --- /dev/null +++ b/lib/editor/tiny/plugins/premium/db/upgrade.php @@ -0,0 +1,55 @@ +. +// + +/** + * Upgrade code for tiny_premium. + * + * @package tiny_premium + * @copyright 2024 David Woloszyn + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Function to upgrade tiny_premium. + * + * @param int $oldversion the version we are upgrading from + * @return bool result + */ +function xmldb_tiny_premium_upgrade($oldversion) { + // Automatically generated Moodle v4.1.0 release upgrade line. + // Put any upgrade step following this. + + // Automatically generated Moodle v4.2.0 release upgrade line. + // Put any upgrade step following this. + + // Automatically generated Moodle v4.3.0 release upgrade line. + // Put any upgrade step following this. + if ($oldversion < 2024042400) { + + // Only enable the premium plugins if we have an API key. + if (!empty(get_config('tiny_premium', 'apikey'))) { + $premiumplugins = \tiny_premium\manager::get_plugins(); + foreach ($premiumplugins as $plugin) { + \tiny_premium\manager::set_plugin_config(['enabled' => 1], $plugin); + }; + } + + upgrade_plugin_savepoint(true, 2024042400, 'tiny', 'premium'); + } + + return true; +} diff --git a/lib/editor/tiny/plugins/premium/lang/en/tiny_premium.php b/lib/editor/tiny/plugins/premium/lang/en/tiny_premium.php index f9843b70337..bddbda2c960 100644 --- a/lib/editor/tiny/plugins/premium/lang/en/tiny_premium.php +++ b/lib/editor/tiny/plugins/premium/lang/en/tiny_premium.php @@ -25,9 +25,28 @@ defined('MOODLE_INTERNAL') || die(); -$string['pluginname'] = 'TinyMCE Premium'; $string['apikey'] = 'API key'; -$string['apikey_desc'] = '

Your API key is available on your Tiny Cloud account page if you have purchased a subscription, or if you are on a free trial.

See the list of available TinyMCE Premium features for Moodle in the documentation TinyMCE editor.

'; +$string['apikey_desc'] = 'Your API key is available on your Tiny Cloud account page if you have purchased a subscription, or if you are on a free trial.'; +$string['emptyapikeywarning'] = 'Enabled TinyMCE Premium plugins will not be available until an API key is added.'; $string['helplinktext'] = 'Premium plugins'; +$string['pluginname'] = 'TinyMCE Premium'; +$string['pluginnotfound'] = 'Tiny Premium plugin {$a} not found.'; $string['premium:accesspremium'] = 'Access TinyMCE Premium features'; -$string['privacy:metadata'] = 'The TinyMCE Premium plugin does not store any personal data.'; +$string['premiumplugin:advtable'] = 'Advanced Table'; +$string['premiumplugin:autocorrect'] = 'Spelling Autocorrect'; +$string['premiumplugin:casechange'] = 'Case Change'; +$string['premiumplugin:checklist'] = 'Checklist'; +$string['premiumplugin:editimage'] = 'Enhanced Image Editing'; +$string['premiumplugin:export'] = 'Export'; +$string['premiumplugin:footnotes'] = 'Footnotes'; +$string['premiumplugin:formatpainter'] = 'Format Painter'; +$string['premiumplugin:linkchecker'] = 'Link Checker'; +$string['premiumplugin:pageembed'] = 'Page Embed'; +$string['premiumplugin:permanentpen'] = 'Permanent Pen'; +$string['premiumplugin:powerpaste'] = 'Powerpaste'; +$string['premiumplugin:tableofcontents'] = 'Table of Contents'; +$string['premiumplugin:tinymcespellchecker'] = 'Spell Checker Pro'; +$string['premiumplugin:typography'] = 'Advanced Typography'; +$string['premiumplugins'] = 'Premium plugins'; +$string['premiumplugins_desc'] = 'Access to TinyMCE Premium plugins requires an API key. Not all listed plugins may be available with your TinyMCE Premium subscription. You can check available plugins on your Tiny Cloud account page.'; +$string['privacy:metadata'] = 'The Tiny premium plugin for TinyMCE does not store any personal data.'; diff --git a/lib/editor/tiny/plugins/premium/pluginsettings.php b/lib/editor/tiny/plugins/premium/pluginsettings.php new file mode 100644 index 00000000000..4111246556f --- /dev/null +++ b/lib/editor/tiny/plugins/premium/pluginsettings.php @@ -0,0 +1,74 @@ +. + +/** + * TinyMCE Premium plugins configuration page. + * + * @package tiny_premium + * @copyright 2024 David Woloszyn + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../../../../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); + +$action = required_param('action', PARAM_ALPHANUMEXT); +$plugin = required_param('plugin', PARAM_ALPHANUMEXT); + +$syscontext = context_system::instance(); +$PAGE->set_url('/lib/editor/tiny/plugins/premium/pluginsettings.php'); +$PAGE->set_context($syscontext); + +require_admin(); +require_sesskey(); + +$return = new moodle_url('/admin/settings.php', ['section' => 'tiny_premium_settings']); + +// Get all Tiny Premium plugins. +$premiumplugins = \tiny_premium\manager::get_plugins(); +if (!in_array($plugin, $premiumplugins)) { + throw new moodle_exception('pluginnotfound', 'tiny_premium', $return, $plugin); +} + +// Get enabled Tiny Premium plugins. +$enabledplugins = \tiny_premium\manager::get_enabled_plugins(); +$pluginname = get_string('premiumplugin:' . $plugin, 'tiny_premium'); + +switch ($action) { + case 'disable': + if (in_array($plugin, $enabledplugins)) { + \tiny_premium\manager::set_plugin_config(['enabled' => 0], $plugin); + + \core\notification::add( + get_string('plugin_disabled', 'core_admin', $pluginname), + \core\notification::SUCCESS + ); + } + break; + + case 'enable': + if (!in_array($plugin, $enabledplugins)) { + \tiny_premium\manager::set_plugin_config(['enabled' => 1], $plugin); + + \core\notification::add( + get_string('plugin_enabled', 'core_admin', $pluginname), + \core\notification::SUCCESS + ); + } + break; +} + +redirect($return); diff --git a/lib/editor/tiny/plugins/premium/readme_moodle.txt b/lib/editor/tiny/plugins/premium/readme_moodle.txt index d0afd906ebb..f175412625e 100644 --- a/lib/editor/tiny/plugins/premium/readme_moodle.txt +++ b/lib/editor/tiny/plugins/premium/readme_moodle.txt @@ -4,9 +4,12 @@ A request to Tiny Cloud is made in the plugin.js file of this plugin. This request passes the Tiny Premium API key as part of a URL. The URL also contains the major version of Tiny and may need to be updated. -The URL looks like this: https://cdn.tiny.cloud/1/YOUR_API_KEY/tinymce/6/plugins.min.js - -Notice that the version (6) is baked into the URL and may need revision. +The URL looks like this: https://cdn.tiny.cloud/1/YOUR_API_KEY/tinymce/VERSION/plugins.min.js When upgrading, check Tiny Cloud's documentation regarding the correct API URL to use. Go to https://www.tiny.cloud/docs/tinymce + +TinyMCE Premium plugins can be individually enabled/disabled by admins. +Each release of TinyMCE may have a different selection of plugins available. +When upgrading, please check the list of available TinyMCE Premium plugins and update the list +with the revisions (lib/editor/tiny/plugins/premium/classes/manager.php). diff --git a/lib/editor/tiny/plugins/premium/settings.php b/lib/editor/tiny/plugins/premium/settings.php index 737ed7ceeaf..128b3debb99 100644 --- a/lib/editor/tiny/plugins/premium/settings.php +++ b/lib/editor/tiny/plugins/premium/settings.php @@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die(); if ($hassiteconfig) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedIf if ($ADMIN->fulltree) { + // Set API key. $setting = new admin_setting_configpasswordunmask( 'tiny_premium/apikey', get_string('apikey', 'tiny_premium'), @@ -35,5 +36,8 @@ if ($hassiteconfig) { '', ); $settings->add($setting); + + // Set individual Tiny Premium plugins. + $settings->add(new \tiny_premium\local\admin_setting_tiny_premium_plugins()); } } diff --git a/lib/editor/tiny/plugins/premium/tests/behat/tiny_premium_settings.feature b/lib/editor/tiny/plugins/premium/tests/behat/tiny_premium_settings.feature new file mode 100644 index 00000000000..e60ce3aee15 --- /dev/null +++ b/lib/editor/tiny/plugins/premium/tests/behat/tiny_premium_settings.feature @@ -0,0 +1,23 @@ +@editor @editor_tiny +Feature: Check the features of the TinyMCE Premium settings + In order to use TinyMCE Premium features + As an admin + I need TinyMCE Premium settings to be configured correctly + + Background: + Given I am logged in as "admin" + And I navigate to "Plugins > Text editors > TinyMCE editor > TinyMCE Premium" in site administration + + @javascript + Scenario: I can see a warning banner when I enable a TinyMCE premium plugin without an API key set + When I click on "Enable Advanced Table" "link" + Then I should see "Advanced Table enabled." + And I should see "Enabled TinyMCE Premium plugins will not be available until an API key is added." + + @javascript + Scenario: I cannot see a warning banner when I enable a TinyMCE premium plugin with an API key set + Given the following config values are set as admin: + | apikey | "123456" | tiny_premium | + When I click on "Enable Advanced Table" "link" + Then I should see "Advanced Table enabled." + And I should not see "Enabled TinyMCE Premium plugins will not be available until an API key is added." diff --git a/lib/editor/tiny/plugins/premium/tests/manager_test.php b/lib/editor/tiny/plugins/premium/tests/manager_test.php new file mode 100644 index 00000000000..bf438e4a1b6 --- /dev/null +++ b/lib/editor/tiny/plugins/premium/tests/manager_test.php @@ -0,0 +1,73 @@ +. + +namespace tiny_premium; + +/** + * Manager tests class for tiny_premium. + * + * @package tiny_premium + * @category test + * @copyright 2024 David Woloszyn + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class manager_test extends \advanced_testcase { + + /** + * Test the getting of all available Tiny Premium plugins. + * + * @covers \tiny_premium\manager + */ + public function test_get_plugins(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Check all Tiny Premium plugins are returned. + $premiumplugins = manager::get_plugins(); + $this->assertCount(15, $premiumplugins); + } + + /** + * Test the getting and setting of enabled Tiny Premium plugins. + * + * @covers \tiny_premium\manager + */ + public function test_get_and_set_enabled_plugins(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Check all enabled Tiny Premium plugins are returned (all disabled by default). + $enabledpremiumplugins = manager::get_enabled_plugins(); + $this->assertCount(0, $enabledpremiumplugins); + + // Enable a couple premium plugins. + manager::set_plugin_config(['enabled' => 1], 'advtable'); + manager::set_plugin_config(['enabled' => 1], 'formatpainter'); + // Check the premium plugins are enabled. + $enabledpremiumplugins = manager::get_enabled_plugins(); + $this->assertCount(2, $enabledpremiumplugins); + $this->assertTrue(manager::is_plugin_enabled('advtable')); + $this->assertTrue(manager::is_plugin_enabled('formatpainter')); + + // Disable a premium plugin. + manager::set_plugin_config(['enabled' => 0], 'advtable'); + // Check the correct premium plugins are enabled. + $enabledpremiumplugins = manager::get_enabled_plugins(); + $this->assertCount(1, $enabledpremiumplugins); + $this->assertFalse(manager::is_plugin_enabled('advtable')); + $this->assertTrue(manager::is_plugin_enabled('formatpainter')); + } +} diff --git a/lib/editor/tiny/plugins/premium/version.php b/lib/editor/tiny/plugins/premium/version.php index e6caa3110eb..e7341171a24 100644 --- a/lib/editor/tiny/plugins/premium/version.php +++ b/lib/editor/tiny/plugins/premium/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024042200; +$plugin->version = 2024043000; $plugin->requires = 2024041600; $plugin->component = 'tiny_premium';