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 da013d88ee..387b206514 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 @@ -588,12 +588,15 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s var selectedElm = editor.selection.getNode(), - currentTarget; + currentTarget, + imgDomElement; 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"), @@ -605,12 +608,16 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s } else { currentTarget["id"] = img.attr("rel"); } + + if(hasDataTmpImg){ + currentTarget["tmpimg"] = img.attr("data-tmpimg"); + } } userService.getCurrentUser().then(function (userData) { if (callback) { angularHelper.safeApply($rootScope, function() { - callback(currentTarget, userData); + callback(currentTarget, userData, imgDomElement); }); } }); @@ -618,25 +625,77 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s }); }, - insertMediaInEditor: function (editor, img) { + insertMediaInEditor: function (editor, img, imgDomElement) { 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; - var data = { - alt: img.altText || "", - src: (img.url) ? img.url : "nothing.jpg", - id: '__mcenew', - 'data-udi': img.udi - }; + if(img.url !== editor.dom.getAttrib(imgDomElement, "src")){ + hasImageSrcChanged = true; + } - editor.selection.setContent(editor.dom.createHTML('img', data)); + // If null/undefinied it will remove the attribute + editor.dom.setAttrib(imgDomElement, "alt", img.altText); - $timeout(function () { - var imgElm = editor.dom.get('__mcenew'); - sizeImageInEditor(editor, imgElm, img.url); - editor.dom.setAttrib(imgElm, 'id', null); - editor.fire('Change'); + // 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); + } - }, 500); + // 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; + } + + }); + + } } }, @@ -1402,7 +1461,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s }); //Create the insert media plugin - self.createMediaPicker(args.editor, function (currentTarget, userData) { + self.createMediaPicker(args.editor, function (currentTarget, userData, imgDomElement) { var startNodeId, startNodeIsVirtual; if (!args.model.config.startNodeId) { @@ -1425,7 +1484,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s startNodeIsVirtual: startNodeIsVirtual, dataTypeKey: args.model.dataTypeKey, submit: function (model) { - self.insertMediaInEditor(args.editor, model.selection[0]); + self.insertMediaInEditor(args.editor, model.selection[0], imgDomElement); editorService.close(); }, close: function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 837c0ebc9d..24b8b999fa 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -98,11 +98,15 @@ angular.module("umbraco") gotoStartNode(); } } else { - //if a target is specified, go look it up - generally this target will just contain ids not the actual full - //media object so we need to look it up + // if a target is specified, go look it up - generally this target will just contain ids not the actual full + // media object so we need to look it up var id = $scope.target.udi ? $scope.target.udi : $scope.target.id; var altText = $scope.target.altText; - entityResource.getById(id, "Media") + + // ID of a UDI or legacy int ID still could be null/undefinied here + // As user may dragged in an image that has not been saved to media section yet + if(id){ + entityResource.getById(id, "Media") .then(function (node) { $scope.target = node; if (ensureWithinStartNode(node)) { @@ -113,6 +117,12 @@ angular.module("umbraco") } }, gotoStartNode); + } + else { + // No ID set - then this is going to be a tmpimg that has not been uploaded + // User editing this will want to be changing the ALT text + $scope.openDetailsDialog(); + } } } @@ -508,4 +518,4 @@ angular.module("umbraco") onInit(); - }); \ No newline at end of file + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediafolderpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediafolderpicker.controller.js index ddfca295d4..1f39c2423e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediafolderpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediafolderpicker.controller.js @@ -27,7 +27,7 @@ function mediaFolderPickerController($scope, editorService, entityResource) { $scope.add = function() { var mediaPickerOptions = { view: "mediapicker", - multiPicker: true, + multiPicker: false, // We only want to allow you to pick one folder at a given time disableFolderSelect: false, onlyImages: false, onlyFolders: true, diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 0b37638afd..2c2c69d181 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using System; +using System.Collections.Generic; using System.Linq; using Umbraco.Core; using Umbraco.Core.Logging; @@ -86,26 +87,63 @@ namespace Umbraco.Web.PropertyEditors if (rawJson.IsNullOrWhiteSpace()) return null; - var grid = JsonConvert.DeserializeObject(rawJson); + var grid = DeserializeGridValue(rawJson, out var rtes); - // Find all controls that use the RTE editor - var controls = grid.Sections.SelectMany(x => x.Rows.SelectMany(r => r.Areas).SelectMany(a => a.Controls)); - var rtes = controls.Where(x => x.Editor.Alias.ToLowerInvariant() == "rte"); + var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId; - foreach(var rte in rtes) + //process the rte values + foreach (var rte in rtes) { // Parse the HTML var html = rte.Value?.ToString(); - var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId; + var parseAndSavedTempImages = TemplateUtilities.FindAndPersistPastedTempImages(html, mediaParentId, userId, _mediaService, _contentTypeBaseServiceProvider, _logger); + var editorValueWithMediaUrlsRemoved = TemplateUtilities.RemoveMediaUrlsFromTextString(parseAndSavedTempImages); - var parsedHtml = TemplateUtilities.FindAndPersistPastedTempImages(html, mediaParentId, userId, _mediaService, _contentTypeBaseServiceProvider, _logger); - rte.Value = parsedHtml; + rte.Value = editorValueWithMediaUrlsRemoved; } // Convert back to raw JSON for persisting return JsonConvert.SerializeObject(grid); } + + /// + /// Ensures that the rich text editor values are processed within the grid + /// + /// + /// + /// + /// + /// + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) + { + var val = property.GetValue(culture, segment); + if (val == null) return string.Empty; + + var grid = DeserializeGridValue(val.ToString(), out var rtes); + + //process the rte values + foreach (var rte in rtes.ToList()) + { + var html = rte.Value?.ToString(); + + var propertyValueWithMediaResolved = TemplateUtilities.ResolveMediaFromTextString(html); + rte.Value = propertyValueWithMediaResolved; + } + + return grid; + } + + private GridValue DeserializeGridValue(string rawJson, out IEnumerable richTextValues) + { + var grid = JsonConvert.DeserializeObject(rawJson); + + // Find all controls that use the RTE editor + var controls = grid.Sections.SelectMany(x => x.Rows.SelectMany(r => r.Areas).SelectMany(a => a.Controls)); + richTextValues = controls.Where(x => x.Editor.Alias.ToLowerInvariant() == "rte"); + + return grid; + } } } } diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index d29b96c1db..3eed40c8bf 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -114,16 +114,16 @@ namespace Umbraco.Web.PropertyEditors if (editorValue.Value == null) return null; - var editorValueWithMediaUrlsRemoved = TemplateUtilities.RemoveMediaUrlsFromTextString(editorValue.Value.ToString()); - var parsed = MacroTagParser.FormatRichTextContentForPersistence(editorValueWithMediaUrlsRemoved); - var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId; var config = editorValue.DataTypeConfiguration as RichTextConfiguration; var mediaParent = config?.MediaParentId; var mediaParentId = mediaParent == null ? Guid.Empty : mediaParent.Guid; - parsed = TemplateUtilities.FindAndPersistPastedTempImages(parsed, mediaParentId, userId, _mediaService, _contentTypeBaseServiceProvider, _logger); + var parseAndSavedTempImages = TemplateUtilities.FindAndPersistPastedTempImages(editorValue.Value.ToString(), mediaParentId, userId, _mediaService, _contentTypeBaseServiceProvider, _logger); + var editorValueWithMediaUrlsRemoved = TemplateUtilities.RemoveMediaUrlsFromTextString(parseAndSavedTempImages); + var parsed = MacroTagParser.FormatRichTextContentForPersistence(editorValueWithMediaUrlsRemoved); + return parsed; } }