diff --git a/ToSic.Eav.Apps/ImportExport/XmlExporter.cs b/ToSic.Eav.Apps/ImportExport/XmlExporter.cs index 396d33dbd..ece774ee0 100644 --- a/ToSic.Eav.Apps/ImportExport/XmlExporter.cs +++ b/ToSic.Eav.Apps/ImportExport/XmlExporter.cs @@ -157,7 +157,7 @@ protected void InitExportXDocument(string defaultLanguage, string moduleVersion) : (ContentType) AppState.GetContentType(attributeSetId); // in case it's the name, not the number // skip system/code-types - if((set.ParentId ?? 0) == Constants.PresetContentTypeFakeParent) + if (set.HasPresetAncestor()) // ((set.ParentId ?? 0) == Constants.PresetContentTypeFakeParent) continue; var attributes = new XElement(XmlConstants.Attributes); @@ -187,7 +187,7 @@ select GetEntityXElement(c.EntityId, c.Type.StaticName) attributes); // Add Ghost-Info if content type inherits from another content type - if (set.ParentId.HasValue) + if (set.HasAncestor()) // .ParentId.HasValue) { var parentStaticName = set.StaticName; attributeSet.Add(new XAttribute(XmlConstants.AttributeSetParentDef, parentStaticName)); diff --git a/ToSic.Eav.Core.Tests/Types/TypesBase.cs b/ToSic.Eav.Core.Tests/Types/TypesBase.cs index 1d2d0bae4..0952413a7 100644 --- a/ToSic.Eav.Core.Tests/Types/TypesBase.cs +++ b/ToSic.Eav.Core.Tests/Types/TypesBase.cs @@ -17,33 +17,34 @@ public abstract class TypesBase: ContentType protected const string DefInp = "default"; private const string UndefinedScope = "Undefined"; - protected TypesBase(string name, string staticName, string scope = UndefinedScope) : base(0, name, staticName) - { - Scope = scope; - Description = "todo"; - Attributes = new List(); - //IsInstalledInPrimaryStorage = false; - RepositoryType = RepositoryTypes.Code; - ParentId = Constants.PresetContentTypeFakeParent; // important that parentid is different, so the GUI regards this as a ghost, and doesn't provide editing features - //I18nKey = i18nKey; - } + // 2021-11-22 2dm disabled all of this, don't believe it's ever used, maybe #cleanup EOY 2021 + //protected TypesBase(string name, string staticName, string scope = UndefinedScope) : base(0, name, staticName) + //{ + // Scope = scope; + // Description = "todo"; + // Attributes = new List(); + // //IsInstalledInPrimaryStorage = false; + // RepositoryType = RepositoryTypes.Code; + // ParentId = Constants.PresetContentTypeFakeParent; // important that parentid is different, so the GUI regards this as a ghost, and doesn't provide editing features + // //I18nKey = i18nKey; + //} - protected ContentTypeAttribute Add(ContentTypeAttribute attDef) - { - Attributes.Add(attDef); - return attDef; - } + //protected ContentTypeAttribute Add(ContentTypeAttribute attDef) + //{ + // Attributes.Add(attDef); + // return attDef; + //} - protected ContentTypeAttribute AttDef(ValueTypes type, string input, string name, string niceName = null, string description = null, string defaultValue = null) - { - var correctedInput = type.ToString().ToLowerInvariant() + "-" + input; - var attDef = new ContentTypeAttribute(AppId, name, /* niceName, */ type.ToString(), /* correctedInput, description, true, defaultValue, */ - attributeMetadata: new List - { - AttDefBuilder.GenerateAttributeMetadata(null /* TODO: NOW NEEDS GlobalTypes, but tests not updated */, AppId, niceName, description, true, - HelpersToRefactor.SerializeValue(defaultValue), correctedInput) - }); - return attDef; - } + //protected ContentTypeAttribute AttDef(ValueTypes type, string input, string name, string niceName = null, string description = null, string defaultValue = null) + //{ + // var correctedInput = type.ToString().ToLowerInvariant() + "-" + input; + // var attDef = new ContentTypeAttribute(AppId, name, /* niceName, */ type.ToString(), /* correctedInput, description, true, defaultValue, */ + // attributeMetadata: new List + // { + // AttDefBuilder.GenerateAttributeMetadata(null /* TODO: NOW NEEDS GlobalTypes, but tests not updated */, AppId, niceName, description, true, + // HelpersToRefactor.SerializeValue(defaultValue), correctedInput) + // }); + // return attDef; + //} } } diff --git a/ToSic.Eav.Core/Data/Builder/ContentTypeBuilder.cs b/ToSic.Eav.Core/Data/Builder/ContentTypeBuilder.cs index 92086da88..9696475b1 100644 --- a/ToSic.Eav.Core/Data/Builder/ContentTypeBuilder.cs +++ b/ToSic.Eav.Core/Data/Builder/ContentTypeBuilder.cs @@ -1,20 +1,22 @@ using System.Collections.Generic; +using ToSic.Eav.Data.Shared; using ToSic.Eav.Repositories; namespace ToSic.Eav.Data.Builder { public static class ContentTypeBuilder { - /// - /// Shortcut go get a new AttributeSet with Scope=System and Name=StaticName - /// - public static ContentType SystemAttributeSet(int appId, string staticName, string description, - List attributes, bool alwaysShareConfiguration = false) - => new ContentType(appId, staticName, staticName, 0, Constants.ScopeSystem, description, null, 0, 0, - alwaysShareConfiguration) - { - Attributes = attributes - }; + // 2021-11-22 2dm removed, seems unused - #cleanup EOY 2021 + ///// + ///// Shortcut go get a new AttributeSet with Scope=System and Name=StaticName + ///// + //public static ContentType SystemAttributeSet(int appId, string staticName, string description, + // List attributes, bool alwaysShareConfiguration = false) + // => new ContentType(appId, staticName, staticName, 0, Constants.ScopeSystem, description, null, 0, 0, + // alwaysShareConfiguration) + // { + // Attributes = attributes + // }; public const int DynTypeId = 1; public const string DynTypeDefScope = Constants.ScopeSystem; @@ -24,8 +26,7 @@ public static ContentType Fake(string typeName) => DynamicContentType(Constants.TransientAppId, typeName, typeName); public static ContentType DynamicContentType(int appId, string typeName, string typeIdentifier, string scope = DynTypeDefScope) - => new ContentType(appId, typeName, typeIdentifier, DynTypeId, scope, DynTypeDefDescription, null, 0, 0, - false) + => new ContentType(appId, typeName, typeIdentifier, DynTypeId, scope, DynTypeDefDescription) { Attributes = new List(), IsDynamic = true @@ -39,8 +40,11 @@ public static void SetSource(this ContentType type, RepositoryTypes repoType) public static void SetSourceAndParent(this ContentType type, RepositoryTypes repoType, int parentId, string address) { type.RepositoryType = repoType; - type.ParentId = parentId; + //type.ParentId = parentId; type.RepositoryAddress = address; + var ancestorDecorator = type.GetDecorator(); + if (ancestorDecorator != null) ancestorDecorator.Id = parentId; + else type.Decorators.Add(new Ancestor(Constants.PresetIdentity, parentId)); } } } diff --git a/ToSic.Eav.Core/Data/ContentTypes/ContentType.Decorators.cs b/ToSic.Eav.Core/Data/ContentTypes/ContentType.Decorators.cs new file mode 100644 index 000000000..35be2735e --- /dev/null +++ b/ToSic.Eav.Core/Data/ContentTypes/ContentType.Decorators.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace ToSic.Eav.Data +{ + public partial class ContentType: IHasDecorators + { + public List> Decorators => + _decorators ?? (_decorators = new List>()); + private List> _decorators; + } +} diff --git a/ToSic.Eav.Core/Data/ContentTypes/ContentType.Import.cs b/ToSic.Eav.Core/Data/ContentTypes/ContentType.Import.cs new file mode 100644 index 000000000..0ad9d9f95 --- /dev/null +++ b/ToSic.Eav.Core/Data/ContentTypes/ContentType.Import.cs @@ -0,0 +1,29 @@ +using ToSic.Eav.Documentation; + +namespace ToSic.Eav.Data +{ + public partial class ContentType + { + + #region Helpers just for creating ContentTypes which will be imported + [PrivateApi] + public void SetImportParameters(string scope, string staticName, string description, bool alwaysShareDef) + { + Scope = scope; + StaticName = staticName; + Description = description; + AlwaysShareConfiguration = alwaysShareDef; + } + + // special values just needed for import / save + // todo: try to place in a sub-object to un-clutter this ContentType object + [PrivateApi] + public bool OnSaveSortAttributes { get; set; } = false; + + [PrivateApi] + public string OnSaveUseParentStaticName { get; set; } + + + #endregion + } +} diff --git a/ToSic.Eav.Core/Data/ContentTypes/ContentType.Metadata.cs b/ToSic.Eav.Core/Data/ContentTypes/ContentType.Metadata.cs new file mode 100644 index 000000000..6550ba299 --- /dev/null +++ b/ToSic.Eav.Core/Data/ContentTypes/ContentType.Metadata.cs @@ -0,0 +1,21 @@ +using System; +using ToSic.Eav.Metadata; + +namespace ToSic.Eav.Data +{ + public partial class ContentType + { + + #region Metadata + + /// + public ContentTypeMetadata Metadata => _metadata ?? (_metadata = new ContentTypeMetadata(StaticName, _metaSourceFinder)); + + private ContentTypeMetadata _metadata; + private readonly Func _metaSourceFinder; + + IMetadataOf IHasMetadata.Metadata => Metadata; + + #endregion + } +} diff --git a/ToSic.Eav.Core/Data/ContentTypes/ContentType.Shared.cs b/ToSic.Eav.Core/Data/ContentTypes/ContentType.Shared.cs new file mode 100644 index 000000000..c4babf45a --- /dev/null +++ b/ToSic.Eav.Core/Data/ContentTypes/ContentType.Shared.cs @@ -0,0 +1,18 @@ +namespace ToSic.Eav.Data +{ + public partial class ContentType + { + + #region Sharing Content Types + ///// + //public int? ParentId { get; internal set; } + ///// + //public int ParentAppId { get; } + ///// + //public int ParentZoneId { get; } + /// + public bool AlwaysShareConfiguration { get; private set; } + + #endregion + } +} diff --git a/ToSic.Eav.Core/Data/ContentTypes/ContentType.cs b/ToSic.Eav.Core/Data/ContentTypes/ContentType.cs index 9cd35c540..1164de7e0 100644 --- a/ToSic.Eav.Core/Data/ContentTypes/ContentType.cs +++ b/ToSic.Eav.Core/Data/ContentTypes/ContentType.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using ToSic.Eav.Apps; +using ToSic.Eav.Data.Shared; using ToSic.Eav.Documentation; using ToSic.Eav.Metadata; using ToSic.Eav.Repositories; @@ -15,7 +17,7 @@ namespace ToSic.Eav.Data // We should actually make it PrivateApi, but other code references this, so we need to change that to IContentType, // Otherwise docs won't generate cross-links as needed [PrivateApi("2021-09-30 hidden now, was internal_don't use Always use the interface, not this class")] - public class ContentType : IContentType, IContentTypeShared + public partial class ContentType : IContentType, IContentTypeShared { #region simple properties @@ -49,6 +51,8 @@ public class ContentType : IContentType, IContentTypeShared /// public bool IsDynamic { get; internal set; } + #endregion + /// public bool Is(string name) => Name.Equals(name, InvariantCultureIgnoreCase) || StaticName.Equals(name, InvariantCultureIgnoreCase); @@ -57,25 +61,13 @@ public class ContentType : IContentType, IContentTypeShared #region New DynamicChildren Navigation - new in 12.03 + + /// [PrivateApi("WIP 12.03")] // Don't cache the result, as it could change during runtime public string DynamicChildrenField => Metadata.GetBestValue(ContentTypes.DynamicChildrenField); - #endregion - - #endregion - - #region Sharing Content Types - /// - public int? ParentId { get; internal set; } - /// - public int ParentAppId { get; } - /// - public int ParentZoneId { get; } - /// - public bool AlwaysShareConfiguration { get; private set; } - #endregion @@ -87,9 +79,11 @@ public class ContentType : IContentType, IContentTypeShared /// [PrivateApi] public ContentType(int appId, string name, string staticName, int attributeSetId, string scope, - string description, int? usesConfigurationOfAttributeSet, - int configZoneId, int configAppId, - bool configurationIsOmnipresent, + string description, + int? parentTypeId = null, + int configZoneId = 0, + int configAppId = 0, + bool configurationIsOmnipresent = false, Func metaSourceFinder = null): this(appId, name, staticName) { ContentTypeId = attributeSetId; @@ -97,11 +91,15 @@ public ContentType(int appId, string name, string staticName, int attributeSetId Scope = scope; // Shared Content-Types - ParentId = usesConfigurationOfAttributeSet; - ParentZoneId = configZoneId; - ParentAppId = configAppId; + //ParentId = parentTypeId; + //ParentZoneId = configZoneId; + //ParentAppId = configAppId; AlwaysShareConfiguration = configurationIsOmnipresent; + if (parentTypeId != null) + Decorators.Add(new Ancestor(new AppIdentity(configZoneId, configAppId), + parentTypeId.Value)); + // Metadata _metaSourceFinder = metaSourceFinder; } @@ -123,43 +121,7 @@ public ContentType(int appId, string name, string staticName = null) #endregion - #region Helpers just for creating ContentTypes which will be imported - [PrivateApi] - public void SetImportParameters(string scope, string staticName, string description, bool alwaysShareDef) - { - Scope = scope; - StaticName = staticName; - Description = description; - AlwaysShareConfiguration = alwaysShareDef; - } - - // special values just needed for import / save - // todo: try to place in a sub-object to un-clutter this ContentType object - [PrivateApi] - public bool OnSaveSortAttributes { get; set; } = false; - - [PrivateApi] - public string OnSaveUseParentStaticName { get; set; } - - #endregion - - #region Metadata - - - - /// - public ContentTypeMetadata Metadata - => _metadata ?? (_metadata = new ContentTypeMetadata(StaticName, _metaSourceFinder)); - //=> _metadata ?? (_metadata = ParentAppId == AppId - //? new ContentTypeMetadata(StaticName, _metaOfThisApp) - //: new ContentTypeMetadata(StaticName, _metaSourceFinder)); - private ContentTypeMetadata _metadata; - //private readonly IHasMetadataSource _metaOfThisApp; - private readonly Func _metaSourceFinder; - - IMetadataOf IHasMetadata.Metadata => Metadata; - #endregion } } \ No newline at end of file diff --git a/ToSic.Eav.Core/Data/ContentTypes/ContentTypeExtensions.cs b/ToSic.Eav.Core/Data/ContentTypes/ContentTypeExtensions.cs new file mode 100644 index 000000000..e5dacddd1 --- /dev/null +++ b/ToSic.Eav.Core/Data/ContentTypes/ContentTypeExtensions.cs @@ -0,0 +1,19 @@ +using ToSic.Eav.Data.Shared; + +namespace ToSic.Eav.Data +{ + public static class ContentTypeExtensions + { + public static bool HasAncestor(this IContentType contentType) + { + var anc = contentType.GetDecorator(); + return anc != null && anc.Id != 0; + } + + public static bool HasPresetAncestor(this IContentType contentType) + { + var anc = contentType.GetDecorator(); + return anc != null && anc.Id == Constants.PresetContentTypeFakeParent; + } + } +} diff --git a/ToSic.Eav.Core/Data/ContentTypes/ContentTypeWrapper.Equality.cs b/ToSic.Eav.Core/Data/ContentTypes/ContentTypeWrapper.Equality.cs new file mode 100644 index 000000000..04ff19888 --- /dev/null +++ b/ToSic.Eav.Core/Data/ContentTypes/ContentTypeWrapper.Equality.cs @@ -0,0 +1,24 @@ +using System; + +namespace ToSic.Eav.Data +{ + public partial class ContentTypeWrapper : IEquatable + { + public bool Equals(ContentTypeWrapper other) + { + if (ReferenceEquals(null, other?.GetContents())) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(GetContents(), other.GetContents()); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((ContentTypeWrapper)obj); + } + + public override int GetHashCode() => (GetContents() != null ? GetContents().GetHashCode() : 0); + } +} diff --git a/ToSic.Eav.Core/Data/ContentTypes/ContentTypeWrapper.cs b/ToSic.Eav.Core/Data/ContentTypes/ContentTypeWrapper.cs new file mode 100644 index 000000000..07d0ce515 --- /dev/null +++ b/ToSic.Eav.Core/Data/ContentTypes/ContentTypeWrapper.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using ToSic.Eav.Metadata; +using ToSic.Eav.Repositories; + +namespace ToSic.Eav.Data +{ + public partial class ContentTypeWrapper: Wrapper, IContentType, IHasDecorators + { + public List> Decorators { get; } = new List>(); + + /// + /// Create a new wrapper. + /// In case we're re-wrapping another wrapper, make sure we use the real, underlying contentType for the Contents + /// + /// + public ContentTypeWrapper(IContentType contentType) : base((contentType as ContentTypeWrapper)?.GetContents() ?? contentType) + { + if (contentType is ContentTypeWrapper wrapper) + Decorators.AddRange(wrapper.Decorators); + + } + + public int AppId => GetContents().AppId; + + public string Name => GetContents().Name; + + public string StaticName => GetContents().StaticName; + + public string Description => GetContents().Description; + + public string Scope => GetContents().Scope; + + public int ContentTypeId => GetContents().ContentTypeId; + + public IList Attributes + { + get => GetContents().Attributes; + set => GetContents().Attributes = value; + } + + public IContentTypeAttribute this[string fieldName] => GetContents()[fieldName]; + + public RepositoryTypes RepositoryType => GetContents().RepositoryType; + + public string RepositoryAddress => GetContents().RepositoryAddress; + + public bool IsDynamic => GetContents().IsDynamic; + + public ContentTypeMetadata Metadata => GetContents().Metadata; + + public bool Is(string name) + { + return GetContents().Is(name); + } + + public string DynamicChildrenField => GetContents().DynamicChildrenField; + + IMetadataOf IHasMetadata.Metadata => ((IHasMetadata)GetContents()).Metadata; + } +} diff --git a/ToSic.Eav.Core/Data/ContentTypes/IContentType.cs b/ToSic.Eav.Core/Data/ContentTypes/IContentType.cs index 5a3b4cc76..c58825fdc 100644 --- a/ToSic.Eav.Core/Data/ContentTypes/IContentType.cs +++ b/ToSic.Eav.Core/Data/ContentTypes/IContentType.cs @@ -84,7 +84,10 @@ public interface IContentType: IAppIdentityLight, IHasMetadata #region WIP 12.03 - + /// + /// This internal property says which of the content-type properties + /// should be used for DynamicChildren, enabling Content.Lightbox.xyz instead of Content.Items.Lightbox.xyz + /// [PrivateApi("WIP 12.03")] string DynamicChildrenField { get; } diff --git a/ToSic.Eav.Core/Data/ContentTypes/IContentTypeShared.cs b/ToSic.Eav.Core/Data/ContentTypes/IContentTypeShared.cs index 488cf7751..638cb2986 100644 --- a/ToSic.Eav.Core/Data/ContentTypes/IContentTypeShared.cs +++ b/ToSic.Eav.Core/Data/ContentTypes/IContentTypeShared.cs @@ -8,20 +8,21 @@ namespace ToSic.Eav.Data [PrivateApi("not yet ready to publish, names will probably change some day")] public interface IContentTypeShared { - /// - /// The parent zone - /// - int ParentZoneId { get; } + // 2021-11-22 2dm disabled / moved all to decorators, #cleanup EOY 2021 + ///// + ///// The parent zone + ///// + //int ParentZoneId { get; } - /// - /// The parent app - /// - int ParentAppId { get; } + ///// + ///// The parent app + ///// + //int ParentAppId { get; } - /// - /// Get the id of the source Content Type if configuration is used from another - /// - int? ParentId { get; } + ///// + ///// Get the id of the source Content Type if configuration is used from another + ///// + //int? ParentId { get; } /// /// If this configuration is auto-shared everywhere diff --git a/ToSic.Eav.Core/Data/Decorators/IHasDecoratorExtensions.cs b/ToSic.Eav.Core/Data/Decorators/IHasDecoratorExtensions.cs index 42ef632c3..270ca6503 100644 --- a/ToSic.Eav.Core/Data/Decorators/IHasDecoratorExtensions.cs +++ b/ToSic.Eav.Core/Data/Decorators/IHasDecoratorExtensions.cs @@ -17,11 +17,11 @@ public static TDecorator GetDecorator(this IEntity parent) where TDe return parentWithDecorator.GetDecorator(); } - //public static TDecorator GetDecorator(this IContentType parent) where TDecorator : class, IDecorator - //{ - // if (!(parent is IHasDecorators parentWithDecorator)) return null; - // return parentWithDecorator.GetDecorator(); - //} + public static TDecorator GetDecorator(this IContentType parent) where TDecorator : class, IDecorator + { + if (!(parent is IHasDecorators parentWithDecorator)) return null; + return parentWithDecorator.GetDecorator(); + } } } diff --git a/ToSic.Eav.Core/Data/Shared/Ancestor.T.cs b/ToSic.Eav.Core/Data/Shared/Ancestor.T.cs new file mode 100644 index 000000000..590737239 --- /dev/null +++ b/ToSic.Eav.Core/Data/Shared/Ancestor.T.cs @@ -0,0 +1,14 @@ +using ToSic.Eav.Apps; + +namespace ToSic.Eav.Data.Shared +{ + public class Ancestor: AppIdentity, IAncestor, IDecorator + { + public Ancestor(IAppIdentity parent, int id) : base(parent) + { + Id = id; + } + + public int Id { get; set; } + } +} diff --git a/ToSic.Eav.Core/Data/Shared/IAncestor.cs b/ToSic.Eav.Core/Data/Shared/IAncestor.cs new file mode 100644 index 000000000..40683aecd --- /dev/null +++ b/ToSic.Eav.Core/Data/Shared/IAncestor.cs @@ -0,0 +1,9 @@ +using ToSic.Eav.Apps; + +namespace ToSic.Eav.Data.Shared +{ + public interface IAncestor: IAppIdentity, IDecorator + { + int Id { get; set; } + } +} diff --git a/ToSic.Eav.Core/Data/Shared/IShareSource.T.cs b/ToSic.Eav.Core/Data/Shared/IShareSource.T.cs new file mode 100644 index 000000000..67902d364 --- /dev/null +++ b/ToSic.Eav.Core/Data/Shared/IShareSource.T.cs @@ -0,0 +1,6 @@ +namespace ToSic.Eav.Data.Shared +{ + public interface IShareSource: IWrapper, IAncestor + { + } +} diff --git a/ToSic.Eav.ImportExport/Json/JsonDeserializer_ContentType.cs b/ToSic.Eav.ImportExport/Json/JsonDeserializer_ContentType.cs index 5314c973e..d10025258 100644 --- a/ToSic.Eav.ImportExport/Json/JsonDeserializer_ContentType.cs +++ b/ToSic.Eav.ImportExport/Json/JsonDeserializer_ContentType.cs @@ -34,7 +34,8 @@ public IContentType DeserializeContentType(string serialized) var type = new ContentType(AppId, jsonType.Name, jsonType.Id, 0, jsonType.Scope, jsonType.Description, - jsonType.Sharing?.ParentId, jsonType.Sharing?.ParentZoneId ?? 0, + jsonType.Sharing?.ParentId, + jsonType.Sharing?.ParentZoneId ?? 0, jsonType.Sharing?.ParentAppId ?? 0, jsonType.Sharing?.AlwaysShare ?? false); diff --git a/ToSic.Eav.ImportExport/Json/JsonSerializer_ContentType.cs b/ToSic.Eav.ImportExport/Json/JsonSerializer_ContentType.cs index d08bff6d7..8b21999e7 100644 --- a/ToSic.Eav.ImportExport/Json/JsonSerializer_ContentType.cs +++ b/ToSic.Eav.ImportExport/Json/JsonSerializer_ContentType.cs @@ -3,6 +3,7 @@ using System.Linq; using Newtonsoft.Json; using ToSic.Eav.Data; +using ToSic.Eav.Data.Shared; using ToSic.Eav.ImportExport.Json.V1; namespace ToSic.Eav.ImportExport.Json @@ -54,7 +55,6 @@ public JsonContentType ToJson(IContentType contentType) public JsonContentType ToJson(IContentType contentType, bool includeSharedTypes) { - var sharableCt = contentType as IContentTypeShared; JsonContentTypeShareable jctShare = null; var attribs = contentType.Attributes @@ -77,21 +77,28 @@ public JsonContentType ToJson(IContentType contentType, bool includeSharedTypes) .ToList() .ForEach(e => e.For = null); - var typeIsShared = sharableCt != null && (sharableCt.AlwaysShareConfiguration || - sharableCt.ParentId.HasValue && sharableCt.ParentId != - Constants.PresetContentTypeFakeParent); - if (typeIsShared && !includeSharedTypes) + var sharableCt = contentType as IContentTypeShared; + //var typeIsShared = sharableCt != null && (sharableCt.AlwaysShareConfiguration || + // sharableCt.ParentId.HasValue && sharableCt.ParentId != + // Constants.PresetContentTypeFakeParent); + + var ancestorDecorator = contentType.GetDecorator(); + var isSharedNew = ancestorDecorator != null && + ancestorDecorator.Id != Constants.PresetContentTypeFakeParent; + + // Note 2021-11-22 2dm - AFAIK this is skipped when creating a JSON for edit-UI + if (isSharedNew /*typeIsShared*/ && !includeSharedTypes) { // if it's a shared type, flush definition as we won't include it - if (sharableCt.ParentId.HasValue) - attribs = null; + if (ancestorDecorator.Id != 0)// sharableCt.ParentId.HasValue) + attribs = null; jctShare = new JsonContentTypeShareable { AlwaysShare = sharableCt.AlwaysShareConfiguration, - ParentAppId = sharableCt.ParentAppId, - ParentZoneId = sharableCt.ParentZoneId, - ParentId = sharableCt.ParentId + ParentAppId = ancestorDecorator.AppId, // sharableCt.ParentAppId, + ParentZoneId = ancestorDecorator.ZoneId, // sharableCt.ParentZoneId, + ParentId = ancestorDecorator.Id, // sharableCt.ParentId }; } var package = new JsonContentType diff --git a/ToSic.Eav.Persistence.Efc/Efc11Loader_ContentTypes.cs b/ToSic.Eav.Persistence.Efc/Efc11Loader_ContentTypes.cs index 465cb5109..36e42617b 100644 --- a/ToSic.Eav.Persistence.Efc/Efc11Loader_ContentTypes.cs +++ b/ToSic.Eav.Persistence.Efc/Efc11Loader_ContentTypes.cs @@ -70,8 +70,7 @@ private IList InitFileSystemContentTypes(AppState app) /// /// Load DB content-types into loader-cache /// - private ImmutableList LoadContentTypesIntoLocalCache(int appId, - IHasMetadataSource source) + private ImmutableList LoadContentTypesIntoLocalCache(int appId, IHasMetadataSource source) { var wrapLog = Log.Call(useTimer: true); // Load from DB @@ -110,13 +109,17 @@ private ImmutableList LoadContentTypesIntoLocalCache(int appId, var shareids = contentTypes.Select(c => c.SharedDefinitionId).ToList(); sqlTime.Start(); + var sharedAttribs = _dbContext.ToSicEavAttributeSets .Include(s => s.ToSicEavAttributesInSets) .ThenInclude(a => a.Attribute) .Where(s => shareids.Contains(s.AttributeSetId)) - .ToDictionary(s => s.AttributeSetId, s => s.ToSicEavAttributesInSets.Select(a - => new ContentTypeAttribute(appId, a.Attribute.StaticName, a.Attribute.Type, a.IsTitle, - a.AttributeId, a.SortOrder, parentApp: s.AppId, metaSourceFinder: () => _appStates.Get(s.AppId)))); + .ToDictionary( + s => s.AttributeSetId, + s => s.ToSicEavAttributesInSets.Select(a + => new ContentTypeAttribute(appId, a.Attribute.StaticName, a.Attribute.Type, a.IsTitle, + a.AttributeId, a.SortOrder, parentApp: s.AppId, + metaSourceFinder: () => _appStates.Get(s.AppId))));// Must get own MetaSourceFinder since they come from other apps sqlTime.Stop(); // Convert to ContentType-Model diff --git a/ToSic.Eav.WebApi/ContentTypeApi.cs b/ToSic.Eav.WebApi/ContentTypeApi.cs index facda993e..f483c6139 100644 --- a/ToSic.Eav.WebApi/ContentTypeApi.cs +++ b/ToSic.Eav.WebApi/ContentTypeApi.cs @@ -4,6 +4,7 @@ using ToSic.Eav.Apps; using ToSic.Eav.Apps.Parts; using ToSic.Eav.Data; +using ToSic.Eav.Data.Shared; using ToSic.Eav.DataFormats.EavLight; using ToSic.Eav.ImportExport; using ToSic.Eav.Logging; @@ -102,25 +103,30 @@ private ContentTypeDto ContentTypeForJson(ContentType t, int count = -1) nameOverride = t.Name; var ser = _dataToDictionaryLazy.Value; - var shareInfo = (IContentTypeShared) t; + //var shareInfo = (IContentTypeShared) t; + var ancestorDecorator = t.GetDecorator(); var properties = ser.Convert(metadata); - var jsonReady = new ContentTypeDto - { - Id = t.ContentTypeId, - Name = t.Name, - Label = nameOverride, - StaticName = t.StaticName, - Scope = t.Scope, - Description = t.Description, - UsesSharedDef = shareInfo.ParentId != null, - SharedDefId = shareInfo.ParentId, - Items = count, - Fields = t.Attributes.Count, - Metadata = properties, + var jsonReady = new ContentTypeDto + { + Id = t.ContentTypeId, + Name = t.Name, + Label = nameOverride, + StaticName = t.StaticName, + Scope = t.Scope, + Description = t.Description, + + IsReadOnly = ancestorDecorator != null ? true : (bool?)null, + IsReadOnlyReason = ancestorDecorator == null ? null : t.HasPresetAncestor() ? "This is a preset ContentType" : "This is an inherited ContentType", + UsesSharedDef = ancestorDecorator != null, // shareInfo.ParentId != null, + SharedDefId = ancestorDecorator?.Id, // shareInfo.ParentId, + + Items = count, + Fields = t.Attributes.Count, + Metadata = properties, Properties = properties, - Permissions = new HasPermissionsDto { Count = t.Metadata.Permissions.Count()}, - }; + Permissions = new HasPermissionsDto { Count = t.Metadata.Permissions.Count() }, + }; return jsonReady; } @@ -180,6 +186,8 @@ public IEnumerable GetFields(string staticName) throw new Exception("type should be a ContentType - something broke"); var fields = type.Attributes.OrderBy(a => a.SortOrder); + var hasAncestor = type.HasAncestor(); + var ancestorDecorator = type.GetDecorator(); var appInputTypes = _appRuntimeLazy.Value.Init(_appId, true, Log).ContentTypes.GetInputTypes(); @@ -206,17 +214,37 @@ public IEnumerable GetFields(string staticName) : e.Type.StaticName; return name.TrimStart('@'); }, - e => ser.Convert(e) + e => InputMetadata(type, a, e, ancestorDecorator, ser) // ser.Convert(e) ), InputTypeConfig = appInputTypes.FirstOrDefault(it => it.Type == inputType), - Permissions = new HasPermissionsDto {Count = a.Metadata.Permissions.Count()}, + Permissions = new HasPermissionsDto { Count = a.Metadata.Permissions.Count() }, // new in 12.01 - IsEphemeral = a.Metadata.GetBestValue(AttributeMetadata.MetadataFieldAllIsEphemeral, AttributeMetadata.TypeGeneral), + IsEphemeral = a.Metadata.GetBestValue(AttributeMetadata.MetadataFieldAllIsEphemeral, + AttributeMetadata.TypeGeneral), HasFormulas = HasCalculations(a), + + // Read-Only new in v13 + IsReadOnly = hasAncestor ? true : (bool?)null, + IsReadOnlyReason = !hasAncestor ? null : type.HasPresetAncestor() ? "From Preset" : "Shared Type" }; }); - + } + + private EavLightEntity InputMetadata(IContentType contentType, IContentTypeAttribute a, IEntity e, IAncestor ancestor, IConvertToEavLight ser) + { + var result = ser.Convert(e); + if (ancestor != null) + result.Add("IdHeader", new + { + e.EntityId, + Ancestor = true, + IsMetadata = true, + OfContentType = contentType.StaticName, + OfAttribute = a.Name, + }); + + return result; } ///