Repository navigation
Content Types: Allow editing compositions on document types that are inherited from (closes #23103) #23433
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
AndyButland
merged 10 commits into
v17/dev
from
v17/bugfix/23103-compositions-locked-by-inheritance
Aug 24, 2026
Merged
Content Types: Allow editing compositions on document types that are inherited from (closes #23103) #23433
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
586a57f
Allow editing compositions on content types that are inherited from.
AndyButland 8b8c2f8
Addressed code review feedback.
AndyButland 263a663
Merge branch 'v17/dev' into v17/bugfix/23103-compositions-locked-by-i…
AndyButland 3b74514
Refresh descendant published types when a composition is added.
AndyButland 42f938e
Addressed Sonarqube and Copilot feedback.
AndyButland 6b3159b
Remove confusing comment.
AndyButland 954cf0d
Merge branch 'v17/dev' into v17/bugfix/23103-compositions-locked-by-i…
lauraneto cc510f9
Content Types: Also check descendants for property alias collisions w…
lauraneto af2eab8
Merge branch 'v17/dev' into v17/bugfix/23103-compositions-locked-by-i…
lauraneto fb222d3
Merge branch 'v17/dev' into v17/bugfix/23103-compositions-locked-by-i…
lauraneto File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| namespace Umbraco.Cms.Core.Services; | ||
|
|
||
| /// <summary> | ||
| /// Specifies which "composed of" relationships to include when resolving the content types that build upon a | ||
| /// given content type. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Inheritance and composition are stored in the same underlying collection and are distinguished only by whether | ||
| /// the referencing content type's tree parent is the content type in question. This lets callers ask for a single | ||
| /// axis or both. | ||
| /// </remarks> | ||
| public enum ComposedOfType | ||
| { | ||
| /// <summary> | ||
| /// Content types that use the content type as a true composition (i.e. not tree inheritance). | ||
| /// </summary> | ||
| Composition, | ||
|
|
||
| /// <summary> | ||
| /// Content types that inherit from the content type in the tree. | ||
| /// </summary> | ||
| Inheritance, | ||
|
|
||
| /// <summary> | ||
| /// Both composition users and inheritance children. | ||
| /// </summary> | ||
| All, | ||
| } |
67 changes: 67 additions & 0 deletions
67
src/Umbraco.Core/Services/ContentTypeEditing/ContentTypeEditingHelper.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| using Umbraco.Cms.Core.Models; | ||
|
|
||
| namespace Umbraco.Cms.Core.Services.ContentTypeEditing; | ||
|
|
||
| /// <summary> | ||
| /// Shared helper logic for content type editing. | ||
| /// </summary> | ||
| internal static class ContentTypeEditingHelper | ||
| { | ||
| /// <summary> | ||
| /// Gets the combined, full effective property alias set (own properties plus everything each one gets from | ||
| /// its own compositions) of every content type descending from <paramref name="source"/>, across both the | ||
| /// tree inheritance and composition axes - excluding whatever already flows into those descendants through | ||
| /// <paramref name="source"/>'s own current state. | ||
| /// </summary> | ||
| /// <param name="source">The content type to find descendants of, or <c>null</c> when there are none (e.g. creating a new content type).</param> | ||
| /// <param name="allContentTypes">All existing content type compositions, used to resolve the descendant tree.</param> | ||
| /// <returns>The property aliases (compared case-insensitively) already effective on every descendant of <paramref name="source"/>, excluding aliases <paramref name="source"/> itself already contributes.</returns> | ||
| /// <remarks> | ||
| /// Inheritance children hold their parent in their own <see cref="IContentTypeComposition.ContentTypeComposition"/>, | ||
| /// so walking the composition graph downward captures inheritance and composition descendants alike. | ||
| /// </remarks> | ||
| internal static HashSet<string> GetPropertyAliasesReservedByDescendants(this IContentTypeComposition? source, IContentTypeComposition[] allContentTypes) | ||
| { | ||
| if (source is null) | ||
| { | ||
| return []; | ||
| } | ||
|
|
||
| // build a "referenced id -> types that directly reference it" lookup once, so the traversal is O(n + d) | ||
| // rather than rescanning every content type for each descendant | ||
| ILookup<int, IContentTypeComposition> directReferencingTypes = allContentTypes | ||
| .SelectMany( | ||
| contentType => contentType.ContentTypeComposition, | ||
| (contentType, referenced) => (ReferencedId: referenced.Id, ContentType: contentType)) | ||
| .ToLookup(x => x.ReferencedId, x => x.ContentType); | ||
|
|
||
| var descendantIds = new HashSet<int>(); | ||
| var descendantPropertyAliases = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase); | ||
| var stack = new Stack<int>(); | ||
| stack.Push(source.Id); | ||
| while (stack.Count > 0) | ||
| { | ||
| var currentId = stack.Pop(); | ||
| foreach (IContentTypeComposition descendant in directReferencingTypes[currentId]) | ||
| { | ||
| if (!descendantIds.Add(descendant.Id)) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| IEnumerable<IPropertyType> allProperties = descendant.PropertyTypes | ||
| .Concat(descendant.CompositionPropertyTypes); | ||
| foreach (IPropertyType descendantPropertyType in allProperties) | ||
| { | ||
| descendantPropertyAliases.Add(descendantPropertyType.Alias); | ||
| } | ||
|
|
||
| stack.Push(descendant.Id); | ||
| } | ||
| } | ||
|
|
||
| descendantPropertyAliases.ExceptWith(source.CompositionPropertyTypes.Select(pt => pt.Alias)); | ||
|
|
||
| return descendantPropertyAliases; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.