From e6d6dce3a8e6b00c97676b920e6ede2f02c27741 Mon Sep 17 00:00:00 2001 From: Mehmet <36473707+ustadstar@users.noreply.github.com> Date: Mon, 28 Feb 2022 13:17:17 +0100 Subject: [PATCH] Fix mssing image caption in RTE (#11807) --- .../Models/RichTextEditorSettings.cs | 2 +- .../src/common/services/tinymce.service.js | 146 +++++++++--------- 2 files changed, 70 insertions(+), 78 deletions(-) diff --git a/src/Umbraco.Core/Configuration/Models/RichTextEditorSettings.cs b/src/Umbraco.Core/Configuration/Models/RichTextEditorSettings.cs index 6ea563c741..de8215a51b 100644 --- a/src/Umbraco.Core/Configuration/Models/RichTextEditorSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/RichTextEditorSettings.cs @@ -7,7 +7,7 @@ namespace Umbraco.Cms.Core.Configuration.Models [UmbracoOptions(Constants.Configuration.ConfigRichTextEditor)] public class RichTextEditorSettings { - internal const string StaticValidElements = "+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*]"; + internal const string StaticValidElements = "+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption"; internal const string StaticInvalidElements = "font"; private static readonly string[] s_default_plugins = new[] diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index e6eb430201..98543a7c68 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -11,7 +11,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s //These are absolutely required in order for the macros to render inline //we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce - var extendedValidElements = "@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang]"; + var extendedValidElements = "@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption"; var fallbackStyles = [{ title: "Page header", block: "h2" }, { title: "Section header", block: "h3" }, { title: "Paragraph header", block: "h4" }, { title: "Normal", block: "p" }, { title: "Quote", block: "blockquote" }, { title: "Code", block: "code" }]; // these languages are available for localization var availableLanguages = [ @@ -292,7 +292,6 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s } function sizeImageInEditor(editor, imageDomElement, imgUrl) { - var size = editor.dom.getSize(imageDomElement); if (editor.settings.maxImageSize && editor.settings.maxImageSize !== 0) { @@ -656,21 +655,19 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s stateSelector: 'img[data-udi]', onclick: function () { - - var selectedElm = editor.selection.getNode(), - currentTarget, - imgDomElement; + var selectedElm = editor.selection.getNode(), + currentTarget; if (selectedElm.nodeName === 'IMG') { var img = $(selectedElm); - imgDomElement = selectedElm; var hasUdi = img.attr("data-udi") ? true : false; var hasDataTmpImg = img.attr("data-tmpimg") ? true : false; currentTarget = { altText: img.attr("alt"), - url: img.attr("src") + url: img.attr("src"), + caption: img.attr('data-caption') }; if (hasUdi) { @@ -687,85 +684,80 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s userService.getCurrentUser().then(function (userData) { if (callback) { angularHelper.safeApply($rootScope, function() { - callback(currentTarget, userData, imgDomElement); + callback(currentTarget, userData); }); } }); } }); }, - - insertMediaInEditor: function (editor, img, imgDomElement) { + /** + * @ngdoc method + * @name umbraco.services.tinyMceService#insetMediaInEditor + * @methodOf umbraco.services.tinyMceService + * + * @description + * Inserts the image element in tinymce plugin + * + * @param {Object} editor the TinyMCE editor instance + */ + insertMediaInEditor: function (editor, img) { if (img) { - // imgElement is only definied if updating an image - // if null/undefinied then its a BRAND new image - if(imgDomElement){ - // Check if the img src has changed - // If it has we will need to do some resizing/recalc again - var hasImageSrcChanged = false; - - if(img.url !== editor.dom.getAttrib(imgDomElement, "src")){ - hasImageSrcChanged = true; + // We need to create a NEW DOM element to insert + // setting an attribute of ID to __mcenew, so we can gather a reference to the node, to be able to update its size accordingly to the size of the image. + var data = { + alt: img.altText || "", + src: (img.url) ? img.url : "nothing.jpg", + id: "__mcenew", + "data-udi": img.udi, + "data-caption": img.caption + }; + var newImage = editor.dom.createHTML('img', data); + var parentElement = editor.selection.getNode().parentElement; + + if (img.caption) { + var figCaption = editor.dom.createHTML('figcaption', {}, img.caption); + var combined = newImage + figCaption; + + if (parentElement.nodeName !== 'FIGURE') { + var fragment = editor.dom.createHTML('figure', {}, combined); + editor.selection.setContent(fragment); } - - // If null/undefinied it will remove the attribute - editor.dom.setAttrib(imgDomElement, "alt", img.altText); - - // It's possible to pick a NEW image - so need to ensure this gets updated - if(img.udi){ - editor.dom.setAttrib(imgDomElement, "data-udi", img.udi); + else { + parentElement.innerHTML = combined; } - - // It's possible to pick a NEW image - so need to ensure this gets updated - if(img.url){ - editor.dom.setAttrib(imgDomElement, "src", img.url); - } - - // Remove width & height attributes (ONLY if imgSrc changed) - // So native image size is used as this needed to re-calc width & height - // For the function sizeImageInEditor() & apply the image resizing querystrings etc.. - if(hasImageSrcChanged){ - editor.dom.setAttrib(imgDomElement, "width", null); - editor.dom.setAttrib(imgDomElement, "height", null); - - //Re-calc the image dimensions - sizeImageInEditor(editor, imgDomElement, img.url); - } - - } else{ - // We need to create a NEW DOM element to insert - // setting an attribute of ID to __mcenew, so we can gather a reference to the node, to be able to update its size accordingly to the size of the image. - var data = { - alt: img.altText || "", - src: (img.url) ? img.url : "nothing.jpg", - id: "__mcenew", - "data-udi": img.udi - }; - - editor.selection.setContent(editor.dom.createHTML('img', data)); - - // Using settimeout to wait for a DoM-render, so we can find the new element by ID. - $timeout(function () { - - var imgElm = editor.dom.get("__mcenew"); - editor.dom.setAttrib(imgElm, "id", null); - - // When image is loaded we are ready to call sizeImageInEditor. - var onImageLoaded = function() { - sizeImageInEditor(editor, imgElm, img.url); - editor.fire("Change"); - } - - // Check if image already is loaded. - if(imgElm.complete === true) { - onImageLoaded(); - } else { - imgElm.onload = onImageLoaded; - } - - }); - } + else { + //if caption is removed, remove the figure element + if (parentElement.nodeName === 'FIGURE') { + parentElement.parentElement.innerHTML = newImage; + } + else { + editor.selection.setContent(newImage); + } + } + + // Using settimeout to wait for a DoM-render, so we can find the new element by ID. + $timeout(function () { + + var imgElm = editor.dom.get("__mcenew"); + editor.dom.setAttrib(imgElm, "id", null); + + // When image is loaded we are ready to call sizeImageInEditor. + var onImageLoaded = function() { + sizeImageInEditor(editor, imgElm, img.url); + editor.fire("Change"); + } + + // Check if image already is loaded. + if(imgElm.complete === true) { + onImageLoaded(); + } else { + imgElm.onload = onImageLoaded; + } + + }); + } },