MDL-78613 TinyMCE: Add source code formatting and styling

Add code formatting (indenting) as well as code style (tag color)
via a TinyMC plugin. To make it easier to make code edits when
viewing the editor content in source code view.
This commit is contained in:
Matt Porritt
2023-08-03 13:43:40 +10:00
parent a1d5d1b2f7
commit 07e2b61128
33 changed files with 36828 additions and 3 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
define("tiny_html/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;var _default={component:"tiny_html",pluginName:"".concat("tiny_html","/plugin"),codeMirrorStyle:"\n .modal-codemirror-container {\n position: absolute;\n top: 40px;\n bottom: 50px;\n left: 15px;\n right: 15px;\n overflow: scroll;\n border: 1px solid #c7cace;\n border-radius: 5px;\n }\n "};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=common.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"common.min.js","sources":["../src/common.js"],"sourcesContent":["// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.\n\n/**\n * Common values helper for the Moodle tiny_html plugin.\n *\n * @module tiny_html/common\n * @copyright 2023 Matt Porritt <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst component = 'tiny_html';\n\nexport default {\n component,\n pluginName: `${component}/plugin`,\n codeMirrorStyle: `\n .modal-codemirror-container {\n position: absolute;\n top: 40px;\n bottom: 50px;\n left: 15px;\n right: 15px;\n overflow: scroll;\n border: 1px solid #c7cace;\n border-radius: 5px;\n }\n `\n};\n"],"names":["component","pluginName","codeMirrorStyle"],"mappings":"uJAyBe,CACXA,UAHc,YAIdC,qBAJc,uBAKdC"}
+11
View File
@@ -0,0 +1,11 @@
define("tiny_html/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./common","./beautify/beautify-html","core/str","./codemirror-lazy"],(function(_exports,_loader,_utils,_common,_beautifyHtml,_str,_codemirrorLazy){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;
/**
* Tiny tiny_html for Moodle.
*
* @module tiny_html/plugin
* @copyright 2023 Matt Porritt <matt.porritt@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const beautifyOptions={indent_size:2,wrap_line_length:80,unformatted:[]};var _default=new Promise((async resolve=>{const[tinyMCE,pluginMetadata,buttonStrings]=await Promise.all([(0,_loader.getTinyMCE)(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName),(0,_str.get_strings)([{key:"cancel",component:"moodle"},{key:"save",component:"moodle"}])]);tinyMCE.PluginManager.add(_common.pluginName,(editor=>{const windowManagerConfig={title:"Source code",size:"large",body:{type:"panel",items:[{type:"htmlpanel",html:'<div id="'+editor.id+'_codeMirrorContainer" style="height: 100%;"></div>'}]},buttons:null,initialData:null,onSubmit:null};return editor.addCommand("mceCodeEditor",(()=>{const content=editor.getContent({source_view:!0}),beautifiedContent=(0,_beautifyHtml.html_beautify)(content,beautifyOptions);let cmInstance,state=_codemirrorLazy.EditorState.create({doc:beautifiedContent,extensions:[_codemirrorLazy.basicSetup,_codemirrorLazy.EditorState.tabSize.of(2),...Object.entries(_codemirrorLazy.lang).map((_ref=>{let[,languagePlugin]=_ref;return languagePlugin()}))]});editor.windowManager.open({...windowManagerConfig,onSubmit:api=>{const cmContent=cmInstance.state.doc.toString();editor.setContent(cmContent,{source_view:!0}),api.close()},buttons:[{type:"cancel",text:buttonStrings[0]},{type:"submit",text:buttonStrings[1],primary:!0}]});const shadowRoot=document.getElementById(editor.id+"_codeMirrorContainer").attachShadow({mode:"open"}),style=document.createElement("style");style.textContent=_common.codeMirrorStyle,shadowRoot.appendChild(style);const div=document.createElement("div");div.classList.add("modal-codemirror-container"),shadowRoot.appendChild(div),cmInstance=new _codemirrorLazy.EditorView({state:state,parent:div}),shadowRoot.addEventListener("keydown",(event=>{if("Tab"===event.key){event.preventDefault();document.getElementById(editor.id+"_codeMirrorContainer").closest(".tox-dialog").querySelector('button[title="'+buttonStrings[1]+'"]').focus()}}))})),pluginMetadata})),resolve(_common.pluginName)}));return _exports.default=_default,_exports.default}));
//# sourceMappingURL=plugin.min.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,41 @@
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
/**
* Common values helper for the Moodle tiny_html plugin.
*
* @module tiny_html/common
* @copyright 2023 Matt Porritt <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const component = 'tiny_html';
export default {
component,
pluginName: `${component}/plugin`,
codeMirrorStyle: `
.modal-codemirror-container {
position: absolute;
top: 40px;
bottom: 50px;
left: 15px;
right: 15px;
overflow: scroll;
border: 1px solid #c7cace;
border-radius: 5px;
}
`
};
@@ -0,0 +1,177 @@
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
/**
* Tiny tiny_html for Moodle.
*
* @module tiny_html/plugin
* @copyright 2023 Matt Porritt <[email protected]>
* @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, codeMirrorStyle} from './common';
/* eslint-disable camelcase */
import {html_beautify} from './beautify/beautify-html';
import {get_strings} from 'core/str';
/* eslint-enable camelcase */
import {
EditorState,
EditorView,
basicSetup,
lang,
} from './codemirror-lazy';
/**
* Options for the html_beautify function.
* We disable the camelCase check here as these are
* variables that we are passing to the js-beautify library.
*/
/* eslint-disable camelcase */
const beautifyOptions = {
indent_size: 2,
wrap_line_length: 80,
unformatted: [],
};
/* eslint-enable camelcase */
// Set up the tiny_html Plugin.
// eslint-disable-next-line no-async-promise-executor
export default new Promise(async(resolve) => {
// Note: The PluginManager.add function does not support asynchronous configuration.
// Perform any asynchronous configuration here, and then call the PluginManager.add function.
const [
tinyMCE,
pluginMetadata,
buttonStrings,
] = await Promise.all([
getTinyMCE(),
getPluginMetadata(component, pluginName),
get_strings([
{key: 'cancel', component: 'moodle'},
{key: 'save', component: 'moodle'},
])
]);
// Reminder: Any asynchronous code must be run before this point.
tinyMCE.PluginManager.add(pluginName, (editor) => {
// Initial configuration for TinyMCE editor the windowManager.
const windowManagerConfig = {
title: 'Source code',
size: 'large',
body: {
type: 'panel',
items: [
{
type: 'htmlpanel',
html: '<div id="' + editor.id + '_codeMirrorContainer" style="height: 100%;"></div>',
},
],
},
buttons: null,
initialData: null,
onSubmit: null,
};
// Overriding the default 'mceCodeEditor' command
editor.addCommand('mceCodeEditor', () => {
// Get the current content of the editor
// eslint-disable-next-line camelcase
const content = editor.getContent({source_view: true});
// Beautify the content using html_beautify
const beautifiedContent = html_beautify(content, beautifyOptions);
// Create the CodeMirror instance
let cmInstance;
let state = EditorState.create({
doc: beautifiedContent,
// This is where basicSetup should go as [basicSetup, ...].
extensions: [
basicSetup,
EditorState.tabSize.of(2),
// Bring in all language extensions.
...Object.entries(lang).map(([, languagePlugin]) => languagePlugin()),
],
});
// Create a new window to display the beautified code
editor.windowManager.open({
...windowManagerConfig,
onSubmit: (api) => {
const cmContent = cmInstance.state.doc.toString();
// eslint-disable-next-line camelcase
editor.setContent(cmContent, {source_view: true});
api.close();
},
buttons: [
{
type: 'cancel',
text: buttonStrings[0],
},
{
type: 'submit',
text: buttonStrings[1],
primary: true,
},
]
});
const container = document.getElementById(editor.id + '_codeMirrorContainer');
// Create a shadow root for the CodeMirror instance.
// This is required to prevent the TinyMCE editor styles from overriding the CodeMirror ones.
const shadowRoot = container.attachShadow({mode: "open"});
// Add the styles to the shadow root
const style = document.createElement('style');
style.textContent = codeMirrorStyle;
shadowRoot.appendChild(style);
// Create a new div and add the class 'my-codemirror-container'
const div = document.createElement('div');
div.classList.add('modal-codemirror-container');
shadowRoot.appendChild(div);
// Create the CodeMirror instance
cmInstance = new EditorView({
state,
parent: div,
});
// Add an event listener to the shadow root to listen for the tab key press.
shadowRoot.addEventListener('keydown', (event) => {
// If the tab key is pressed, prevent the default action and select the save button.
// We need to do this as the shadow root is not part of the DOM, so the tab key will not
// be caught by the TinyMCE dialog.
if (event.key === 'Tab') {
event.preventDefault();
const codeMirrorContainer = document.getElementById(editor.id + '_codeMirrorContainer');
const dialogElement = codeMirrorContainer.closest('.tox-dialog');
const cancelButton = dialogElement.querySelector('button[title="' + buttonStrings[1] + '"]');
cancelButton.focus();
}
});
});
// Return the pluginMetadata object. This is used by TinyMCE to display a help link for your plugin.
return pluginMetadata;
});
resolve(pluginName);
});
@@ -0,0 +1 @@
node_modules
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
SCRIPTPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPTPATH"
# Install codemirror, rollup, and any codemirror plugins we want to use.
# Note: We don't want to put these into the packages.json because we want to fetch the latest version when we run.
echo "Installing codemirror and rollup"
npm install --no-save \
codemirror \
@codemirror/lang-javascript \
@codemirror/lang-html \
@codemirror/lang-xml \
rollup \
@rollup/plugin-node-resolve
# Create the rollup.
npx rollup \
./codemirror.mjs \
-f esm \
-o ../amd/src/codemirror-lazy.js \
-p @rollup/plugin-node-resolve
# Next install js-beautify
echo "Installing js-beautify"
API_URL="https://api.github.com/repos/beautify-web/js-beautify/releases/latest"
# Get the .zip download URL
ZIP_URL=$(curl -s $API_URL | jq -r ".zipball_url")
# Download the latest release
curl -L -o latest_release.zip $ZIP_URL
# Create a temporary directory
TEMP_DIR=$(mktemp -d)
# Extract the .zip file to the temporary directory.
unzip -q latest_release.zip -d $TEMP_DIR
# Find the subdirectory that starts with "beautify"
SUB_DIR=$(find $TEMP_DIR -type d -name "beautify*" | head -n 1)
# Copy the js-beautify files to the correct location.
cp -v $SUB_DIR/js/lib/beautify*.js ../amd/src/beautify
# Copy the License file to the correct location.
cp -v $SUB_DIR/LICENSE ../amd/src/beautify/LICENSE
# Remove the temporary directory, node_modules directory and the js-beautify zip.
rm -rf $TEMP_DIR
rm -rf node_modules
rm latest_release.zip
@@ -0,0 +1,40 @@
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
/**
* Rollup generator for CodeMirror
*
* @copyright 2023 Matt Porritt <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import { EditorView, basicSetup } from "codemirror"
import { EditorState } from "@codemirror/state";
import { html } from "@codemirror/lang-html"
import { xml } from "@codemirror/lang-xml"
import { javascript } from "@codemirror/lang-javascript"
const lang = {
html,
javascript,
xml,
};
export {
EditorView,
EditorState,
basicSetup,
lang,
};
@@ -0,0 +1,11 @@
{
"name": "moodle-codemirror-builder",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
@@ -0,0 +1,9 @@
import {nodeResolve} from "@rollup/plugin-node-resolve"
export default {
input: "./codemirror.mjs",
output: {
file: "../amd/src/codemirror.js",
format: "esm"
},
plugins: [nodeResolve()]
}
@@ -0,0 +1,37 @@
<?php
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
/**
* Tiny TinyMCE HTML formatter plugin for Moodle.
*
* @package tiny_html
* @copyright 2023 Matt Porritt <[email protected]>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tiny_html;
use editor_tiny\plugin;
/**
* Tiny TinyMCE HTML formatter plugin for Moodle.
*
* @package tiny_html
* @copyright 2023 Matt Porritt <[email protected]>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plugininfo extends plugin {
}
@@ -0,0 +1,45 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for the HTML formatter plugin for TinyMCE.
*
* @package tiny_html
* @copyright 2023 Matt Porritt <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tiny_html\privacy;
/**
* Privacy Subsystem implementation for the HTML plugin for TinyMCE.
*
* @package tiny_html
* @copyright 2023 Matt Porritt <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
/**
* Plugin strings are defined here.
*
* @package tiny_html
* @category string
* @copyright 2023 Matt Porritt <[email protected]>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$string['pluginname'] = 'TinyMCE HTML formatter';
$string['privacy:metadata'] = 'The HTML formatter plugin for TinyMCE does not store any personal data.';
@@ -0,0 +1,31 @@
Description of updating JS-Beautify and CodeMirror libraries into Moodle.
There is a build script that should automate the process of updating the libraries.
It is located in the build directory and is called build.sh.
It requires the following pre-requisites: jq, npm and npx.
Most Moodle LMS development systems should have at least npm and npx installed.
However, jq is not a common tool and may need to be installed separately.
For example, on Ubuntu you would use:
sudo apt-get install jq
And on macOS with Homebrew:
brew install jq
Once you have the pre-requisites installed, you can run the build script.
from the root of the plugin (e.g lib/editor/tiny/plugins/html), run:
./build/build.sh
Once the script has run, you should see the following files updated:
* lib/editor/tiny/plugins/html/amd/src/beautify/beautify.js
* lib/editor/tiny/plugins/html/amd/src/beautify/beautify-css.js
* lib/editor/tiny/plugins/html/amd/src/beautify/beautify-html.js
* lib/editor/tiny/plugins/html/amd/src/codemirror/codemirror-lazy.js
Next, you need to run the build script for the Moodle plugin.
From the root of the plugin (e.g lib/editor/tiny/plugins/html), run:
cd amd
grunt
Once the process is complete update thirdpartylibs.xml with the new version numbers.
@@ -0,0 +1,108 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* TinyMCE HTML plugin custom behat steps definitions.
*
* @package tiny_html
* @category test
* @copyright 2023 Matt Porritt <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use Behat\Mink\Exception\ExpectationException;
use Behat\Gherkin\Node\{PyStringNode};
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../../tests/behat/editor_tiny_helpers.php');
require_once(__DIR__ . '/../../../../../../behat/behat_base.php');
/**
* TinyMCE HTML plugin custom behat steps definitions.
*
* @package tiny_html
* @category test
* @copyright 2023 Matt Porritt <[email protected]>
*/
class behat_tiny_html extends behat_base {
use editor_tiny_helpers;
/**
* Get Javascript to navigate to the shadow DOM of the editor,
* and find specified sourcecode text.
*
* @param string $editorid The editor id to search within.
* @param string $sourcecode The sourcecode to find.
* @return string The Javascript to execute.
*/
protected function get_javascript_sourcecode_search(string $editorid, string $sourcecode): string {
return <<<EOF
const container = document.getElementById('{$editorid}_codeMirrorContainer');
const shadowRoot = container.shadowRoot;
const sourceCode = shadowRoot.querySelector('.modal-codemirror-container [contenteditable="true"]').innerText
const textToFind = `$sourcecode`;
if (sourceCode == textToFind) {
resolve(true);
} else {
resolve(false);
}
EOF;
}
/**
* Gets the specified formatted single line source code from the editor
* and compares it to what is expected.
*
* @When /^I should see "(?P<sourcecodelocator_string>(?:[^"]|\\")*)" source code for the "(?P<locator_string>(?:[^"]|\\")*)" TinyMCE editor$/
* @throws ExpectationException
* @param string $sourcecode The type of element to select (for example `p` or `span`)
* @param string $locator The editor to select within
*/
public function get_source_code(string $sourcecode, string $locator): void {
$this->require_tiny_tags();
$editor = $this->get_textarea_for_locator($locator);
$editorid = $editor->getAttribute('id');
$js = $this->get_javascript_sourcecode_search($editorid, $sourcecode);
if ($this->evaluate_javascript_for_editor($editorid, $js) != 'true') {
throw new ExpectationException("Source code does not match expected.", $this->getSession());
}
}
/**
* Gets the specified formatted multiline source code from the editor
* and compares it to what is expected.
*
* @When /^I should see this multiline source code for the "(?P<locator_string>(?:[^"]|\\")*)" TinyMCE editor:$/
* @throws ExpectationException
* @param string $locator
* @param PyStringNode $sourcecode
* @return void
*/
public function get_multiline_source_code(string $locator, PyStringNode $sourcecode): void {
$this->require_tiny_tags();
$editor = $this->get_textarea_for_locator($locator);
$editorid = $editor->getAttribute('id');
$js = $this->get_javascript_sourcecode_search($editorid, $sourcecode);
if ($this->evaluate_javascript_for_editor($editorid, $js) != 'true') {
throw new ExpectationException("Source code is not indented as expected.", $this->getSession());
}
}
}
@@ -0,0 +1,24 @@
@editor @editor_tiny @tiny_html @javascript
Feature: Edit HTML in TinyMCE
To write rich text - I need to be able to easily edit the HTML.
Scenario: View HTML in TinyMCE source code view
Given I log in as "admin"
When I open my profile in edit mode
And I set the field "Description" to "This is my draft"
And I click on the "View > Source code" menu item for the "Description" TinyMCE editor
And I should see "Source code"
Then I should see "<p>This is my draft</p>" source code for the "Description" TinyMCE editor
Scenario: View multiline HTML with indenting in TinyMCE source code view
Given I log in as "admin"
When I open my profile in edit mode
And I set the field "Description" to "<div><p>This is my draft</p></div>"
And I click on the "View > Source code" menu item for the "Description" TinyMCE editor
And I should see "Source code"
Then I should see this multiline source code for the "Description" TinyMCE editor:
"""
<div>
<p>This is my draft</p>
</div>
"""
@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<libraries>
<library>
<location>amd/src/codemirror-lazy.js</location>
<name>codemirror</name>
<description>CodeMirror is a versatile text editor implemented in JavaScript for the browser.</description>
<version>6.0.1</version>
<license>MIT</license>
<repository>https://github.com/codemirror/</repository>
</library>
<library>
<location>amd/src/beautify</location>
<name>js-beautify</name>
<description>Beautifier for javascript.</description>
<version>1.14.8</version>
<license>MIT</license>
<repository>https://github.com/beautify-web/js-beautify/</repository>
</library>
</libraries>
+31
View File
@@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
/**
* Plugin version and other meta-data are defined here.
*
* @package tiny_html
* @copyright 2023 Matt Porritt <[email protected]>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'tiny_html';
$plugin->release = '2023050400';
$plugin->version = 2023063000;
$plugin->requires = 2022112800;
$plugin->maturity = MATURITY_STABLE;
@@ -1,8 +1,11 @@
@core @editor_tiny @source_code @javascript
Feature: A user can insert script tag in TinyMCE
Feature: A user can insert script tag in TinyMCE using the default TinyMCE functionalities.
Scenario: Allow script elements in the editor
Given I log in as "admin"
Scenario: Allow script elements in the editor with the additional HTML plugin disabled.
Given the following config values are set as admin:
| config | value | plugin |
| disabled | 1 | tiny_html |
And I log in as "admin"
And I open my profile in edit mode
When I click on the "Tools > Source code" menu item for the "Description" TinyMCE editor
Then I set the field with xpath "//textarea[@class='tox-textarea']" to "<p><script>alert('script in tiny');</script></p>"