From bce5b4974027f42586aab8b032e7a640ebb31ca1 Mon Sep 17 00:00:00 2001 From: Jeavon Date: Thu, 17 Oct 2019 17:26:32 +0100 Subject: [PATCH 01/12] Add additional null conditionals as this method is sometimes called for front end rendering where this is no current user (DTGE) --- src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 3eed40c8bf..5743e9c1d5 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -114,7 +114,7 @@ namespace Umbraco.Web.PropertyEditors if (editorValue.Value == null) return null; - var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId; + var userId = _umbracoContextAccessor.UmbracoContext?.Security?.CurrentUser?.Id ?? Constants.Security.SuperUserId; var config = editorValue.DataTypeConfiguration as RichTextConfiguration; var mediaParent = config?.MediaParentId; From 5e299d7880ca979797720dd1276b7bf9b92b90c8 Mon Sep 17 00:00:00 2001 From: Jeavon Date: Mon, 21 Oct 2019 10:10:08 +0100 Subject: [PATCH 02/12] Add additional null checks on GridPropertyEditor in case it's executed in the front end context without CurrentUser --- src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 2c2c69d181..9b7045f3b7 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -89,7 +89,7 @@ namespace Umbraco.Web.PropertyEditors var grid = DeserializeGridValue(rawJson, out var rtes); - var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? Constants.Security.SuperUserId; + var userId = _umbracoContextAccessor.UmbracoContext?.Security?.CurrentUser?.Id ?? Constants.Security.SuperUserId; //process the rte values foreach (var rte in rtes) From 5db26a4502cf39e2a941138835f7e74fb954345b Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 16 Oct 2019 17:29:45 +1100 Subject: [PATCH 03/12] starts writing tests --- .../PublishedContent/NuCacheChildrenTests.cs | 108 ++++++++++++++++-- 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 998c9589b0..ba6f6079bc 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -213,22 +213,24 @@ namespace Umbraco.Tests.PublishedContent var level = path.Count(x => x == ','); var now = DateTime.Now; + var contentData = new ContentData + { + Name = "N" + id, + Published = true, + TemplateId = 0, + VersionId = 1, + VersionDate = now, + WriterId = 0, + Properties = new Dictionary(), + CultureInfos = new Dictionary() + }; + return new ContentNodeKit { ContentTypeId = _contentTypeInvariant.Id, Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), DraftData = null, - PublishedData = new ContentData - { - Name = "N" + id, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } + PublishedData = contentData }; } @@ -333,7 +335,7 @@ namespace Umbraco.Tests.PublishedContent CultureInfos = GetCultureInfos(id, now) }; - var withDraft = id%2==0; + var withDraft = id % 2 == 0; var withPublished = !withDraft; return new ContentNodeKit @@ -1063,6 +1065,88 @@ namespace Umbraco.Tests.PublishedContent AssertLinkedNode(child3.contentNode, 1, 2, -1, -1, -1); } + /// + /// This addresses issue: https://github.com/umbraco/Umbraco-CMS/issues/6698 + /// + /// + /// This test mimics if someone were to: + /// 1) Unpublish a "middle child" + /// 2) Save and publish it + /// 3) Publish it with descendants + /// 4) Repeat steps 2 and 3 + /// + /// Which has caused an exception. To replicate this test: + /// 1) RefreshBranch with kits for a branch where the top most node is unpublished + /// 2) RefreshBranch with kits for the branch where the top most node is published + /// 3) RefreshBranch with kits for the branch where the top most node is published + /// 4) RefreshBranch with kits for the branch where the top most node is published + /// 5) RefreshBranch with kits for the branch where the top most node is published + /// + [Test] + public void Refresh_Branch_With_Alternating_Publish_Flags() + { + // NOTE: these tests are not using real scopes, in which case a Scope does not control + // how the snapshots generations work. We are forcing new snapshot generations manually. + + IEnumerable GetKits() + { + var paths = new Dictionary { { -1, "-1" } }; + + //root + yield return CreateInvariantKit(1, -1, 1, paths); + + //children + yield return CreateInvariantKit(2, 1, 1, paths); + yield return CreateInvariantKit(3, 1, 2, paths); //middle child + yield return CreateInvariantKit(4, 1, 3, paths); + } + + //init with all published + Init(GetKits()); + + var snapshotService = (PublishedSnapshotService)_snapshotService; + var contentStore = snapshotService.GetContentStore(); + + var rootKit = _source.Kits[1].Clone(); + + void ChangePublishFlagOfRoot(bool published, int assertGen) + { + //This will set a flag to force creating a new Gen next time the store is locked (i.e. In Notify) + contentStore.CreateSnapshot(); + + Assert.IsFalse(contentStore.Test.NextGen); + + //Change the root publish flag + var kit = rootKit.Clone(); + kit.DraftData = published ? null : kit.PublishedData; + kit.PublishedData = published? kit.PublishedData : null; + _source.Kits[1] = kit; + + _snapshotService.Notify(new[] + { + new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch) + }, out _, out _); + + Assert.AreEqual(assertGen, contentStore.Test.LiveGen); + Assert.IsTrue(contentStore.Test.NextGen); + } + + //unpublish the root + ChangePublishFlagOfRoot(false, 2); + + //publish the root + ChangePublishFlagOfRoot(true, 3); + + //publish root + descendants + ChangePublishFlagOfRoot(true, 4); + + //publish the root + ChangePublishFlagOfRoot(true, 5); + + //publish root + descendants + ChangePublishFlagOfRoot(true, 6); //TODO: This should fail, need to figure out what the diff is between this and a website + } + [Test] public void Refresh_Branch_Ensures_Linked_List() { From e7096fd5c80138738d53dfe29475ff2941bdf43a Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 15:47:15 +1100 Subject: [PATCH 04/12] Gets a test to confirm the issue, now to fix --- .../PublishedContent/NuCacheChildrenTests.cs | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index ba6f6079bc..6ea764c3bc 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -1079,7 +1079,7 @@ namespace Umbraco.Tests.PublishedContent /// 1) RefreshBranch with kits for a branch where the top most node is unpublished /// 2) RefreshBranch with kits for the branch where the top most node is published /// 3) RefreshBranch with kits for the branch where the top most node is published - /// 4) RefreshBranch with kits for the branch where the top most node is published + /// 4) RefreshNode /// 5) RefreshBranch with kits for the branch where the top most node is published /// [Test] @@ -1093,12 +1093,17 @@ namespace Umbraco.Tests.PublishedContent var paths = new Dictionary { { -1, "-1" } }; //root - yield return CreateInvariantKit(1, -1, 1, paths); + yield return CreateInvariantKit(100, -1, 1, paths); - //children - yield return CreateInvariantKit(2, 1, 1, paths); - yield return CreateInvariantKit(3, 1, 2, paths); //middle child - yield return CreateInvariantKit(4, 1, 3, paths); + //site + yield return CreateInvariantKit(2, 100, 1, paths); + yield return CreateInvariantKit(1, 100, 1, paths); //middle child + yield return CreateInvariantKit(3, 100, 1, paths); + + //children of 1 + yield return CreateInvariantKit(20, 1, 1, paths); + yield return CreateInvariantKit(30, 1, 2, paths); + yield return CreateInvariantKit(40, 1, 3, paths); } //init with all published @@ -1109,7 +1114,7 @@ namespace Umbraco.Tests.PublishedContent var rootKit = _source.Kits[1].Clone(); - void ChangePublishFlagOfRoot(bool published, int assertGen) + void ChangePublishFlagOfRoot(bool published, int assertGen, TreeChangeTypes changeType) { //This will set a flag to force creating a new Gen next time the store is locked (i.e. In Notify) contentStore.CreateSnapshot(); @@ -1124,7 +1129,7 @@ namespace Umbraco.Tests.PublishedContent _snapshotService.Notify(new[] { - new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch) + new ContentCacheRefresher.JsonPayload(1, Guid.Empty, changeType) }, out _, out _); Assert.AreEqual(assertGen, contentStore.Test.LiveGen); @@ -1132,19 +1137,19 @@ namespace Umbraco.Tests.PublishedContent } //unpublish the root - ChangePublishFlagOfRoot(false, 2); + ChangePublishFlagOfRoot(false, 2, TreeChangeTypes.RefreshBranch); - //publish the root - ChangePublishFlagOfRoot(true, 3); + //publish the root (since it's not published, it will cause a RefreshBranch) + ChangePublishFlagOfRoot(true, 3, TreeChangeTypes.RefreshBranch); //publish root + descendants - ChangePublishFlagOfRoot(true, 4); + ChangePublishFlagOfRoot(true, 4, TreeChangeTypes.RefreshBranch); - //publish the root - ChangePublishFlagOfRoot(true, 5); + //save/publish the root (since it's already published, it will just cause a RefreshNode + ChangePublishFlagOfRoot(true, 5, TreeChangeTypes.RefreshNode); //publish root + descendants - ChangePublishFlagOfRoot(true, 6); //TODO: This should fail, need to figure out what the diff is between this and a website + ChangePublishFlagOfRoot(true, 6, TreeChangeTypes.RefreshBranch); //TODO: This should fail, need to figure out what the diff is between this and a website } [Test] From 4641e617436dcdd63b435e2abbc00b1154721b47 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 18:41:05 +1100 Subject: [PATCH 05/12] Fixes issue - when calling Set, we were not setting all node graph elements. --- .../PublishedContent/NuCacheChildrenTests.cs | 14 +++++++++---- .../PublishedCache/NuCache/ContentNode.cs | 2 ++ .../PublishedCache/NuCache/ContentStore.cs | 21 ++++++++++++++++--- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 6ea764c3bc..7ae3eed9a2 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -1097,8 +1097,8 @@ namespace Umbraco.Tests.PublishedContent //site yield return CreateInvariantKit(2, 100, 1, paths); - yield return CreateInvariantKit(1, 100, 1, paths); //middle child - yield return CreateInvariantKit(3, 100, 1, paths); + yield return CreateInvariantKit(1, 100, 2, paths); //middle child + yield return CreateInvariantKit(3, 100, 3, paths); //children of 1 yield return CreateInvariantKit(20, 1, 1, paths); @@ -1134,11 +1134,17 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(assertGen, contentStore.Test.LiveGen); Assert.IsTrue(contentStore.Test.NextGen); + + //get the latest gen for content Id 1 + var (gen, contentNode) = contentStore.Test.GetValues(1)[0]; + Assert.AreEqual(assertGen, gen); + //even when unpublishing/re-publishing/etc... the linked list is always maintained + AssertLinkedNode(contentNode, 100, 2, 3, 20, 40); } //unpublish the root ChangePublishFlagOfRoot(false, 2, TreeChangeTypes.RefreshBranch); - + //publish the root (since it's not published, it will cause a RefreshBranch) ChangePublishFlagOfRoot(true, 3, TreeChangeTypes.RefreshBranch); @@ -1149,7 +1155,7 @@ namespace Umbraco.Tests.PublishedContent ChangePublishFlagOfRoot(true, 5, TreeChangeTypes.RefreshNode); //publish root + descendants - ChangePublishFlagOfRoot(true, 6, TreeChangeTypes.RefreshBranch); //TODO: This should fail, need to figure out what the diff is between this and a website + ChangePublishFlagOfRoot(true, 6, TreeChangeTypes.RefreshBranch); } [Test] diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs index 5f8e81fd31..3b4859432d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentNode.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using Umbraco.Core.Models.PublishedContent; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -6,6 +7,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { // represents a content "node" ie a pair of draft + published versions // internal, never exposed, to be accessed from ContentStore (only!) + [DebuggerDisplay("Id: {Id}, Path: {Path}")] internal class ContentNode { // special ctor for root pseudo node diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index f0d695f090..e90e67c050 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -549,7 +549,10 @@ namespace Umbraco.Web.PublishedCache.NuCache // manage children if (existing != null) + { kit.Node.FirstChildContentId = existing.FirstChildContentId; + kit.Node.LastChildContentId = existing.LastChildContentId; + } // set SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); @@ -571,6 +574,8 @@ namespace Umbraco.Web.PublishedCache.NuCache { // replacing existing, handle siblings kit.Node.NextSiblingContentId = existing.NextSiblingContentId; + //TODO: What about previous sibling?? + kit.Node.PreviousSiblingContentId = existing.PreviousSiblingContentId; } _xmap[kit.Node.Uid] = kit.Node.Id; @@ -729,7 +734,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // clear if (existing != null) { + //this zero's out the branch (recursively), if we're in a new gen this will add a NULL placeholder for the gen ClearBranchLocked(existing); + //TODO: This removes the current GEN from the tree - do we really want to do that? RemoveTreeNodeLocked(existing); } @@ -1002,11 +1009,19 @@ namespace Umbraco.Web.PublishedCache.NuCache } // else it's going somewhere in the middle, - // and this is bad, perfs-wise - we only do it when moving - // inserting in linked list is slow, optimizing would require trees - // but... that should not happen very often - and not on large amount of data + // TODO: There was a note about performance when this occurs and that this only happens when moving and not very often, but that is not true, + // this also happens anytime a middle node is unpublished or republished (which causes a branch update), i'm unsure if this has perf impacts, + // i think this used to but it doesn't seem bad anymore that I can see... while (child.NextSiblingContentId > 0) { + if (child.NextSiblingContentId == content.Id) + { + content.PreviousSiblingContentId = child.Id; + child = content; + continue; + } + + // get next child var nextChildLink = GetRequiredLinkedNode(child.NextSiblingContentId, "next child", null); var nextChild = nextChildLink.Value; From 652591d1c89eff9754f018de47ebda6dda26a9e9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 18:43:09 +1100 Subject: [PATCH 06/12] removes TODO --- src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index e90e67c050..8ac7eec2b6 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -574,7 +574,6 @@ namespace Umbraco.Web.PublishedCache.NuCache { // replacing existing, handle siblings kit.Node.NextSiblingContentId = existing.NextSiblingContentId; - //TODO: What about previous sibling?? kit.Node.PreviousSiblingContentId = existing.PreviousSiblingContentId; } From 296e1ee80f0d8710d6ae6d8744310390a9db279e Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 18:43:54 +1100 Subject: [PATCH 07/12] removes test code --- src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index 8ac7eec2b6..f71abd6aa7 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -1013,14 +1013,6 @@ namespace Umbraco.Web.PublishedCache.NuCache // i think this used to but it doesn't seem bad anymore that I can see... while (child.NextSiblingContentId > 0) { - if (child.NextSiblingContentId == content.Id) - { - content.PreviousSiblingContentId = child.Id; - child = content; - continue; - } - - // get next child var nextChildLink = GetRequiredLinkedNode(child.NextSiblingContentId, "next child", null); var nextChild = nextChildLink.Value; From e2d63f553f6abdf87544f77c15bee5fb8ad31aaa Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 17 Oct 2019 18:51:15 +1100 Subject: [PATCH 08/12] Adds simpler test to validate RefreshNode --- .../PublishedContent/NuCacheChildrenTests.cs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 7ae3eed9a2..4286732177 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -1065,6 +1065,60 @@ namespace Umbraco.Tests.PublishedContent AssertLinkedNode(child3.contentNode, 1, 2, -1, -1, -1); } + [Test] + public void Refresh_Node_Ensures_Linked_list() + { + // NOTE: these tests are not using real scopes, in which case a Scope does not control + // how the snapshots generations work. We are forcing new snapshot generations manually. + + IEnumerable GetKits() + { + var paths = new Dictionary { { -1, "-1" } }; + + //root + yield return CreateInvariantKit(100, -1, 1, paths); + + //site + yield return CreateInvariantKit(2, 100, 1, paths); + yield return CreateInvariantKit(1, 100, 2, paths); //middle child + yield return CreateInvariantKit(3, 100, 3, paths); + + //children of 1 + yield return CreateInvariantKit(20, 1, 1, paths); + yield return CreateInvariantKit(30, 1, 2, paths); + yield return CreateInvariantKit(40, 1, 3, paths); + } + + Init(GetKits()); + + var snapshotService = (PublishedSnapshotService)_snapshotService; + var contentStore = snapshotService.GetContentStore(); + + Assert.AreEqual(1, contentStore.Test.LiveGen); + Assert.IsTrue(contentStore.Test.NextGen); + + var middleNode = contentStore.Test.GetValues(1)[0]; + Assert.AreEqual(1, middleNode.gen); + AssertLinkedNode(middleNode.contentNode, 100, 2, 3, 20, 40); + + //This will set a flag to force creating a new Gen next time the store is locked (i.e. In Notify) + contentStore.CreateSnapshot(); + + Assert.IsFalse(contentStore.Test.NextGen); + + _snapshotService.Notify(new[] + { + new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshNode) + }, out _, out _); + + Assert.AreEqual(2, contentStore.Test.LiveGen); + Assert.IsTrue(contentStore.Test.NextGen); + + middleNode = contentStore.Test.GetValues(1)[0]; + Assert.AreEqual(2, middleNode.gen); + AssertLinkedNode(middleNode.contentNode, 100, 2, 3, 20, 40); + } + /// /// This addresses issue: https://github.com/umbraco/Umbraco-CMS/issues/6698 /// From e0a34d387338fe8ed9578b1afa24453348b73904 Mon Sep 17 00:00:00 2001 From: Nathan Woulfe Date: Thu, 26 Sep 2019 14:31:48 +1000 Subject: [PATCH 09/12] adding visibility:hidden to the un-hovered list fixes the problem. have also tidied up the css a bit to remove duplicate class references --- .../umb-editor-navigation-item.less | 366 +++++++++--------- 1 file changed, 181 insertions(+), 185 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less index 8a8032ee16..c26c89a478 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less @@ -1,199 +1,195 @@ .umb-sub-views-nav-item { position: relative; display: block; -} -.umb-sub-views-nav-item__action, -.umb-sub-views-nav-item > a { - background: transparent; - text-align: center; - cursor: pointer; - display: block; - padding: 4px 10px 0 10px; - min-width: 70px; - border: 0 none; - border-right: 1px solid @gray-9; - box-sizing: border-box; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: calc(~'@{editorHeaderHeight}' - ~'1px'); // need to offset the 1px border-bottom on .umb-editor-header - avoids overflowing top of the container - position: relative; - - color: @ui-active-type; - - &:focus, - &:hover { - color: @ui-active-type-hover !important; - } - - &::after { - content: ""; - height: 0px; - left: 8px; - right: 8px; - background-color: @ui-light-active-border; - position: absolute; - bottom: 0; - border-radius: 3px 3px 0 0; - opacity: 0; - transition: all .2s linear; - } -} + &__action, + > a { + background: transparent; + text-align: center; + cursor: pointer; + display: block; + padding: 4px 10px 0 10px; + min-width: 70px; + border: 0 none; + border-right: 1px solid @gray-9; + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: calc(~'@{editorHeaderHeight}'- ~'1px'); // need to offset the 1px border-bottom on .umb-editor-header - avoids overflowing top of the container + position: relative; + color: @ui-active-type; -.umb-sub-views-nav-item__action:focus, -.umb-sub-views-nav-item__action:active, -.umb-sub-views-nav-item > a:active { - .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); -} -.umb-sub-views-nav-item__action:focus, -.umb-sub-views-nav-item > a:focus { - outline: none; -} + &:focus, + &:hover { + color: @ui-active-type-hover !important; + text-decoration: none; + } -.umb-sub-views-nav-item__action:hover, -.umb-sub-views-nav-item__action:focus, -.umb-sub-views-nav-item > a:hover, -.umb-sub-views-nav-item > a:focus { - text-decoration: none; -} -.umb-sub-views-nav-item__action.is-active, -.umb-sub-views-nav-item > a.is-active { - - color: @ui-light-active-type; - - &::after { + &:focus { + outline: none; + } + + &::after { + content: ""; + height: 0px; + left: 8px; + right: 8px; + background-color: @ui-light-active-border; + position: absolute; + bottom: 0; + border-radius: 3px 3px 0 0; + opacity: 0; + transition: all .2s linear; + } + + &.is-active { + color: @ui-light-active-type; + + &::after { + opacity: 1; + height: 4px; + } + } + } + + &__action:focus, + &__action:active, + & > a:active { + .box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)"); + } + + &:focus-within &__anchor_dropdown, + &:hover &__anchor_dropdown { + visibility: visible; opacity: 1; - height: 4px; + transition: opacity 20ms 0; + } + + .icon { + font-size: 24px; + display: block; + text-align: center; + margin-bottom: 7px; + } + + .badge { + position: absolute; + top: 6px; + right: 6px; + min-width: 16px; + color: @white; + background-color: @ui-active-type; + border: 2px solid @white; + border-radius: 50%; + font-size: 10px; + font-weight: bold; + padding: 2px; + line-height: 16px; + display: block; + + &.-type-alert { + background-color: @red; + } + + &.-type-warning { + background-color: @yellow-d2; + } + + &:empty { + height: 12px; + min-width: 12px; + } + } + + &-text { + font-size: 12px; + line-height: 1em; + } + + &__anchor_dropdown { + // inherits from .dropdown-menu + margin: 0; + overflow: hidden; + + // center align horizontal + left: 50%; + transform: translateX(-50%); + opacity: 0; + transition: opacity 250ms 250ms; + visibility: hidden; + + li { + &.is-active a { + border-left-color: @ui-selected-border; + } + + a { + border-left: 4px solid transparent; + } + } + } + + // Currently Edge 18 does unfortunately not support :focus-within so for now we will use the "old" behavior - Support is coming with the upcoming release of Edge 76 + // See https://caniuse.com/#search=focus-within + @supports (-ms-ime-align:auto) { + &:hover &__anchor_dropdown { + transition: visibility 0 0, opacity 20ms 0; + } + + &__anchor_dropdown { + visibility: hidden; + transition: visibility 0 500ms, opacity 250ms 250ms; + } + } + + // -------------------------------- + // item__more, appears when there is not enough room for the visible items. + // -------------------------------- + + &-more__icon { + margin-bottom: 10px; + + i { + height: 5px; + width: 5px; + border-radius: 50%; + background: @ui-active-type; // fallback if browser doesnt support currentColor + background: currentColor; + display: inline-block; + margin: 0 5px 0 0; + } + + i:last-of-type { + margin-right: 0; + } + } + + &-more__dropdown { + left: auto; + right: 0; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + min-width: auto; + margin-top: 10px; + + > li { + display: flex; + } + + .umb-sub-views-nav-item:first { + border-left: none; + } } } +// Validation .umb-sub-views-nav-item__action.-has-error, .show-validation .umb-sub-views-nav-item > a.-has-error { - color: @red; - &::after { - background-color: @red; - } -} + color: @red; -.umb-sub-views-nav-item .icon { - font-size: 24px; - display: block; - text-align: center; - margin-bottom: 7px; -} - -.umb-sub-views-nav-item .badge { - position: absolute; - top: 6px; - right: 6px; - min-width: 16px; - color: @white; - background-color: @ui-active-type; - border: 2px solid @white; - border-radius: 50%; - font-size: 10px; - font-weight: bold; - padding: 2px; - line-height: 16px; - display: block; - - &.-type-alert { - background-color: @red; - } - &.-type-warning { - background-color: @yellow-d2; - } - &:empty { - height: 12px; - min-width: 12px; - } -} - -.umb-sub-views-nav-item-text { - font-size: 12px; - line-height: 1em; -} - - -.umb-sub-views-nav-item__anchor_dropdown {// inherits from .dropdown-menu - display: block; - margin: 0; - overflow: hidden; - - // center align horizontal - left: 50%; - transform: translateX(-50%); - opacity: 0; - transition: opacity 250ms 250ms; -} - -// Currently Edge 18 does unfortunately not support :focus-within so for now we will use the "old" behavior - Support is coming with the upcoming release of Edge 76 -// See https://caniuse.com/#search=focus-within -@supports (-ms-ime-align:auto) { - .umb-sub-views-nav-item__anchor_dropdown { - visibility: hidden; - transition: visibility 0 500ms, opacity 250ms 250ms; + &::after { + background-color: @red; } -} - -.umb-sub-views-nav-item__anchor_dropdown li a { - border-left: 4px solid transparent; -} -.umb-sub-views-nav-item__anchor_dropdown li.is-active a { - border-left-color: @ui-selected-border; -} - -.umb-sub-views-nav-item:focus-within .umb-sub-views-nav-item__anchor_dropdown, -.umb-sub-views-nav-item:hover .umb-sub-views-nav-item__anchor_dropdown { - visibility:visible; - opacity: 1; - transition: opacity 20ms 0; -} - -// Currently Edge 18 does unfortunately not support :focus-within so for now we will use the "old" behavior - Support is coming with the upcoming release of Edge 76 -// See https://caniuse.com/#search=focus-within -@supports (-ms-ime-align:auto) { - .umb-sub-views-nav-item:hover .umb-sub-views-nav-item__anchor_dropdown { - transition: visibility 0 0, opacity 20ms 0; - } -} - - -// -------------------------------- -// item__more, appears when there is not enough room for the visible items. -// -------------------------------- - -.umb-sub-views-nav-item-more__icon { - margin-bottom: 10px; -} - -.umb-sub-views-nav-item-more__icon i { - height: 5px; - width: 5px; - border-radius: 50%; - background: @ui-active-type;// fallback if browser doesnt support currentColor - background: currentColor; - display: inline-block; - margin: 0 5px 0 0; -} - -.umb-sub-views-nav-item-more__icon i:last-of-type { - margin-right: 0; -} - -.umb-sub-views-nav-item-more__dropdown { - left: auto; - right: 0; - display: grid; - grid-template-columns: 1fr 1fr 1fr; - min-width: auto; - margin-top: 10px; -} -.umb-sub-views-nav-item-more__dropdown > li { - display: flex; -} -.umb-sub-views-nav-item-more__dropdown .umb-sub-views-nav-item:first { - border-left: none; -} +} \ No newline at end of file From 5e684487c07994ceb6ad553b42b82279bd1cb59b Mon Sep 17 00:00:00 2001 From: Claus Date: Tue, 22 Oct 2019 14:33:31 +0200 Subject: [PATCH 10/12] fixes images not showing up when rendering rte from the grid. --- src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Rte.cshtml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Rte.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Rte.cshtml index b7e293eed8..34bb744596 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Rte.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/Rte.cshtml @@ -1,5 +1,9 @@ @model dynamic @using Umbraco.Web.Composing @using Umbraco.Web.Templates - -@Html.Raw(TemplateUtilities.ParseInternalLinks(Model.value.ToString(), Current.UmbracoContext.UrlProvider)) +@{ + var value = TemplateUtilities.ParseInternalLinks(Model.value.ToString(), Current.UmbracoContext.UrlProvider); + value = TemplateUtilities.ResolveUrlsFromTextString(value); + value = TemplateUtilities.ResolveMediaFromTextString(value); +} +@Html.Raw(value) From 54bb5bb8354074f9de4eb73e4f5cdcb56e3576da Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 22 Oct 2019 16:07:37 +0200 Subject: [PATCH 11/12] Bump version to 8.2.1 --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index a4e859988e..eccecd359c 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -18,5 +18,5 @@ using System.Resources; [assembly: AssemblyVersion("8.0.0")] // these are FYI and changed automatically -[assembly: AssemblyFileVersion("8.2.0")] -[assembly: AssemblyInformationalVersion("8.2.0")] +[assembly: AssemblyFileVersion("8.2.1")] +[assembly: AssemblyInformationalVersion("8.2.1")] diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 4141b60a3d..837d632da9 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -345,9 +345,9 @@ False True - 8200 + 8210 / - http://localhost:8200/ + http://localhost:8210/ False False @@ -429,4 +429,4 @@ - \ No newline at end of file + From a4c4a978d98f1b2fc153db6d29cbee0b06a22a94 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 22 Oct 2019 16:22:19 +0200 Subject: [PATCH 12/12] Downgraded cherry picked test to work on 8.2.1 --- .../PublishedContent/NuCacheChildrenTests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 4286732177..c116c12f59 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -1108,7 +1108,7 @@ namespace Umbraco.Tests.PublishedContent _snapshotService.Notify(new[] { - new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshNode) + new ContentCacheRefresher.JsonPayload(1, TreeChangeTypes.RefreshNode) }, out _, out _); Assert.AreEqual(2, contentStore.Test.LiveGen); @@ -1128,7 +1128,7 @@ namespace Umbraco.Tests.PublishedContent /// 2) Save and publish it /// 3) Publish it with descendants /// 4) Repeat steps 2 and 3 - /// + /// /// Which has caused an exception. To replicate this test: /// 1) RefreshBranch with kits for a branch where the top most node is unpublished /// 2) RefreshBranch with kits for the branch where the top most node is published @@ -1156,7 +1156,7 @@ namespace Umbraco.Tests.PublishedContent //children of 1 yield return CreateInvariantKit(20, 1, 1, paths); - yield return CreateInvariantKit(30, 1, 2, paths); + yield return CreateInvariantKit(30, 1, 2, paths); yield return CreateInvariantKit(40, 1, 3, paths); } @@ -1183,7 +1183,7 @@ namespace Umbraco.Tests.PublishedContent _snapshotService.Notify(new[] { - new ContentCacheRefresher.JsonPayload(1, Guid.Empty, changeType) + new ContentCacheRefresher.JsonPayload(1, changeType) }, out _, out _); Assert.AreEqual(assertGen, contentStore.Test.LiveGen); @@ -1193,12 +1193,12 @@ namespace Umbraco.Tests.PublishedContent var (gen, contentNode) = contentStore.Test.GetValues(1)[0]; Assert.AreEqual(assertGen, gen); //even when unpublishing/re-publishing/etc... the linked list is always maintained - AssertLinkedNode(contentNode, 100, 2, 3, 20, 40); + AssertLinkedNode(contentNode, 100, 2, 3, 20, 40); } //unpublish the root ChangePublishFlagOfRoot(false, 2, TreeChangeTypes.RefreshBranch); - + //publish the root (since it's not published, it will cause a RefreshBranch) ChangePublishFlagOfRoot(true, 3, TreeChangeTypes.RefreshBranch);