From 83b4b6a5f4d84f5e57cdbb55f8d4970df1af28a4 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sat, 8 Jun 2019 22:50:56 +0200 Subject: [PATCH 01/40] Annotate each crop with "Automatic" or "User defined" --- .../src/less/property-editors.less | 16 ++++++++++++++-- .../imagecropper/imagecropper.controller.js | 6 +++++- .../imagecropper/imagecropper.html | 2 ++ src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 ++ src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 ++ src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 8 +++++--- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 9e8dd37ab9..3ff8432f07 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -710,7 +710,8 @@ } .imagecropper .umb-sortable-thumbnails li .crop-name, - .imagecropper .umb-sortable-thumbnails li .crop-size { + .imagecropper .umb-sortable-thumbnails li .crop-size, + .imagecropper .umb-sortable-thumbnails li .crop-annotation { display: block; text-align: left; font-size: 13px; @@ -722,17 +723,28 @@ margin: 10px 0 5px; } - .imagecropper .umb-sortable-thumbnails li .crop-size { + .imagecropper .umb-sortable-thumbnails li .crop-size, + .imagecropper .umb-sortable-thumbnails li .crop-annotation { font-size: 10px; font-style: italic; margin: 0 0 5px; } + .imagecropper .umb-sortable-thumbnails li .crop-annotation { + color: @gray-6; + } + .btn-crop-delete { display: block; text-align: left; } + .imagecropper .cropList-container { + h5 { + margin-left: 10px; + margin-top: 0; + } + } // // folder-browser // -------------------------------------------------- diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js index b5ae731c94..3a8e6db8d8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js @@ -13,7 +13,7 @@ angular.module('umbraco') $scope.clear = clear; $scope.reset = reset; $scope.close = close; - $scope.focalPointChanged = focalPointChanged; + $scope.isCustomCrop = isCustomCrop; //declare a special method which will be called whenever the value has changed from the server $scope.model.onValueChanged = onValueChanged; @@ -201,6 +201,10 @@ angular.module('umbraco') $scope.imageCropperForm.$setDirty(); }; + function isCustomCrop(crop) { + return !!crop.coordinates; + } + var unsubscribe = $scope.$on("formSubmitting", function () { $scope.currentCrop = null; $scope.currentPoint = null; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html index 7438fa04da..5604ac553d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html @@ -58,6 +58,8 @@
{{value.alias}} {{value.width}}px x {{value.height}}px + User defined + Automatic
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 9e6bdc5e57..b70f0f07af 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1019,6 +1019,8 @@ Mange hilsner fra Umbraco robotten Nulstil Acceptér Fortryd + Automatisk + Brugerdefineret Vælg en version at sammenligne med den nuværende version diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 189bd9f10b..4bf77a48d6 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1273,6 +1273,8 @@ To manage your website, simply open the Umbraco back office and start adding con Add new crop Done Undo edits + Automatic + User defined Select a version to compare with the current version diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 6ce6f82ccc..010fe7fb4a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1274,11 +1274,13 @@ To manage your website, simply open the Umbraco back office and start adding con Enter the link - Reset crop + Reset crop Save crop Add new crop - Done - Undo edits + Done + Undo edits + Automatic + User defined Select a version to compare with the current version From 6425592cc587b072129fe7bfbf520bb18c635a83 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 9 Jun 2019 12:11:45 +0200 Subject: [PATCH 02/40] Only show "custom crop" text ("automatic crop" is implicit) --- .../src/views/propertyeditors/imagecropper/imagecropper.html | 3 +-- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 - src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html index 5604ac553d..e2428cec05 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html @@ -58,8 +58,7 @@
{{value.alias}} {{value.width}}px x {{value.height}}px - User defined - Automatic + User defined 
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index b70f0f07af..15ee17ca3a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -1019,7 +1019,6 @@ Mange hilsner fra Umbraco robotten Nulstil Acceptér Fortryd - Automatisk Brugerdefineret diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 4bf77a48d6..1035848a1c 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1273,7 +1273,6 @@ To manage your website, simply open the Umbraco back office and start adding con Add new crop Done Undo edits - Automatic User defined diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 010fe7fb4a..35d89d283f 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1279,7 +1279,6 @@ To manage your website, simply open the Umbraco back office and start adding con Add new crop Done Undo edits - Automatic User defined From 201c91d3a8e90409a0b949249b8cc9b853835d50 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 9 Jun 2019 12:32:27 +0200 Subject: [PATCH 03/40] Whoops, accidental removal of focalPointChanged --- .../propertyeditors/imagecropper/imagecropper.controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js index 3a8e6db8d8..e3576426a3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js @@ -14,6 +14,7 @@ angular.module('umbraco') $scope.reset = reset; $scope.close = close; $scope.isCustomCrop = isCustomCrop; + $scope.focalPointChanged = focalPointChanged; //declare a special method which will be called whenever the value has changed from the server $scope.model.onValueChanged = onValueChanged; From 3031459b8a43e33d501433e2e0e916ac67b31793 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 16 Jul 2019 19:44:11 +0200 Subject: [PATCH 04/40] Don't allow logins for users with no content and/or media start nodes --- src/Umbraco.Web/Security/BackOfficeSignInManager.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Umbraco.Web/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web/Security/BackOfficeSignInManager.cs index b33487bc8d..66b90a1396 100644 --- a/src/Umbraco.Web/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web/Security/BackOfficeSignInManager.cs @@ -112,6 +112,19 @@ namespace Umbraco.Web.Security return SignInStatus.LockedOut; } + // We need to verify that the user belongs to one or more groups that define content and media start nodes. + // To do so we have to create the user claims identity and validate the calculated start nodes. + var userIdentity = await CreateUserIdentityAsync(user); + if(userIdentity is UmbracoBackOfficeIdentity backOfficeIdentity) + { + if(backOfficeIdentity.StartContentNodes.Length == 0 || backOfficeIdentity.StartMediaNodes.Length == 0) + { + _logger.WriteCore(TraceEventType.Information, 0, + $"Login attempt failed for username {userName} from IP address {_request.RemoteIpAddress}, no content and/or media start nodes could be found for any of the user's groups", null, null); + return SignInStatus.Failure; + } + } + await UserManager.ResetAccessFailedCountAsync(user.Id); return await SignInOrTwoFactor(user, isPersistent); } From f4d8f58505bdb3d0c942ddfb165ffe095977aaf4 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 25 Jul 2019 07:40:38 +0200 Subject: [PATCH 05/40] Update src/Umbraco.Web/Security/BackOfficeSignInManager.cs Co-Authored-By: Ronald Barendse --- src/Umbraco.Web/Security/BackOfficeSignInManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web/Security/BackOfficeSignInManager.cs index 66b90a1396..fb8aff42e8 100644 --- a/src/Umbraco.Web/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web/Security/BackOfficeSignInManager.cs @@ -115,7 +115,7 @@ namespace Umbraco.Web.Security // We need to verify that the user belongs to one or more groups that define content and media start nodes. // To do so we have to create the user claims identity and validate the calculated start nodes. var userIdentity = await CreateUserIdentityAsync(user); - if(userIdentity is UmbracoBackOfficeIdentity backOfficeIdentity) + if (userIdentity is UmbracoBackOfficeIdentity backOfficeIdentity) { if(backOfficeIdentity.StartContentNodes.Length == 0 || backOfficeIdentity.StartMediaNodes.Length == 0) { From f27b7549671e666928f422024f81bb82837b0c2d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 25 Jul 2019 07:40:44 +0200 Subject: [PATCH 06/40] Update src/Umbraco.Web/Security/BackOfficeSignInManager.cs Co-Authored-By: Ronald Barendse --- src/Umbraco.Web/Security/BackOfficeSignInManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Security/BackOfficeSignInManager.cs b/src/Umbraco.Web/Security/BackOfficeSignInManager.cs index fb8aff42e8..8e5e532731 100644 --- a/src/Umbraco.Web/Security/BackOfficeSignInManager.cs +++ b/src/Umbraco.Web/Security/BackOfficeSignInManager.cs @@ -117,7 +117,7 @@ namespace Umbraco.Web.Security var userIdentity = await CreateUserIdentityAsync(user); if (userIdentity is UmbracoBackOfficeIdentity backOfficeIdentity) { - if(backOfficeIdentity.StartContentNodes.Length == 0 || backOfficeIdentity.StartMediaNodes.Length == 0) + if (backOfficeIdentity.StartContentNodes.Length == 0 || backOfficeIdentity.StartMediaNodes.Length == 0) { _logger.WriteCore(TraceEventType.Information, 0, $"Login attempt failed for username {userName} from IP address {_request.RemoteIpAddress}, no content and/or media start nodes could be found for any of the user's groups", null, null); From a7873fc01c9b6d8803b16be0a222eb5463b5c850 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 28 Oct 2019 20:30:18 +0100 Subject: [PATCH 07/40] Trigger an RTE resize after inserting images --- .../src/common/services/tinymce.service.js | 2 ++ 1 file changed, 2 insertions(+) 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 1351da081a..e7f5a02936 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 @@ -248,6 +248,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s var src = imgUrl + "?width=" + newSize.width + "&height=" + newSize.height; editor.dom.setAttrib(imageDomElement, 'data-mce-src', src); } + + editor.execCommand("mceAutoResize", false, null, null); } } From 9c2360ef4362fc958f045e1f3c9d520a20d6f9b8 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 28 Oct 2019 22:51:30 +0100 Subject: [PATCH 08/40] Add autofocus to treepicker search box --- .../directives/components/tree/umbtreesearchbox.directive.js | 3 ++- .../views/common/infiniteeditors/treepicker/treepicker.html | 3 ++- .../src/views/components/tree/umb-tree-search-box.html | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js index 6f195dcc52..3e2e7e362e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js @@ -14,7 +14,8 @@ function treeSearchBox(localizationService, searchService, $q) { section: "@", datatypeKey: "@", hideSearchCallback: "=", - searchCallback: "=" + searchCallback: "=", + autoFocus: "=" }, restrict: "E", // restrict to an element replace: true, // replace the html element with the template diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html index 78c75f6f8d..756a08df34 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.html @@ -37,7 +37,8 @@ search-from-name="{{vm.searchInfo.searchFromName}}" show-search="{{vm.searchInfo.showSearch}}" datatype-key="{{vm.searchInfo.dataTypeKey}}" - section="{{vm.section}}"> + section="{{vm.section}}" + auto-focus="true"> diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-box.html b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-box.html index 6bb2d120c8..217489f14c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-box.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-box.html @@ -5,9 +5,9 @@ ng-model="term" class="umb-search-field search-query -full-width-input" placeholder="{{searchPlaceholderText}}" - focus-when="{{showSearch}}"> + umb-auto-focus="{{autoFocus ? 'true' : 'false'}}">

Search {{searchFromName}}

- \ No newline at end of file + From b22dba2062e99c8a9a404cac8d018c92f60912d3 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 31 Oct 2019 08:00:20 +0100 Subject: [PATCH 09/40] Use warning button style when opening doctype in infinite editing with unsaved changes --- .../components/content/umbcontentnodeinfo.directive.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js index 78a2111fc5..0ed1b12000 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js @@ -130,6 +130,7 @@ view: "default", content: labels.doctypeChangeWarning, submitButtonLabelKey: "general_continue", + submitButtonStyle: "warning", closeButtonLabelKey: "general_cancel", submit: function () { openDocTypeEditor(documentType); From 0819f1a917f8ca6e390c16c93c22541a64117b9f Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 31 Oct 2019 08:08:08 +0100 Subject: [PATCH 10/40] Mark the current variant as dirty when its name changes --- .../components/content/umbvariantcontent.directive.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js index 5dc8903e65..6fec20b256 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbvariantcontent.directive.js @@ -58,9 +58,9 @@ /** Called when the component has linked all elements, this is when the form controller is available */ function postLink() { //set the content to dirty if the header changes - unsubscribe.push($scope.$watch("contentHeaderForm.$dirty", - function(newValue, oldValue) { - if (newValue === true) { + unsubscribe.push($scope.$watch("vm.editor.content.name", + function (newValue, oldValue) { + if (newValue !== oldValue) { vm.editor.content.isDirty = true; } })); From 6b570cd5e3ea71cd5f55864cd6815e7506973255 Mon Sep 17 00:00:00 2001 From: LiamL Date: Wed, 15 Jan 2020 14:56:06 +0000 Subject: [PATCH 11/40] adds fk index to cmsDictionary -> parentId --- .../Migrations/Upgrade/UmbracoPlan.cs | 3 ++ .../AddDatabaseIndexesMissingOnForeignKeys.cs | 53 +++++++++++++++++++ .../Persistence/Dtos/DictionaryDto.cs | 4 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 3b2005bef6..b32d9fa550 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -187,6 +187,9 @@ namespace Umbraco.Core.Migrations.Upgrade To("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}"); To("{EE288A91-531B-4995-8179-1D62D9AA3E2E}"); + + To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}"); + //FINAL } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs new file mode 100644 index 0000000000..1fcb855eef --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs @@ -0,0 +1,53 @@ +using NPoco; +using System; +using System.Linq; +using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 +{ + public class AddDatabaseIndexesMissingOnForeignKeys : MigrationBase + { + public AddDatabaseIndexesMissingOnForeignKeys(IMigrationContext context) + : base(context) + { + + } + + public override void Migrate() + { + var newIndexes = new[] + { + (typeof(DictionaryDto), nameof(DictionaryDto.Parent)) + }; + + foreach (var (type, propertyName) in newIndexes) + { + CreateIndexIfNotExists(type, propertyName); + } + } + + private void CreateIndexIfNotExists(Type dto, string propertyName) + { + var property = dto.GetProperty(propertyName); + var indexName = property.GetCustomAttributes(false).OfType().Single().Name; + + if (IndexExists(indexName)) + { + return; + } + + var tableName = dto.GetCustomAttributes(false).Single().Value; + var columnName = property.GetCustomAttributes(false).OfType().Single().Name; + + + Create + .Index(indexName) + .OnTable(tableName) + .OnColumn(columnName) + .Ascending() + .WithOptions().NonClustered() // All newly defined indexes are non-clustered + .Do(); + } + } +} diff --git a/src/Umbraco.Core/Persistence/Dtos/DictionaryDto.cs b/src/Umbraco.Core/Persistence/Dtos/DictionaryDto.cs index 655474b217..d357e9adbc 100644 --- a/src/Umbraco.Core/Persistence/Dtos/DictionaryDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/DictionaryDto.cs @@ -5,11 +5,12 @@ using Umbraco.Core.Persistence.DatabaseAnnotations; namespace Umbraco.Core.Persistence.Dtos { - [TableName(Constants.DatabaseSchema.Tables.DictionaryEntry)] + [TableName(TableName)] [PrimaryKey("pk")] [ExplicitColumns] internal class DictionaryDto { + public const string TableName = Constants.DatabaseSchema.Tables.DictionaryEntry; [Column("pk")] [PrimaryKeyColumn] public int PrimaryKey { get; set; } @@ -21,6 +22,7 @@ namespace Umbraco.Core.Persistence.Dtos [Column("parent")] [NullSetting(NullSetting = NullSettings.Null)] [ForeignKey(typeof(DictionaryDto), Column = "id")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Parent")] public Guid? Parent { get; set; } [Column("key")] diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 1393971898..01cbb66fb6 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -129,6 +129,7 @@ --> + From 93e85f77f2002a8b248759400cc3e849bdf47365 Mon Sep 17 00:00:00 2001 From: LiamL Date: Fri, 17 Jan 2020 15:15:29 +0000 Subject: [PATCH 12/40] removes generics stuff & makes the migration simpler --- .../AddDatabaseIndexesMissingOnForeignKeys.cs | 50 ++++++------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs index 1fcb855eef..6c574bd3c9 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs @@ -1,8 +1,4 @@ -using NPoco; -using System; -using System.Linq; -using Umbraco.Core.Persistence.DatabaseAnnotations; -using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 { @@ -14,40 +10,26 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 } + /// + /// Adds an index to the foreign key column parent on DictionaryDto's table + /// if it doesn't already exist + /// public override void Migrate() { - var newIndexes = new[] - { - (typeof(DictionaryDto), nameof(DictionaryDto.Parent)) - }; + var tableInfo = Context.Database.PocoDataFactory.ForType(typeof(DictionaryDto)).TableInfo; + tableInfo.TableName = tableInfo.TableName; + var indexName = "IX_" + tableInfo.TableName + "_Parent"; - foreach (var (type, propertyName) in newIndexes) + if (IndexExists(indexName) == false) { - CreateIndexIfNotExists(type, propertyName); + Create + .Index(indexName) + .OnTable(tableInfo.TableName) + .OnColumn("parent") + .Ascending() + .WithOptions().NonClustered() + .Do(); } } - - private void CreateIndexIfNotExists(Type dto, string propertyName) - { - var property = dto.GetProperty(propertyName); - var indexName = property.GetCustomAttributes(false).OfType().Single().Name; - - if (IndexExists(indexName)) - { - return; - } - - var tableName = dto.GetCustomAttributes(false).Single().Value; - var columnName = property.GetCustomAttributes(false).OfType().Single().Name; - - - Create - .Index(indexName) - .OnTable(tableName) - .OnColumn(columnName) - .Ascending() - .WithOptions().NonClustered() // All newly defined indexes are non-clustered - .Do(); - } } } From 1047929880c72ce5382d0fb127cb82ef933245fc Mon Sep 17 00:00:00 2001 From: LiamL Date: Wed, 22 Jan 2020 13:22:09 +0000 Subject: [PATCH 13/40] simplifies tablename for dictionary dto --- .../V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs index 6c574bd3c9..e46f78dd72 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs @@ -15,16 +15,14 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 /// if it doesn't already exist /// public override void Migrate() - { - var tableInfo = Context.Database.PocoDataFactory.ForType(typeof(DictionaryDto)).TableInfo; - tableInfo.TableName = tableInfo.TableName; - var indexName = "IX_" + tableInfo.TableName + "_Parent"; + { + var indexName = "IX_" + DictionaryDto.TableName + "_Parent"; if (IndexExists(indexName) == false) { Create .Index(indexName) - .OnTable(tableInfo.TableName) + .OnTable(DictionaryDto.TableName) .OnColumn("parent") .Ascending() .WithOptions().NonClustered() From 42fe20a11ebf7cd0fe93089ad3cf293ef7db9a25 Mon Sep 17 00:00:00 2001 From: jmcaveney Date: Wed, 29 Jan 2020 07:16:17 -0500 Subject: [PATCH 14/40] Uses AppSettings.Path constant for SystemDirectories.Umbraco (#7440) --- src/Umbraco.Core/Constants-AppSettings.cs | 15 +++++++++++++++ src/Umbraco.Core/IO/SystemDirectories.cs | 8 ++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Constants-AppSettings.cs b/src/Umbraco.Core/Constants-AppSettings.cs index 85d6b24ae0..704617d90c 100644 --- a/src/Umbraco.Core/Constants-AppSettings.cs +++ b/src/Umbraco.Core/Constants-AppSettings.cs @@ -43,6 +43,21 @@ namespace Umbraco.Core /// public const string Path = "Umbraco.Core.Path"; + /// + /// Gets the path to the css directory (/css by default). + /// + public const string CssPath = "umbracoCssPath"; + + /// + /// Gets the path to the scripts directory (/scripts by default). + /// + public const string ScriptsPath = "umbracoScriptsPath"; + + /// + /// Gets the path to media directory (/media by default). + /// + public const string MediaPath = "umbracoMediaPath"; + /// /// The reserved urls from web.config. /// diff --git a/src/Umbraco.Core/IO/SystemDirectories.cs b/src/Umbraco.Core/IO/SystemDirectories.cs index d6fb63b0a1..b4688d2e9f 100644 --- a/src/Umbraco.Core/IO/SystemDirectories.cs +++ b/src/Umbraco.Core/IO/SystemDirectories.cs @@ -29,13 +29,13 @@ namespace Umbraco.Core.IO public static string MacroPartials => MvcViews + "/MacroPartials/"; - public static string Media => IOHelper.ReturnPath("umbracoMediaPath", "~/media"); + public static string Media => IOHelper.ReturnPath(Constants.AppSettings.MediaPath, "~/media"); - public static string Scripts => IOHelper.ReturnPath("umbracoScriptsPath", "~/scripts"); + public static string Scripts => IOHelper.ReturnPath(Constants.AppSettings.ScriptsPath, "~/scripts"); - public static string Css => IOHelper.ReturnPath("umbracoCssPath", "~/css"); + public static string Css => IOHelper.ReturnPath(Constants.AppSettings.CssPath, "~/css"); - public static string Umbraco => IOHelper.ReturnPath("umbracoPath", "~/umbraco"); + public static string Umbraco => IOHelper.ReturnPath(Constants.AppSettings.Path, "~/umbraco"); public static string Packages => Data + "/packages"; From a945b0c511febd4617a6c9cbd327e9cca5d59069 Mon Sep 17 00:00:00 2001 From: Nathan Woulfe Date: Thu, 30 Jan 2020 02:52:20 +1000 Subject: [PATCH 15/40] use default color when previous selection removed (#7448) Thanks Nathan! --- .../iconpicker/iconpicker.controller.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js index 471d23ae84..5bfee22c4e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js @@ -16,7 +16,7 @@ function IconPickerController($scope, iconHelper, localizationService) { vm.close = close; vm.colors = [ - { name: "Black", value: "color-black" }, + { name: "Black", value: "color-black", default: true }, { name: "Blue Grey", value: "color-blue-grey" }, { name: "Grey", value: "color-grey" }, { name: "Brown", value: "color-brown" }, @@ -49,7 +49,7 @@ function IconPickerController($scope, iconHelper, localizationService) { }); // set a default color if nothing is passed in - vm.color = $scope.model.color ? findColor($scope.model.color) : vm.colors[0]; + vm.color = $scope.model.color ? findColor($scope.model.color) : vm.colors.find(x => x.default); // if an icon is passed in - preselect it vm.icon = $scope.model.icon ? $scope.model.icon : undefined; @@ -71,12 +71,13 @@ function IconPickerController($scope, iconHelper, localizationService) { } function findColor(value) { - return _.findWhere(vm.colors, {value: value}); + return vm.colors.find(x => x.value === value); } - function selectColor(color, $index, $event) { - $scope.model.color = color.value; - vm.color = color; + function selectColor(color) { + let newColor = (color || vm.colors.find(x => x.default)); + $scope.model.color = newColor.value; + vm.color = newColor; } function close() { From b6a073a12e5484f3acf7bdef9301d95fb9ddd650 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 29 Jan 2020 17:54:56 +0100 Subject: [PATCH 16/40] Add missing documentation for web.routing in umbracoSettings.config (#7468) Cheers Kenn :) --- src/Umbraco.Web.UI/config/umbracoSettings.config | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 9e60a9499c..c3df6cfb41 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -231,6 +231,14 @@ By default you can call any content Id in the url and show the content with that id, for example: http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting this setting to true stops that behavior + @disableRedirectUrlTracking + When the URL changes for content, redirects are automatically created for redirect handling within the + request pipeline. Setting this setting to true stops the automatic creation of redirects. Note that this + does not stop the request pipeline from handling any previously created redirects. + @urlProviderMode + By default Umbraco automatically figures out if internal URLs should be rendered as relative or absolute, + depending on the current request and the configured domains. By setting this setting to "Relative" or + "Absolute" you can force Umbraco to always render URLs as either relative or absolute. @umbracoApplicationUrl The url of the Umbraco application. By default, Umbraco will figure it out from the first request. Configure it here if you need anything specific. Needs to be a complete url with scheme and umbraco From 423a21da10820bab3aea6a84c7f11876c5ca43f2 Mon Sep 17 00:00:00 2001 From: Matthew-Wise <6782865+Matthew-Wise@users.noreply.github.com> Date: Thu, 30 Jan 2020 10:20:16 +0000 Subject: [PATCH 17/40] Added dialog aria labelling (#7000) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks Matt 👍 --- src/Umbraco.Web.UI.Client/src/views/content/rights.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/rights.html b/src/Umbraco.Web.UI.Client/src/views/content/rights.html index 6ceec1db51..3395468cb0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/rights.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/rights.html @@ -6,8 +6,8 @@ -
- + -
Set permissions for {{ currentNode.name }}
-

+
Set permissions for {{ currentNode.name }}
+

Date: Thu, 30 Jan 2020 11:45:50 +0100 Subject: [PATCH 18/40] =?UTF-8?q?V8:=20Change=20"Include=20subfolders=20in?= =?UTF-8?q?=20search"=20to=20"Search=20only=20in=20th=E2=80=A6=20(#4720)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lots of lovely work here - this is one of those PRs that show how good communication can really make the experience of contributing one of learning for all parties. Thanks Kenn and all who contributed their time here --- .../mediapicker/mediapicker.controller.js | 10 ++++++++-- .../infiniteeditors/mediapicker/mediapicker.html | 9 ++++----- src/Umbraco.Web.UI/Umbraco/config/lang/da.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 2 +- src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) 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 4419805194..c189131646 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 @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.MediaPickerController", - function ($scope, $timeout, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService) { + function ($scope, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService, umbSessionStorage) { var vm = this; @@ -37,6 +37,11 @@ angular.module("umbraco") $scope.cropSize = dialogOptions.cropSize; $scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId"); $scope.lockedFolder = true; + $scope.allowMediaEdit = dialogOptions.allowMediaEdit ? dialogOptions.allowMediaEdit : false; + + $scope.filterOptions = { + excludeSubFolders: umbSessionStorage.get("mediaPickerExcludeSubFolders") || false + }; var userStartNodes = []; @@ -392,6 +397,7 @@ angular.module("umbraco") } function toggle() { + umbSessionStorage.set("mediaPickerExcludeSubFolders", $scope.filterOptions.excludeSubFolders); // Make sure to activate the changeSearch function everytime the toggle is clicked changeSearch(); } @@ -404,7 +410,7 @@ angular.module("umbraco") function searchMedia() { vm.loading = true; - entityResource.getPagedDescendants($scope.currentFolder.id, "Media", vm.searchOptions) + entityResource.getPagedDescendants($scope.filterOptions.excludeSubFolders ? $scope.currentFolder.id : $scope.startNodeId, "Media", vm.searchOptions) .then(function (data) { // update image data to work with image grid angular.forEach(data.items, function (mediaItem) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html index 82612d5e19..e1a89061b6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.html @@ -32,10 +32,10 @@
+ label-key="general_excludeFromSubFolders">
@@ -112,9 +112,8 @@ disable-folder-select={{disableFolderSelect}} only-images={{onlyImages}} only-folders={{onlyFolders}} - include-sub-folders={{showChilds}} - current-folder-id="{{currentFolder.id}}" - allow-open-folder="!disableFolderSelect"> + include-sub-folders={{!filterOptions.excludeSubFolders}} + current-folder-id="{{currentFolder.id}}"> diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 98ef2a892a..ae8925e911 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -661,7 +661,7 @@ Ikon Id Importer - Inkludér undermapper i søgning + Søg kun i denne mappe Info Indre margen Indsæt diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index baa04cef7c..a85df5714b 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -674,7 +674,7 @@ Icon Id Import - Include subfolders in search + Search only this folder Info Inner margin Insert diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 4b3c1fa870..d14fb03727 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -676,7 +676,7 @@ Icon Id Import - Include subfolders in search + Search only this folder Info Inner margin Insert From 48a3ec0581045824999b25f992e1dc6001a80058 Mon Sep 17 00:00:00 2001 From: Nik Date: Tue, 28 Jan 2020 18:52:29 +0000 Subject: [PATCH 19/40] [FIX] Corrected Create method so that String UDI is correctly identified - [FIX] Changed check of UDI type to check for GUID type and then String type as previously was checking for GUID type twice. This resulted in the method never creating a String UDI if needed. - [ISSUE] Github Issue #7507 https://github.com/umbraco/Umbraco-CMS/issues/7507 --- src/Umbraco.Core/Udi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs index ea3ec0ed2d..b11ce250ad 100644 --- a/src/Umbraco.Core/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -316,7 +316,7 @@ namespace Umbraco.Core if (udiType == UdiType.GuidUdi) return new GuidUdi(uri); - if (udiType == UdiType.GuidUdi) + if (udiType == UdiType.StringUdi) return new StringUdi(uri); throw new ArgumentException(string.Format("Uri \"{0}\" is not a valid udi.", uri)); From 0a89fcc248135752199ae1f394846551c1160f28 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 30 Jan 2020 17:06:15 +0100 Subject: [PATCH 20/40] =?UTF-8?q?V8:=20Ensure=20consistent=20template=20fi?= =?UTF-8?q?le=20casing=20no=20matter=20how=20templa=E2=80=A6=20(#4889)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks Kenn! Wonderful, as ever --- src/Umbraco.Web/Editors/TemplateController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index 8da5e80f2e..903446d23c 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -194,7 +194,9 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } - var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Alias, display.Content, master); + // we need to pass the template name as alias to keep the template file casing consistent with templates created with content + // - see comment in FileService.CreateTemplateForContentType for additional details + var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Name, display.Content, master); Mapper.Map(template, display); } From a3bf9d2a51a9315750d42f0adbbe6febce20e9ae Mon Sep 17 00:00:00 2001 From: Robert Foster Date: Sat, 1 Feb 2020 20:06:16 +1000 Subject: [PATCH 21/40] Renamed "DermineIsChildOfListView" to "DetermineIsChildOfListView" --- src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs | 4 ++-- src/Umbraco.Web/Models/Mapping/MediaMapDefinition.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs index dc0df4ca96..c811c236d4 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs @@ -80,7 +80,7 @@ namespace Umbraco.Web.Models.Mapping target.Icon = source.ContentType.Icon; target.Id = source.Id; target.IsBlueprint = source.Blueprint; - target.IsChildOfListView = DermineIsChildOfListView(source); + target.IsChildOfListView = DetermineIsChildOfListView(source); target.IsContainer = source.ContentType.IsContainer; target.IsElement = source.ContentType.IsElement; target.Key = source.Key; @@ -211,7 +211,7 @@ namespace Umbraco.Web.Models.Mapping return source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"({source.Name})"; } - private bool DermineIsChildOfListView(IContent source) + private bool DetermineIsChildOfListView(IContent source) { // map the IsChildOfListView (this is actually if it is a descendant of a list view!) var parent = _contentService.GetParent(source); diff --git a/src/Umbraco.Web/Models/Mapping/MediaMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/MediaMapDefinition.cs index 05c006ec41..fa89496392 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaMapDefinition.cs @@ -56,7 +56,7 @@ namespace Umbraco.Web.Models.Mapping target.CreateDate = source.CreateDate; target.Icon = source.ContentType.Icon; target.Id = source.Id; - target.IsChildOfListView = DermineIsChildOfListView(source); + target.IsChildOfListView = DetermineIsChildOfListView(source); target.Key = source.Key; target.MediaLink = string.Join(",", source.GetUrls(Current.Configs.Settings().Content, _logger)); target.Name = source.Name; @@ -95,7 +95,7 @@ namespace Umbraco.Web.Models.Mapping target.VariesByCulture = source.ContentType.VariesByCulture(); } - private bool DermineIsChildOfListView(IMedia source) + private bool DetermineIsChildOfListView(IMedia source) { // map the IsChildOfListView (this is actually if it is a descendant of a list view!) var parent = _mediaService.GetParent(source); From c96c0cff320495d0c028cb65a1bc2c4905a5cff0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2020 03:27:33 +0000 Subject: [PATCH 22/40] Bump tinymce from 4.9.2 to 4.9.7 in /src/Umbraco.Web.UI.Client Bumps [tinymce](https://github.com/tinymce/tinymce-dist) from 4.9.2 to 4.9.7. - [Release notes](https://github.com/tinymce/tinymce-dist/releases) - [Changelog](https://github.com/tinymce/tinymce-dist/blob/4.9.7/changelog.txt) - [Commits](https://github.com/tinymce/tinymce-dist/compare/4.9.2...4.9.7) Signed-off-by: dependabot[bot] --- src/Umbraco.Web.UI.Client/package-lock.json | 6 +++--- src/Umbraco.Web.UI.Client/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index dd58b5ca18..95beae8316 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -15313,9 +15313,9 @@ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "tinymce": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.2.tgz", - "integrity": "sha512-ZRoTGG4GAsOI73QPSNkabO7nkoYw9H6cglRB44W2mMkxSiqxYi8WJlgkUphk0fDqo6ZD6r3E+NSP4UHxF2lySg==" + "version": "4.9.7", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.9.7.tgz", + "integrity": "sha512-cj0HvUuniTuIjOAJdRt5BUfeQqM5yHjbA2NOub9HUHXlCrT9OwD9WBPU6tGlaPC2l2I4eGoOnT8llosZRdQU5Q==" }, "tmp": { "version": "0.0.33", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index beff2b45d1..508409b4ea 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -42,7 +42,7 @@ "npm": "6.13.6", "signalr": "2.4.0", "spectrum-colorpicker": "1.8.0", - "tinymce": "4.9.2", + "tinymce": "4.9.7", "typeahead.js": "0.11.1", "underscore": "1.9.1" }, From a5fa7465b10ace6926affd19e0a86849e4be8f53 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 14:27:19 +0000 Subject: [PATCH 23/40] In ConvertTinyMceAndGridMediaUrlsToLocalLink, avoid unnecessary updates --- ...nvertTinyMceAndGridMediaUrlsToLocalLink.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs index b68aa23d78..c369af4adf 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs @@ -43,6 +43,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 var value = property.TextValue; if (string.IsNullOrWhiteSpace(value)) continue; + + bool propertyChanged = false; if (property.PropertyTypeDto.DataTypeDto.EditorAlias == Constants.PropertyEditors.Aliases.Grid) { try @@ -55,7 +57,8 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 var controlValue = control["value"]; if (controlValue?.Type == JTokenType.String) { - control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value()); + control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value(), out var controlChanged); + propertyChanged |= controlChanged; } } @@ -76,10 +79,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 } else { - property.TextValue = UpdateMediaUrls(mediaLinkPattern, value); + property.TextValue = UpdateMediaUrls(mediaLinkPattern, value, out propertyChanged); } - Database.Update(property); + if (propertyChanged) + Database.Update(property); } @@ -91,10 +95,14 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 Context.AddPostMigration(); } - private string UpdateMediaUrls(Regex mediaLinkPattern, string value) + private string UpdateMediaUrls(Regex mediaLinkPattern, string value, out bool changed) { - return mediaLinkPattern.Replace(value, match => + bool matched = false; + + var result = mediaLinkPattern.Replace(value, match => { + matched = true; + // match groups: // - 1 = from the beginning of the a tag until href attribute value begins // - 2 = the href attribute value excluding the querystring (if present) @@ -106,6 +114,10 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 ? match.Value : $"{match.Groups[1].Value}/{{localLink:{media.GetUdi()}}}{match.Groups[3].Value}"; }); + + changed = matched; + + return result; } } } From c154af84d84f5493ada27245be3a77c4fd73d1a3 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 14:36:07 +0000 Subject: [PATCH 24/40] In AddTypedLabels, clear VarcharValue as well as TextValue --- .../Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs index 309f8acbc3..033197a78e 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs @@ -101,16 +101,18 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 foreach (var value in values) Database.Execute(Sql() .Update(u => u - .Set(x => x.IntegerValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (int?) null : int.Parse(value.VarcharValue, NumberStyles.Any, CultureInfo.InvariantCulture)) - .Set(x => x.TextValue, null)) + .Set(x => x.IntegerValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (int?)null : int.Parse(value.VarcharValue, NumberStyles.Any, CultureInfo.InvariantCulture)) + .Set(x => x.TextValue, null) + .Set(x => x.VarcharValue, null)) .Where(x => x.Id == value.Id)); values = Database.Fetch(Sql().Select(x => x.Id, x => x.VarcharValue).From().WhereIn(x => x.PropertyTypeId, dtPropertyTypes)); foreach (var value in values) Database.Execute(Sql() .Update(u => u - .Set(x => x.DateValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (DateTime?) null : DateTime.Parse(value.VarcharValue, CultureInfo.InvariantCulture, DateTimeStyles.None)) - .Set(x => x.TextValue, null)) + .Set(x => x.DateValue, string.IsNullOrWhiteSpace(value.VarcharValue) ? (DateTime?)null : DateTime.Parse(value.VarcharValue, CultureInfo.InvariantCulture, DateTimeStyles.None)) + .Set(x => x.TextValue, null) + .Set(x => x.VarcharValue, null)) .Where(x => x.Id == value.Id)); // anything that's custom... ppl will have to figure it out manually, there isn't much we can do about it From 1677b32274a1fd13eceeaa4395d033a8d99b9bb6 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 14:37:31 +0000 Subject: [PATCH 25/40] Create temporary index on umbracoPropertyData --- .../Migrations/Upgrade/V_8_0_0/VariantsMigration.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 8c60d30680..7bd1cca3b1 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -19,6 +19,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 public override void Migrate() { MigratePropertyData(); + CreatePropertyDataIndexes(); MigrateContentAndPropertyTypes(); MigrateContent(); MigrateVersions(); @@ -90,6 +91,15 @@ HAVING COUNT(v2.id) <> 1").Any()) Rename.Table(PreTables.PropertyData).To(Constants.DatabaseSchema.Tables.PropertyData).Do(); } + private void CreatePropertyDataIndexes() + { + // Creates a temporary index on umbracoPropertyData to speed up other migrations which update property values. + // It will be removed in CreateKeysAndIndexes before the normal indexes for the table are created + var tableDefinition = Persistence.DatabaseModelDefinitions.DefinitionFactory.GetTableDefinition(typeof(PropertyDataDto), SqlSyntax); + Execute.Sql(SqlSyntax.FormatPrimaryKey(tableDefinition)).Do(); + Execute.Sql($"CREATE UNIQUE NONCLUSTERED INDEX IX_umbracoPropertyData_Temp ON {PreTables.PropertyData} (versionId,propertyTypeId,languageId,segment)").Do(); + } + private void MigrateContentAndPropertyTypes() { if (!ColumnExists(PreTables.ContentType, "variations")) From 681264d233e36fcfef16cfbb29c5e4ae3496d8d8 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 14:40:24 +0000 Subject: [PATCH 26/40] Remove temporary index --- .../Migrations/Upgrade/Common/CreateKeysAndIndexes.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Core/Migrations/Upgrade/Common/CreateKeysAndIndexes.cs b/src/Umbraco.Core/Migrations/Upgrade/Common/CreateKeysAndIndexes.cs index 29006c8811..4872e0019d 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/Common/CreateKeysAndIndexes.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/Common/CreateKeysAndIndexes.cs @@ -12,6 +12,7 @@ namespace Umbraco.Core.Migrations.Upgrade.Common { // remove those that may already have keys Delete.KeysAndIndexes(Constants.DatabaseSchema.Tables.KeyValue).Do(); + Delete.KeysAndIndexes(Constants.DatabaseSchema.Tables.PropertyData).Do(); // re-create *all* keys and indexes foreach (var x in DatabaseSchemaCreator.OrderedTables) From a6d89f5363e9c49a4d0ad237bc55f4638d6522be Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 14:53:31 +0000 Subject: [PATCH 27/40] SQLCE does not support UPDATE...FROM, but we can still use it for SQL Server --- .../V_8_0_0/RenameMediaVersionTable.cs | 23 +++++--- .../Upgrade/V_8_0_0/VariantsMigration.cs | 54 ++++++++++++++----- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs index b60923fcba..2b27bdafe8 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 { @@ -17,14 +18,24 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 AddColumn("id", out var sqls); - // SQLCE does not support UPDATE...FROM - var temp2 = Database.Fetch($@"SELECT v.versionId, v.id + if (Database.DatabaseType.IsSqlCe()) + { + // SQLCE does not support UPDATE...FROM + var versions = Database.Fetch($@"SELECT v.versionId, v.id FROM cmsContentVersion v JOIN umbracoNode n on v.contentId=n.id WHERE n.nodeObjectType='{Constants.ObjectTypes.Media}'"); - foreach (var t in temp2) - Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.MediaVersion} SET id={t.id} WHERE versionId='{t.versionId}'").Do(); - + foreach (var t in versions) + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.MediaVersion} SET id={t.id} WHERE versionId='{t.versionId}'").Do(); + } + else + { + Database.Execute($@"UPDATE {Constants.DatabaseSchema.Tables.MediaVersion} SET id=v.id +FROM {Constants.DatabaseSchema.Tables.MediaVersion} m +JOIN cmsContentVersion v on m.versionId = v.versionId +JOIN umbracoNode n on v.contentId=n.id +WHERE n.nodeObjectType='{Constants.ObjectTypes.Media}'"); + } foreach (var sql in sqls) Execute.Sql(sql).Do(); diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 8c60d30680..053a64a8e3 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -74,10 +74,18 @@ HAVING COUNT(v2.id) <> 1").Any()) { Alter.Table(PreTables.PropertyData).AddColumn("versionId2").AsInt32().Nullable().Do(); - // SQLCE does not support UPDATE...FROM - var temp = Database.Fetch($"SELECT id, versionId FROM {PreTables.ContentVersion}"); - foreach (var t in temp) - Database.Execute($"UPDATE {PreTables.PropertyData} SET versionId2=@v2 WHERE versionId=@v1", new { v1 = t.versionId, v2 = t.id }); + if (Database.DatabaseType.IsSqlCe()) + { + // SQLCE does not support UPDATE...FROM + var versions = Database.Fetch($"SELECT id, versionId FROM {PreTables.ContentVersion}"); + foreach (var t in versions) + Database.Execute($"UPDATE {PreTables.PropertyData} SET versionId2=@v2 WHERE versionId=@v1", new { v1 = t.versionId, v2 = t.id }); + } + else + { + Database.Execute($"UPDATE {PreTables.PropertyData} SET versionId2={PreTables.ContentVersion}.id FROM {PreTables.ContentVersion} INNER JOIN {PreTables.PropertyData} ON {PreTables.ContentVersion}.versionId = {PreTables.PropertyData}.versionId"); + } + Delete.Column("versionId").FromTable(PreTables.PropertyData).Do(); ReplaceColumn(PreTables.PropertyData, "versionId2", "versionId"); } @@ -153,22 +161,40 @@ HAVING COUNT(v2.id) <> 1").Any()) ReplaceColumn(PreTables.ContentVersion, "ContentId", "nodeId"); // populate contentVersion text, current and userId columns for documents - // SQLCE does not support UPDATE...FROM - var temp1 = Database.Fetch($"SELECT versionId, text, newest, documentUser FROM {PreTables.Document}"); - foreach (var t in temp1) - Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=@current, userId=@userId WHERE versionId=@versionId", - new { text = t.text, current = t.newest, userId=t.documentUser, versionId=t.versionId }); + if (Database.DatabaseType.IsSqlCe()) + { + // SQLCE does not support UPDATE...FROM + var documents = Database.Fetch($"SELECT versionId, text, published, newest, documentUser FROM {PreTables.Document}"); + foreach (var t in documents) + Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=@current, userId=@userId WHERE versionId=@versionId", + new { text = t.text, current = t.newest && !t.published, userId = t.documentUser, versionId = t.versionId }); + } + else + { + Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=d.text, {SqlSyntax.GetQuotedColumnName("current")}=(d.newest & ~d.published), userId=d.documentUser +FROM {PreTables.ContentVersion} v INNER JOIN {PreTables.Document} d ON d.versionId = v.versionId"); + } // populate contentVersion text and current columns for non-documents, userId is default - // SQLCE does not support UPDATE...FROM - var temp2 = Database.Fetch($@"SELECT cver.versionId, n.text + if (Database.DatabaseType.IsSqlCe()) + { + // SQLCE does not support UPDATE...FROM + var otherContent = Database.Fetch($@"SELECT cver.versionId, n.text FROM {PreTables.ContentVersion} cver JOIN {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.Node)} n ON cver.nodeId=n.id WHERE cver.versionId NOT IN (SELECT versionId FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)})"); - foreach (var t in temp2) - Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=1, userId=0 WHERE versionId=@versionId", - new { text = t.text, versionId=t.versionId }); + foreach (var t in otherContent) + Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=1, userId=0 WHERE versionId=@versionId", + new { text = t.text, versionId = t.versionId }); + } + else + { + Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=n.text, {SqlSyntax.GetQuotedColumnName("current")}=1, userId=0 +FROM {PreTables.ContentVersion} cver +JOIN {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.Node)} n ON cver.nodeId=n.id +WHERE cver.versionId NOT IN (SELECT versionId FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)})"); + } // create table Create.Table(withoutKeysAndIndexes: true).Do(); From 2b56a5aa2c6b3ed5932011679cf198eaca3edf4a Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 14:23:33 +0000 Subject: [PATCH 28/40] Use sp_rename in ReplaceColumn when available --- .../Migrations/MigrationBase_Extra.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Migrations/MigrationBase_Extra.cs b/src/Umbraco.Core/Migrations/MigrationBase_Extra.cs index bf07e4d08f..f4c6150073 100644 --- a/src/Umbraco.Core/Migrations/MigrationBase_Extra.cs +++ b/src/Umbraco.Core/Migrations/MigrationBase_Extra.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.SqlSyntax; @@ -84,10 +85,18 @@ namespace Umbraco.Core.Migrations protected void ReplaceColumn(string tableName, string currentName, string newName) { - AddColumn(tableName, newName, out var sqls); - Execute.Sql($"UPDATE {SqlSyntax.GetQuotedTableName(tableName)} SET {SqlSyntax.GetQuotedColumnName(newName)}={SqlSyntax.GetQuotedColumnName(currentName)}").Do(); - foreach (var sql in sqls) Execute.Sql(sql).Do(); - Delete.Column(currentName).FromTable(tableName).Do(); + if (DatabaseType.IsSqlCe()) + { + AddColumn(tableName, newName, out var sqls); + Execute.Sql($"UPDATE {SqlSyntax.GetQuotedTableName(tableName)} SET {SqlSyntax.GetQuotedColumnName(newName)}={SqlSyntax.GetQuotedColumnName(currentName)}").Do(); + foreach (var sql in sqls) Execute.Sql(sql).Do(); + Delete.Column(currentName).FromTable(tableName).Do(); + } + else + { + Execute.Sql(SqlSyntax.FormatColumnRename(tableName, currentName, newName)).Do(); + AlterColumn(tableName, newName); + } } protected bool TableExists(string tableName) From e830af26ce819a329a9c6b2545b7c44293756326 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 16:15:54 +0000 Subject: [PATCH 29/40] VariantsMigration - setting edited flag for versions --- .../Migrations/Upgrade/V_8_0_0/VariantsMigration.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 8c60d30680..fd2844638c 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Umbraco.Core.Migrations.Install; @@ -223,7 +224,7 @@ WHERE versionId NOT IN (SELECT (versionId) FROM {PreTables.ContentVersion} WHERE if (!ColumnExists(PreTables.Document, "edited")) { AddColumn(PreTables.Document, "edited", out var sqls); - Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=0"); + Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=~published"); foreach (var sql in sqls) Database.Execute(sql); } @@ -240,11 +241,15 @@ JOIN {Constants.DatabaseSchema.Tables.PropertyData} v1 ON cv1.id=v1.versionId JOIN {PreTables.ContentVersion} cv2 ON n.id=cv2.nodeId JOIN {Constants.DatabaseSchema.Tables.DocumentVersion} dv ON cv2.id=dv.id AND dv.published=1 JOIN {Constants.DatabaseSchema.Tables.PropertyData} v2 ON cv2.id=v2.versionId -WHERE v1.propertyTypeId=v2.propertyTypeId AND v1.languageId=v2.languageId AND v1.segment=v2.segment"); +WHERE v1.propertyTypeId=v2.propertyTypeId +AND (v1.languageId=v2.languageId OR (v1.languageId IS NULL AND v2.languageId IS NULL)) +AND (v1.segment=v2.segment OR (v1.segment IS NULL AND v2.segment IS NULL))"); + var updatedIds = new HashSet(); foreach (var t in temp) if (t.intValue1 != t.intValue2 || t.decimalValue1 != t.decimalValue2 || t.dateValue1 != t.dateValue2 || t.varcharValue1 != t.varcharValue2 || t.textValue1 != t.textValue2) - Database.Execute("UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=1 WHERE nodeId=@nodeIdd", new { t.id }); + if (updatedIds.Add((int)t.id)) + Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.Document)} SET edited=1 WHERE nodeId=@nodeId", new { nodeId = t.id }); // drop more columns Delete.Column("versionId").FromTable(PreTables.ContentVersion).Do(); From cb42eebb24a78978e533cb121213999e301a0c63 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 16:27:37 +0000 Subject: [PATCH 30/40] Migrations - ensure that documents with a published version are marked as published --- .../Migrations/Upgrade/V_8_0_0/VariantsMigration.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 8c60d30680..ce66094026 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -209,6 +209,10 @@ VALUES (@id, @templateId, 0)", new { id=id, templateId=t.templateId }); Database.Execute($@"DELETE FROM {PreTables.Document} WHERE versionId NOT IN (SELECT (versionId) FROM {PreTables.ContentVersion} WHERE {SqlSyntax.GetQuotedColumnName("current")} = 1) AND (published<>1 OR newest<>1)"); + // ensure that documents with a published version are marked as published + Database.Execute($@"UPDATE {PreTables.Document} SET published=1 WHERE nodeId IN ( +SELECT nodeId FROM {PreTables.ContentVersion} cv INNER JOIN {Constants.DatabaseSchema.Tables.DocumentVersion} dv ON dv.id = cv.id WHERE dv.published=1)"); + // drop some document columns Delete.Column("text").FromTable(PreTables.Document).Do(); Delete.Column("templateId").FromTable(PreTables.Document).Do(); From 03631cfe9a594af69682080dfc067350cdfc893a Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 16:53:35 +0000 Subject: [PATCH 31/40] Add extra "published=newest" versions with INSERT...SELECT instead of foreach --- .../Upgrade/V_8_0_0/VariantsMigration.cs | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 8c60d30680..3c22f8923c 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -179,31 +179,33 @@ SELECT cver.id, doc.templateId, doc.published FROM {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver JOIN {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId"); + // need to add extra rows for where published=newest // 'cos INSERT above has inserted the 'published' document version // and v8 always has a 'edited' document version too - var temp3 = Database.Fetch($@"SELECT doc.nodeId, doc.updateDate, doc.documentUser, doc.text, doc.templateId, cver.id versionId + Database.Execute($@" +INSERT INTO {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} (nodeId, versionId, versionDate, userId, {SqlSyntax.GetQuotedColumnName("current")}, text) +SELECT doc.nodeId, NEWID(), doc.updateDate, doc.documentUser, 1, doc.text FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId WHERE doc.newest=1 AND doc.published=1"); - var getIdentity = "@@@@IDENTITY"; - foreach (var t in temp3) - { - Database.Execute($@"INSERT INTO {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} (nodeId, versionId, versionDate, userId, {SqlSyntax.GetQuotedColumnName("current")}, text) -VALUES (@nodeId, @versionId, @versionDate, @userId, 1, @text)", new { nodeId=t.nodeId, versionId=Guid.NewGuid(), versionDate=t.updateDate, userId=t.documentUser, text=t.text }); - var id = Database.ExecuteScalar("SELECT " + getIdentity); - Database.Execute($"UPDATE {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} SET {SqlSyntax.GetQuotedColumnName("current")}=0 WHERE nodeId=@0 AND id<>@1", (int) t.nodeId, id); - Database.Execute($@"INSERT INTO {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.DocumentVersion)} (id, templateId, published) -VALUES (@id, @templateId, 0)", new { id=id, templateId=t.templateId }); - var versionId = (int) t.versionId; - var pdatas = Database.Fetch(Sql().Select().From().Where(x => x.VersionId == versionId)); - foreach (var pdata in pdatas) - { - pdata.VersionId = id; - Database.Insert(pdata); - } - } + Database.Execute($@" +INSERT INTO {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.DocumentVersion)} (id, templateId, published) +SELECT cverNew.id, doc.templateId, 0 +FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc +JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cverNew ON doc.nodeId = cverNew.nodeId +WHERE doc.newest=1 AND doc.published=1 AND cverNew.{SqlSyntax.GetQuotedColumnName("current")} = 1"); + + Database.Execute($@" +INSERT INTO {SqlSyntax.GetQuotedTableName(PreTables.PropertyData)} (propertytypeid,languageId,segment,textValue,varcharValue,decimalValue,intValue,dateValue,versionId) +SELECT propertytypeid,languageId,segment,textValue,varcharValue,decimalValue,intValue,dateValue,cverNew.id +FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc +JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId +JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cverNew ON doc.nodeId = cverNew.nodeId +JOIN {SqlSyntax.GetQuotedTableName(PreTables.PropertyData)} pd ON pd.versionId=cver.id +WHERE doc.newest=1 AND doc.published=1 AND cverNew.{SqlSyntax.GetQuotedColumnName("current")} = 1"); + // reduce document to 1 row per content Database.Execute($@"DELETE FROM {PreTables.Document} From 0187967cd8ddd2343ad935530e327515d9a39026 Mon Sep 17 00:00:00 2001 From: Steve Megson Date: Mon, 3 Feb 2020 17:01:49 +0000 Subject: [PATCH 32/40] Don't mark a version as current if it is published --- .../Migrations/Upgrade/V_8_0_0/VariantsMigration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 3c22f8923c..fe566c8668 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -154,10 +154,10 @@ HAVING COUNT(v2.id) <> 1").Any()) // populate contentVersion text, current and userId columns for documents // SQLCE does not support UPDATE...FROM - var temp1 = Database.Fetch($"SELECT versionId, text, newest, documentUser FROM {PreTables.Document}"); + var temp1 = Database.Fetch($"SELECT versionId, text, published, newest, documentUser FROM {PreTables.Document}"); foreach (var t in temp1) Database.Execute($@"UPDATE {PreTables.ContentVersion} SET text=@text, {SqlSyntax.GetQuotedColumnName("current")}=@current, userId=@userId WHERE versionId=@versionId", - new { text = t.text, current = t.newest, userId=t.documentUser, versionId=t.versionId }); + new { text = t.text, current = t.newest && !t.published, userId=t.documentUser, versionId=t.versionId }); // populate contentVersion text and current columns for non-documents, userId is default // SQLCE does not support UPDATE...FROM From a426a15b5a0cec83f5c630b06a87330eb7e85d8b Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 4 Feb 2020 15:49:05 +1100 Subject: [PATCH 33/40] fixes migration and uses migration syntax --- .../Migrations/Upgrade/V_8_0_0/VariantsMigration.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 7bd1cca3b1..ebeffd4898 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -97,7 +97,14 @@ HAVING COUNT(v2.id) <> 1").Any()) // It will be removed in CreateKeysAndIndexes before the normal indexes for the table are created var tableDefinition = Persistence.DatabaseModelDefinitions.DefinitionFactory.GetTableDefinition(typeof(PropertyDataDto), SqlSyntax); Execute.Sql(SqlSyntax.FormatPrimaryKey(tableDefinition)).Do(); - Execute.Sql($"CREATE UNIQUE NONCLUSTERED INDEX IX_umbracoPropertyData_Temp ON {PreTables.PropertyData} (versionId,propertyTypeId,languageId,segment)").Do(); + Create.Index("IX_umbracoPropertyData_Temp").OnTable(PropertyDataDto.TableName) + .WithOptions().Unique() + .WithOptions().NonClustered() + .OnColumn("versionId").Ascending() + .OnColumn("propertyTypeId").Ascending() + .OnColumn("languageId").Ascending() + .OnColumn("segment").Ascending() + .Do(); } private void MigrateContentAndPropertyTypes() From 4cb307978ae4353184f1b3e940b52cb916743224 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 4 Feb 2020 16:41:33 +1100 Subject: [PATCH 34/40] Fixes issue with upgrading from v7 -> 8.6, will cherry pick this --- .../Upgrade/V_8_0_0/AddTypedLabels.cs | 3 +- .../V_8_0_0/ContentVariationMigration.cs | 58 +------ .../ConvertRelatedLinksToMultiUrlPicker.cs | 11 +- .../V_8_0_0/Models/ContentTypeDto80.cs | 63 ++++++++ .../V_8_0_0/Models/PropertyDataDto80.cs | 142 ++++++++++++++++++ .../V_8_0_0/Models/PropertyTypeDto80.cs | 80 ++++++++++ ...nvertTinyMceAndGridMediaUrlsToLocalLink.cs | 11 +- .../AddDatabaseIndexesMissingOnForeignKeys.cs | 2 +- .../V_8_6_0/MissingContentVersionsIndexes.cs | 27 ++-- src/Umbraco.Core/Umbraco.Core.csproj | 3 + 10 files changed, 326 insertions(+), 74 deletions(-) create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs index 309f8acbc3..b50de05b95 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs @@ -75,8 +75,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 var labelPropertyTypes = Database.Fetch(Sql() .Select(x => x.Id, x => x.Alias) .From() - .Where(x => x.DataTypeId == Constants.DataTypes.LabelString) - ); + .Where(x => x.DataTypeId == Constants.DataTypes.LabelString)); var intPropertyAliases = new[] { Constants.Conventions.Media.Width, Constants.Conventions.Media.Height, Constants.Conventions.Member.FailedPasswordAttempts }; var bigintPropertyAliases = new[] { Constants.Conventions.Media.Bytes }; diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs index 84dd393b0d..eabbd34b08 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs @@ -4,8 +4,10 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using NPoco; +using Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 @@ -47,14 +49,14 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 } } - var propertyTypes = Database.Fetch(Sql().Select().From()); + var propertyTypes = Database.Fetch(Sql().Select().From()); foreach (var dto in propertyTypes) { dto.Variations = GetNewValue(dto.Variations); Database.Update(dto); } - var contentTypes = Database.Fetch(Sql().Select().From()); + var contentTypes = Database.Fetch(Sql().Select().From()); foreach (var dto in contentTypes) { dto.Variations = GetNewValue(dto.Variations); @@ -62,57 +64,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 } } - // we *need* to use this private DTO here, which does *not* have extra properties, which would kill the migration + // we *need* to use these private DTOs here, which does *not* have extra properties, which would kill the migration - [TableName(TableName)] - [PrimaryKey("pk")] - [ExplicitColumns] - private class ContentTypeDto - { - public const string TableName = Constants.DatabaseSchema.Tables.ContentType; + - [Column("pk")] - [PrimaryKeyColumn(IdentitySeed = 535)] - public int PrimaryKey { get; set; } - - [Column("nodeId")] - [ForeignKey(typeof(NodeDto))] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsContentType")] - public int NodeId { get; set; } - - [Column("alias")] - [NullSetting(NullSetting = NullSettings.Null)] - public string Alias { get; set; } - - [Column("icon")] - [Index(IndexTypes.NonClustered)] - [NullSetting(NullSetting = NullSettings.Null)] - public string Icon { get; set; } - - [Column("thumbnail")] - [Constraint(Default = "folder.png")] - public string Thumbnail { get; set; } - - [Column("description")] - [NullSetting(NullSetting = NullSettings.Null)] - [Length(1500)] - public string Description { get; set; } - - [Column("isContainer")] - [Constraint(Default = "0")] - public bool IsContainer { get; set; } - - [Column("allowAtRoot")] - [Constraint(Default = "0")] - public bool AllowAtRoot { get; set; } - - [Column("variations")] - [Constraint(Default = "1" /*ContentVariation.InvariantNeutral*/)] - public byte Variations { get; set; } - - [ResultColumn] - public NodeDto NodeDto { get; set; } - } + } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs index 1956876402..1e327346cf 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs @@ -2,8 +2,7 @@ using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; -using Umbraco.Core.Logging; -using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; @@ -34,11 +33,11 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 } var sqlPropertyTpes = Sql() - .Select() - .From() - .Where(x => dataTypeIds.Contains(x.DataTypeId)); + .Select() + .From() + .Where(x => dataTypeIds.Contains(x.DataTypeId)); - var propertyTypeIds = Database.Fetch(sqlPropertyTpes).Select(x => x.Id).ToList(); + var propertyTypeIds = Database.Fetch(sqlPropertyTpes).Select(x => x.Id).ToList(); if (propertyTypeIds.Count == 0) return; diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs new file mode 100644 index 0000000000..bc41e5e32a --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs @@ -0,0 +1,63 @@ +using NPoco; +using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models +{ + + /// + /// Snapshot of the as it was at version 8.0 + /// + /// + /// This is required during migrations the schema of this table changed and running SQL against the new table would result in errors + /// + [TableName(TableName)] + [PrimaryKey("pk")] + [ExplicitColumns] + internal class ContentTypeDto80 + { + public const string TableName = Constants.DatabaseSchema.Tables.ContentType; + + [Column("pk")] + [PrimaryKeyColumn(IdentitySeed = 535)] + public int PrimaryKey { get; set; } + + [Column("nodeId")] + [ForeignKey(typeof(NodeDto))] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsContentType")] + public int NodeId { get; set; } + + [Column("alias")] + [NullSetting(NullSetting = NullSettings.Null)] + public string Alias { get; set; } + + [Column("icon")] + [Index(IndexTypes.NonClustered)] + [NullSetting(NullSetting = NullSettings.Null)] + public string Icon { get; set; } + + [Column("thumbnail")] + [Constraint(Default = "folder.png")] + public string Thumbnail { get; set; } + + [Column("description")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(1500)] + public string Description { get; set; } + + [Column("isContainer")] + [Constraint(Default = "0")] + public bool IsContainer { get; set; } + + [Column("allowAtRoot")] + [Constraint(Default = "0")] + public bool AllowAtRoot { get; set; } + + [Column("variations")] + [Constraint(Default = "1" /*ContentVariation.InvariantNeutral*/)] + public byte Variations { get; set; } + + [ResultColumn] + public NodeDto NodeDto { get; set; } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs new file mode 100644 index 0000000000..f0a36b223d --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs @@ -0,0 +1,142 @@ +using NPoco; +using System; +using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models +{ + /// + /// Snapshot of the as it was at version 8.0 + /// + /// + /// This is required during migrations the schema of this table changed and running SQL against the new table would result in errors + /// + [TableName(TableName)] + [PrimaryKey("id")] + [ExplicitColumns] + internal class PropertyDataDto80 + { + public const string TableName = Constants.DatabaseSchema.Tables.PropertyData; + public const int VarcharLength = 512; + public const int SegmentLength = 256; + + private decimal? _decimalValue; + + // pk, not used at the moment (never updating) + [Column("id")] + [PrimaryKeyColumn] + public int Id { get; set; } + + [Column("versionId")] + [ForeignKey(typeof(ContentVersionDto))] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_VersionId", ForColumns = "versionId,propertyTypeId,languageId,segment")] + public int VersionId { get; set; } + + [Column("propertyTypeId")] + [ForeignKey(typeof(PropertyTypeDto80))] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_PropertyTypeId")] + public int PropertyTypeId { get; set; } + + [Column("languageId")] + [ForeignKey(typeof(LanguageDto))] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_LanguageId")] + [NullSetting(NullSetting = NullSettings.Null)] + public int? LanguageId { get; set; } + + [Column("segment")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Segment")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(SegmentLength)] + public string Segment { get; set; } + + [Column("intValue")] + [NullSetting(NullSetting = NullSettings.Null)] + public int? IntegerValue { get; set; } + + [Column("decimalValue")] + [NullSetting(NullSetting = NullSettings.Null)] + public decimal? DecimalValue + { + get => _decimalValue; + set => _decimalValue = value?.Normalize(); + } + + [Column("dateValue")] + [NullSetting(NullSetting = NullSettings.Null)] + public DateTime? DateValue { get; set; } + + [Column("varcharValue")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(VarcharLength)] + public string VarcharValue { get; set; } + + [Column("textValue")] + [NullSetting(NullSetting = NullSettings.Null)] + [SpecialDbType(SpecialDbTypes.NTEXT)] + public string TextValue { get; set; } + + [ResultColumn] + [Reference(ReferenceType.OneToOne, ColumnName = "PropertyTypeId")] + public PropertyTypeDto80 PropertyTypeDto { get; set; } + + [Ignore] + public object Value + { + get + { + if (IntegerValue.HasValue) + return IntegerValue.Value; + + if (DecimalValue.HasValue) + return DecimalValue.Value; + + if (DateValue.HasValue) + return DateValue.Value; + + if (!string.IsNullOrEmpty(VarcharValue)) + return VarcharValue; + + if (!string.IsNullOrEmpty(TextValue)) + return TextValue; + + return null; + } + } + + public PropertyDataDto80 Clone(int versionId) + { + return new PropertyDataDto80 + { + VersionId = versionId, + PropertyTypeId = PropertyTypeId, + LanguageId = LanguageId, + Segment = Segment, + IntegerValue = IntegerValue, + DecimalValue = DecimalValue, + DateValue = DateValue, + VarcharValue = VarcharValue, + TextValue = TextValue, + PropertyTypeDto = PropertyTypeDto + }; + } + + protected bool Equals(PropertyDataDto other) + { + return Id == other.Id; + } + + public override bool Equals(object other) + { + return + !ReferenceEquals(null, other) // other is not null + && (ReferenceEquals(this, other) // and either ref-equals, or same id + || other is PropertyDataDto pdata && pdata.Id == Id); + } + + public override int GetHashCode() + { + // ReSharper disable once NonReadonlyMemberInGetHashCode + return Id; + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs new file mode 100644 index 0000000000..6e2bd7371a --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs @@ -0,0 +1,80 @@ +using NPoco; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models +{ + /// + /// Snapshot of the as it was at version 8.0 + /// + /// + /// This is required during migrations before 8.6 since the schema has changed and running SQL against the new table would result in errors + /// + [TableName(Constants.DatabaseSchema.Tables.PropertyType)] + [PrimaryKey("id")] + [ExplicitColumns] + internal class PropertyTypeDto80 + { + [Column("id")] + [PrimaryKeyColumn(IdentitySeed = 50)] + public int Id { get; set; } + + [Column("dataTypeId")] + [ForeignKey(typeof(DataTypeDto), Column = "nodeId")] + public int DataTypeId { get; set; } + + [Column("contentTypeId")] + [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")] + public int ContentTypeId { get; set; } + + [Column("propertyTypeGroupId")] + [NullSetting(NullSetting = NullSettings.Null)] + [ForeignKey(typeof(PropertyTypeGroupDto))] + public int? PropertyTypeGroupId { get; set; } + + [Index(IndexTypes.NonClustered, Name = "IX_cmsPropertyTypeAlias")] + [Column("Alias")] + public string Alias { get; set; } + + [Column("Name")] + [NullSetting(NullSetting = NullSettings.Null)] + public string Name { get; set; } + + [Column("sortOrder")] + [Constraint(Default = "0")] + public int SortOrder { get; set; } + + [Column("mandatory")] + [Constraint(Default = "0")] + public bool Mandatory { get; set; } + + [Column("validationRegExp")] + [NullSetting(NullSetting = NullSettings.Null)] + public string ValidationRegExp { get; set; } + + [Column("Description")] + [NullSetting(NullSetting = NullSettings.Null)] + [Length(2000)] + public string Description { get; set; } + + [Column("variations")] + [Constraint(Default = "1" /*ContentVariation.InvariantNeutral*/)] + public byte Variations { get; set; } + + [ResultColumn] + [Reference(ReferenceType.OneToOne, ColumnName = "DataTypeId")] + public DataTypeDto DataTypeDto { get; set; } + + [Column("UniqueID")] + [NullSetting(NullSetting = NullSettings.NotNull)] + [Constraint(Default = SystemMethods.NewGuid)] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyTypeUniqueID")] + public Guid UniqueId { get; set; } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs index b68aa23d78..fdf692056a 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs @@ -5,6 +5,7 @@ using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Umbraco.Core.Migrations.PostMigrations; +using Umbraco.Core.Migrations.Upgrade.V_8_0_0.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Services; @@ -27,15 +28,15 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0 RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); var sqlPropertyData = Sql() - .Select(r => r.Select(x => x.PropertyTypeDto, r1 => r1.Select(x => x.DataTypeDto))) - .From() - .InnerJoin().On((left, right) => left.PropertyTypeId == right.Id) - .InnerJoin().On((left, right) => left.DataTypeId == right.NodeId) + .Select(r => r.Select(x => x.PropertyTypeDto, r1 => r1.Select(x => x.DataTypeDto))) + .From() + .InnerJoin().On((left, right) => left.PropertyTypeId == right.Id) + .InnerJoin().On((left, right) => left.DataTypeId == right.NodeId) .Where(x => x.EditorAlias == Constants.PropertyEditors.Aliases.TinyMce || x.EditorAlias == Constants.PropertyEditors.Aliases.Grid); - var properties = Database.Fetch(sqlPropertyData); + var properties = Database.Fetch(sqlPropertyData); var exceptions = new List(); foreach (var property in properties) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs index e46f78dd72..070ff82a2c 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 { var indexName = "IX_" + DictionaryDto.TableName + "_Parent"; - if (IndexExists(indexName) == false) + if (!IndexExists(indexName)) { Create .Index(indexName) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs index 75de01dd7f..c744409c2f 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs @@ -4,21 +4,30 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 { public class MissingContentVersionsIndexes : MigrationBase { + private const string IndexName = "IX_" + ContentVersionDto.TableName + "_NodeId"; + public MissingContentVersionsIndexes(IMigrationContext context) : base(context) { } public override void Migrate() { - Create - .Index("IX_" + ContentVersionDto.TableName + "_NodeId") - .OnTable(ContentVersionDto.TableName) - .OnColumn("nodeId") - .Ascending() - .OnColumn("current") - .Ascending() - .WithOptions().NonClustered() - .Do(); + // We must check before we create an index because if we are upgrading from v7 we force re-create all + // indexes in the whole DB and then this would throw + + if (!IndexExists(IndexName)) + { + Create + .Index(IndexName) + .OnTable(ContentVersionDto.TableName) + .OnColumn("nodeId") + .Ascending() + .OnColumn("current") + .Ascending() + .WithOptions().NonClustered() + .Do(); + } + } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c36e628dec..a198074066 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -128,6 +128,9 @@ --> + + + From 6bd5ff9b33c4c7efeac5e42d870afb3a18d8005e Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 4 Feb 2020 16:45:47 +1100 Subject: [PATCH 35/40] puts new migration in the right place with the right name --- src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs | 2 +- .../MissingDictionaryIndex.cs} | 4 ++-- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/Umbraco.Core/Migrations/Upgrade/{V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs => V_8_7_0/MissingDictionaryIndex.cs} (84%) diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index c835a889d1..7143be2b90 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -191,7 +191,7 @@ namespace Umbraco.Core.Migrations.Upgrade To("{2AB29964-02A1-474D-BD6B-72148D2A53A2}"); - To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}"); + To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}"); //FINAL } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs similarity index 84% rename from src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs rename to src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs index 070ff82a2c..fa5116c990 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_6_0/AddDatabaseIndexesMissingOnForeignKeys.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs @@ -2,9 +2,9 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0 { - public class AddDatabaseIndexesMissingOnForeignKeys : MigrationBase + public class MissingDictionaryIndex : MigrationBase { - public AddDatabaseIndexesMissingOnForeignKeys(IMigrationContext context) + public MissingDictionaryIndex(IMigrationContext context) : base(context) { diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index a198074066..1fc3709e88 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -136,7 +136,7 @@ - + From 3479bad6050d73fc734b15246a803d36c2fccf84 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 5 Feb 2020 15:41:29 +1100 Subject: [PATCH 36/40] Fixes migration syntax --- .../Migrations/Upgrade/V_8_0_0/VariantsMigration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 5ff544eeab..87509a4ba7 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -215,12 +215,12 @@ JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cverNew ON doc.nod WHERE doc.newest=1 AND doc.published=1 AND cverNew.{SqlSyntax.GetQuotedColumnName("current")} = 1"); Database.Execute($@" -INSERT INTO {SqlSyntax.GetQuotedTableName(PreTables.PropertyData)} (propertytypeid,languageId,segment,textValue,varcharValue,decimalValue,intValue,dateValue,versionId) +INSERT INTO {SqlSyntax.GetQuotedTableName(PropertyDataDto.TableName)} (propertytypeid,languageId,segment,textValue,varcharValue,decimalValue,intValue,dateValue,versionId) SELECT propertytypeid,languageId,segment,textValue,varcharValue,decimalValue,intValue,dateValue,cverNew.id FROM {SqlSyntax.GetQuotedTableName(PreTables.Document)} doc JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cver ON doc.nodeId=cver.nodeId AND doc.versionId=cver.versionId JOIN {SqlSyntax.GetQuotedTableName(PreTables.ContentVersion)} cverNew ON doc.nodeId = cverNew.nodeId -JOIN {SqlSyntax.GetQuotedTableName(PreTables.PropertyData)} pd ON pd.versionId=cver.id +JOIN {SqlSyntax.GetQuotedTableName(PropertyDataDto.TableName)} pd ON pd.versionId=cver.id WHERE doc.newest=1 AND doc.published=1 AND cverNew.{SqlSyntax.GetQuotedColumnName("current")} = 1"); From d6b1e0d4c75ebd6ae33d28bd968b69b254ee5856 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 5 Feb 2020 16:02:40 +1100 Subject: [PATCH 37/40] formats sql onto new lines --- .../Migrations/Upgrade/V_8_0_0/VariantsMigration.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 57568d0a7d..3e813fe1cb 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -84,7 +84,9 @@ HAVING COUNT(v2.id) <> 1").Any()) } else { - Database.Execute($"UPDATE {PreTables.PropertyData} SET versionId2={PreTables.ContentVersion}.id FROM {PreTables.ContentVersion} INNER JOIN {PreTables.PropertyData} ON {PreTables.ContentVersion}.versionId = {PreTables.PropertyData}.versionId"); + Database.Execute($@"UPDATE {PreTables.PropertyData} SET versionId2={PreTables.ContentVersion}.id +FROM {PreTables.ContentVersion} +INNER JOIN {PreTables.PropertyData} ON {PreTables.ContentVersion}.versionId = {PreTables.PropertyData}.versionId"); } Delete.Column("versionId").FromTable(PreTables.PropertyData).Do(); From 44e261b36af0b142a6cae18ac823f4c8f24ddbe3 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 5 Feb 2020 22:04:28 +0100 Subject: [PATCH 38/40] V8: Improve treepicker keyboard navigation for trees with listviews (#7032) * Remember the current tab focus when opening a mini list view in the tree picker * Automatically assign focus to the search box in mini list views --- .../infiniteeditors/treepicker/treepicker.controller.js | 9 +++++++++ .../src/views/components/umb-mini-list-view.html | 1 + 2 files changed, 10 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js index 0faee7bf90..3ec6a3f3cb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/treepicker/treepicker.controller.js @@ -64,6 +64,8 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", var currentNode = $scope.model.currentNode; + var previouslyFocusedElement = null; + function initDialogTree() { vm.dialogTreeApi.callbacks.treeLoaded(treeLoadedHandler); // TODO: Also deal with unexpanding!! @@ -487,6 +489,7 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", } function openMiniListView(node) { + previouslyFocusedElement = document.activeElement; vm.miniListView = node; } @@ -650,6 +653,12 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController", function closeMiniListView() { vm.miniListView = undefined; + if (previouslyFocusedElement) { + $timeout(function () { + previouslyFocusedElement.focus(); + previouslyFocusedElement = null; + }); + } } function listViewItemsLoaded(items) { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html index da1e5c3aa7..d66e7f7f90 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-list-view.html @@ -43,6 +43,7 @@ ng-model="search" ng-change="searchMiniListView(search, miniListView)" prevent-enter-submit + umb-auto-focus no-dirty-check>
From fbd95f1c8edfc3afc45f3e758b7894a393a40b86 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 6 Feb 2020 15:10:05 +0100 Subject: [PATCH 39/40] Fixes problem with #7530 where UmbracoContext is empty when HtmlImageSourceParser gets initialized --- .../Templates/HtmlImageSourceParser.cs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs b/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs index dcc5318b89..9a3d86c9da 100644 --- a/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs +++ b/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs @@ -13,14 +13,10 @@ namespace Umbraco.Web.Templates this._getMediaUrl = getMediaUrl; } + private readonly IUmbracoContextAccessor _umbracoContextAccessor; public HtmlImageSourceParser(IUmbracoContextAccessor umbracoContextAccessor) { - if (umbracoContextAccessor?.UmbracoContext?.UrlProvider == null) - { - return; - } - - _getMediaUrl = (guid) => umbracoContextAccessor.UmbracoContext.UrlProvider.GetMediaUrl(guid); + _umbracoContextAccessor = umbracoContextAccessor; } private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)((?:\?[^""]*)?""[^>]*data-udi="")([^""]*)(""[^>]*>)", @@ -29,7 +25,7 @@ namespace Umbraco.Web.Templates private static readonly Regex DataUdiAttributeRegex = new Regex(@"data-udi=\\?(?:""|')(?umb://[A-z0-9\-]+/[A-z0-9]+)\\?(?:""|')", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); - private readonly Func _getMediaUrl; + private Func _getMediaUrl; /// /// Parses out media UDIs from an html string based on 'data-udi' html attributes @@ -57,12 +53,11 @@ namespace Umbraco.Web.Templates /// Umbraco image tags are identified by their data-udi attributes public string EnsureImageSources(string text) { - // no point in doing any processing if we don't have - // a function to retrieve Urls - if (_getMediaUrl == null) - { - return text; - } + if (_umbracoContextAccessor.UmbracoContext == null) + throw new InvalidOperationException("Could not parse image sources, there is no current UmbracoContext"); + + if(_getMediaUrl == null) + _getMediaUrl = (guid) => _umbracoContextAccessor.UmbracoContext.UrlProvider.GetMediaUrl(guid); return ResolveImgPattern.Replace(text, match => { From d285d40578522249f2e902f8ef0820db154df1f3 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 6 Feb 2020 17:52:36 +0100 Subject: [PATCH 40/40] That safeguard unfortunately broke all the tests --- src/Umbraco.Web/Templates/HtmlImageSourceParser.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs b/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs index 9a3d86c9da..539ce303fd 100644 --- a/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs +++ b/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs @@ -53,9 +53,6 @@ namespace Umbraco.Web.Templates /// Umbraco image tags are identified by their data-udi attributes public string EnsureImageSources(string text) { - if (_umbracoContextAccessor.UmbracoContext == null) - throw new InvalidOperationException("Could not parse image sources, there is no current UmbracoContext"); - if(_getMediaUrl == null) _getMediaUrl = (guid) => _umbracoContextAccessor.UmbracoContext.UrlProvider.GetMediaUrl(guid);