diff --git a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs index 24ec91ba75..ef2a3738e5 100644 --- a/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/DataTypeCacheRefresher.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Serialization; @@ -62,6 +63,11 @@ namespace Umbraco.Cms.Core.Cache foreach (var payload in payloads) { _idKeyMap.ClearCache(payload.Id); + + if (dataTypeCache.Success) + { + dataTypeCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); + } } // TODO: not sure I like these? diff --git a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs index 433278b8c0..8c4e572442 100644 --- a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs @@ -262,7 +262,7 @@ namespace Umbraco.Extensions // else... if we have a property, at least let the converter return its own // vision of 'no value' (could be an empty enumerable) - otherwise, default - return property == null ? default : property.Value(publishedValueFallback, culture, segment, fallback, defaultValue); + return property == null ? default : property.Value(publishedValueFallback, culture, segment); } #endregion diff --git a/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs b/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs index 46000ab3aa..c559fc9a74 100644 --- a/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs +++ b/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs @@ -32,16 +32,9 @@ namespace Umbraco.Extensions // we have a value // try to cast or convert it var value = property.GetValue(culture, segment); - if (value is T valueAsT) - { - return valueAsT; - } - + if (value is T valueAsT) return valueAsT; var valueConverted = value.TryConvertTo(); - if (valueConverted) - { - return valueConverted.Result; - } + if (valueConverted) return valueConverted.Result; // cannot cast nor convert the value, nothing we can return but 'default' // note: we don't want to fallback in that case - would make little sense @@ -50,28 +43,14 @@ namespace Umbraco.Extensions // we don't have a value, try fallback if (publishedValueFallback.TryGetValue(property, culture, segment, fallback, defaultValue, out var fallbackValue)) - { return fallbackValue; - } // we don't have a value - neither direct nor fallback // give a chance to the converter to return something (eg empty enumerable) var noValue = property.GetValue(culture, segment); - if (noValue == null) - { - return default; - } - - if (noValue is T noValueAsT) - { - return noValueAsT; - } - + if (noValue is T noValueAsT) return noValueAsT; var noValueConverted = noValue.TryConvertTo(); - if (noValueConverted) - { - return noValueConverted.Result; - } + if (noValueConverted) return noValueConverted.Result; // cannot cast noValue nor convert it, nothing we can return but 'default' return default; diff --git a/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs b/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs index 06faedea0d..967347e83d 100644 --- a/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs @@ -5,6 +5,9 @@ /// public class MediaPickerConfiguration : IIgnoreUserStartNodesConfig { + [ConfigurationField("notice", "You can NOT change the property editor", "obsoletemediapickernotice")] + public bool Notice { get; set; } + [ConfigurationField("multiPicker", "Pick multiple items", "boolean")] public bool Multiple { get; set; } diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs index 6e57dba620..248325a30e 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs @@ -151,14 +151,14 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install //New UDI pickers with newer Ids _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = new Guid("FD1E0DA5-5606-4862-B679-5D0CF3A52A59"), Text = "Content Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = new Guid("1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"), Text = "Member Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker (old)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = new Guid("B4E3535A-1753-47E2-8568-602CF8CFEE6F"), Text = "Multi URL Picker (old)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "(Obsolete) Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "(Obsolete) Multiple Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = new Guid("B4E3535A-1753-47E2-8568-602CF8CFEE6F"), Text = "Multi URL Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1051, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1051", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3Guid, Text = "Media Picker 3", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1052, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1052", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleGuid, Text = "Multiple Media Picker 3", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1053, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1053", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3SingleImageGuid, Text = "Image Media Picker 3", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1054, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1054", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleImagesGuid, Text = "Multiple Image Media Picker 3", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1051, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1051", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3Guid, Text = "Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1052, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1052", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleGuid, Text = "Multiple Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1053, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1053", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3SingleImageGuid, Text = "Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1054, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1054", SortOrder = 2, UniqueId = Cms.Core.Constants.DataTypes.Guids.MediaPicker3MultipleImagesGuid, Text = "Multiple Image Media Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs index 290f90e299..0b80116c2e 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Core.PropertyEditors [DataEditor( Constants.PropertyEditors.Aliases.MediaPicker3, EditorType.PropertyValue, - "Media Picker v3", + "Media Picker", "mediapicker3", ValueType = ValueTypes.Json, Group = Constants.PropertyEditors.Groups.Media, diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs index ec1d52ba1d..a3cd7278a6 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs @@ -19,11 +19,12 @@ namespace Umbraco.Cms.Core.PropertyEditors [DataEditor( Constants.PropertyEditors.Aliases.MediaPicker, EditorType.PropertyValue | EditorType.MacroParameter, - "Media Picker", + "(Obsolete)Media Picker", "mediapicker", ValueType = ValueTypes.Text, Group = Constants.PropertyEditors.Groups.Media, - Icon = Constants.Icons.MediaImage)] + Icon = Constants.Icons.MediaImage, + IsDeprecated = true)] public class MediaPickerPropertyEditor : DataEditor { private readonly IIOHelper _ioHelper; diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs index b4f5abc1f2..2aba18cfe6 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs @@ -129,7 +129,7 @@ namespace Umbraco.Tests.TestHelpers.Entities content.SetValue("ddl", "1234"); content.SetValue("chklist", "randomc"); content.SetValue("contentPicker", Udi.Create(Constants.UdiEntityType.Document, new Guid("74ECA1D4-934E-436A-A7C7-36CC16D4095C")).ToString()); - content.SetValue("mediaPicker", Udi.Create(Constants.UdiEntityType.Media, new Guid("44CB39C8-01E5-45EB-9CF8-E70AAF2D1691")).ToString()); + content.SetValue("mediaPicker3", "[{\"key\": \"8f78ce9e-8fe0-4500-a52d-4c4f35566ba9\",\"mediaKey\": \"44CB39C8-01E5-45EB-9CF8-E70AAF2D1691\",\"crops\": [],\"focalPoint\": {\"left\": 0.5,\"top\": 0.5}}]"); content.SetValue("memberPicker", Udi.Create(Constants.UdiEntityType.Member, new Guid("9A50A448-59C0-4D42-8F93-4F1D55B0F47D")).ToString()); content.SetValue("multiUrlPicker", "[{\"name\":\"https://test.com\",\"url\":\"https://test.com\"}]"); content.SetValue("tags", "this,is,tags"); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index 0d4a669be4..f9bdbe25ab 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -355,7 +355,7 @@ namespace Umbraco.Tests.TestHelpers.Entities contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.DropDownListFlexible, ValueStorageType.Integer) { Alias = "ddl", Name = "Dropdown List", Mandatory = false, SortOrder = 14, DataTypeId = -42 }); contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.CheckBoxList, ValueStorageType.Nvarchar) { Alias = "chklist", Name = "Checkbox List", Mandatory = false, SortOrder = 15, DataTypeId = -43 }); contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.ContentPicker, ValueStorageType.Integer) { Alias = "contentPicker", Name = "Content Picker", Mandatory = false, SortOrder = 16, DataTypeId = 1046 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MediaPicker, ValueStorageType.Integer) { Alias = "mediaPicker", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeId = 1048 }); + contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MediaPicker3, ValueStorageType.Integer) { Alias = "mediapicker3", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeId = 1051 }); contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MemberPicker, ValueStorageType.Integer) { Alias = "memberPicker", Name = "Member Picker", Mandatory = false, SortOrder = 18, DataTypeId = 1047 }); contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MultiUrlPicker, ValueStorageType.Nvarchar) { Alias = "multiUrlPicker", Name = "Multi URL Picker", Mandatory = false, SortOrder = 21, DataTypeId = 1050 }); contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Tags, ValueStorageType.Ntext) { Alias = "tags", Name = "Tags", Mandatory = false, SortOrder = 22, DataTypeId = 1041 }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js index 231ee4e866..590f9627ab 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditors.directive.js @@ -12,6 +12,11 @@ var isLeftColumnAbove = false; scope.editors = []; + /* we need to keep a count of open editors because the length of the editors array is first changed when animations are done + we do this because some infinite editors close more than one editor at the time and we get the wrong count from editors.length + because of the animation */ + let editorCount = 0; + function addEditor(editor) { editor.inFront = true; editor.moveRight = true; @@ -51,13 +56,16 @@ updateEditors(-1); - if(scope.editors.length === 1){ + if(scope.editors.length === 1) { if(isLeftColumnAbove){ $('#leftcolumn').addClass(aboveBackDropCssClass); } isLeftColumnAbove = false; + } + // when the last editor is closed remove the focus lock + if (editorCount === 0) { // Remove the inert attribute from the #mainwrapper focusLockService.removeInertAttribute(); } @@ -105,16 +113,19 @@ } evts.push(eventsService.on("appState.editors.open", function (name, args) { + editorCount = editorCount + 1; addEditor(args.editor); })); evts.push(eventsService.on("appState.editors.close", function (name, args) { // remove the closed editor if (args && args.editor) { + editorCount = editorCount - 1; removeEditor(args.editor); } // close all editors if (args && !args.editor && args.editors.length === 0) { + editorCount = 0; scope.editors = []; } })); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js index 03d376e36a..e1639dde26 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbfocuslock.directive.js @@ -40,7 +40,9 @@ function getDomNodes(){ infiniteEditorsWrapper = document.querySelector('.umb-editors'); - infiniteEditors = Array.from(infiniteEditorsWrapper.querySelectorAll('.umb-editor')); + if(infiniteEditorsWrapper) { + infiniteEditors = Array.from(infiniteEditorsWrapper.querySelectorAll('.umb-editor') || []); + } } function getFocusableElements(targetElm) { @@ -84,22 +86,24 @@ var defaultFocusedElement = getAutoFocusElement(focusableElements); var lastKnownElement; - // If an inifite editor is being closed then we reset the focus to the element that triggered the the overlay + // If an infinite editor is being closed then we reset the focus to the element that triggered the the overlay if(closingEditor){ - var lastItemIndex = $rootScope.lastKnownFocusableElements.length - 1; - var editorInfo = infiniteEditors[0].querySelector('.editor-info'); // If there is only one editor open, search for the "editor-info" inside it and set focus on it // This is relevant when a property editor has been selected and the editor where we selected it from // is closed taking us back to the first layer // Otherwise set it to the last element in the lastKnownFocusedElements array - if(infiniteEditors.length === 1 && editorInfo !== null){ - lastKnownElement = editorInfo; + if(infiniteEditors && infiniteEditors.length === 1){ + var editorInfo = infiniteEditors[0].querySelector('.editor-info'); + if(infiniteEditors && infiniteEditors.length === 1 && editorInfo !== null) { + lastKnownElement = editorInfo; - // Clear the array - clearLastKnownFocusedElements(); + // Clear the array + clearLastKnownFocusedElements(); + } } else { + var lastItemIndex = $rootScope.lastKnownFocusableElements.length - 1; lastKnownElement = $rootScope.lastKnownFocusableElements[lastItemIndex]; // Remove the last item from the array so we always set the correct lastKnowFocus for each layer @@ -149,20 +153,24 @@ } function cleanupEventHandlers() { - var activeEditor = infiniteEditors[infiniteEditors.length - 1]; - var inactiveEditors = infiniteEditors.filter(editor => editor !== activeEditor); + //if we're in infinite editing mode + if(infiniteEditors.length > 0) { + var activeEditor = infiniteEditors[infiniteEditors.length - 1]; + var inactiveEditors = infiniteEditors.filter(editor => editor !== activeEditor); - if(inactiveEditors.length > 0) { - for (var index = 0; index < inactiveEditors.length; index++) { - var inactiveEditor = inactiveEditors[index]; + if(inactiveEditors.length > 0) { + for (var index = 0; index < inactiveEditors.length; index++) { + var inactiveEditor = inactiveEditors[index]; - // Remove event handlers from inactive editors - inactiveEditor.removeEventListener('keydown', handleKeydown); + // Remove event handlers from inactive editors + inactiveEditor.removeEventListener('keydown', handleKeydown); + } + } + else { + // Why is this one only begin called if there is no other infinite editors, wouldn't it make sense always to clean this up? + // Remove event handlers from the active editor + activeEditor.removeEventListener('keydown', handleKeydown); } - } - else { - // Remove event handlers from the active editor - activeEditor.removeEventListener('keydown', handleKeydown); } } @@ -173,10 +181,7 @@ // Fetch the DOM nodes we need getDomNodes(); - // Cleanup event handlers if we're in infinite editing mode - if(infiniteEditors.length > 0){ - cleanupEventHandlers(); - } + cleanupEventHandlers(); getFocusableElements(targetElm); @@ -204,17 +209,19 @@ // Make sure to disconnect the observer so we potentially don't end up with having many active ones disconnectObserver = true; - // Pass the correct editor in order to find the focusable elements - var newTarget = infiniteEditors[infiniteEditors.length - 2]; + if(infiniteEditors && infiniteEditors.length > 1) { + // Pass the correct editor in order to find the focusable elements + var newTarget = infiniteEditors[infiniteEditors.length - 2]; - if(infiniteEditors.length > 1){ - // Setting closing till true will let us re-apply the last known focus to then opened layer that then becomes - // active - closingEditor = true; + if(infiniteEditors.length > 1) { + // Setting closing till true will let us re-apply the last known focus to then opened layer that then becomes + // active + closingEditor = true; - onInit(newTarget); + onInit(newTarget); - return; + return; + } } // Clear lastKnownFocusableElements diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js index 744e4280db..60ba71d7a5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js @@ -26,7 +26,7 @@ angular.module("umbraco.directives") forceUpdate: '@?' }, - link: function (scope, element, attrs) { + link: function (scope, element, attrs, windowResizeListener) { var unsubscribe = []; let sliderRef = null; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 3f90b0c7ab..9733429b21 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -48,8 +48,6 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt }); } else if (value[0]) { - //notificationsService.error("Validation", value[0]); - console.log({type:messageType, header:"Validation", message:value[0]}) notificationsService.showNotification({type:messageType, header:"Validation", message:value[0]}) } } 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 6c0165ebfe..bcaeb32096 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 @@ -441,7 +441,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s // We are not ready to limit the pasted elements further than default, we will return to this feature. ( TODO: Make this feature an option. ) // We keep spans here, cause removing spans here also removes b-tags inside of them, instead we strip them out later. (TODO: move this definition to the config file... ) var validPasteElements = "-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody,img[src|alt|width|height],ul,ol,li,hr,pre,dl,dt,figure,figcaption,wbr" - + // add elements from user configurated styleFormats to our list of validPasteElements. // (This means that we only allow H3-element if its configured as a styleFormat on this specific propertyEditor.) var style, i = 0; @@ -610,7 +610,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s 'contenteditable': false }, embed.preview); - + // Only replace if activeElement is an Embed element. if (activeElement && activeElement.nodeName.toUpperCase() === "DIV" && activeElement.classList.contains("embeditem")){ activeElement.replaceWith(wrapper); // directly replaces the html node @@ -738,9 +738,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s 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 () { @@ -761,7 +761,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s } }); - + } } }, @@ -1401,11 +1401,26 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s function syncContent() { + if(args.model.value === args.editor.getContent()) { + return; + } + //stop watching before we update the value stopWatch(); angularHelper.safeApply($rootScope, function () { args.model.value = args.editor.getContent(); + + //make the form dirty manually so that the track changes works, setting our model doesn't trigger + // the angular bits because tinymce replaces the textarea. + if (args.currentForm) { + args.currentForm.$setDirty(); + } + // With complex validation we need to set a input field to dirty, not the form. but we will keep the old code for backwards compatibility. + if (args.currentFormInput) { + args.currentFormInput.$setDirty(); + } }); + //re-watch the value startWatch(); } @@ -1430,7 +1445,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s // Upload BLOB images (dragged/pasted ones) // find src attribute where value starts with `blob:` - // search is case-insensitive and allows single or double quotes + // search is case-insensitive and allows single or double quotes if(content.search(/src=["']blob:.*?["']/gi) !== -1){ args.editor.uploadImages(function(data) { // Once all images have been uploaded @@ -1496,6 +1511,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s args.editor.on('Change', function (e) { syncContent(); }); + args.editor.on('Keyup', function (e) { + syncContent(); + }); //when we leave the editor (maybe) args.editor.on('blur', function (e) { @@ -1520,12 +1538,6 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s args.editor.on('Dirty', function (e) { syncContent(); // Set model.value to the RTE's content - - //make the form dirty manually so that the track changes works, setting our model doesn't trigger - // the angular bits because tinymce replaces the textarea. - if (args.currentForm) { - args.currentForm.$setDirty(); - } }); let self = this; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less index cc5c17ba70..6c1980a6e4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less @@ -19,7 +19,6 @@ padding: 2px 6px; } .umb-range-slider .noUi-handle { - outline: none; cursor: grab; border-radius: 100px; border: none; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html index afa3451899..f4302551ab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediaentryeditor/mediaentryeditor.html @@ -35,7 +35,7 @@ width="{{crop.width}}" max-size="75"> - {{crop.alias}} + {{crop.label}} diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/obsoletemediapickernotice.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/obsoletemediapickernotice.html new file mode 100644 index 0000000000..cc861dcb4b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/obsoletemediapickernotice.html @@ -0,0 +1 @@ +

Important: switching from the (Obsolete) Media Picker to Media Picker will mean all data (references to previously selected media items) will be deleted and no longer available.

\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index 74a70118eb..9faa012a4d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -5,12 +5,12 @@ angular.module("umbraco") // TODO: A lot of the code below should be shared between the grid rte and the normal rte $scope.isLoading = true; - + //To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias // because now we have to support having 2x (maybe more at some stage) content editors being displayed at once. This is because // we have this mini content editor panel that can be launched with MNTP. $scope.textAreaHtmlId = $scope.model.alias + "_" + String.CreateGuid(); - + var editorConfig = $scope.model.config ? $scope.model.config.editor : null; if (!editorConfig || Utilities.isString(editorConfig)) { editorConfig = tinyMceService.defaultPrevalues(); @@ -28,14 +28,14 @@ angular.module("umbraco") $scope.containerOverflow = editorConfig.mode === "distraction-free" ? (height ? "auto" : "inherit") : "inherit"; var promises = []; - + // we need to make sure that the element is initialized before we can init TinyMCE, because we find the placeholder by ID, so it needs to be appended to document before. var initPromise = $q((resolve, reject) => { this.$onInit = resolve; }); - + promises.push(initPromise); - + //queue file loading tinyMceAssets.forEach(function (tinyJsAsset) { promises.push(assetsService.loadJs(tinyJsAsset, $scope)); @@ -50,50 +50,50 @@ angular.module("umbraco") toolbar: editorConfig.toolbar, mode: editorConfig.mode })); - + //wait for queue to end $q.all(promises).then(function (result) { - + var standardConfig = result[promises.length - 1]; - + if (height !== null) { standardConfig.plugins.splice(standardConfig.plugins.indexOf("autoresize"), 1); } - + //create a baseline Config to extend upon var baseLineConfigObj = { maxImageSize: editorConfig.maxImageSize, width: width, height: height }; - + baseLineConfigObj.setup = function (editor) { - + //set the reference tinyMceEditor = editor; - + tinyMceEditor.on('init', function (e) { $timeout(function () { $scope.isLoading = false; }); }); - + //initialize the standard editor functionality for Umbraco tinyMceService.initializeEditor({ editor: editor, model: $scope.model, - currentForm: angularHelper.getCurrentForm($scope) + currentFormInput: $scope.rteForm.modelValue }); - + }; - + angular.extend(baseLineConfigObj, standardConfig); - + // We need to wait for DOM to have rendered before we can find the element by ID. $timeout(function () { tinymce.init(baseLineConfigObj); }, 150); - + //listen for formSubmitting event (the result is callback used to remove the event subscription) var unsubscribe = $scope.$on("formSubmitting", function () { if (tinyMceEditor !== undefined && tinyMceEditor != null && !$scope.isLoading) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html index d0b5823f74..21e9065d92 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html @@ -1,8 +1,10 @@
-
- -
-
+ +
+ +
+
+
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 1de7a25d31..2ead7665e0 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -235,8 +235,12 @@ / http://localhost:8121 8130 + 8140 / - http://localhost:8130 + http://localhost:8140 + 8131 + / + http://localhost:8131 False False diff --git a/src/umbraco.sln.DotSettings b/src/umbraco.sln.DotSettings index 6fb927035e..1394ee3884 100644 --- a/src/umbraco.sln.DotSettings +++ b/src/umbraco.sln.DotSettings @@ -1,4 +1,5 @@  + True <data><IncludeFilters /><ExcludeFilters /></data> <data /> Disposable construction