From 54669ee2bc6572893ee83aacc0ce6badc0966330 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 13 Jan 2016 16:25:35 +0100 Subject: [PATCH] Ensures you cannot move a child doc type, changes the result of GetAvailableCompositions to be a model instead of a tuple, the result also includes the current ancestor list which we can use to disable items in the list, makes these models/methods internal since they'll most likely be changing, --- .../ContentTypeAvailableCompositionsResult.cs | 17 ++++++ ...ContentTypeAvailableCompositionsResults.cs | 26 ++++++++++ .../Models/ContentTypeCompositionBase.cs | 12 ++--- .../Services/ContentTypeServiceExtensions.cs | 52 +++++++++++++------ src/Umbraco.Core/Umbraco.Core.csproj | 4 +- .../ContentTypeServiceExtensionsTests.cs | 18 +++---- .../compositions/compositions.html | 10 ++-- .../Editors/ContentTypeControllerBase.cs | 13 +++-- .../Trees/ContentTypeTreeController.cs | 16 +++++- 9 files changed, 126 insertions(+), 42 deletions(-) create mode 100644 src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs create mode 100644 src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs diff --git a/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs new file mode 100644 index 0000000000..b1d2b45dc5 --- /dev/null +++ b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResult.cs @@ -0,0 +1,17 @@ +namespace Umbraco.Core.Models +{ + /// + /// Used when determining available compositions for a given content type + /// + internal class ContentTypeAvailableCompositionsResult + { + public ContentTypeAvailableCompositionsResult(IContentTypeComposition composition, bool allowed) + { + Composition = composition; + Allowed = allowed; + } + + public IContentTypeComposition Composition { get; private set; } + public bool Allowed { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs new file mode 100644 index 0000000000..653d7a10a9 --- /dev/null +++ b/src/Umbraco.Core/Models/ContentTypeAvailableCompositionsResults.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Models +{ + /// + /// Used when determining available compositions for a given content type + /// + internal class ContentTypeAvailableCompositionsResults + { + public ContentTypeAvailableCompositionsResults() + { + Ancestors = Enumerable.Empty(); + Results = Enumerable.Empty(); + } + + public ContentTypeAvailableCompositionsResults(IEnumerable ancestors, IEnumerable results) + { + Ancestors = ancestors; + Results = results; + } + + public IEnumerable Ancestors { get; private set; } + public IEnumerable Results { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs index 5ac21885d7..a6f8a2ef37 100644 --- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs @@ -23,8 +23,8 @@ namespace Umbraco.Core.Models protected ContentTypeCompositionBase(IContentTypeComposition parent) : this(parent, null) - { - } + { + } protected ContentTypeCompositionBase(IContentTypeComposition parent, string alias) : base(parent, alias) @@ -122,10 +122,10 @@ namespace Umbraco.Core.Models return false; RemovedContentTypeKeyTracker.Add(contentTypeComposition.Id); - + //If the ContentType we are removing has Compositions of its own these needs to be removed as well var compositionIdsToRemove = contentTypeComposition.CompositionIds().ToList(); - if(compositionIdsToRemove.Any()) + if (compositionIdsToRemove.Any()) RemovedContentTypeKeyTracker.AddRange(compositionIdsToRemove); OnPropertyChanged(ContentTypeCompositionSelector); @@ -218,8 +218,8 @@ namespace Umbraco.Core.Models return false; // get and ensure a group local to this content type - var group = PropertyGroups.Contains(propertyGroupName) - ? PropertyGroups[propertyGroupName] + var group = PropertyGroups.Contains(propertyGroupName) + ? PropertyGroups[propertyGroupName] : AddAndReturnPropertyGroup(propertyGroupName); if (group == null) return false; diff --git a/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs b/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs index 0a5cff6d4b..ed04edc6bf 100644 --- a/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs +++ b/src/Umbraco.Core/Services/ContentTypeServiceExtensions.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Services /// be looked up via the db, they need to be passed in. /// /// - public static IEnumerable> GetAvailableCompositeContentTypes(this IContentTypeService ctService, + internal static ContentTypeAvailableCompositionsResults GetAvailableCompositeContentTypes(this IContentTypeService ctService, IContentTypeComposition source, IContentTypeComposition[] allContentTypes, string[] filterContentTypes = null, @@ -54,7 +54,7 @@ namespace Umbraco.Core.Services if (isUsing.Length > 0) { //if already in use a composition, do not allow any composited types - return new List>(); + return new ContentTypeAvailableCompositionsResults(); } // if it is not used then composition is possible @@ -68,7 +68,7 @@ namespace Umbraco.Core.Services .Where(x => x.ContentTypeComposition.Any() == false).ToArray(); foreach (var x in usableContentTypes) list.Add(x); - + // indirect types are those that we use, directly or indirectly var indirectContentTypes = GetDirectOrIndirect(source).ToArray(); foreach (var x in indirectContentTypes) @@ -93,21 +93,46 @@ namespace Umbraco.Core.Services }) .OrderBy(x => x.Name) .ToList(); - - //now we can create our result based on what is still available + + //get ancestor ids - we will filter all ancestors + var ancestors = GetAncestors(source, allContentTypes); + var ancestorIds = ancestors.Select(x => x.Id).ToArray(); + + //now we can create our result based on what is still available and the ancestors var result = list //not itself .Where(x => x.Id != sourceId) .OrderBy(x => x.Name) .Select(composition => filtered.Contains(composition) - ? new Tuple(composition, true) - : new Tuple(composition, false)).ToList(); + ? new ContentTypeAvailableCompositionsResult(composition, ancestorIds.Contains(composition.Id) == false) + : new ContentTypeAvailableCompositionsResult(composition, false)).ToList(); - return result; + return new ContentTypeAvailableCompositionsResults(ancestors, result); + } + + private static IContentTypeComposition[] GetAncestors(IContentTypeComposition ctype, IContentTypeComposition[] allContentTypes) + { + if (ctype == null) return new IContentTypeComposition[] {}; + var ancestors = new List(); + var parentId = ctype.ParentId; + while (parentId > 0) + { + var parent = allContentTypes.FirstOrDefault(x => x.Id == parentId); + if (parent != null) + { + ancestors.Add(parent); + parentId = parent.ParentId; + } + else + { + parentId = -1; + } + } + return ancestors.ToArray(); } /// - /// Get those that we use directly or indirectly + /// Get those that we use directly /// /// /// @@ -121,12 +146,9 @@ namespace Umbraco.Core.Services x => x.Id)); var stack = new Stack(); - - if (ctype != null) - { - foreach (var x in ctype.ContentTypeComposition) - stack.Push(x); - } + + foreach (var x in ctype.ContentTypeComposition) + stack.Push(x); while (stack.Count > 0) { diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 5b404470b7..00ca6863ed 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -181,7 +181,7 @@ - + @@ -370,6 +370,8 @@ + + diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceExtensionsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceExtensionsTests.cs index 6057443fa0..ed29cb2108 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceExtensionsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceExtensionsTests.cs @@ -47,7 +47,7 @@ namespace Umbraco.Tests.Services new[] { ct1, ct2, ct3, ct4, ct5 }, new[] { ct2.Alias }, new[] { "blah" }) - .Where(x => x.Item2).Select(x => x.Item1).ToArray(); + .Results.Where(x => x.Allowed).Select(x => x.Composition).ToArray(); Assert.AreEqual(1, availableTypes.Count()); Assert.AreEqual(ct4.Id, availableTypes.ElementAt(0).Id); @@ -84,7 +84,7 @@ namespace Umbraco.Tests.Services new[] { ct1, ct2, ct3, ct4 }, new string[] { }, new[] { "title" }) - .Where(x => x.Item2).Select(x => x.Item1).ToArray(); + .Results.Where(x => x.Allowed).Select(x => x.Composition).ToArray(); Assert.AreEqual(1, availableTypes.Count()); Assert.AreEqual(ct4.Id, availableTypes.ElementAt(0).Id); @@ -120,7 +120,7 @@ namespace Umbraco.Tests.Services ct1, new[] { ct1, ct2, ct3, ct4 }, new [] {ct2.Alias}) - .Where(x => x.Item2).Select(x => x.Item1).ToArray(); + .Results.Where(x => x.Allowed).Select(x => x.Composition).ToArray(); Assert.AreEqual(1, availableTypes.Count()); Assert.AreEqual(ct4.Id, availableTypes.ElementAt(0).Id); @@ -141,7 +141,7 @@ namespace Umbraco.Tests.Services var availableTypes = service.Object.GetAvailableCompositeContentTypes( ct1, new[] {ct1, ct2, ct3}) - .Where(x => x.Item2).Select(x => x.Item1).ToArray(); + .Results.Where(x => x.Allowed).Select(x => x.Composition).ToArray(); Assert.AreEqual(2, availableTypes.Count()); Assert.AreEqual(ct2.Id, availableTypes.ElementAt(0).Id); @@ -163,7 +163,7 @@ namespace Umbraco.Tests.Services var availableTypes = service.Object.GetAvailableCompositeContentTypes( ct1, - new[] { ct1, ct2, ct3 }); + new[] { ct1, ct2, ct3 }).Results; Assert.AreEqual(0, availableTypes.Count()); } @@ -185,7 +185,7 @@ namespace Umbraco.Tests.Services var availableTypes = service.Object.GetAvailableCompositeContentTypes( ct1, - new[] { ct1, ct2, ct3 }); + new[] { ct1, ct2, ct3 }).Results; Assert.AreEqual(0, availableTypes.Count()); } @@ -207,7 +207,7 @@ namespace Umbraco.Tests.Services var availableTypes = service.Object.GetAvailableCompositeContentTypes( ct1, new[] { ct1, ct2, ct3 }) - .Where(x => x.Item2).Select(x => x.Item1).ToArray(); + .Results.Where(x => x.Allowed).Select(x => x.Composition).ToArray(); Assert.AreEqual(1, availableTypes.Count()); Assert.AreEqual(ct3.Id, availableTypes.Single().Id); @@ -230,7 +230,7 @@ namespace Umbraco.Tests.Services var availableTypes = service.Object.GetAvailableCompositeContentTypes( ct1, new[] { ct1, ct2, ct3 }) - .Where(x => x.Item2).Select(x => x.Item1).ToArray(); + .Results.Where(x => x.Allowed).Select(x => x.Composition).ToArray(); Assert.AreEqual(2, availableTypes.Count()); Assert.AreEqual(ct2.Id, availableTypes.ElementAt(0).Id); @@ -257,7 +257,7 @@ namespace Umbraco.Tests.Services var availableTypes = service.Object.GetAvailableCompositeContentTypes( ct1, new[] { ct1, ct2, ct3 }) - .Where(x => x.Item2).Select(x => x.Item1).ToArray(); + .Results.Where(x => x.Allowed).Select(x => x.Composition).ToArray(); Assert.AreEqual(3, availableTypes.Count()); Assert.AreEqual(ct2.Id, availableTypes.ElementAt(0).Id); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html index d936bfa679..a113e8309c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contenttypeeditor/compositions/compositions.html @@ -31,22 +31,22 @@
  • + ng-class="{'-disabled': vm.isDisabled(compositeContentType.contentType.alias), '-selected': vm.isSelected(compositeContentType.contentType.alias)}">
    + ng-class="{ '-selected': model.compositeContentTypes.indexOf(compositeContentType.contentType.alias)+1 }">
    -