diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php
index 727e5694385..92079d2fe8b 100644
--- a/lib/classes/plugin_manager.php
+++ b/lib/classes/plugin_manager.php
@@ -2029,6 +2029,7 @@ class core_plugin_manager {
),
'tiny' => [
+ 'h5p',
],
'tinymce' => array(
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js b/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js
new file mode 100644
index 00000000000..89ae0b09c20
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js
@@ -0,0 +1,3 @@
+define("tiny_h5p/commands",["exports","editor_tiny/utils","./ui","core/str","./common"],(function(_exports,_utils,_ui,_str,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0;_exports.getSetup=async()=>{const[buttonText,buttonImage]=await Promise.all([(0,_str.get_string)("buttontitle",_common.component),(0,_utils.getButtonImage)("icon",_common.component)]);return editor=>{editor.ui.registry.addIcon(_common.icon,buttonImage.html),editor.ui.registry.addToggleButton(_common.buttonName,{icon:_common.icon,tooltip:buttonText,onAction:()=>(0,_ui.handleAction)(editor),onSetup:api=>{api.setActive(editor.formatter.match("h5p"));const changed=editor.formatter.formatChanged("h5p",(state=>api.setActive(state)));return()=>changed.unbind()}}),editor.ui.registry.addMenuItem(_common.buttonName,{icon:_common.icon,text:buttonText,onAction:()=>(0,_ui.handleAction)(editor)})}}}));
+
+//# sourceMappingURL=commands.min.js.map
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js.map b/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js.map
new file mode 100644
index 00000000000..fd2305c7fa0
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/commands.min.js.map
@@ -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 .\n\n/**\n * Tiny H5P Content configuration.\n *\n * @module tiny_h5p/commands\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getButtonImage} from 'editor_tiny/utils';\nimport {handleAction} from './ui';\nimport {get_string as getString} from 'core/str';\nimport {\n component,\n buttonName,\n icon,\n} from './common';\n\nexport const getSetup = async() => {\n const [\n buttonText,\n buttonImage,\n ] = await Promise.all([\n getString('buttontitle', component),\n getButtonImage('icon', component),\n ]);\n\n return (editor) => {\n // Register the H5P Icon.\n editor.ui.registry.addIcon(icon, buttonImage.html);\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing H5P element it will show as toggled on.\n editor.ui.registry.addToggleButton(buttonName, {\n icon,\n tooltip: buttonText,\n onAction: () => handleAction(editor),\n onSetup: (api) => {\n // Set the button to be active if the current selection matches the h5p formatter registered above during PreInit.\n api.setActive(editor.formatter.match('h5p'));\n const changed = editor.formatter.formatChanged('h5p', (state) => api.setActive(state));\n return () => changed.unbind();\n },\n });\n\n // Add the H5P Menu Item.\n // This allows it to be added to a standard menu, or a context menu.\n editor.ui.registry.addMenuItem(buttonName, {\n icon,\n text: buttonText,\n onAction: () => handleAction(editor),\n });\n };\n};\n"],"names":["async","buttonText","buttonImage","Promise","all","component","editor","ui","registry","addIcon","icon","html","addToggleButton","buttonName","tooltip","onAction","onSetup","api","setActive","formatter","match","changed","formatChanged","state","unbind","addMenuItem","text"],"mappings":"uOAgCwBA,gBAEhBC,WACAC,mBACMC,QAAQC,IAAI,EAClB,mBAAU,cAAeC,oBACzB,yBAAe,OAAQA,4BAGnBC,SAEJA,OAAOC,GAAGC,SAASC,QAAQC,aAAMR,YAAYS,MAI7CL,OAAOC,GAAGC,SAASI,gBAAgBC,mBAAY,CAC3CH,KAAAA,aACAI,QAASb,WACTc,SAAU,KAAM,oBAAaT,QAC7BU,QAAUC,MAENA,IAAIC,UAAUZ,OAAOa,UAAUC,MAAM,cAC/BC,QAAUf,OAAOa,UAAUG,cAAc,OAAQC,OAAUN,IAAIC,UAAUK,eACxE,IAAMF,QAAQG,YAM7BlB,OAAOC,GAAGC,SAASiB,YAAYZ,mBAAY,CACvCH,KAAAA,aACAgB,KAAMzB,WACNc,SAAU,KAAM,oBAAaT"}
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/common.min.js b/lib/editor/tiny/plugins/h5p/amd/build/common.min.js
new file mode 100644
index 00000000000..0e5d22803a8
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/common.min.js
@@ -0,0 +1,3 @@
+define("tiny_h5p/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={pluginName:"tiny_h5p/plugin",component:"tiny_h5p",buttonName:"tiny_h5p",icon:"tiny_h5p"},_exports.default}));
+
+//# sourceMappingURL=common.min.js.map
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/common.min.js.map b/lib/editor/tiny/plugins/h5p/amd/build/common.min.js.map
new file mode 100644
index 00000000000..ff892befb84
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/common.min.js.map
@@ -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 .\n\n/**\n * Tiny H5P Common values.\n *\n * @module tiny_h5p/common\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n pluginName: 'tiny_h5p/plugin',\n component: 'tiny_h5p',\n buttonName: 'tiny_h5p',\n icon: 'tiny_h5p',\n};\n"],"names":["pluginName","component","buttonName","icon"],"mappings":"iKAuBe,CACXA,WAAY,kBACZC,UAAW,WACXC,WAAY,WACZC,KAAM"}
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/configuration.min.js b/lib/editor/tiny/plugins/h5p/amd/build/configuration.min.js
new file mode 100644
index 00000000000..28f82582890
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/configuration.min.js
@@ -0,0 +1,3 @@
+define("tiny_h5p/configuration",["exports","./common","editor_tiny/utils"],(function(_exports,_common,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;_exports.configure=instanceConfig=>({toolbar:(0,_utils.addToolbarButton)(instanceConfig.toolbar,"content",_common.component),menu:(0,_utils.addMenubarItem)(instanceConfig.menu,"insert",_common.component)})}));
+
+//# sourceMappingURL=configuration.min.js.map
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/configuration.min.js.map b/lib/editor/tiny/plugins/h5p/amd/build/configuration.min.js.map
new file mode 100644
index 00000000000..98016c9056e
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/configuration.min.js.map
@@ -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 .\n\n/**\n * Tiny H5P Content configuration.\n *\n * @module tiny_h5p/configuration\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {component as buttonName} from './common';\nimport {\n addMenubarItem,\n addToolbarButton,\n} from 'editor_tiny/utils';\n\nexport const configure = (instanceConfig) => {\n // Update the instance configuration to add the H5P menu option to the menus and toolbars.\n return {\n toolbar: addToolbarButton(instanceConfig.toolbar, 'content', buttonName),\n menu: addMenubarItem(instanceConfig.menu, 'insert', buttonName),\n };\n};\n"],"names":["instanceConfig","toolbar","buttonName","menu"],"mappings":"mNA6B0BA,iBAEf,CACHC,SAAS,2BAAiBD,eAAeC,QAAS,UAAWC,mBAC7DC,MAAM,yBAAeH,eAAeG,KAAM,SAAUD"}
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/filtercontent.min.js b/lib/editor/tiny/plugins/h5p/amd/build/filtercontent.min.js
new file mode 100644
index 00000000000..8078589c5be
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/filtercontent.min.js
@@ -0,0 +1,3 @@
+define("tiny_h5p/filtercontent",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setup=void 0;_exports.setup=async editor=>{editor.on("PreInit",(()=>{editor.formatter.register("h5p",{inline:"div",classes:"h5p-placeholder"})})),editor.on("SetContent",(()=>{editor.getBody().querySelectorAll(".h5p-placeholder:not([contenteditable])").forEach((node=>{node.contentEditable=!1}))}))}}));
+
+//# sourceMappingURL=filtercontent.min.js.map
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/filtercontent.min.js.map b/lib/editor/tiny/plugins/h5p/amd/build/filtercontent.min.js.map
new file mode 100644
index 00000000000..f2cb6c50b9b
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/filtercontent.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"filtercontent.min.js","sources":["../src/filtercontent.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 H5P Content configuration.\n *\n * @module tiny_h5p/filtercontent\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport const setup = async(editor) => {\n // Register the H5P Formatter for use in all buttons.\n editor.on('PreInit', () => {\n editor.formatter.register('h5p', {\n inline: 'div',\n classes: 'h5p-placeholder',\n });\n });\n\n editor.on('SetContent', () => {\n // Listen to the SetContent event on the editor and update any h5p-placeholder to not be editable.\n // Doing this means that the inner content of the placeholder cannot be changed without using the dialogue.\n // The SetContent event is called whenever content is changed by actions such as initial load, paste, undo, etc.\n editor.getBody().querySelectorAll('.h5p-placeholder:not([contenteditable])').forEach((node) => {\n node.contentEditable = false;\n });\n });\n};\n"],"names":["async","editor","on","formatter","register","inline","classes","getBody","querySelectorAll","forEach","node","contentEditable"],"mappings":"6JAuBqBA,MAAAA,SAEjBC,OAAOC,GAAG,WAAW,KACjBD,OAAOE,UAAUC,SAAS,MAAO,CAC7BC,OAAQ,MACRC,QAAS,uBAIjBL,OAAOC,GAAG,cAAc,KAIpBD,OAAOM,UAAUC,iBAAiB,2CAA2CC,SAASC,OAClFA,KAAKC,iBAAkB"}
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/modal.min.js b/lib/editor/tiny/plugins/h5p/amd/build/modal.min.js
new file mode 100644
index 00000000000..cc5ef830e94
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/modal.min.js
@@ -0,0 +1,3 @@
+define("tiny_h5p/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 H5PModal=(_defineProperty(_class=class extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}},"TYPE","tiny_h5p/modal"),_defineProperty(_class,"TEMPLATE","tiny_h5p/modal"),_class);_modal_registry.default.register(H5PModal.TYPE,H5PModal,H5PModal.TEMPLATE);var _default=H5PModal;return _exports.default=_default,_exports.default}));
+
+//# sourceMappingURL=modal.min.js.map
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/modal.min.js.map b/lib/editor/tiny/plugins/h5p/amd/build/modal.min.js.map
new file mode 100644
index 00000000000..0a082c4bb03
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/modal.min.js.map
@@ -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 .\n\n/**\n * H5P Management Modal for Tiny.\n *\n * @module tiny_h5p/modal\n * @copyright 2022 Andrew Lyons \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 H5PModal = class extends Modal {\n static TYPE = 'tiny_h5p/modal';\n\n static TEMPLATE = 'tiny_h5p/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(H5PModal.TYPE, H5PModal, H5PModal.TEMPLATE);\n\nexport default H5PModal;\n"],"names":["H5PModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","register","TYPE","TEMPLATE"],"mappings":"uiBA0BMA,iCAAW,cAAcC,eAK3BC,+BAEUA,8BAGDC,2BACAC,iCAVK,oDAEI,kDAYRC,SAASL,SAASM,KAAMN,SAAUA,SAASO,uBAE1CP"}
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/options.min.js b/lib/editor/tiny/plugins/h5p/amd/build/options.min.js
new file mode 100644
index 00000000000..52511a008c3
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/options.min.js
@@ -0,0 +1,11 @@
+define("tiny_h5p/options",["exports","editor_tiny/options","./common"],(function(_exports,_options,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.getPermissions=void 0;
+/**
+ * Options helper for Tiny H5P plugin.
+ *
+ * @module tiny_h5p/options
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+const permissionsName=(0,_options.getPluginOptionName)(_common.pluginName,"permissions");_exports.register=editor=>{(0,editor.options.register)(permissionsName,{processor:"object",default:{upload:!1,embed:!1}})};_exports.getPermissions=editor=>editor.options.get(permissionsName)}));
+
+//# sourceMappingURL=options.min.js.map
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/options.min.js.map b/lib/editor/tiny/plugins/h5p/amd/build/options.min.js.map
new file mode 100644
index 00000000000..d2346ff9373
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/options.min.js.map
@@ -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 .\n\n/**\n * Options helper for Tiny H5P plugin.\n *\n * @module tiny_h5p/options\n * @copyright 2022 Andrew Lyons \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 './common';\n\nconst permissionsName = getPluginOptionName(pluginName, 'permissions');\n\n/**\n * Register the options for the Tiny H5P 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 upload: false,\n embed: false,\n },\n });\n};\n\n/**\n * Get the permissions configuration for the Tiny H5P plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getPermissions = (editor) => editor.options.get(permissionsName);\n"],"names":["permissionsName","pluginName","editor","registerOption","options","register","processor","upload","embed","get"],"mappings":";;;;;;;;MA0BMA,iBAAkB,gCAAoBC,mBAAY,iCAO/BC,UAGrBC,EAFuBD,OAAOE,QAAQC,UAEvBL,gBAAiB,CAC5BM,UAAW,iBACA,CACPC,QAAQ,EACRC,OAAO,8BAWYN,QAAWA,OAAOE,QAAQK,IAAIT"}
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/plugin.min.js b/lib/editor/tiny/plugins/h5p/amd/build/plugin.min.js
new file mode 100644
index 00000000000..2a4e8972b59
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/plugin.min.js
@@ -0,0 +1,10 @@
+define("tiny_h5p/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./common","./filtercontent","./commands","./configuration","./options"],(function(_exports,_loader,_utils,_common,FilterContent,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 H5P plugin for Moodle.
+ *
+ * @module tiny_h5p/plugin
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,FilterContent=_interopRequireWildcard(FilterContent),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),FilterContent.setup(editor),setupCommands(editor),pluginMetadata))),resolve(["".concat(_common.component,"/plugin"),Configuration])}));return _exports.default=_default,_exports.default}));
+
+//# sourceMappingURL=plugin.min.js.map
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/plugin.min.js.map b/lib/editor/tiny/plugins/h5p/amd/build/plugin.min.js.map
new file mode 100644
index 00000000000..caf967b7e4b
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/plugin.min.js.map
@@ -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 .\n\n/**\n * Tiny H5P plugin for Moodle.\n *\n * @module tiny_h5p/plugin\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {getTinyMCE} from 'editor_tiny/loader';\nimport {getPluginMetadata} from 'editor_tiny/utils';\n\nimport {component, pluginName} from './common';\nimport * as FilterContent from './filtercontent';\nimport * as Commands from './commands';\nimport * as Configuration from './configuration';\nimport * as Options from './options';\n\n// Setup the H5P Plugin to add a button and menu option.\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 // Note: The PluginManager.add function does not accept a Promise.\n // Any asynchronous code must be run before this point.\n tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {\n // Register options.\n Options.register(editor);\n\n // Setup the Formatter.\n FilterContent.setup(editor);\n\n // Setup the Commands (buttons, menu items, and so on).\n setupCommands(editor);\n\n return pluginMetadata;\n });\n\n // Resolve the H5P 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","FilterContent","setup","resolve","Configuration"],"mappings":";;;;;;;gSAgCe,IAAIA,SAAQC,MAAAA,gBAEnBC,QACAC,cACAC,sBACMJ,QAAQK,IAAI,EAClB,wBACAC,SAASC,YACT,4BAAkBC,kBAAWC,sBAKjCP,QAAQQ,cAAcC,cAAOH,8BAAqBI,SAE9CC,QAAQC,SAASF,QAGjBG,cAAcC,MAAMJ,QAGpBT,cAAcS,QAEPR,kBAIXa,QAAQ,WAAIT,6BAAoBU"}
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/ui.min.js b/lib/editor/tiny/plugins/h5p/amd/build/ui.min.js
new file mode 100644
index 00000000000..6e162626d39
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/ui.min.js
@@ -0,0 +1,10 @@
+define("tiny_h5p/ui",["exports","editor_tiny/utils","./common","./options","core/normalise","core/templates","tiny_h5p/modal","core/modal_events","core/modal_factory"],(function(_exports,_utils,_common,_options,_normalise,_templates,_modal,_modal_events,_modal_factory){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
+/**
+ * Tiny H5P Content configuration.
+ *
+ * @module tiny_h5p/commands
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.handleAction=void 0,_modal=_interopRequireDefault(_modal),_modal_events=_interopRequireDefault(_modal_events),_modal_factory=_interopRequireDefault(_modal_factory);let openingSelection=null;_exports.handleAction=editor=>{openingSelection=editor.selection.getBookmark(),displayDialogue(editor)};const getTemplateContext=(editor,data)=>{var _permissions$upload,_permissions$embed,_data$url;const permissions=(0,_options.getPermissions)(editor),canUpload=null!==(_permissions$upload=permissions.upload)&&void 0!==_permissions$upload&&_permissions$upload,canEmbed=null!==(_permissions$embed=permissions.embed)&&void 0!==_permissions$embed&&_permissions$embed,canUploadAndEmbed=canUpload&&canEmbed;return Object.assign({},{elementid:editor.id,canUpload:canUpload,canEmbed:canEmbed,canUploadAndEmbed:canUploadAndEmbed,showOptions:!1,fileURL:null!==(_data$url=null==data?void 0:data.url)&&void 0!==_data$url?_data$url:""},data)},handleDialogueSubmission=async(editor,modal,data)=>{const form=(0,_normalise.getList)(modal.getRoot())[0].querySelector("form");if(!form)return void displayDialogue(editor,Object.assign({},data));const submittedUrl=form.querySelector('input[name="url"]').value,url=((form,submittedUrl,permissions)=>{if(!submittedUrl)return null;const url=new URL(submittedUrl);return null!=permissions&&permissions.upload&&form.querySelector('[name="download"]').checked&&url.searchParams.append("export",1),null!=permissions&&permissions.embed&&form.querySelector('[name="embed"]').checked&&url.searchParams.append("embed",1),form.querySelector('[name="copyright"]').checked&&url.searchParams.append("copyright",1),url})(form,submittedUrl,(0,_options.getPermissions)(editor));if(!url)return void displayDialogue(editor,Object.assign({},data,{url:submittedUrl,invalidUrl:!0}));const content=await(0,_templates.renderForPromise)("".concat(_common.component,"/content"),{url:url.toString()});editor.selection.moveToBookmark(openingSelection),editor.execCommand("mceInsertContent",!1,content.html),editor.selection.moveToBookmark(openingSelection)},getCurrentH5PData=currentH5P=>{const data={};let url;try{url=new URL(currentH5P.textContent)}catch(error){return data}return url.searchParams.has("export")&&(data.download=!0,data.showOptions=!0,url.searchParams.delete("export")),url.searchParams.has("embed")&&(data.embed=!0,data.showOptions=!0,url.searchParams.delete("embed")),url.searchParams.has("copyright")&&(data.copyright=!0,data.showOptions=!0,url.searchParams.delete("copyright")),data.url=url.toString(),data},displayDialogue=async function(editor){let data=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const selection=editor.selection.getNode(),currentH5P=selection.closest(".h5p-placeholder");currentH5P&&Object.assign(data,getCurrentH5PData(currentH5P));const modal=await _modal_factory.default.create({type:_modal.default.TYPE,templateContext:getTemplateContext(editor,data),large:!0});modal.show();const $root=modal.getRoot(),root=$root[0];$root.on(_modal_events.default.save,((event,modal)=>{handleDialogueSubmission(editor,modal,data)})),root.addEventListener("click",(e=>{e.target.closest('[data-target="filepicker"]')&&(0,_utils.displayFilepicker)(editor,"h5p").then((params=>{if(""!==params.url){root.querySelector('form input[name="url"]').value=params.url}return params})).catch()}))}}));
+
+//# sourceMappingURL=ui.min.js.map
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/build/ui.min.js.map b/lib/editor/tiny/plugins/h5p/amd/build/ui.min.js.map
new file mode 100644
index 00000000000..33bfbb9230c
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/build/ui.min.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"ui.min.js","sources":["../src/ui.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 H5P Content configuration.\n *\n * @module tiny_h5p/commands\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {component} from './common';\nimport {getPermissions} from './options';\n\nimport {getList} from 'core/normalise';\nimport {renderForPromise} from 'core/templates';\nimport Modal from 'tiny_h5p/modal';\nimport ModalEvents from 'core/modal_events';\nimport ModalFactory from 'core/modal_factory';\n\nlet openingSelection = null;\n\nexport const handleAction = (editor) => {\n openingSelection = editor.selection.getBookmark();\n displayDialogue(editor);\n};\n\n/**\n * Get the template context for the dialogue.\n *\n * @param {Editor} editor\n * @param {object} data\n * @returns {object} data\n */\nconst getTemplateContext = (editor, data) => {\n const permissions = getPermissions(editor);\n\n const canUpload = permissions.upload ?? false;\n const canEmbed = permissions.embed ?? false;\n const canUploadAndEmbed = canUpload && canEmbed;\n\n return Object.assign({}, {\n elementid: editor.id,\n canUpload,\n canEmbed,\n canUploadAndEmbed,\n showOptions: false,\n fileURL: data?.url ?? '',\n }, data);\n};\n\n/**\n * Get the URL from the submitted form.\n *\n * @param {FormNode} form\n * @param {string} submittedUrl\n * @param {object} permissions\n * @returns {URL|null}\n */\nconst getUrlFromSubmission = (form, submittedUrl, permissions) => {\n if (!submittedUrl) {\n return null;\n }\n\n // Generate a URL Object for the submitted URL.\n const url = new URL(submittedUrl);\n\n if (permissions?.upload) {\n if (form.querySelector('[name=\"download\"]').checked) {\n url.searchParams.append('export', 1);\n }\n }\n\n if (permissions?.embed) {\n if (form.querySelector('[name=\"embed\"]').checked) {\n url.searchParams.append('embed', 1);\n }\n }\n if (form.querySelector('[name=\"copyright\"]').checked) {\n url.searchParams.append('copyright', 1);\n }\n\n return url;\n};\n\nconst handleDialogueSubmission = async(editor, modal, data) => {\n const form = getList(modal.getRoot())[0].querySelector('form');\n if (!form) {\n // The form couldn't be found, which is weird.\n // This should not happen.\n // Display the dialogue again\n displayDialogue(editor, Object.assign({}, data));\n return;\n }\n\n // Get the URL from the submitted form.\n const submittedUrl = form.querySelector('input[name=\"url\"]').value;\n const url = getUrlFromSubmission(form, submittedUrl, getPermissions(editor));\n\n if (!url) {\n // The URL is invalid.\n // Fill it in and represent the dialogue with an error.\n displayDialogue(editor, Object.assign({}, data, {\n url: submittedUrl,\n invalidUrl: true,\n }));\n return;\n }\n\n\n const content = await renderForPromise(`${component}/content`, {\n url: url.toString(),\n });\n\n editor.selection.moveToBookmark(openingSelection);\n editor.execCommand('mceInsertContent', false, content.html);\n editor.selection.moveToBookmark(openingSelection);\n};\n\nconst getCurrentH5PData = (currentH5P) => {\n const data = {};\n let url;\n try {\n url = new URL(currentH5P.textContent);\n } catch (error) {\n return data;\n }\n\n if (url.searchParams.has('export')) {\n data.download = true;\n data.showOptions = true;\n url.searchParams.delete('export');\n }\n\n if (url.searchParams.has('embed')) {\n data.embed = true;\n data.showOptions = true;\n url.searchParams.delete('embed');\n }\n\n if (url.searchParams.has('copyright')) {\n data.copyright = true;\n data.showOptions = true;\n url.searchParams.delete('copyright');\n }\n\n data.url = url.toString();\n\n return data;\n};\n\nconst displayDialogue = async(editor, data = {}) => {\n const selection = editor.selection.getNode();\n const currentH5P = selection.closest('.h5p-placeholder');\n if (currentH5P) {\n Object.assign(data, getCurrentH5PData(currentH5P));\n }\n\n const modal = await ModalFactory.create({\n type: Modal.TYPE,\n templateContext: getTemplateContext(editor, data),\n large: true,\n });\n modal.show();\n\n const $root = modal.getRoot();\n const root = $root[0];\n $root.on(ModalEvents.save, (event, modal) => {\n handleDialogueSubmission(editor, modal, data);\n });\n\n root.addEventListener('click', (e) => {\n const filepickerButton = e.target.closest('[data-target=\"filepicker\"]');\n if (filepickerButton) {\n displayFilepicker(editor, 'h5p').then((params) => {\n if (params.url !== '') {\n const input = root.querySelector('form input[name=\"url\"]');\n input.value = params.url;\n }\n return params;\n })\n .catch();\n }\n });\n};\n"],"names":["openingSelection","editor","selection","getBookmark","displayDialogue","getTemplateContext","data","permissions","canUpload","upload","canEmbed","embed","canUploadAndEmbed","Object","assign","elementid","id","showOptions","fileURL","url","handleDialogueSubmission","async","modal","form","getRoot","querySelector","submittedUrl","value","URL","checked","searchParams","append","getUrlFromSubmission","invalidUrl","content","component","toString","moveToBookmark","execCommand","html","getCurrentH5PData","currentH5P","textContent","error","has","download","delete","copyright","getNode","closest","ModalFactory","create","type","Modal","TYPE","templateContext","large","show","$root","root","on","ModalEvents","save","event","addEventListener","e","target","then","params","catch"],"mappings":";;;;;;;8OAiCIA,iBAAmB,2BAEMC,SACzBD,iBAAmBC,OAAOC,UAAUC,cACpCC,gBAAgBH,eAUdI,mBAAqB,CAACJ,OAAQK,mEAC1BC,aAAc,2BAAeN,QAE7BO,sCAAYD,YAAYE,2DACxBC,oCAAWH,YAAYI,wDACvBC,kBAAoBJ,WAAaE,gBAEhCG,OAAOC,OAAO,GAAI,CACrBC,UAAWd,OAAOe,GAClBR,UAAAA,UACAE,SAAAA,SACAE,kBAAAA,kBACAK,aAAa,EACbC,0BAASZ,MAAAA,YAAAA,KAAMa,mCAAO,IACvBb,OAqCDc,yBAA2BC,MAAMpB,OAAQqB,MAAOhB,cAC5CiB,MAAO,sBAAQD,MAAME,WAAW,GAAGC,cAAc,YAClDF,iBAIDnB,gBAAgBH,OAAQY,OAAOC,OAAO,GAAIR,aAKxCoB,aAAeH,KAAKE,cAAc,qBAAqBE,MACvDR,IAtCmB,EAACI,KAAMG,aAAcnB,mBACzCmB,oBACM,WAILP,IAAM,IAAIS,IAAIF,qBAEhBnB,MAAAA,aAAAA,YAAaE,QACTc,KAAKE,cAAc,qBAAqBI,SACxCV,IAAIW,aAAaC,OAAO,SAAU,GAItCxB,MAAAA,aAAAA,YAAaI,OACTY,KAAKE,cAAc,kBAAkBI,SACrCV,IAAIW,aAAaC,OAAO,QAAS,GAGrCR,KAAKE,cAAc,sBAAsBI,SACzCV,IAAIW,aAAaC,OAAO,YAAa,GAGlCZ,KAeKa,CAAqBT,KAAMG,cAAc,2BAAezB,aAE/DkB,gBAGDf,gBAAgBH,OAAQY,OAAOC,OAAO,GAAIR,KAAM,CAC5Ca,IAAKO,aACLO,YAAY,WAMdC,cAAgB,yCAAoBC,8BAAqB,CAC3DhB,IAAKA,IAAIiB,aAGbnC,OAAOC,UAAUmC,eAAerC,kBAChCC,OAAOqC,YAAY,oBAAoB,EAAOJ,QAAQK,MACtDtC,OAAOC,UAAUmC,eAAerC,mBAG9BwC,kBAAqBC,mBACjBnC,KAAO,OACTa,QAEAA,IAAM,IAAIS,IAAIa,WAAWC,aAC3B,MAAOC,cACErC,YAGPa,IAAIW,aAAac,IAAI,YACrBtC,KAAKuC,UAAW,EAChBvC,KAAKW,aAAc,EACnBE,IAAIW,aAAagB,OAAO,WAGxB3B,IAAIW,aAAac,IAAI,WACrBtC,KAAKK,OAAQ,EACbL,KAAKW,aAAc,EACnBE,IAAIW,aAAagB,OAAO,UAGxB3B,IAAIW,aAAac,IAAI,eACrBtC,KAAKyC,WAAY,EACjBzC,KAAKW,aAAc,EACnBE,IAAIW,aAAagB,OAAO,cAG5BxC,KAAKa,IAAMA,IAAIiB,WAER9B,MAGLF,gBAAkBiB,eAAMpB,YAAQK,4DAAO,SACnCJ,UAAYD,OAAOC,UAAU8C,UAC7BP,WAAavC,UAAU+C,QAAQ,oBACjCR,YACA5B,OAAOC,OAAOR,KAAMkC,kBAAkBC,mBAGpCnB,YAAc4B,uBAAaC,OAAO,CACpCC,KAAMC,eAAMC,KACZC,gBAAiBlD,mBAAmBJ,OAAQK,MAC5CkD,OAAO,IAEXlC,MAAMmC,aAEAC,MAAQpC,MAAME,UACdmC,KAAOD,MAAM,GACnBA,MAAME,GAAGC,sBAAYC,MAAM,CAACC,MAAOzC,SAC/BF,yBAAyBnB,OAAQqB,MAAOhB,SAG5CqD,KAAKK,iBAAiB,SAAUC,IACHA,EAAEC,OAAOjB,QAAQ,4DAEpBhD,OAAQ,OAAOkE,MAAMC,YAChB,KAAfA,OAAOjD,IAAY,CACLwC,KAAKlC,cAAc,0BAC3BE,MAAQyC,OAAOjD,WAElBiD,UAENC"}
\ No newline at end of file
diff --git a/lib/editor/tiny/plugins/h5p/amd/src/commands.js b/lib/editor/tiny/plugins/h5p/amd/src/commands.js
new file mode 100644
index 00000000000..4c487525491
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/src/commands.js
@@ -0,0 +1,68 @@
+// 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 .
+
+/**
+ * Tiny H5P Content configuration.
+ *
+ * @module tiny_h5p/commands
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import {getButtonImage} from 'editor_tiny/utils';
+import {handleAction} from './ui';
+import {get_string as getString} from 'core/str';
+import {
+ component,
+ buttonName,
+ icon,
+} from './common';
+
+export const getSetup = async() => {
+ const [
+ buttonText,
+ buttonImage,
+ ] = await Promise.all([
+ getString('buttontitle', component),
+ getButtonImage('icon', component),
+ ]);
+
+ return (editor) => {
+ // Register the H5P Icon.
+ editor.ui.registry.addIcon(icon, buttonImage.html);
+
+ // Register the Menu Button as a toggle.
+ // This means that when highlighted over an existing H5P element it will show as toggled on.
+ editor.ui.registry.addToggleButton(buttonName, {
+ icon,
+ tooltip: buttonText,
+ onAction: () => handleAction(editor),
+ onSetup: (api) => {
+ // Set the button to be active if the current selection matches the h5p formatter registered above during PreInit.
+ api.setActive(editor.formatter.match('h5p'));
+ const changed = editor.formatter.formatChanged('h5p', (state) => api.setActive(state));
+ return () => changed.unbind();
+ },
+ });
+
+ // Add the H5P Menu Item.
+ // This allows it to be added to a standard menu, or a context menu.
+ editor.ui.registry.addMenuItem(buttonName, {
+ icon,
+ text: buttonText,
+ onAction: () => handleAction(editor),
+ });
+ };
+};
diff --git a/lib/editor/tiny/plugins/h5p/amd/src/common.js b/lib/editor/tiny/plugins/h5p/amd/src/common.js
new file mode 100644
index 00000000000..040545ace20
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/src/common.js
@@ -0,0 +1,29 @@
+// 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 .
+
+/**
+ * Tiny H5P Common values.
+ *
+ * @module tiny_h5p/common
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+export default {
+ pluginName: 'tiny_h5p/plugin',
+ component: 'tiny_h5p',
+ buttonName: 'tiny_h5p',
+ icon: 'tiny_h5p',
+};
diff --git a/lib/editor/tiny/plugins/h5p/amd/src/configuration.js b/lib/editor/tiny/plugins/h5p/amd/src/configuration.js
new file mode 100644
index 00000000000..0d2a91372e4
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/src/configuration.js
@@ -0,0 +1,36 @@
+// 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 .
+
+/**
+ * Tiny H5P Content configuration.
+ *
+ * @module tiny_h5p/configuration
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import {component as buttonName} from './common';
+import {
+ addMenubarItem,
+ addToolbarButton,
+} from 'editor_tiny/utils';
+
+export const configure = (instanceConfig) => {
+ // Update the instance configuration to add the H5P menu option to the menus and toolbars.
+ return {
+ toolbar: addToolbarButton(instanceConfig.toolbar, 'content', buttonName),
+ menu: addMenubarItem(instanceConfig.menu, 'insert', buttonName),
+ };
+};
diff --git a/lib/editor/tiny/plugins/h5p/amd/src/filtercontent.js b/lib/editor/tiny/plugins/h5p/amd/src/filtercontent.js
new file mode 100644
index 00000000000..69f35cbdc15
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/src/filtercontent.js
@@ -0,0 +1,41 @@
+// 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 .
+
+/**
+ * Tiny H5P Content configuration.
+ *
+ * @module tiny_h5p/filtercontent
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+export const setup = async(editor) => {
+ // Register the H5P Formatter for use in all buttons.
+ editor.on('PreInit', () => {
+ editor.formatter.register('h5p', {
+ inline: 'div',
+ classes: 'h5p-placeholder',
+ });
+ });
+
+ editor.on('SetContent', () => {
+ // Listen to the SetContent event on the editor and update any h5p-placeholder to not be editable.
+ // Doing this means that the inner content of the placeholder cannot be changed without using the dialogue.
+ // The SetContent event is called whenever content is changed by actions such as initial load, paste, undo, etc.
+ editor.getBody().querySelectorAll('.h5p-placeholder:not([contenteditable])').forEach((node) => {
+ node.contentEditable = false;
+ });
+ });
+};
diff --git a/lib/editor/tiny/plugins/h5p/amd/src/modal.js b/lib/editor/tiny/plugins/h5p/amd/src/modal.js
new file mode 100644
index 00000000000..e3798a5bdf8
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/src/modal.js
@@ -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 .
+
+/**
+ * H5P Management Modal for Tiny.
+ *
+ * @module tiny_h5p/modal
+ * @copyright 2022 Andrew Lyons
+ * @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 H5PModal = class extends Modal {
+ static TYPE = 'tiny_h5p/modal';
+
+ static TEMPLATE = 'tiny_h5p/modal';
+
+ registerEventListeners() {
+ // Call the parent registration.
+ super.registerEventListeners();
+
+ // Register to close on save/cancel.
+ this.registerCloseOnSave();
+ this.registerCloseOnCancel();
+ }
+};
+
+ModalRegistry.register(H5PModal.TYPE, H5PModal, H5PModal.TEMPLATE);
+
+export default H5PModal;
diff --git a/lib/editor/tiny/plugins/h5p/amd/src/options.js b/lib/editor/tiny/plugins/h5p/amd/src/options.js
new file mode 100644
index 00000000000..8a32b20909f
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/src/options.js
@@ -0,0 +1,52 @@
+// 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 .
+
+/**
+ * Options helper for Tiny H5P plugin.
+ *
+ * @module tiny_h5p/options
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import {getPluginOptionName} from 'editor_tiny/options';
+import {pluginName} from './common';
+
+const permissionsName = getPluginOptionName(pluginName, 'permissions');
+
+/**
+ * Register the options for the Tiny H5P plugin.
+ *
+ * @param {TinyMCE} editor
+ */
+export const register = (editor) => {
+ const registerOption = editor.options.register;
+
+ registerOption(permissionsName, {
+ processor: 'object',
+ "default": {
+ upload: false,
+ embed: false,
+ },
+ });
+};
+
+/**
+ * Get the permissions configuration for the Tiny H5P plugin.
+ *
+ * @param {TinyMCE} editor
+ * @returns {object}
+ */
+export const getPermissions = (editor) => editor.options.get(permissionsName);
diff --git a/lib/editor/tiny/plugins/h5p/amd/src/plugin.js b/lib/editor/tiny/plugins/h5p/amd/src/plugin.js
new file mode 100644
index 00000000000..68450d4b10a
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/src/plugin.js
@@ -0,0 +1,61 @@
+// 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 .
+
+/**
+ * Tiny H5P plugin for Moodle.
+ *
+ * @module tiny_h5p/plugin
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+import {getTinyMCE} from 'editor_tiny/loader';
+import {getPluginMetadata} from 'editor_tiny/utils';
+
+import {component, pluginName} from './common';
+import * as FilterContent from './filtercontent';
+import * as Commands from './commands';
+import * as Configuration from './configuration';
+import * as Options from './options';
+
+// Setup the H5P Plugin to add a button and menu option.
+export default new Promise(async(resolve) => {
+ const [
+ tinyMCE,
+ setupCommands,
+ pluginMetadata,
+ ] = await Promise.all([
+ getTinyMCE(),
+ Commands.getSetup(),
+ getPluginMetadata(component, pluginName),
+ ]);
+
+ // Note: The PluginManager.add function does not accept a Promise.
+ // Any asynchronous code must be run before this point.
+ tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {
+ // Register options.
+ Options.register(editor);
+
+ // Setup the Formatter.
+ FilterContent.setup(editor);
+
+ // Setup the Commands (buttons, menu items, and so on).
+ setupCommands(editor);
+
+ return pluginMetadata;
+ });
+
+ // Resolve the H5P Plugin and include configuration.
+ resolve([`${component}/plugin`, Configuration]);
+});
diff --git a/lib/editor/tiny/plugins/h5p/amd/src/ui.js b/lib/editor/tiny/plugins/h5p/amd/src/ui.js
new file mode 100644
index 00000000000..b72b1d9c065
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/amd/src/ui.js
@@ -0,0 +1,198 @@
+// 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 .
+
+/**
+ * Tiny H5P Content configuration.
+ *
+ * @module tiny_h5p/commands
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+import {displayFilepicker} from 'editor_tiny/utils';
+import {component} from './common';
+import {getPermissions} from './options';
+
+import {getList} from 'core/normalise';
+import {renderForPromise} from 'core/templates';
+import Modal from 'tiny_h5p/modal';
+import ModalEvents from 'core/modal_events';
+import ModalFactory from 'core/modal_factory';
+
+let openingSelection = null;
+
+export const handleAction = (editor) => {
+ openingSelection = editor.selection.getBookmark();
+ displayDialogue(editor);
+};
+
+/**
+ * Get the template context for the dialogue.
+ *
+ * @param {Editor} editor
+ * @param {object} data
+ * @returns {object} data
+ */
+const getTemplateContext = (editor, data) => {
+ const permissions = getPermissions(editor);
+
+ const canUpload = permissions.upload ?? false;
+ const canEmbed = permissions.embed ?? false;
+ const canUploadAndEmbed = canUpload && canEmbed;
+
+ return Object.assign({}, {
+ elementid: editor.id,
+ canUpload,
+ canEmbed,
+ canUploadAndEmbed,
+ showOptions: false,
+ fileURL: data?.url ?? '',
+ }, data);
+};
+
+/**
+ * Get the URL from the submitted form.
+ *
+ * @param {FormNode} form
+ * @param {string} submittedUrl
+ * @param {object} permissions
+ * @returns {URL|null}
+ */
+const getUrlFromSubmission = (form, submittedUrl, permissions) => {
+ if (!submittedUrl) {
+ return null;
+ }
+
+ // Generate a URL Object for the submitted URL.
+ const url = new URL(submittedUrl);
+
+ if (permissions?.upload) {
+ if (form.querySelector('[name="download"]').checked) {
+ url.searchParams.append('export', 1);
+ }
+ }
+
+ if (permissions?.embed) {
+ if (form.querySelector('[name="embed"]').checked) {
+ url.searchParams.append('embed', 1);
+ }
+ }
+ if (form.querySelector('[name="copyright"]').checked) {
+ url.searchParams.append('copyright', 1);
+ }
+
+ return url;
+};
+
+const handleDialogueSubmission = async(editor, modal, data) => {
+ const form = getList(modal.getRoot())[0].querySelector('form');
+ if (!form) {
+ // The form couldn't be found, which is weird.
+ // This should not happen.
+ // Display the dialogue again
+ displayDialogue(editor, Object.assign({}, data));
+ return;
+ }
+
+ // Get the URL from the submitted form.
+ const submittedUrl = form.querySelector('input[name="url"]').value;
+ const url = getUrlFromSubmission(form, submittedUrl, getPermissions(editor));
+
+ if (!url) {
+ // The URL is invalid.
+ // Fill it in and represent the dialogue with an error.
+ displayDialogue(editor, Object.assign({}, data, {
+ url: submittedUrl,
+ invalidUrl: true,
+ }));
+ return;
+ }
+
+
+ const content = await renderForPromise(`${component}/content`, {
+ url: url.toString(),
+ });
+
+ editor.selection.moveToBookmark(openingSelection);
+ editor.execCommand('mceInsertContent', false, content.html);
+ editor.selection.moveToBookmark(openingSelection);
+};
+
+const getCurrentH5PData = (currentH5P) => {
+ const data = {};
+ let url;
+ try {
+ url = new URL(currentH5P.textContent);
+ } catch (error) {
+ return data;
+ }
+
+ if (url.searchParams.has('export')) {
+ data.download = true;
+ data.showOptions = true;
+ url.searchParams.delete('export');
+ }
+
+ if (url.searchParams.has('embed')) {
+ data.embed = true;
+ data.showOptions = true;
+ url.searchParams.delete('embed');
+ }
+
+ if (url.searchParams.has('copyright')) {
+ data.copyright = true;
+ data.showOptions = true;
+ url.searchParams.delete('copyright');
+ }
+
+ data.url = url.toString();
+
+ return data;
+};
+
+const displayDialogue = async(editor, data = {}) => {
+ const selection = editor.selection.getNode();
+ const currentH5P = selection.closest('.h5p-placeholder');
+ if (currentH5P) {
+ Object.assign(data, getCurrentH5PData(currentH5P));
+ }
+
+ const modal = await ModalFactory.create({
+ type: Modal.TYPE,
+ templateContext: getTemplateContext(editor, data),
+ large: true,
+ });
+ modal.show();
+
+ const $root = modal.getRoot();
+ const root = $root[0];
+ $root.on(ModalEvents.save, (event, modal) => {
+ handleDialogueSubmission(editor, modal, data);
+ });
+
+ root.addEventListener('click', (e) => {
+ const filepickerButton = e.target.closest('[data-target="filepicker"]');
+ if (filepickerButton) {
+ displayFilepicker(editor, 'h5p').then((params) => {
+ if (params.url !== '') {
+ const input = root.querySelector('form input[name="url"]');
+ input.value = params.url;
+ }
+ return params;
+ })
+ .catch();
+ }
+ });
+};
diff --git a/lib/editor/tiny/plugins/h5p/classes/plugininfo.php b/lib/editor/tiny/plugins/h5p/classes/plugininfo.php
new file mode 100644
index 00000000000..35dc467b0bc
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/classes/plugininfo.php
@@ -0,0 +1,76 @@
+.
+
+namespace tiny_h5p;
+
+use context;
+use editor_tiny\plugin;
+use editor_tiny\plugin_with_buttons;
+use editor_tiny\plugin_with_menuitems;
+use editor_tiny\plugin_with_configuration;
+
+/**
+ * Tiny H5P plugin for Moodle.
+ *
+ * @package tiny_h5p
+ * @copyright 2021 Andrew Lyons
+ * @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 {
+
+ public static function is_enabled(
+ context $context,
+ array $options,
+ array $fpoptions,
+ ?\editor_tiny\editor $editor = null
+ ): bool {
+ // Users must have permission to embed content.
+ return has_capability('atto/h5p:addembed', $context);
+ }
+
+ public static function get_available_buttons(): array {
+ return [
+ 'tiny_h5p/h5p',
+ ];
+ }
+
+ public static function get_available_menuitems(): array {
+ return [
+ 'tiny_h5p/h5p',
+ ];
+ }
+
+ public static function get_plugin_configuration_for_context(
+ context $context,
+ array $options,
+ array $fpoptions,
+ ?\editor_tiny\editor $editor = null
+ ): array {
+ $permissions = [
+ 'embed' => has_capability('tiny/h5p:addembed', $context),
+ 'upload' => has_capability('moodle/h5p:deploy', $context),
+ ];
+ $permissions['uploadandembed'] = $permissions['embed'] && $permissions['upload'];
+
+ return [
+ 'permissions' => $permissions,
+ 'storeinrepo' => true,
+ ];
+ }
+}
diff --git a/lib/editor/tiny/plugins/h5p/classes/privacy/provider.php b/lib/editor/tiny/plugins/h5p/classes/privacy/provider.php
new file mode 100644
index 00000000000..28b48091b01
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/classes/privacy/provider.php
@@ -0,0 +1,30 @@
+.
+
+namespace tiny_h5p\privacy;
+
+/**
+ * Privacy Subsystem implementation for the H5P plugin for TinyMCE.
+ *
+ * @package tiny_h5p
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class provider implements \core_privacy\local\metadata\null_provider {
+ public static function get_reason(): string {
+ return 'privacy:metadata';
+ }
+}
diff --git a/lib/editor/tiny/plugins/h5p/db/access.php b/lib/editor/tiny/plugins/h5p/db/access.php
new file mode 100644
index 00000000000..feaf1df12e2
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/db/access.php
@@ -0,0 +1,36 @@
+.
+
+/**
+ * Capabilities for the tiny_h5p plugin.
+ *
+ * @package tiny_h5p
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$capabilities = [
+ 'tiny/h5p:addembed' => [
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_MODULE,
+ 'archetypes' => [
+ 'editingteacher' => CAP_ALLOW,
+ ],
+ 'clonepermissionsfrom' => 'atto/h5p:addembed',
+ ]
+];
diff --git a/lib/editor/tiny/plugins/h5p/editor_styles.css b/lib/editor/tiny/plugins/h5p/editor_styles.css
new file mode 100644
index 00000000000..c2c1283c0f0
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/editor_styles.css
@@ -0,0 +1,11 @@
+.mce-content-body .h5p-placeholder + br {
+ display: none;
+}
+.mce-content-body .h5p-placeholder {
+ color: #6c757d;
+ width: 100%;
+ word-break: break-all;
+ height: 260px;
+ cursor: pointer;
+ background: url([[pix:tiny_h5p|icon-white]]) center center / 100px auto no-repeat #6c757d;
+}
diff --git a/lib/editor/tiny/plugins/h5p/lang/en/tiny_h5p.php b/lib/editor/tiny/plugins/h5p/lang/en/tiny_h5p.php
new file mode 100644
index 00000000000..841cfac6020
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/lang/en/tiny_h5p.php
@@ -0,0 +1,42 @@
+.
+
+/**
+ * Strings for component 'tiny_h5p', language 'en'.
+ *
+ * @package tiny_h5p
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['helplinktext'] = 'Moodle H5P Helper';
+$string['modaltitle'] = 'Insert H5P content';
+$string['buttontitle'] = 'Configure H5P content';
+$string['insert'] = 'Insert H5P content';
+$string['pluginname'] = 'Tiny H5P plugin for Moodle';
+$string['browserepositories'] = 'Browse repositories...';
+$string['copyrightbutton'] = 'Copyright button';
+$string['downloadbutton'] = 'Allow download';
+$string['embedbutton'] = 'Embed button';
+$string['h5p:addembed'] = 'Add embedded H5P';
+$string['h5pfile'] = 'H5P file upload';
+$string['h5pfileorurl'] = 'H5P URL or file upload';
+$string['h5poptions'] = 'H5P options';
+$string['h5purl'] = 'H5P URL';
+$string['invalidh5purl'] = 'Invalid URL';
+$string['instructions'] = 'You can insert H5P content by either entering a URL or by uploading an H5P file.';
+$string['noh5pcontent'] = 'No H5P content added';
+$string['privacy:metadata'] = 'The H5P plugin for TinyMCE does not store any personal data.';
diff --git a/lib/editor/tiny/plugins/h5p/pix/icon-white.png b/lib/editor/tiny/plugins/h5p/pix/icon-white.png
new file mode 100644
index 00000000000..d2ac8b26ad6
Binary files /dev/null and b/lib/editor/tiny/plugins/h5p/pix/icon-white.png differ
diff --git a/lib/editor/tiny/plugins/h5p/pix/icon-white.svg b/lib/editor/tiny/plugins/h5p/pix/icon-white.svg
new file mode 100644
index 00000000000..54e8e585197
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/pix/icon-white.svg
@@ -0,0 +1,18 @@
+
+
+
diff --git a/lib/editor/tiny/plugins/h5p/pix/icon.png b/lib/editor/tiny/plugins/h5p/pix/icon.png
new file mode 100644
index 00000000000..51a640dc9f2
Binary files /dev/null and b/lib/editor/tiny/plugins/h5p/pix/icon.png differ
diff --git a/lib/editor/tiny/plugins/h5p/pix/icon.svg b/lib/editor/tiny/plugins/h5p/pix/icon.svg
new file mode 100644
index 00000000000..2aaf327ffe2
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/pix/icon.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/lib/editor/tiny/plugins/h5p/templates/content.mustache b/lib/editor/tiny/plugins/h5p/templates/content.mustache
new file mode 100644
index 00000000000..015c6ac76dc
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/templates/content.mustache
@@ -0,0 +1,38 @@
+{{!
+ This file is part of Moodle - http://moodle.org/
+
+ Moodle is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Moodle is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Moodle. If not, see .
+}}
+{{!
+ @template tiny_h5p/content
+
+ Templated used when inserting H5P Placeholder content into the text.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * none
+
+ Context variables required for this template:
+
+ Example context (json):
+ {
+ "url": "https://moodle.h5p.com/content/1290772960722742119"
+ }
+}}
+
+
+ {{ url }}
+
diff --git a/lib/editor/tiny/plugins/h5p/templates/modal.mustache b/lib/editor/tiny/plugins/h5p/templates/modal.mustache
new file mode 100644
index 00000000000..cf741c404e9
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/templates/modal.mustache
@@ -0,0 +1,167 @@
+{{!
+ This file is part of Moodle - http://moodle.org/
+
+ Moodle is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Moodle is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Moodle. If not, see .
+}}
+{{!
+ @template tiny_h5p/modal
+
+ Modal to manage an H5P activity within the Tiny Edigtor.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * none
+
+ Context variables required for this template:
+
+ Example context (json):
+ {
+ "elementid": "exampleId"
+ }
+}}
+
+{{< core/modal }}
+
+ {{$title}}
+ {{#str}} modaltitle, tiny_h5p {{/str}}
+ {{/title}}
+
+ {{$body}}
+
+ {{/body}}
+
+ {{$footer}}
+
+
+ {{/footer}}
+{{/ core/modal }}
diff --git a/lib/editor/tiny/plugins/h5p/version.php b/lib/editor/tiny/plugins/h5p/version.php
new file mode 100644
index 00000000000..540ca533f87
--- /dev/null
+++ b/lib/editor/tiny/plugins/h5p/version.php
@@ -0,0 +1,29 @@
+.
+
+/**
+ * Tiny text editor H5P Plugin version file.
+ *
+ * @package tiny_h5p
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2022101700;
+$plugin->requires = 2020061500;
+$plugin->component = 'tiny_h5p';