Skip to content
Snippets Groups Projects
Commit bd6947d9 authored by Oleksandr Tymoshchuk's avatar Oleksandr Tymoshchuk Committed by Brian Osborne
Browse files

Issue #3314483 by el7cosmos, o_tymoshchuk, bkosborne: Add support for CKEditor 5

parent 0051df0d
No related branches found
Tags 3.0.0-alpha1
4 merge requests!13Issue #3477713: TypeError: editor.config.get(...).enabled_optional_attributes.includes is not a function,!8Issue #3457032: fixed selection.getFirstPosition() is null error,!7Issue #3457032: fixed selection.getFirstPosition() is null error,!4Resolve #3314483 "Migrate to ckeditor"
Showing
with 5570 additions and 20 deletions
node_modules
\ No newline at end of file
ckeditor_iframe_embed_iframeembed:
ckeditor5:
plugins:
- iframeembed.IframeEmbed
drupal:
label: Iframe embed
library: ckeditor_iframe/iframeembed
admin_library: ckeditor_iframe/admin.iframeembed
class: Drupal\ckeditor_iframe\Plugin\CKEditor5Plugin\Iframe
toolbar_items:
iframeEmbed:
label: Iframe Embed
elements:
- <iframe>
- <iframe align frameborder height width longdesc name scrolling src tabindex title>
......@@ -2,7 +2,7 @@ name: CKEditor iFrame
type: module
description: "Adds the iFrame dialog plugin to CKEditor."
package: CKEditor
core_version_requirement: ^8 || ^9 || ^10
dependencies:
- drupal:ckeditor
- fakeobjects:fakeobjects
core_version_requirement: ^9 || ^10
# Not listing CKE4 or CKE5 modules as dependencies yet,
# allowing sites to run both at the same time to support
# the transition.
......@@ -11,23 +11,25 @@
function ckeditor_iframe_requirements($phase) {
$requirements = [];
if ($phase === 'install' || $phase === 'runtime') {
$plugin_detected = file_exists(DRUPAL_ROOT . '/libraries/iframe/plugin.js');
if (\Drupal::moduleHandler()->moduleExists('ckeditor')) {
if ($phase === 'install' || $phase === 'runtime') {
$plugin_detected = file_exists(DRUPAL_ROOT . '/libraries/iframe/plugin.js');
if ($plugin_detected) {
$requirements['ckeditor_iframe'] = [
'title' => t('CKEditor iFrame'),
'value' => t('Plugin detected'),
'severity' => REQUIREMENT_OK,
];
}
else {
$requirements['ckeditor_iframe'] = [
'title' => t('CKEditor iFrame'),
'value' => t('Plugin not detected'),
'severity' => REQUIREMENT_ERROR,
'description' => t('You must download the <a href=":plugin_url">CKEditor iFrame plugin</a> before enabling the CKEditor Iframe module. Place the plugin in the /libraries/iframe folder.', [':plugin_url' => 'http://ckeditor.com/addon/iframe']),
];
if ($plugin_detected) {
$requirements['ckeditor_iframe'] = [
'title' => t('CKEditor iFrame (CKEditor 4)'),
'value' => t('Plugin detected'),
'severity' => REQUIREMENT_OK,
];
}
else {
$requirements['ckeditor_iframe'] = [
'title' => t('CKEditor iFrame (CKEditor 4)'),
'value' => t('Plugin not detected'),
'severity' => REQUIREMENT_ERROR,
'description' => t('You must download the <a href=":plugin_url">CKEditor4 iFrame plugin</a> before enabling the CKEditor Iframe module. Place the plugin in the /libraries/iframe folder.', [':plugin_url' => 'http://ckeditor.com/addon/iframe']),
];
}
}
}
......
iframeembed:
js:
js/build/iframeembed.js: { preprocess: false, minified: true }
dependencies:
- core/ckeditor5
admin.iframeembed:
css:
theme:
css/iframe.admin.css: {}
# Plugin \Drupal\ckeditor_iframe\Plugin\CKEditor5Plugin\Iframe
ckeditor5.plugin.ckeditor_iframe_embed_iframeembed:
type: mapping
label: iFrame
mapping:
enabled_optional_attributes:
type: sequence
orderby: ~
label: 'Allowed optional attributes'
sequence:
type: string
.ckeditor5-toolbar-button-iframeEmbed {
background-image: url(../icons/iframe.svg);
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M21 2H3a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2M11 17.5L9.5 19L5 14.5L9.5 10l1.5 1.5l-3 3l3 3m3.5 1.5L13 17.5l3-3l-3-3l1.5-1.5l4.5 4.5l-4.5 4.5M21 7H3V4h18v3z" /></svg>
This diff is collapsed.
import { Plugin } from "ckeditor5/src/core";
import IframeEmbedUI from "./iframeEmbedUI";
import IframeEmbedEditing from "./iframeEmbedEditing";
import IframeEmbedToolbar from "./iframeEmbedToolbar";
import './theme/iframe.css';
export default class IframeEmbed extends Plugin {
static get requires() {
return [ IframeEmbedEditing, IframeEmbedUI, IframeEmbedToolbar ];
}
}
import { Plugin } from 'ckeditor5/src/core';
import { toWidget } from "ckeditor5/src/widget";
export default class IframeEmbedEditing extends Plugin {
init() {
this._defineSchema();
this._defineConverters();
}
_defineSchema() {
const { schema } = this.editor.model;
schema.register("iframeEmbed", {
allowWhere: "$inlineObject",
isObject: true,
isInline: true,
allowAttributes: ["align", "frameborder", "height", "width", "longdesc", "name", "scrolling", "src", "tabindex", "title"],
});
}
_defineConverters() {
const { conversion, model } = this.editor;
model.schema.getDefinition("iframeEmbed").allowAttributes.forEach((attribute) => {
conversion.attributeToAttribute({
model: {
name: "iframeEmbed",
key: attribute
},
view: attribute
})
})
conversion.for("upcast").elementToElement({
model: "iframeEmbed",
view: {
name: "iframe",
},
});
conversion.for("dataDowncast").elementToElement({
model: "iframeEmbed",
view: {
name: "iframe",
},
});
conversion.for("editingDowncast").elementToElement({
model: "iframeEmbed",
view: (modelElement, { writer }) => {
const container = writer.createContainerElement("iframe");
writer.setCustomProperty("iframe-emb", true, container);
return toWidget(container, writer, { label: "Iframe Embed" });
},
});
}
}
import { Model, View, LabeledFieldView, createLabeledInputText, createLabeledInputNumber, ButtonView, submitHandler, createDropdown, addListToDropdown, SwitchButtonView } from "ckeditor5/src/ui";
import { Collection } from "ckeditor5/src/utils";
import { icons } from 'ckeditor5/src/core';
export default class IframeEmbedFormView extends View {
constructor( locale, editor ) {
super( locale );
let childViews = [];
this.src = this._createInput("URL");
childViews.push(this.src);
if (this._attrIsEnabled(editor, 'name')) {
this.name = this._createInput("Name");
childViews.push(this.name);
}
if (this._attrIsEnabled(editor, 'width')) {
this.width = this._createInput("Width", true);
childViews.push(this.width);
}
if (this._attrIsEnabled(editor, 'height')) {
this.height = this._createInput("Height", true);
childViews.push(this.height);
}
if (this._attrIsEnabled(editor, 'title')) {
this.title = this._createInput("Advisory Title");
childViews.push(this.title);
}
if (this._attrIsEnabled(editor, 'longdesc')) {
this.longdesc = this._createInput("Long Description");
childViews.push(this.longdesc);
}
if (this._attrIsEnabled(editor, 'align')) {
this.alignDropdown = this._createDropdown("Add align", ["None", "Left", "Right", "Top", "Middle", "Bottom"]);
this.align = null;
this.listenTo( this.alignDropdown, 'execute', evt => {
const choice = evt.source.element.textContent;
this.alignDropdown.buttonView.label = `Align ${ choice }`;
this.align = (choice === 'None') ? null : choice.toLowerCase();
} );
childViews.push(this.alignDropdown);
}
if (this._attrIsEnabled(editor, 'scrolling')) {
this.scrolling = this._createSwitch("Enable scrollbars");
childViews.push(this.scrolling);
}
if (this._attrIsEnabled(editor, 'frameborder')) {
this.frameborder = this._createSwitch("Show frame border");
childViews.push(this.frameborder);
}
if (this._attrIsEnabled(editor, 'tabindex')) {
this.tabindex = this._createSwitch("Remove from tabindex");
childViews.push(this.tabindex);
}
this.saveButtonView = this._createButton(
'Save', icons.check, 'ck-button-save'
);
// Set the type to 'submit', which will trigger
// the submit event on entire form when clicked.
this.saveButtonView.type = 'submit';
childViews.push(this.saveButtonView);
this.cancelButtonView = this._createButton(
'Cancel', icons.cancel, 'ck-button-cancel'
);
childViews.push(this.cancelButtonView);
// Delegate ButtonView#execute to FormView#cancel.
this.cancelButtonView.delegate( 'execute' ).to( this, 'cancel' );
this.children = this.createCollection(childViews);
this.setTemplate( {
tag: 'form',
attributes: {
class: [ 'ck', 'ck-iframe-embed-form' ],
tabindex: '-1'
},
children: this.children
} );
}
render() {
super.render();
// Submit the form when the user clicked the save button
// or pressed enter in the input.
submitHandler( {
view: this
} );
}
focus() {
this.children.first.focus();
}
// Create input
_createInput(label, isNumber = false) {
const labeledInput = new LabeledFieldView(
this.locale,
isNumber ? createLabeledInputNumber : createLabeledInputText
);
labeledInput.label = label;
return labeledInput;
}
// Create button
_createButton( label, icon = false, className = false ) {
const button = new ButtonView();
button.set( {
label,
icon,
tooltip: true,
class: className
} );
return button;
}
// Create dropdown
_createDropdown(label, options) {
const dropdown = createDropdown(this.locale);
const labelButton = {
label,
withText: true
};
dropdown.buttonView.set( labelButton );
const items = new Collection();
options.forEach( option => {
const buttonObject = {
type: 'button',
text: option,
model: new Model( {
label: option,
withText: true
} )
};
items.add( buttonObject );
} );
addListToDropdown( dropdown, items );
dropdown.render();
return dropdown;
}
_createSwitch(label) {
const switchButton = new SwitchButtonView();
switchButton.set( {
label: label,
withText: true,
isOn: false
} );
switchButton.on( 'execute', () => { switchButton.isOn = !switchButton.isOn } );
return switchButton;
}
_attrIsEnabled(editor, attr) {
return editor.config.get('iframe').enabled_optional_attributes.includes(attr);
}
}
import { icons, Plugin } from "ckeditor5/src/core";
import { WidgetToolbarRepository } from "ckeditor5/src/widget";
import {
ButtonView,
clickOutsideHandler,
ContextualBalloon
} from "ckeditor5/src/ui";
import IframeEmbedFormView from "./iframeEmbedFormView";
import {
getClosestSelectedIframeEmbedElement,
getClosestSelectedIframeEmbedWidget,
} from "./iframeEmbedUtils";
/**
* @private
*/
export default class IframeEmbedToolbar extends Plugin {
/**
* @inheritdoc
*/
static get requires() {
return [WidgetToolbarRepository, ContextualBalloon];
}
/**
* @inheritdoc
*/
static get pluginName() {
return "IframeEmbedToolbar";
}
/**
* @inheritdoc
*/
init() {
const { editor } = this;
// Create the balloon and the form view.
this._balloon = this.editor.plugins.get(ContextualBalloon);
editor.ui.componentFactory.add("iframeEmbEdit", (locale) => {
const buttonView = new ButtonView(locale);
buttonView.set({
label: editor.t("Edit Iframe"),
icon: icons.pencil,
tooltip: true,
withText: true,
});
this.listenTo(buttonView, "execute", () => {
const { selection } = editor.model.document;
const selectedIframe = getClosestSelectedIframeEmbedElement(selection);
if (selectedIframe) {
this._showUI(this._createFormView(selectedIframe));
}
});
return buttonView;
});
}
/**
* @inheritdoc
*/
afterInit() {
const { editor } = this;
const widgetToolbarRepository = editor.plugins.get(
"WidgetToolbarRepository"
);
widgetToolbarRepository.register("iframeEmbed", {
items: ["iframeEmbEdit"],
// Get the selected iframe.
getRelatedElement: (selection) => {
return getClosestSelectedIframeEmbedWidget(selection);
}
});
}
/**
* @param {module:engine/model/element~Element} modelElement
*/
_createFormView(modelElement) {
const { model, locale } = this.editor;
const formView = new IframeEmbedFormView(locale, this.editor);
// Set values on the form from the model.
formView['src'].fieldView.value = modelElement.getAttribute('src');
['width', 'height', 'name', 'title', 'longdesc'].forEach((attr) => {
if (formView[attr]) {
formView[attr].fieldView.value = modelElement.getAttribute(attr);
}
});
if (formView['scrolling']) {
formView['scrolling'].isOn = modelElement.getAttribute('scrolling') === 'yes';
}
if (formView['frameborder']) {
formView['frameborder'].isOn = modelElement.getAttribute('frameborder') === 1;
}
if (formView['tabindex']) {
formView['tabindex'].isOn = modelElement.getAttribute('tabindex') === -1;
}
if (formView['align']) {
formView['align'] = modelElement.getAttribute('align');
}
// Update the model when the form is submitted.
this.listenTo(formView, 'submit', () => {
model.change((writer) => {
writer.setAttribute('src', formView.src.fieldView.element.value ?? null, modelElement);
['width', 'height', 'name', 'title', 'longdesc'].forEach((attr) => {
if (formView[attr]) {
if (formView[attr].fieldView.element.value) {
writer.setAttribute(attr, formView[attr].fieldView.element.value, modelElement);
}
else {
writer.removeAttribute(attr, modelElement);
}
}
});
if (formView['scrolling']) {
if (formView['scrolling'].isOn) {
writer.setAttribute('scrolling', 'yes', modelElement);
}
else {
writer.removeAttribute('scrolling', modelElement);
}
}
if (formView['frameborder']) {
if (formView['frameborder'].isOn) {
writer.setAttribute('frameborder', 1, modelElement);
}
else {
writer.removeAttribute('frameborder', modelElement);
}
}
if (formView['tabindex']) {
if (formView['tabindex'].isOn) {
writer.setAttribute('tabindex', -1, modelElement);
}
else {
writer.removeAttribute('tabindex', modelElement);
}
}
if (formView['align']) {
writer.setAttribute('align', formView['align'], modelElement);
}
else {
writer.removeAttribute('align', modelElement);
}
});
this._hideUI(formView);
});
// Hide the form view after clicking the "Cancel" button.
this.listenTo(formView, 'cancel', () => {
this._hideUI(formView);
});
// Hide the form view when clicking outside the balloon.
clickOutsideHandler({
emitter: formView,
activator: () => this._balloon.visibleView === formView,
contextElements: [this._balloon.view.element],
callback: () => this._hideUI(formView)
});
return formView;
}
/**
* @param {IframeEmbedFormView} formView
*/
_hideUI(formView) {
this._balloon.remove(formView);
formView.destroy();
// Focus the editing view after closing the form view.
this.editor.editing.view.focus();
}
_getBalloonPositionData() {
const view = this.editor.editing.view;
const viewDocument = view.document;
let target = null;
// Set a target position by converting view selection range to DOM.
target = () => view.domConverter.viewRangeToDom(
viewDocument.selection.getFirstRange()
);
return {
target
};
}
/**
* @param {IframeEmbedFormView} formView
*/
_showUI(formView) {
this._balloon.add({
view: formView,
position: this._getBalloonPositionData()
});
formView.focus();
}
}
import { Plugin } from "ckeditor5/src/core";
import {ButtonView, ContextualBalloon, clickOutsideHandler} from "ckeditor5/src/ui";
import IframeEmbedFormView from "./iframeEmbedFormView"
import icon from "../../../../icons/iframe.svg";
export default class IframeEmbedUI extends Plugin {
static get requires() {
return [ ContextualBalloon ];
}
init() {
const editor = this.editor;
// Create the balloon and the form view.
this._balloon = this.editor.plugins.get( ContextualBalloon );
this.formView = this._createFormView();
editor.ui.componentFactory.add('iframeEmbed', (locale) => {
const button = new ButtonView(locale);
button.set({
label: editor.t("Iframe Embed"),
icon,
tooltip: true,
});
// Execute a callback function when the button is clicked.
button.on('execute', () => {
this._showUI();
});
if (editor.plugins.has('SourceEditing')) {
const plugin = editor.plugins.get('SourceEditing');
button.bind('isEnabled').to(plugin, 'isSourceEditingMode', isSourceEditingMode => !isSourceEditingMode);
}
return button;
})
}
_createFormView() {
const { model, locale} = this.editor;
const formView = new IframeEmbedFormView( locale, this.editor );
// Create an iframe element when the form is submitted.
this.listenTo( formView, 'submit', () => {
model.change((writer) => {
let attrs = {};
attrs['src'] = formView.src.fieldView.element.value ?? null;
['width', 'height', 'name', 'title', 'longdesc'].forEach((attr) => {
if (formView[attr] && formView[attr].fieldView.element.value) {
attrs[attr] = formView[attr].fieldView.element.value;
}
});
if (formView['scrolling'] && formView['scrolling'].isOn) {
attrs['scrolling'] = 'yes';
}
if (formView['frameborder'] && formView['frameborder'].isOn) {
attrs['frameborder'] = 1;
}
if (formView['tabindex'] && formView['tabindex'].isOn) {
attrs['tabindex'] = -1;
}
if (formView['align']) {
attrs['align'] = formView['align'];
}
const iframe = writer.createElement('iframeEmbed', attrs);
model.insertContent(iframe);
});
this._hideUI();
} );
// Hide the form view after clicking the "Cancel" button.
this.listenTo( formView, 'cancel', () => {
this._hideUI();
} );
// Hide the form view when clicking outside the balloon.
clickOutsideHandler( {
emitter: formView,
activator: () => this._balloon.visibleView === formView,
contextElements: [ this._balloon.view.element ],
callback: () => this._hideUI()
} );
return formView;
}
_hideUI() {
['src', 'width', 'height', 'name', 'title', 'longdesc'].forEach((attr) => {
if (this.formView[attr]) {
this.formView[attr].fieldView.element.value = null;
}
});
['scrolling', 'frameborder', 'tabindex'].forEach((attr) => {
if (this.formView[attr]) {
this.formView[attr].isOn = false;
}
});
this.formView.element.reset();
this._balloon.remove( this.formView );
// Focus the editing view after closing the form view.
this.editor.editing.view.focus();
}
_getBalloonPositionData() {
const view = this.editor.editing.view;
const viewDocument = view.document;
let target = null;
// Set a target position by converting view selection range to DOM.
target = () => view.domConverter.viewRangeToDom(
viewDocument.selection.getFirstRange()
);
return {
target
};
}
_showUI() {
this._balloon.add( {
view: this.formView,
position: this._getBalloonPositionData()
} );
this.formView.focus();
}
}
import {isWidget} from "ckeditor5/src/widget";
/**
* Checks if the provided model element is `iframeEmbed`.
*
* @param {module:engine/model/element~Element} modelElement
* The model element to be checked.
* @return {boolean}
* A boolean indicating if the element is a iframeEmbed element.
*
* @private
*/
export function isIframeEmbed(modelElement) {
return !!modelElement && modelElement.is('element', 'iframeEmbed');
}
/**
* Checks if view element is <iframeEmbed> element.
*
* @param {module:engine/view/element~Element} viewElement
* The view element.
* @return {boolean}
* A boolean indicating if the element is a <iframeEmbed> element.
*
* @private
*/
export function isIframeEmbedWidget(viewElement) {
return isWidget(viewElement) && !!viewElement.getCustomProperty('iframe-emb');
}
/**
* Gets `iframeEmbed` element from selection.
*
* @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
* The current selection.
* @return {module:engine/model/element~Element|null}
* The `iframeEmbed` element which could be either the current selected an
* ancestor of the selection. Returns null if the selection has no Iframe
* Embed element.
*
* @private
*/
export function getClosestSelectedIframeEmbedElement(selection) {
const selectedElement = selection.getSelectedElement();
return isIframeEmbed(selectedElement)
? selectedElement
: selection.getFirstPosition().findAncestor('iframeEmbed');
}
/**
* Gets selected IframeEmbed widget if only iframeEmbed is currently selected.
*
* @param {module:engine/model/selection~Selection} selection
* The current selection.
* @return {module:engine/view/element~Element|null}
* The currently selected Grid widget or null.
*
* @private
*/
export function getClosestSelectedIframeEmbedWidget(selection) {
const viewElement = selection.getSelectedElement();
if (viewElement && isIframeEmbedWidget(viewElement)) {
return viewElement;
}
let { parent } = selection.getFirstPosition();
while (parent) {
if (parent.is("element") && isIframeEmbedWidget(parent)) {
return parent;
}
parent = parent.parent;
}
return null;
}
import IframeEmbed from "./iframeEmbed";
export default {
IframeEmbed,
};
.ck iframe.ck-widget {
pointer-events: none;
}
.ck.ck-iframe-embed-form {
padding: 5px;
row-gap: 5px;
display: grid;
}
This diff is collapsed.
{
"name": "drupal-ckeditor5-iframe",
"version": "1.0.0",
"description": "Drupal CKEditor 5 iFrame plugin",
"author": "",
"license": "GPL-2.0-or-later",
"scripts": {
"watch": "webpack --mode development --watch",
"build": "webpack"
},
"devDependencies": {
"@ckeditor/ckeditor5-dev-utils": "^30.0.0",
"ckeditor5": "~34.1.0",
"raw-loader": "^4.0.2",
"terser-webpack-plugin": "^5.2.0",
"webpack": "^5.51.1",
"webpack-cli": "^4.4.0",
"css-loader": "5",
"style-loader": "2"
}
}
<?php
declare(strict_types=1);
namespace Drupal\ckeditor_iframe\Plugin\CKEditor5Plugin;
use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface;
use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableTrait;
use Drupal\ckeditor5\Plugin\CKEditor5PluginElementsSubsetInterface;
use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
use Drupal\Core\Form\FormStateInterface;
use Drupal\editor\EditorInterface;
/**
* Defines a Drupal IFrame plugin for CKEditor5.
*/
class Iframe extends CKEditor5PluginDefault implements CKEditor5PluginElementsSubsetInterface, CKEditor5PluginConfigurableInterface {
use CKEditor5PluginConfigurableTrait;
/**
* {@inheritDoc}
*/
public function getElementsSubset(): array {
$requiredAttributes = [
'src',
];
$allowedAttributes = array_merge($requiredAttributes, $this->configuration['enabled_optional_attributes']);
$allowedAttributes = array_combine($allowedAttributes, $allowedAttributes);
return [
'<iframe>',
sprintf("<iframe %s>", implode(' ', $allowedAttributes))
];
}
/**
* {@inheritDoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$checkboxOptions = [];
foreach ($this->getOptionalAttributes() as $attribute) {
$label = $attribute;
if (in_array($attribute, $this->getDeprecatedOptionalAttributes())) {
$label .= ' ' . $this->t('(deprecated)');
}
$checkboxOptions[$attribute] = $label;
}
$form['enabled_optional_attributes'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Allowed optional attributes'),
'#options' => $checkboxOptions,
'#default_value' => $this->configuration['enabled_optional_attributes'],
'#description' => $this->t("Beyond src, which attributes to allow on iframes. Refer to <a href=':url'>Mozilla's iFrame documentation</a> for a description of these attributes.", [
':url' => 'https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe',
]),
];
return $form;
}
/**
* {@inheritDoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
}
/**
* {@inheritDoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$submittedAttributes = array_filter(array_values($form_state->getValue('enabled_optional_attributes')));
$this->configuration['enabled_optional_attributes'] = $submittedAttributes;
}
/**
* {@inheritDoc}
*/
public function defaultConfiguration() {
return [
'enabled_optional_attributes' => array_diff($this->getOptionalAttributes(), $this->getDeprecatedOptionalAttributes()),
];
}
/**
* {@inheritDoc}
*/
public function getDynamicPluginConfig(array $static_plugin_config, EditorInterface $editor): array {
return [
'iframe' => [
'enabled_optional_attributes' => $this->configuration['enabled_optional_attributes'],
],
];
}
/**
* Get a list of available attributes to allow for an iFrame.
*
* @return array
* The attributes.
*/
private function getOptionalAttributes(): array {
return [
'align',
'frameborder',
'height',
'width',
'longdesc',
'name',
'scrolling',
'tabindex',
'title',
];
}
/**
* List of attributes that shouldn't be used anymore.
*
* @return string[]
* The list of deprecated attributes.
*/
private function getDeprecatedOptionalAttributes(): array {
return [
'align',
'frameborder',
'longdesc',
'scrolling',
];
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment