diff --git a/vendor/magento/module-page-builder/etc/adminhtml/di.xml b/vendor/magento/module-page-builder/etc/adminhtml/di.xml index 27ba46bfe..a41431e3b 100644 --- a/vendor/magento/module-page-builder/etc/adminhtml/di.xml +++ b/vendor/magento/module-page-builder/etc/adminhtml/di.xml @@ -555,6 +555,10 @@ Category Block + + data-content-type + contenteditable + Root Container diff --git a/vendor/magento/module-page-builder/etc/di.xml b/vendor/magento/module-page-builder/etc/di.xml index 54f6f9885..452ae35fa 100644 --- a/vendor/magento/module-page-builder/etc/di.xml +++ b/vendor/magento/module-page-builder/etc/di.xml @@ -414,6 +414,7 @@ data-video-overlay-color data-video-play-only-visible data-video-src + data-placeholder href id role diff --git a/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/banner/preview.js b/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/banner/preview.js index 10c79fc2e..98a91c95b 100644 --- a/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/banner/preview.js +++ b/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/banner/preview.js @@ -423,6 +423,14 @@ define(["jarallax", "jarallaxVideo", "jquery", "jquery/z-index", "mage/translate _proto.bindEvents = function bindEvents() { var _this8 = this; + this.contentType.dataStore.subscribe(function (state) { + var sanitizedContent = (0, _editor.removeReservedHtmlAttributes)(state.message); + + if (sanitizedContent !== state.message) { + state.message = sanitizedContent; + } + }, "message"); + _preview2.prototype.bindEvents.call(this); _events.on("banner:mountAfter", function (args) { diff --git a/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/slide/preview.js b/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/slide/preview.js index 3d7419d41..ab7680e3c 100644 --- a/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/slide/preview.js +++ b/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/slide/preview.js @@ -420,6 +420,14 @@ define(["jarallax", "jarallaxVideo", "jquery", "knockout", "mage/translate", "Ma _proto.bindEvents = function bindEvents() { var _this8 = this; + this.contentType.dataStore.subscribe(function (state) { + var sanitizedContent = (0, _editor.removeReservedHtmlAttributes)(state.content); + + if (sanitizedContent !== state.content) { + state.content = sanitizedContent; + } + }, "content"); + _preview2.prototype.bindEvents.call(this); _events.on("slide:mountAfter", function (args) { diff --git a/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/text/preview.js b/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/text/preview.js index 298b68954..ddefc717d 100644 --- a/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/text/preview.js +++ b/vendor/magento/module-page-builder/view/adminhtml/web/js/content-type/text/preview.js @@ -290,15 +290,16 @@ define(["jquery", "Magento_PageBuilder/js/events", "underscore", "Magento_PageBu _proto.bindEvents = function bindEvents() { var _this7 = this; - _preview2.prototype.bindEvents.call(this); - this.contentType.dataStore.subscribe(function (state) { - var sanitizedContent = (0, _editor.replaceDoubleQuoteWithSingleQuoteWithinVariableDirective)((0, _editor.escapeDoubleQuoteWithinWidgetDirective)(state.content)); + var sanitizedContent = (0, _editor.removeReservedHtmlAttributes)((0, _editor.replaceDoubleQuoteWithSingleQuoteWithinVariableDirective)((0, _editor.escapeDoubleQuoteWithinWidgetDirective)(state.content))); if (sanitizedContent !== state.content) { state.content = sanitizedContent; } - }); // After drop of new content type open TinyMCE and focus + }, "content"); + + _preview2.prototype.bindEvents.call(this); // After drop of new content type open TinyMCE and focus + _events.on("text:dropAfter", function (args) { if (args.id === _this7.contentType.id) { diff --git a/vendor/magento/module-page-builder/view/adminhtml/web/js/utils/editor.js b/vendor/magento/module-page-builder/view/adminhtml/web/js/utils/editor.js index 98c881044..89cf16e8b 100644 --- a/vendor/magento/module-page-builder/view/adminhtml/web/js/utils/editor.js +++ b/vendor/magento/module-page-builder/view/adminhtml/web/js/utils/editor.js @@ -432,6 +432,44 @@ define(["jquery", "mage/adminhtml/tools", "mage/translate", "mageUtils", "Magent return "<" + tag + sanitizedAttributes + ">"; }); } + /** + * Remove Page Builder reserved html tag attributes from the content + * + * @param {string} content + * @returns {string} + */ + + + function removeReservedHtmlAttributes(content) { + var attributes = _config.getConfig("stage_config").reserved_html_attributes || {}; + + for (var _i = 0, _Object$keys = Object.keys(attributes); _i < _Object$keys.length; _i++) { + var attribute = _Object$keys[_i]; + content = removeHtmlTagAttribute(content, attribute); + } + + return content; + } + /** + * Remove attribute from html tags + * + * @param {string} content + * @param {string} name + * @returns {string} + */ + + + function removeHtmlTagAttribute(content, name) { + if (typeof content === "string" && content.indexOf(name + "=") !== -1) { + var html = new DOMParser().parseFromString(content, "text/html"); + html.querySelectorAll("[" + name + "]").forEach(function (child) { + child.removeAttribute(name); + }); + content = html.body.innerHTML; + } + + return content; + } return { isWysiwygSupported: isWysiwygSupported, @@ -448,7 +486,8 @@ define(["jquery", "mage/adminhtml/tools", "mage/translate", "mageUtils", "Magent processInlineStyles: processInlineStyles, escapeDoubleQuoteWithinWidgetDirective: escapeDoubleQuoteWithinWidgetDirective, unescapeDoubleQuoteWithinWidgetDirective: unescapeDoubleQuoteWithinWidgetDirective, - replaceDoubleQuoteWithSingleQuoteWithinVariableDirective: replaceDoubleQuoteWithSingleQuoteWithinVariableDirective + replaceDoubleQuoteWithSingleQuoteWithinVariableDirective: replaceDoubleQuoteWithSingleQuoteWithinVariableDirective, + removeReservedHtmlAttributes: removeReservedHtmlAttributes }; }); //# sourceMappingURL=editor.js.map \ No newline at end of file diff --git a/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/banner/preview.ts b/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/banner/preview.ts index 2e88154da..6d885562b 100644 --- a/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/banner/preview.ts +++ b/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/banner/preview.ts @@ -24,6 +24,7 @@ import { isWysiwygSupported, lockImageSize, moveToBookmark, + removeReservedHtmlAttributes, unlockImageSize, } from "../../utils/editor"; import nestingLinkDialog from "../../utils/nesting-link-dialog"; @@ -493,6 +494,14 @@ export default class Preview extends BasePreview { * @inheritDoc */ protected bindEvents() { + this.contentType.dataStore.subscribe((state: DataObject) => { + const sanitizedContent = removeReservedHtmlAttributes(state.message); + + if (sanitizedContent !== state.message) { + state.message = sanitizedContent; + } + }, "message"); + super.bindEvents(); events.on("banner:mountAfter", (args: ContentTypeReadyEventParamsInterface) => { if (args.id === this.contentType.id) { diff --git a/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/slide/preview.ts b/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/slide/preview.ts index a3a2b019e..d96bfb84f 100644 --- a/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/slide/preview.ts +++ b/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/slide/preview.ts @@ -25,6 +25,7 @@ import { isWysiwygSupported, lockImageSize, moveToBookmark, + removeReservedHtmlAttributes, unlockImageSize, } from "../../utils/editor"; import nestingLinkDialog from "../../utils/nesting-link-dialog"; @@ -488,6 +489,14 @@ export default class Preview extends BasePreview { * @inheritDoc */ protected bindEvents() { + this.contentType.dataStore.subscribe((state: DataObject) => { + const sanitizedContent = removeReservedHtmlAttributes(state.content); + + if (sanitizedContent !== state.content) { + state.content = sanitizedContent; + } + }, "content"); + super.bindEvents(); events.on("slide:mountAfter", (args: ContentTypeReadyEventParamsInterface) => { diff --git a/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/text/preview.ts b/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/text/preview.ts index bff354634..9ce6981ac 100644 --- a/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/text/preview.ts +++ b/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/content-type/text/preview.ts @@ -20,6 +20,7 @@ import { isWysiwygSupported, lockImageSize, moveToBookmark, + removeReservedHtmlAttributes, replaceDoubleQuoteWithSingleQuoteWithinVariableDirective, unlockImageSize, } from "../../utils/editor"; @@ -306,17 +307,19 @@ export default class Preview extends BasePreview { * Bind events */ protected bindEvents() { - super.bindEvents(); - this.contentType.dataStore.subscribe((state: DataObject) => { - const sanitizedContent = replaceDoubleQuoteWithSingleQuoteWithinVariableDirective( - escapeDoubleQuoteWithinWidgetDirective(state.content), - ); + const sanitizedContent = removeReservedHtmlAttributes( + replaceDoubleQuoteWithSingleQuoteWithinVariableDirective( + escapeDoubleQuoteWithinWidgetDirective(state.content), + ), + ); if (sanitizedContent !== state.content) { state.content = sanitizedContent; } - }); + }, "content"); + + super.bindEvents(); // After drop of new content type open TinyMCE and focus events.on("text:dropAfter", (args: ContentTypeMountEventParamsInterface) => { diff --git a/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/utils/editor.ts b/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/utils/editor.ts index 2efc19458..8729b6c64 100644 --- a/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/utils/editor.ts +++ b/vendor/magento/module-page-builder/view/adminhtml/web/ts/js/utils/editor.ts @@ -469,6 +469,40 @@ export function replaceDoubleQuoteWithSingleQuoteWithinVariableDirective(content ); } +/** + * Remove Page Builder reserved html tag attributes from the content + * + * @param {string} content + * @returns {string} + */ +export function removeReservedHtmlAttributes(content: string): string +{ + const attributes: {[key: string]: string} = Config.getConfig("stage_config").reserved_html_attributes || {}; + for (const attribute of Object.keys(attributes)) { + content = removeHtmlTagAttribute(content, attribute); + } + return content; +} + +/** + * Remove attribute from html tags + * + * @param {string} content + * @param {string} name + * @returns {string} + */ +function removeHtmlTagAttribute(content: string, name: string): string +{ + if (typeof content === "string" && content.indexOf(`${name}=`) !== -1) { + const html = new DOMParser().parseFromString(content, "text/html"); + html.querySelectorAll(`[${name}]`).forEach((child: Element) => { + child.removeAttribute(name); + }); + content = html.body.innerHTML; + } + return content; +} + interface IdBookmark { id: string; keep?: boolean;