From 96ea387a6c17acb8c106f28162551f66c3f893ce Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Fri, 21 Oct 2022 10:31:39 +0200 Subject: [PATCH 01/39] Bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 579911bae3..b36746fe7c 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "11.0.0-rc1", + "version": "11.0.0-rc2", "assemblyVersion": { "precision": "build" }, From 1560e84f1418b59ea9e1e784dfe8edf3a945eb34 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Tue, 25 Oct 2022 18:40:11 +0200 Subject: [PATCH 02/39] Add IContextCache to deploy connectors (#13287) * Add IContextCache and implementations * Update connector interfaces to use IContextCache * Minor cleanup * Move DeployContextCache prefix to constant * Move default implementations to obsolete methods * Remove DeployContextCache and DictionaryCache Co-authored-by: Andy Butland --- src/Umbraco.Core/Deploy/ArtifactBase.cs | 89 +++++++++--------- .../Deploy/ArtifactDeployState.cs | 37 ++++++++ .../ArtifactDeployStateOfTArtifactTEntity.cs | 38 -------- src/Umbraco.Core/Deploy/IContextCache.cs | 31 +++++++ .../Deploy/IDataTypeConfigurationConnector.cs | 62 +++++++++---- src/Umbraco.Core/Deploy/IServiceConnector.cs | 93 +++++++++++++------ src/Umbraco.Core/Deploy/IValueConnector.cs | 57 +++++++++--- src/Umbraco.Core/Deploy/PassThroughCache.cs | 34 +++++++ .../Deploy/IGridCellValueConnector.cs | 71 ++++++++++---- .../Umbraco.Core/CoreThings/UdiTests.cs | 4 +- 10 files changed, 359 insertions(+), 157 deletions(-) delete mode 100644 src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs create mode 100644 src/Umbraco.Core/Deploy/IContextCache.cs create mode 100644 src/Umbraco.Core/Deploy/PassThroughCache.cs diff --git a/src/Umbraco.Core/Deploy/ArtifactBase.cs b/src/Umbraco.Core/Deploy/ArtifactBase.cs index cc2415f4cd..0d354b65de 100644 --- a/src/Umbraco.Core/Deploy/ArtifactBase.cs +++ b/src/Umbraco.Core/Deploy/ArtifactBase.cs @@ -1,50 +1,49 @@ -namespace Umbraco.Cms.Core.Deploy +namespace Umbraco.Cms.Core.Deploy; + +/// +/// Provides a base class to all artifacts. +/// +public abstract class ArtifactBase : IArtifact + where TUdi : Udi { - /// - /// Provides a base class to all artifacts. - /// - public abstract class ArtifactBase : IArtifact - where TUdi : Udi + protected ArtifactBase(TUdi udi, IEnumerable? dependencies = null) { - protected ArtifactBase(TUdi udi, IEnumerable? dependencies = null) - { - Udi = udi ?? throw new ArgumentNullException("udi"); - Name = Udi.ToString(); + Udi = udi ?? throw new ArgumentNullException("udi"); + Name = Udi.ToString(); - _dependencies = dependencies ?? Enumerable.Empty(); - _checksum = new Lazy(GetChecksum); - } - - private readonly Lazy _checksum; - - private IEnumerable _dependencies; - - protected abstract string GetChecksum(); - - Udi IArtifactSignature.Udi => Udi; - - public TUdi Udi { get; set; } - - public string Checksum => _checksum.Value; - - /// - /// Prevents the property from being serialized. - /// - /// - /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore] - /// as that would require an external dependency in Umbraco.Cms.Core. - /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm - /// - public bool ShouldSerializeChecksum() => false; - - public IEnumerable Dependencies - { - get => _dependencies; - set => _dependencies = value.OrderBy(x => x.Udi); - } - - public string Name { get; set; } - - public string Alias { get; set; } = string.Empty; + _dependencies = dependencies ?? Enumerable.Empty(); + _checksum = new Lazy(GetChecksum); } + + private readonly Lazy _checksum; + + private IEnumerable _dependencies; + + protected abstract string GetChecksum(); + + Udi IArtifactSignature.Udi => Udi; + + public TUdi Udi { get; set; } + + public string Checksum => _checksum.Value; + + /// + /// Prevents the property from being serialized. + /// + /// + /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore] + /// as that would require an external dependency in Umbraco.Cms.Core. + /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm + /// + public bool ShouldSerializeChecksum() => false; + + public IEnumerable Dependencies + { + get => _dependencies; + set => _dependencies = value.OrderBy(x => x.Udi); + } + + public string Name { get; set; } + + public string Alias { get; set; } = string.Empty; } diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployState.cs b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs index 1b75fe11c0..e2dd343af1 100644 --- a/src/Umbraco.Core/Deploy/ArtifactDeployState.cs +++ b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs @@ -44,3 +44,40 @@ public abstract class ArtifactDeployState /// protected abstract IArtifact GetArtifactAsIArtifact(); } + +/// +/// Represent the state of an artifact being deployed. +/// +/// The type of the artifact. +/// The type of the entity. +public class ArtifactDeployState : ArtifactDeployState + where TArtifact : IArtifact +{ + /// + /// Initializes a new instance of the class. + /// + /// The artifact. + /// The entity. + /// The service connector deploying the artifact. + /// The next pass number. + public ArtifactDeployState(TArtifact art, TEntity? entity, IServiceConnector connector, int nextPass) + { + Artifact = art; + Entity = entity; + Connector = connector; + NextPass = nextPass; + } + + /// + /// Gets or sets the artifact. + /// + public new TArtifact Artifact { get; set; } + + /// + /// Gets or sets the entity. + /// + public TEntity? Entity { get; set; } + + /// + protected sealed override IArtifact GetArtifactAsIArtifact() => Artifact; +} diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs b/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs deleted file mode 100644 index 0ff1e20e87..0000000000 --- a/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Umbraco.Cms.Core.Deploy; - -/// -/// Represent the state of an artifact being deployed. -/// -/// The type of the artifact. -/// The type of the entity. -public class ArtifactDeployState : ArtifactDeployState - where TArtifact : IArtifact -{ - /// - /// Initializes a new instance of the class. - /// - /// The artifact. - /// The entity. - /// The service connector deploying the artifact. - /// The next pass number. - public ArtifactDeployState(TArtifact art, TEntity? entity, IServiceConnector connector, int nextPass) - { - Artifact = art; - Entity = entity; - Connector = connector; - NextPass = nextPass; - } - - /// - /// Gets or sets the artifact. - /// - public new TArtifact Artifact { get; set; } - - /// - /// Gets or sets the entity. - /// - public TEntity? Entity { get; set; } - - /// - protected sealed override IArtifact GetArtifactAsIArtifact() => Artifact; -} diff --git a/src/Umbraco.Core/Deploy/IContextCache.cs b/src/Umbraco.Core/Deploy/IContextCache.cs new file mode 100644 index 0000000000..d175064905 --- /dev/null +++ b/src/Umbraco.Core/Deploy/IContextCache.cs @@ -0,0 +1,31 @@ +namespace Umbraco.Cms.Core.Deploy; + +/// +/// Represents a context cache used by Deploy operations. +/// +public interface IContextCache +{ + /// + /// Creates the item on the context cache using the specified . + /// + /// The type of the cached item. + /// The key of the cached item. + /// The item. + void Create(string key, T item); + + /// + /// Gets an item from the context cache or creates and stores it using the specified . + /// + /// The type of the cached item. + /// The key of the cached item. + /// The factory method to create the item (if it doesn't exist yet). + /// + /// The item. + /// + T? GetOrCreate(string key, Func factory); + + /// + /// Clears all cached items on this context. + /// + void Clear(); +} diff --git a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs index 6b91926b57..506d1f5745 100644 --- a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs +++ b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs @@ -1,37 +1,67 @@ -using System.Diagnostics.CodeAnalysis; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert data type configuration to / from an environment-agnostic string. +/// Defines methods that can convert data type configuration to / from an environment-agnostic string. /// /// -/// Configuration may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. +/// Configuration may contain values such as content identifiers, that would be local +/// to one environment, and need to be converted in order to be deployed. /// -[SuppressMessage( - "ReSharper", - "UnusedMember.Global", - Justification = "This is actual only used by Deploy, but we don't want third parties to have references on deploy, that's why this interface is part of core.")] public interface IDataTypeConfigurationConnector { /// - /// Gets the property editor aliases that the value converter supports by default. + /// Gets the property editor aliases that the value converter supports by default. /// + /// + /// The property editor aliases. + /// IEnumerable PropertyEditorAliases { get; } /// - /// Gets the artifact datatype configuration corresponding to the actual datatype configuration. + /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. /// - /// The datatype. + /// The data type. /// The dependencies. - string? ToArtifact(IDataType dataType, ICollection dependencies); + /// + /// The artifact configuration value. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + string? ToArtifact(IDataType dataType, ICollection dependencies) + => ToArtifact(dataType, dependencies, PassThroughCache.Instance); /// - /// Gets the actual datatype configuration corresponding to the artifact configuration. + /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. /// - /// The datatype. - /// The artifact configuration. - object? FromArtifact(IDataType dataType, string? configuration); + /// The data type. + /// The dependencies. + /// The context cache. + /// + /// The artifact configuration value. + /// + string? ToArtifact(IDataType dataType, ICollection dependencies, IContextCache contextCache); + + /// + /// Gets the data type configuration corresponding to an artifact configuration value. + /// + /// The data type. + /// The artifact configuration value. + /// + /// The data type configuration. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + object? FromArtifact(IDataType dataType, string? configuration) + => FromArtifact(dataType, configuration, PassThroughCache.Instance); + + /// + /// Gets the data type configuration corresponding to an artifact configuration value. + /// + /// The data type. + /// The artifact configuration value. + /// The context cache. + /// + /// The data type configuration. + /// + object? FromArtifact(IDataType dataType, string? configuration, IContextCache contextCache); } diff --git a/src/Umbraco.Core/Deploy/IServiceConnector.cs b/src/Umbraco.Core/Deploy/IServiceConnector.cs index f6cd7c8002..b4f530fb35 100644 --- a/src/Umbraco.Core/Deploy/IServiceConnector.cs +++ b/src/Umbraco.Core/Deploy/IServiceConnector.cs @@ -3,34 +3,65 @@ using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Deploy; /// -/// Connects to an Umbraco service. +/// Connects to an Umbraco service. /// +/// public interface IServiceConnector : IDiscoverable { /// - /// Gets an artifact. + /// Gets an artifact. /// /// The entity identifier of the artifact. - /// The corresponding artifact, or null. - IArtifact? GetArtifact(Udi udi); + /// + /// The corresponding artifact, or null. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + IArtifact? GetArtifact(Udi udi) + => GetArtifact(udi, PassThroughCache.Instance); /// - /// Gets an artifact. + /// Gets an artifact. + /// + /// The entity identifier of the artifact. + /// The context cache. + /// + /// The corresponding artifact, or null. + /// + IArtifact? GetArtifact(Udi udi, IContextCache contextCache); + + /// + /// Gets an artifact. /// /// The entity. - /// The corresponding artifact. - IArtifact GetArtifact(object entity); + /// + /// The corresponding artifact. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + IArtifact GetArtifact(object entity) + => GetArtifact(entity, PassThroughCache.Instance); /// - /// Initializes processing for an artifact. + /// Gets an artifact. + /// + /// The entity. + /// The context cache. + /// + /// The corresponding artifact. + /// + IArtifact GetArtifact(object entity, IContextCache contextCache); + + /// + /// Initializes processing for an artifact. /// /// The artifact. /// The deploy context. - /// The mapped artifact. + /// + /// The mapped artifact. + /// ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context); /// - /// Processes an artifact. + /// Processes an artifact. /// /// The mapped artifact. /// The deploy context. @@ -38,46 +69,56 @@ public interface IServiceConnector : IDiscoverable void Process(ArtifactDeployState dart, IDeployContext context, int pass); /// - /// Explodes a range into udis. + /// Explodes a range into udis. /// /// The range. /// The list of udis where to add the new udis. - /// Also, it's cool to have a method named Explode. Kaboom! + /// + /// Also, it's cool to have a method named Explode. Kaboom! + /// void Explode(UdiRange range, List udis); /// - /// Gets a named range for a specified udi and selector. + /// Gets a named range for a specified udi and selector. /// /// The udi. /// The selector. - /// The named range for the specified udi and selector. + /// + /// The named range for the specified udi and selector. + /// NamedUdiRange GetRange(Udi udi, string selector); /// - /// Gets a named range for specified entity type, identifier and selector. + /// Gets a named range for specified entity type, identifier and selector. /// /// The entity type. /// The identifier. /// The selector. - /// The named range for the specified entity type, identifier and selector. + /// + /// The named range for the specified entity type, identifier and selector. + /// /// - /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now? - /// - /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do - /// not manage guids but only ints... so we have to provide a way to support it. The string id here - /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to - /// indicate the "root" i.e. an open udi. - /// + /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now? + /// + /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do + /// not manage guids but only ints... so we have to provide a way to support it. The string id here + /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to + /// indicate the "root" i.e. an open udi. + /// /// NamedUdiRange GetRange(string entityType, string sid, string selector); /// - /// Compares two artifacts. + /// Compares two artifacts. /// /// The first artifact. /// The second artifact. /// A collection of differences to append to, if not null. - /// A boolean value indicating whether the artifacts are identical. - /// ServiceConnectorBase{TArtifact} provides a very basic default implementation. + /// + /// A boolean value indicating whether the artifacts are identical. + /// + /// + /// ServiceConnectorBase{TArtifact} provides a very basic default implementation. + /// bool Compare(IArtifact? art1, IArtifact? art2, ICollection? differences = null); } diff --git a/src/Umbraco.Core/Deploy/IValueConnector.cs b/src/Umbraco.Core/Deploy/IValueConnector.cs index f2a776c7ca..9f3b17f71c 100644 --- a/src/Umbraco.Core/Deploy/IValueConnector.cs +++ b/src/Umbraco.Core/Deploy/IValueConnector.cs @@ -3,35 +3,70 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert a property value to / from an environment-agnostic string. +/// Defines methods that can convert a property value to and from an environment-agnostic string. /// /// -/// Property values may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. Connectors also deal -/// with serializing to / from string. +/// Property values may contain values such as content identifiers, that would be local +/// to one environment and need to be converted in order to be deployed. Connectors also deal +/// with serializing and deserializing the content value to an environment-agnostic string. /// public interface IValueConnector { /// - /// Gets the property editor aliases that the value converter supports by default. + /// Gets the property editor aliases that the value converter supports by default. /// + /// + /// The property editor aliases. + /// IEnumerable PropertyEditorAliases { get; } /// - /// Gets the deploy property value corresponding to a content property value, and gather dependencies. + /// Gets the deploy property value corresponding to a content property value, and gather dependencies. /// /// The content property value. /// The value property type /// The content dependencies. - /// The deploy property value. - string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies); + /// + /// The deploy property value. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies) + => ToArtifact(value, propertyType, dependencies, PassThroughCache.Instance); /// - /// Gets the content property value corresponding to a deploy property value. + /// Gets the deploy property value corresponding to a content property value, and gather dependencies. + /// + /// The content property value. + /// The value property type + /// The content dependencies. + /// The context cache. + /// + /// The deploy property value. + /// + string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies, IContextCache contextCache); + + /// + /// Gets the content property value corresponding to a deploy property value. /// /// The deploy property value. /// The value property type /// The current content property value. - /// The content property value. - object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue); + /// + /// The content property value. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue) + => FromArtifact(value, propertyType, currentValue, PassThroughCache.Instance); + + /// + /// Gets the content property value corresponding to a deploy property value. + /// + /// The deploy property value. + /// The value property type + /// The current content property value. + /// The context cache. + /// + /// The content property value. + /// + object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue, IContextCache contextCache); } diff --git a/src/Umbraco.Core/Deploy/PassThroughCache.cs b/src/Umbraco.Core/Deploy/PassThroughCache.cs new file mode 100644 index 0000000000..064fc10ad5 --- /dev/null +++ b/src/Umbraco.Core/Deploy/PassThroughCache.cs @@ -0,0 +1,34 @@ +namespace Umbraco.Cms.Core.Deploy; + +/// +/// A pass through context cache that always creates the items. +/// +/// +public sealed class PassThroughCache : IContextCache +{ + /// + /// Gets the instance. + /// + /// + /// The instance. + /// + public static PassThroughCache Instance { get; } = new PassThroughCache(); + + /// + /// Prevents a default instance of the class from being created. + /// + private PassThroughCache() + { } + + /// + public void Create(string key, T item) + { } + + /// + public T? GetOrCreate(string key, Func factory) + => factory(); + + /// + public void Clear() + { } +} diff --git a/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs b/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs index eb1bc518ac..a9ed5fd84d 100644 --- a/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs +++ b/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs @@ -1,44 +1,77 @@ using Umbraco.Cms.Core.Models; +using static Umbraco.Cms.Core.Models.GridValue; namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert a grid cell value to / from an environment-agnostic string. +/// Defines methods that can convert a grid cell value to / from an environment-agnostic string. /// /// -/// Grid cell values may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. +/// Grid cell values may contain values such as content identifiers, that would be local +/// to one environment, and need to be converted in order to be deployed. /// public interface IGridCellValueConnector { /// - /// Gets a value indicating whether the connector supports a specified grid editor view. + /// Gets a value indicating whether the connector supports a specified grid editor view. /// - /// - /// The grid editor view. It needs to be the view instead of the alias as the view is really what - /// identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases - /// using the same kind of view. - /// - /// A value indicating whether the connector supports the grid editor view. - /// Note that can be string.Empty to indicate the "default" connector. + /// The grid editor view. It needs to be the view instead of the alias as the view is really what + /// identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases + /// using the same kind of view. + /// + /// true if the specified view is connector; otherwise, false. + /// + /// + /// A value indicating whether the connector supports the grid editor view. + /// bool IsConnector(string view); /// - /// Gets the value to be deployed from the control value as a string. + /// Gets the value to be deployed from the control value as a string. /// /// The control containing the value. /// The dependencies of the property. - /// The grid cell value to be deployed. - /// Note that - string? GetValue(GridValue.GridControl gridControl, ICollection dependencies); + /// + /// The grid cell value to be deployed. + /// + /// + /// Note that + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + string? GetValue(GridValue.GridControl gridControl, ICollection dependencies) + => GetValue(gridControl, dependencies, PassThroughCache.Instance); /// - /// Allows you to modify the value of a control being deployed. + /// Gets the value to be deployed from the control value as a string. + /// + /// The control containing the value. + /// The dependencies of the property. + /// The context cache. + /// + /// The grid cell value to be deployed. + /// + string? GetValue(GridValue.GridControl gridControl, ICollection dependencies, IContextCache contextCache); + + /// + /// Allows you to modify the value of a control being deployed. /// /// The control being deployed. /// - /// Follows the pattern of the property value connectors (). The SetValue method is - /// used to modify the value of the . + /// Follows the pattern of the property value connectors (). + /// The SetValue method is used to modify the value of the . /// - void SetValue(GridValue.GridControl gridControl); + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + void SetValue(GridValue.GridControl gridControl) + => SetValue(gridControl, PassThroughCache.Instance); + + /// + /// Allows you to modify the value of a control being deployed. + /// + /// The control being deployed. + /// The context cache. + /// + /// Follows the pattern of the property value connectors (). + /// The SetValue method is used to modify the value of the . + /// + void SetValue(GridValue.GridControl gridControl, IContextCache contextCache); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs index 1596bf9888..3cc54f4933 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs @@ -307,9 +307,9 @@ public class UdiTests [UdiDefinition("foo", UdiType.GuidUdi)] public class FooConnector : IServiceConnector { - public IArtifact GetArtifact(Udi udi) => throw new NotImplementedException(); + public IArtifact GetArtifact(Udi udi, IContextCache contextCache) => throw new NotImplementedException(); - public IArtifact GetArtifact(object entity) => throw new NotImplementedException(); + public IArtifact GetArtifact(object entity, IContextCache contextCache) => throw new NotImplementedException(); public ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context) => throw new NotImplementedException(); From 10d03f0b585866394db18c31dddb50ff3aa6e4da Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Tue, 25 Oct 2022 18:40:11 +0200 Subject: [PATCH 03/39] Add IContextCache to deploy connectors (#13287) * Add IContextCache and implementations * Update connector interfaces to use IContextCache * Minor cleanup * Move DeployContextCache prefix to constant * Move default implementations to obsolete methods * Remove DeployContextCache and DictionaryCache Co-authored-by: Andy Butland --- src/Umbraco.Core/Deploy/ArtifactBase.cs | 89 +++++++++--------- .../Deploy/ArtifactDeployState.cs | 37 ++++++++ .../ArtifactDeployStateOfTArtifactTEntity.cs | 38 -------- src/Umbraco.Core/Deploy/IContextCache.cs | 31 +++++++ .../Deploy/IDataTypeConfigurationConnector.cs | 62 +++++++++---- src/Umbraco.Core/Deploy/IServiceConnector.cs | 93 +++++++++++++------ src/Umbraco.Core/Deploy/IValueConnector.cs | 57 +++++++++--- src/Umbraco.Core/Deploy/PassThroughCache.cs | 34 +++++++ .../Deploy/IGridCellValueConnector.cs | 71 ++++++++++---- .../Umbraco.Core/CoreThings/UdiTests.cs | 4 +- 10 files changed, 359 insertions(+), 157 deletions(-) delete mode 100644 src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs create mode 100644 src/Umbraco.Core/Deploy/IContextCache.cs create mode 100644 src/Umbraco.Core/Deploy/PassThroughCache.cs diff --git a/src/Umbraco.Core/Deploy/ArtifactBase.cs b/src/Umbraco.Core/Deploy/ArtifactBase.cs index cc2415f4cd..0d354b65de 100644 --- a/src/Umbraco.Core/Deploy/ArtifactBase.cs +++ b/src/Umbraco.Core/Deploy/ArtifactBase.cs @@ -1,50 +1,49 @@ -namespace Umbraco.Cms.Core.Deploy +namespace Umbraco.Cms.Core.Deploy; + +/// +/// Provides a base class to all artifacts. +/// +public abstract class ArtifactBase : IArtifact + where TUdi : Udi { - /// - /// Provides a base class to all artifacts. - /// - public abstract class ArtifactBase : IArtifact - where TUdi : Udi + protected ArtifactBase(TUdi udi, IEnumerable? dependencies = null) { - protected ArtifactBase(TUdi udi, IEnumerable? dependencies = null) - { - Udi = udi ?? throw new ArgumentNullException("udi"); - Name = Udi.ToString(); + Udi = udi ?? throw new ArgumentNullException("udi"); + Name = Udi.ToString(); - _dependencies = dependencies ?? Enumerable.Empty(); - _checksum = new Lazy(GetChecksum); - } - - private readonly Lazy _checksum; - - private IEnumerable _dependencies; - - protected abstract string GetChecksum(); - - Udi IArtifactSignature.Udi => Udi; - - public TUdi Udi { get; set; } - - public string Checksum => _checksum.Value; - - /// - /// Prevents the property from being serialized. - /// - /// - /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore] - /// as that would require an external dependency in Umbraco.Cms.Core. - /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm - /// - public bool ShouldSerializeChecksum() => false; - - public IEnumerable Dependencies - { - get => _dependencies; - set => _dependencies = value.OrderBy(x => x.Udi); - } - - public string Name { get; set; } - - public string Alias { get; set; } = string.Empty; + _dependencies = dependencies ?? Enumerable.Empty(); + _checksum = new Lazy(GetChecksum); } + + private readonly Lazy _checksum; + + private IEnumerable _dependencies; + + protected abstract string GetChecksum(); + + Udi IArtifactSignature.Udi => Udi; + + public TUdi Udi { get; set; } + + public string Checksum => _checksum.Value; + + /// + /// Prevents the property from being serialized. + /// + /// + /// Note that we can't use here as that works only on fields, not properties. And we want to avoid using [JsonIgnore] + /// as that would require an external dependency in Umbraco.Cms.Core. + /// So using this method of excluding properties from serialized data, documented here: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm + /// + public bool ShouldSerializeChecksum() => false; + + public IEnumerable Dependencies + { + get => _dependencies; + set => _dependencies = value.OrderBy(x => x.Udi); + } + + public string Name { get; set; } + + public string Alias { get; set; } = string.Empty; } diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployState.cs b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs index 1b75fe11c0..e2dd343af1 100644 --- a/src/Umbraco.Core/Deploy/ArtifactDeployState.cs +++ b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs @@ -44,3 +44,40 @@ public abstract class ArtifactDeployState /// protected abstract IArtifact GetArtifactAsIArtifact(); } + +/// +/// Represent the state of an artifact being deployed. +/// +/// The type of the artifact. +/// The type of the entity. +public class ArtifactDeployState : ArtifactDeployState + where TArtifact : IArtifact +{ + /// + /// Initializes a new instance of the class. + /// + /// The artifact. + /// The entity. + /// The service connector deploying the artifact. + /// The next pass number. + public ArtifactDeployState(TArtifact art, TEntity? entity, IServiceConnector connector, int nextPass) + { + Artifact = art; + Entity = entity; + Connector = connector; + NextPass = nextPass; + } + + /// + /// Gets or sets the artifact. + /// + public new TArtifact Artifact { get; set; } + + /// + /// Gets or sets the entity. + /// + public TEntity? Entity { get; set; } + + /// + protected sealed override IArtifact GetArtifactAsIArtifact() => Artifact; +} diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs b/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs deleted file mode 100644 index 0ff1e20e87..0000000000 --- a/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Umbraco.Cms.Core.Deploy; - -/// -/// Represent the state of an artifact being deployed. -/// -/// The type of the artifact. -/// The type of the entity. -public class ArtifactDeployState : ArtifactDeployState - where TArtifact : IArtifact -{ - /// - /// Initializes a new instance of the class. - /// - /// The artifact. - /// The entity. - /// The service connector deploying the artifact. - /// The next pass number. - public ArtifactDeployState(TArtifact art, TEntity? entity, IServiceConnector connector, int nextPass) - { - Artifact = art; - Entity = entity; - Connector = connector; - NextPass = nextPass; - } - - /// - /// Gets or sets the artifact. - /// - public new TArtifact Artifact { get; set; } - - /// - /// Gets or sets the entity. - /// - public TEntity? Entity { get; set; } - - /// - protected sealed override IArtifact GetArtifactAsIArtifact() => Artifact; -} diff --git a/src/Umbraco.Core/Deploy/IContextCache.cs b/src/Umbraco.Core/Deploy/IContextCache.cs new file mode 100644 index 0000000000..d175064905 --- /dev/null +++ b/src/Umbraco.Core/Deploy/IContextCache.cs @@ -0,0 +1,31 @@ +namespace Umbraco.Cms.Core.Deploy; + +/// +/// Represents a context cache used by Deploy operations. +/// +public interface IContextCache +{ + /// + /// Creates the item on the context cache using the specified . + /// + /// The type of the cached item. + /// The key of the cached item. + /// The item. + void Create(string key, T item); + + /// + /// Gets an item from the context cache or creates and stores it using the specified . + /// + /// The type of the cached item. + /// The key of the cached item. + /// The factory method to create the item (if it doesn't exist yet). + /// + /// The item. + /// + T? GetOrCreate(string key, Func factory); + + /// + /// Clears all cached items on this context. + /// + void Clear(); +} diff --git a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs index 6b91926b57..506d1f5745 100644 --- a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs +++ b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs @@ -1,37 +1,67 @@ -using System.Diagnostics.CodeAnalysis; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert data type configuration to / from an environment-agnostic string. +/// Defines methods that can convert data type configuration to / from an environment-agnostic string. /// /// -/// Configuration may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. +/// Configuration may contain values such as content identifiers, that would be local +/// to one environment, and need to be converted in order to be deployed. /// -[SuppressMessage( - "ReSharper", - "UnusedMember.Global", - Justification = "This is actual only used by Deploy, but we don't want third parties to have references on deploy, that's why this interface is part of core.")] public interface IDataTypeConfigurationConnector { /// - /// Gets the property editor aliases that the value converter supports by default. + /// Gets the property editor aliases that the value converter supports by default. /// + /// + /// The property editor aliases. + /// IEnumerable PropertyEditorAliases { get; } /// - /// Gets the artifact datatype configuration corresponding to the actual datatype configuration. + /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. /// - /// The datatype. + /// The data type. /// The dependencies. - string? ToArtifact(IDataType dataType, ICollection dependencies); + /// + /// The artifact configuration value. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + string? ToArtifact(IDataType dataType, ICollection dependencies) + => ToArtifact(dataType, dependencies, PassThroughCache.Instance); /// - /// Gets the actual datatype configuration corresponding to the artifact configuration. + /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. /// - /// The datatype. - /// The artifact configuration. - object? FromArtifact(IDataType dataType, string? configuration); + /// The data type. + /// The dependencies. + /// The context cache. + /// + /// The artifact configuration value. + /// + string? ToArtifact(IDataType dataType, ICollection dependencies, IContextCache contextCache); + + /// + /// Gets the data type configuration corresponding to an artifact configuration value. + /// + /// The data type. + /// The artifact configuration value. + /// + /// The data type configuration. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + object? FromArtifact(IDataType dataType, string? configuration) + => FromArtifact(dataType, configuration, PassThroughCache.Instance); + + /// + /// Gets the data type configuration corresponding to an artifact configuration value. + /// + /// The data type. + /// The artifact configuration value. + /// The context cache. + /// + /// The data type configuration. + /// + object? FromArtifact(IDataType dataType, string? configuration, IContextCache contextCache); } diff --git a/src/Umbraco.Core/Deploy/IServiceConnector.cs b/src/Umbraco.Core/Deploy/IServiceConnector.cs index f6cd7c8002..b4f530fb35 100644 --- a/src/Umbraco.Core/Deploy/IServiceConnector.cs +++ b/src/Umbraco.Core/Deploy/IServiceConnector.cs @@ -3,34 +3,65 @@ using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Deploy; /// -/// Connects to an Umbraco service. +/// Connects to an Umbraco service. /// +/// public interface IServiceConnector : IDiscoverable { /// - /// Gets an artifact. + /// Gets an artifact. /// /// The entity identifier of the artifact. - /// The corresponding artifact, or null. - IArtifact? GetArtifact(Udi udi); + /// + /// The corresponding artifact, or null. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + IArtifact? GetArtifact(Udi udi) + => GetArtifact(udi, PassThroughCache.Instance); /// - /// Gets an artifact. + /// Gets an artifact. + /// + /// The entity identifier of the artifact. + /// The context cache. + /// + /// The corresponding artifact, or null. + /// + IArtifact? GetArtifact(Udi udi, IContextCache contextCache); + + /// + /// Gets an artifact. /// /// The entity. - /// The corresponding artifact. - IArtifact GetArtifact(object entity); + /// + /// The corresponding artifact. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + IArtifact GetArtifact(object entity) + => GetArtifact(entity, PassThroughCache.Instance); /// - /// Initializes processing for an artifact. + /// Gets an artifact. + /// + /// The entity. + /// The context cache. + /// + /// The corresponding artifact. + /// + IArtifact GetArtifact(object entity, IContextCache contextCache); + + /// + /// Initializes processing for an artifact. /// /// The artifact. /// The deploy context. - /// The mapped artifact. + /// + /// The mapped artifact. + /// ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context); /// - /// Processes an artifact. + /// Processes an artifact. /// /// The mapped artifact. /// The deploy context. @@ -38,46 +69,56 @@ public interface IServiceConnector : IDiscoverable void Process(ArtifactDeployState dart, IDeployContext context, int pass); /// - /// Explodes a range into udis. + /// Explodes a range into udis. /// /// The range. /// The list of udis where to add the new udis. - /// Also, it's cool to have a method named Explode. Kaboom! + /// + /// Also, it's cool to have a method named Explode. Kaboom! + /// void Explode(UdiRange range, List udis); /// - /// Gets a named range for a specified udi and selector. + /// Gets a named range for a specified udi and selector. /// /// The udi. /// The selector. - /// The named range for the specified udi and selector. + /// + /// The named range for the specified udi and selector. + /// NamedUdiRange GetRange(Udi udi, string selector); /// - /// Gets a named range for specified entity type, identifier and selector. + /// Gets a named range for specified entity type, identifier and selector. /// /// The entity type. /// The identifier. /// The selector. - /// The named range for the specified entity type, identifier and selector. + /// + /// The named range for the specified entity type, identifier and selector. + /// /// - /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now? - /// - /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do - /// not manage guids but only ints... so we have to provide a way to support it. The string id here - /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to - /// indicate the "root" i.e. an open udi. - /// + /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now? + /// + /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do + /// not manage guids but only ints... so we have to provide a way to support it. The string id here + /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to + /// indicate the "root" i.e. an open udi. + /// /// NamedUdiRange GetRange(string entityType, string sid, string selector); /// - /// Compares two artifacts. + /// Compares two artifacts. /// /// The first artifact. /// The second artifact. /// A collection of differences to append to, if not null. - /// A boolean value indicating whether the artifacts are identical. - /// ServiceConnectorBase{TArtifact} provides a very basic default implementation. + /// + /// A boolean value indicating whether the artifacts are identical. + /// + /// + /// ServiceConnectorBase{TArtifact} provides a very basic default implementation. + /// bool Compare(IArtifact? art1, IArtifact? art2, ICollection? differences = null); } diff --git a/src/Umbraco.Core/Deploy/IValueConnector.cs b/src/Umbraco.Core/Deploy/IValueConnector.cs index f2a776c7ca..9f3b17f71c 100644 --- a/src/Umbraco.Core/Deploy/IValueConnector.cs +++ b/src/Umbraco.Core/Deploy/IValueConnector.cs @@ -3,35 +3,70 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert a property value to / from an environment-agnostic string. +/// Defines methods that can convert a property value to and from an environment-agnostic string. /// /// -/// Property values may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. Connectors also deal -/// with serializing to / from string. +/// Property values may contain values such as content identifiers, that would be local +/// to one environment and need to be converted in order to be deployed. Connectors also deal +/// with serializing and deserializing the content value to an environment-agnostic string. /// public interface IValueConnector { /// - /// Gets the property editor aliases that the value converter supports by default. + /// Gets the property editor aliases that the value converter supports by default. /// + /// + /// The property editor aliases. + /// IEnumerable PropertyEditorAliases { get; } /// - /// Gets the deploy property value corresponding to a content property value, and gather dependencies. + /// Gets the deploy property value corresponding to a content property value, and gather dependencies. /// /// The content property value. /// The value property type /// The content dependencies. - /// The deploy property value. - string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies); + /// + /// The deploy property value. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies) + => ToArtifact(value, propertyType, dependencies, PassThroughCache.Instance); /// - /// Gets the content property value corresponding to a deploy property value. + /// Gets the deploy property value corresponding to a content property value, and gather dependencies. + /// + /// The content property value. + /// The value property type + /// The content dependencies. + /// The context cache. + /// + /// The deploy property value. + /// + string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies, IContextCache contextCache); + + /// + /// Gets the content property value corresponding to a deploy property value. /// /// The deploy property value. /// The value property type /// The current content property value. - /// The content property value. - object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue); + /// + /// The content property value. + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue) + => FromArtifact(value, propertyType, currentValue, PassThroughCache.Instance); + + /// + /// Gets the content property value corresponding to a deploy property value. + /// + /// The deploy property value. + /// The value property type + /// The current content property value. + /// The context cache. + /// + /// The content property value. + /// + object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue, IContextCache contextCache); } diff --git a/src/Umbraco.Core/Deploy/PassThroughCache.cs b/src/Umbraco.Core/Deploy/PassThroughCache.cs new file mode 100644 index 0000000000..064fc10ad5 --- /dev/null +++ b/src/Umbraco.Core/Deploy/PassThroughCache.cs @@ -0,0 +1,34 @@ +namespace Umbraco.Cms.Core.Deploy; + +/// +/// A pass through context cache that always creates the items. +/// +/// +public sealed class PassThroughCache : IContextCache +{ + /// + /// Gets the instance. + /// + /// + /// The instance. + /// + public static PassThroughCache Instance { get; } = new PassThroughCache(); + + /// + /// Prevents a default instance of the class from being created. + /// + private PassThroughCache() + { } + + /// + public void Create(string key, T item) + { } + + /// + public T? GetOrCreate(string key, Func factory) + => factory(); + + /// + public void Clear() + { } +} diff --git a/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs b/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs index eb1bc518ac..a9ed5fd84d 100644 --- a/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs +++ b/src/Umbraco.Infrastructure/Deploy/IGridCellValueConnector.cs @@ -1,44 +1,77 @@ using Umbraco.Cms.Core.Models; +using static Umbraco.Cms.Core.Models.GridValue; namespace Umbraco.Cms.Core.Deploy; /// -/// Defines methods that can convert a grid cell value to / from an environment-agnostic string. +/// Defines methods that can convert a grid cell value to / from an environment-agnostic string. /// /// -/// Grid cell values may contain values such as content identifiers, that would be local -/// to one environment, and need to be converted in order to be deployed. +/// Grid cell values may contain values such as content identifiers, that would be local +/// to one environment, and need to be converted in order to be deployed. /// public interface IGridCellValueConnector { /// - /// Gets a value indicating whether the connector supports a specified grid editor view. + /// Gets a value indicating whether the connector supports a specified grid editor view. /// - /// - /// The grid editor view. It needs to be the view instead of the alias as the view is really what - /// identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases - /// using the same kind of view. - /// - /// A value indicating whether the connector supports the grid editor view. - /// Note that can be string.Empty to indicate the "default" connector. + /// The grid editor view. It needs to be the view instead of the alias as the view is really what + /// identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases + /// using the same kind of view. + /// + /// true if the specified view is connector; otherwise, false. + /// + /// + /// A value indicating whether the connector supports the grid editor view. + /// bool IsConnector(string view); /// - /// Gets the value to be deployed from the control value as a string. + /// Gets the value to be deployed from the control value as a string. /// /// The control containing the value. /// The dependencies of the property. - /// The grid cell value to be deployed. - /// Note that - string? GetValue(GridValue.GridControl gridControl, ICollection dependencies); + /// + /// The grid cell value to be deployed. + /// + /// + /// Note that + /// + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + string? GetValue(GridValue.GridControl gridControl, ICollection dependencies) + => GetValue(gridControl, dependencies, PassThroughCache.Instance); /// - /// Allows you to modify the value of a control being deployed. + /// Gets the value to be deployed from the control value as a string. + /// + /// The control containing the value. + /// The dependencies of the property. + /// The context cache. + /// + /// The grid cell value to be deployed. + /// + string? GetValue(GridValue.GridControl gridControl, ICollection dependencies, IContextCache contextCache); + + /// + /// Allows you to modify the value of a control being deployed. /// /// The control being deployed. /// - /// Follows the pattern of the property value connectors (). The SetValue method is - /// used to modify the value of the . + /// Follows the pattern of the property value connectors (). + /// The SetValue method is used to modify the value of the . /// - void SetValue(GridValue.GridControl gridControl); + [Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")] + void SetValue(GridValue.GridControl gridControl) + => SetValue(gridControl, PassThroughCache.Instance); + + /// + /// Allows you to modify the value of a control being deployed. + /// + /// The control being deployed. + /// The context cache. + /// + /// Follows the pattern of the property value connectors (). + /// The SetValue method is used to modify the value of the . + /// + void SetValue(GridValue.GridControl gridControl, IContextCache contextCache); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs index 1596bf9888..3cc54f4933 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs @@ -307,9 +307,9 @@ public class UdiTests [UdiDefinition("foo", UdiType.GuidUdi)] public class FooConnector : IServiceConnector { - public IArtifact GetArtifact(Udi udi) => throw new NotImplementedException(); + public IArtifact GetArtifact(Udi udi, IContextCache contextCache) => throw new NotImplementedException(); - public IArtifact GetArtifact(object entity) => throw new NotImplementedException(); + public IArtifact GetArtifact(object entity, IContextCache contextCache) => throw new NotImplementedException(); public ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context) => throw new NotImplementedException(); From 2cb926cada45a8a9ddd5eedddc46132cd431e5ae Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+zeegaan@users.noreply.github.com> Date: Tue, 25 Oct 2022 09:18:38 +0200 Subject: [PATCH 04/39] Parse lockId as invariant (#13284) Co-authored-by: Zeegaan --- .../Services/SqliteDistributedLockingMechanism.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs index 009f0cc122..b0d01331c4 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs +++ b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDistributedLockingMechanism.cs @@ -1,5 +1,6 @@ using System.Data; using System.Data.Common; +using System.Globalization; using Microsoft.Data.Sqlite; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -141,7 +142,7 @@ public class SqliteDistributedLockingMechanism : IDistributedLockingMechanism "SqliteDistributedLockingMechanism requires a transaction to function."); } - var query = @$"UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id = {LockId}"; + var query = @$"UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id = {LockId.ToString(CultureInfo.InvariantCulture)}"; DbCommand command = db.CreateCommand(db.Connection, CommandType.Text, query); From bacc81d01a8c365469a8ad64aeed6545c2fe163f Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Mon, 24 Oct 2022 13:45:11 +0200 Subject: [PATCH 05/39] Fix Sqlite database locking issue (#13246) * Add locking for creating scope * Lock the repository instead * Add scope in action instead of locking in service * Fix up post-merge Co-authored-by: Zeegaan --- .../Controllers/ContentTypeController.cs | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs index e14ac00fff..43374162e6 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs @@ -15,6 +15,7 @@ using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -46,6 +47,7 @@ public class ContentTypeController : ContentTypeControllerBase private readonly ILogger _logger; private readonly PackageDataInstallation _packageDataInstallation; private readonly BlockGridSampleHelper _blockGridSampleHelper; + private readonly ICoreScopeProvider _coreScopeProvider; private readonly PropertyEditorCollection _propertyEditors; // TODO: Split this controller apart so that authz is consistent, currently we need to authz each action individually. @@ -99,7 +101,7 @@ public class ContentTypeController : ContentTypeControllerBase { } - [ActivatorUtilitiesConstructor] + [Obsolete("Please use constructor that takes an ICoreScopeProvider")] public ContentTypeController( ICultureDictionary cultureDictionary, IContentTypeService contentTypeService, @@ -120,6 +122,52 @@ public class ContentTypeController : ContentTypeControllerBase EditorValidatorCollection editorValidatorCollection, PackageDataInstallation packageDataInstallation, BlockGridSampleHelper blockGridSampleHelper) + : this( + cultureDictionary, + contentTypeService, + mediaTypeService, + memberTypeService, + umbracoMapper, + localizedTextService, + serializer, + propertyEditors, + backofficeSecurityAccessor, + dataTypeService, + shortStringHelper, + fileService, + logger, + contentService, + contentTypeBaseServiceProvider, + hostingEnvironment, + editorValidatorCollection, + packageDataInstallation, + blockGridSampleHelper, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + [ActivatorUtilitiesConstructor] + public ContentTypeController( + ICultureDictionary cultureDictionary, + IContentTypeService contentTypeService, + IMediaTypeService mediaTypeService, + IMemberTypeService memberTypeService, + IUmbracoMapper umbracoMapper, + ILocalizedTextService localizedTextService, + IEntityXmlSerializer serializer, + PropertyEditorCollection propertyEditors, + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + IDataTypeService dataTypeService, + IShortStringHelper shortStringHelper, + IFileService fileService, + ILogger logger, + IContentService contentService, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + IHostingEnvironment hostingEnvironment, + EditorValidatorCollection editorValidatorCollection, + PackageDataInstallation packageDataInstallation, + BlockGridSampleHelper blockGridSampleHelper, + ICoreScopeProvider coreScopeProvider) : base( cultureDictionary, editorValidatorCollection, @@ -144,6 +192,7 @@ public class ContentTypeController : ContentTypeControllerBase _hostingEnvironment = hostingEnvironment; _packageDataInstallation = packageDataInstallation; _blockGridSampleHelper = blockGridSampleHelper; + _coreScopeProvider = coreScopeProvider; } [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] @@ -444,6 +493,7 @@ public class ContentTypeController : ContentTypeControllerBase private ITemplate? CreateTemplateForContentType(string contentTypeAlias, string? contentTypeName) { + using ICoreScope scope = _coreScopeProvider.CreateCoreScope(); ITemplate? template = _fileService.GetTemplate(contentTypeAlias); if (template == null) { @@ -460,6 +510,8 @@ public class ContentTypeController : ContentTypeControllerBase template = tryCreateTemplate.Result?.Entity; } + scope.Complete(); + return template; } From 054f5db6bb573af74313fc04bbb37191937a2361 Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Wed, 26 Oct 2022 10:17:40 +0200 Subject: [PATCH 06/39] Bump version to next minor --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index b36746fe7c..e8639110f4 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "11.0.0-rc2", + "version": "11.1.0-rc", "assemblyVersion": { "precision": "build" }, From 86d9e51039a2ccb4ab76311e151792853a984cfd Mon Sep 17 00:00:00 2001 From: Justin Neville <67802060+justin-nevitech@users.noreply.github.com> Date: Wed, 26 Oct 2022 12:56:09 +0100 Subject: [PATCH 07/39] Fix for UseExceptionHandler no longer working since v10.3 RC (#13218) * Fix for UseExceptionHandler no longer working since v10.3 RC * Update the management api path to match the new one Co-authored-by: Nikolaj --- .../ManagementApiComposer.cs | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs index b73bc40ed2..071b6de63a 100644 --- a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs +++ b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs @@ -84,24 +84,37 @@ public class ManagementApiComposer : IComposer "BackofficeSwagger", applicationBuilder => { - applicationBuilder.UseExceptionHandler(exceptionBuilder => exceptionBuilder.Run(async context => - { - Exception? exception = context.Features.Get()?.Error; - if (exception is null) + // Only use the API exception handler when we are requesting an API + applicationBuilder.UseWhen( + httpContext => { - return; - } + GlobalSettings? settings = httpContext.RequestServices.GetRequiredService>().Value; + IHostingEnvironment hostingEnvironment = httpContext.RequestServices.GetRequiredService(); + var officePath = settings.GetBackOfficePath(hostingEnvironment); - var response = new ProblemDetails + return httpContext.Request.Path.Value?.StartsWith($"{officePath}/management/api/") ?? false; + }, + innerBuilder => { - Title = exception.Message, - Detail = exception.StackTrace, - Status = StatusCodes.Status500InternalServerError, - Instance = exception.GetType().Name, - Type = "Error", - }; - await context.Response.WriteAsJsonAsync(response); - })); + innerBuilder.UseExceptionHandler(exceptionBuilder => exceptionBuilder.Run(async context => + { + Exception? exception = context.Features.Get()?.Error; + if (exception is null) + { + return; + } + + var response = new ProblemDetails + { + Title = exception.Message, + Detail = exception.StackTrace, + Status = StatusCodes.Status500InternalServerError, + Instance = exception.GetType().Name, + Type = "Error", + }; + await context.Response.WriteAsJsonAsync(response); + })); + }); }, applicationBuilder => { From 6ff4d27d4716f45f25b3b04f1fef0fd4023534dd Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Thu, 27 Oct 2022 08:46:21 +0200 Subject: [PATCH 08/39] New backoffice: Cleanup management API routes (#13296) * Rename ModelsBuilderDashboard folder to ModelsBuilder * Fix modelsbuilder paths and related naming * Rename analytics route to telemetry * Fix controller bases - routes and tags * Fix items route * Fix more controllerbase routes * Fix route * Fix OpenApi file * Merging DictionaryItem and Dictionary * Fix TrackedReferences naming * Update OpenApi file * Rename Analytics* related types to Telemetry* * New Backoffice: Return AnalyticsLevelViewModel from Telemetry/ (#13298) * Return TelemetryLevelViewModel instead of TelemetryLevel * Fix schema * Change telemetry/current to telemetry/level (cherry picked from commit f2b8494c669cbbf04b623753abbf1be211973aa9) * Add contants for tree and recycle-bin subpaths (cherry picked from commit 4449f56bc00832ea6d357a3854b454791c80e0e2) Co-authored-by: Mole --- .../Analytics/AllAnalyticsController.cs | 22 - .../Analytics/AnalyticsControllerBase.cs | 13 - .../Analytics/GetAnalyticsController.cs | 18 - .../Tree/DataTypeTreeControllerBase.cs | 4 +- .../Tree/ItemsDataTypeTreeController.cs | 2 +- .../Tree/ChildrenDictionaryTreeController.cs} | 6 +- .../Tree/DictionaryTreeControllerBase.cs} | 12 +- .../Tree/ItemsDictionaryTreeController.cs} | 8 +- .../Tree/RootDictionaryTreeController.cs} | 6 +- .../DocumentRecycleBinControllerBase.cs | 2 +- .../Tree/DocumentTreeControllerBase.cs | 2 +- .../Tree/ItemsDocumentTreeController.cs | 2 +- .../DocumentBlueprintTreeControllerBase.cs | 4 +- .../ItemsDocumentBlueprintTreeController.cs | 2 +- .../Tree/DocumentTypeTreeControllerBase.cs | 4 +- .../Tree/ItemsDocumentTypeTreeController.cs | 2 +- .../MediaRecycleBinControllerBase.cs | 2 +- .../Media/Tree/ItemsMediaTreeController.cs | 2 +- .../Media/Tree/MediaTreeControllerBase.cs | 2 +- .../Tree/ItemsMediaTypeTreeController.cs | 2 +- .../Tree/MediaTypeTreeControllerBase.cs | 4 +- .../Tree/ItemsMemberGroupTreeController.cs | 2 +- .../Tree/MemberGroupTreeControllerBase.cs | 4 +- .../Tree/ItemsMemberTypeTreeController.cs | 2 +- .../Tree/MemberTypeTreeControllerBase.cs | 4 +- .../BuildModelsBuilderController.cs | 2 +- .../GetModelsBuilderController.cs | 4 +- .../ModelsBuilderControllerBase.cs | 4 +- .../StatusModelsBuilderController.cs | 2 +- .../Tree/ItemsPartialViewTreeController.cs | 2 +- .../Tree/PartialViewTreeControllerBase.cs | 4 +- .../PublishedCacheControllerBase.cs | 2 +- .../Relation/ByChildRelationController.cs | 2 +- .../Tree/ItemsRelationTypeTreeController.cs | 2 +- .../Tree/RelationTypeTreeControllerBase.cs | 4 +- .../Script/Tree/ItemsScriptTreeController.cs | 2 +- .../Script/Tree/ScriptTreeControllerBase.cs | 2 +- .../Tree/ItemsStaticFileTreeController.cs | 2 +- .../Tree/StaticFileTreeControllerBase.cs | 4 +- .../Tree/ItemsStylesheetTreeController.cs | 2 +- .../Tree/StylesheetTreeControllerBase.cs | 2 +- .../Telemetry/AllTelemetryController.cs | 23 + .../Telemetry/GetTelemetryController.cs | 18 + .../SetTelemetryController.cs} | 21 +- .../Telemetry/TelemetryControllerBase.cs | 13 + .../Tree/ItemsTemplateTreeController.cs | 2 +- .../Tree/TemplateTreeControllerBase.cs | 2 +- .../ByIdTrackedReferenceController.cs} | 6 +- .../DescendantsTrackedReferenceController.cs} | 6 +- .../ItemsTrackedReferenceController.cs} | 8 +- .../TrackedReferencesControllerBase.cs | 8 +- src/Umbraco.Cms.ManagementApi/OpenApi.json | 611 +++++++++--------- .../Analytics/AnalyticsLevelViewModel.cs | 10 - .../Telemetry/TelemetryLevelViewModel.cs | 10 + src/Umbraco.Core/Constants-Web.cs | 6 + .../NewBackoffice/OpenAPIContractTest.cs | 4 - 56 files changed, 454 insertions(+), 469 deletions(-) delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs => Dictionary/Tree/ChildrenDictionaryTreeController.cs} (82%) rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs => Dictionary/Tree/DictionaryTreeControllerBase.cs} (78%) rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs => Dictionary/Tree/ItemsDictionaryTreeController.cs} (72%) rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/RootDictionaryItemTreeController.cs => Dictionary/Tree/RootDictionaryTreeController.cs} (82%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/BuildModelsBuilderController.cs (96%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/GetModelsBuilderController.cs (89%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/ModelsBuilderControllerBase.cs (72%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/StatusModelsBuilderController.cs (94%) create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs rename src/Umbraco.Cms.ManagementApi/Controllers/{Analytics/SetAnalyticsController.cs => Telemetry/SetTelemetryController.cs} (51%) create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences/ForItemTrackedReferencesController.cs => TrackedReference/ByIdTrackedReferenceController.cs} (89%) rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences/DescendantsTrackedReferencesController.cs => TrackedReference/DescendantsTrackedReferenceController.cs} (89%) rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences/MultipleTrackedReferencesController.cs => TrackedReference/ItemsTrackedReferenceController.cs} (87%) rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences => TrackedReference}/TrackedReferencesControllerBase.cs (54%) delete mode 100644 src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs create mode 100644 src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryLevelViewModel.cs diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs deleted file mode 100644 index 780d6f90d3..0000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.ManagementApi.ViewModels.Pagination; - -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; - -public class AllAnalyticsController : AnalyticsControllerBase -{ - [HttpGet("all")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task> GetAll(int skip, int take) - { - TelemetryLevel[] levels = Enum.GetValues(); - return await Task.FromResult(new PagedViewModel - { - Total = levels.Length, - Items = levels.Skip(skip).Take(take), - }); - } -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs deleted file mode 100644 index 1ce29ea5c6..0000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; -using Umbraco.New.Cms.Web.Common.Routing; - -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; - -[ApiController] -[VersionedApiBackOfficeRoute("analytics")] -[OpenApiTag("Analytics")] -[ApiVersion("1.0")] -public abstract class AnalyticsControllerBase : ManagementApiControllerBase -{ -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs deleted file mode 100644 index 1c45e41e71..0000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.ManagementApi.ViewModels.Analytics; - -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; - -public class GetAnalyticsController : AnalyticsControllerBase -{ - private readonly IMetricsConsentService _metricsConsentService; - - public GetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; - - [HttpGet] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(AnalyticsLevelViewModel), StatusCodes.Status200OK)] - public async Task Get() => await Task.FromResult(new AnalyticsLevelViewModel { AnalyticsLevel = _metricsConsentService.GetConsentLevel() }); -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs index 4bdae5a2fa..8c0c6598ae 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs @@ -12,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DataType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DataType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DataType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DataType}")] +[OpenApiTag("Data Type")] public class DataTypeTreeControllerBase : FolderTreeControllerBase { private readonly IDataTypeService _dataTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs index 82eabcb6f4..34779cca3d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs @@ -12,7 +12,7 @@ public class ItemsDataTypeTreeController : DataTypeTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs similarity index 82% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs index 0969b808c9..8b2e6b2328 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs @@ -6,11 +6,11 @@ using Umbraco.Cms.ManagementApi.Services.Paging; using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.Tree; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; -public class ChildrenDictionaryItemTreeController : DictionaryItemTreeControllerBase +public class ChildrenDictionaryTreeController : DictionaryTreeControllerBase { - public ChildrenDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService) + public ChildrenDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService) : base(entityService, localizationService) { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs similarity index 78% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs index 0d6a513e2d..c91a267c9f 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs @@ -7,17 +7,17 @@ using Umbraco.Cms.ManagementApi.Controllers.Tree; using Umbraco.Cms.ManagementApi.ViewModels.Tree; using Umbraco.New.Cms.Web.Common.Routing; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DictionaryItem}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DictionaryItem))] -// NOTE: at the moment dictionary items aren't supported by EntityService, so we have little use of the +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/dictionary")] +[OpenApiTag("Dictionary")] +// NOTE: at the moment dictionary items (renamed to dictionary tree) aren't supported by EntityService, so we have little use of the // tree controller base. We'll keep it though, in the hope that we can mend EntityService. -public class DictionaryItemTreeControllerBase : EntityTreeControllerBase +public class DictionaryTreeControllerBase : EntityTreeControllerBase { - public DictionaryItemTreeControllerBase(IEntityService entityService, ILocalizationService localizationService) + public DictionaryTreeControllerBase(IEntityService entityService, ILocalizationService localizationService) : base(entityService) => LocalizationService = localizationService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ItemsDictionaryTreeController.cs similarity index 72% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ItemsDictionaryTreeController.cs index 69d2fda33d..ea2de2058a 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ItemsDictionaryTreeController.cs @@ -4,16 +4,16 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.ManagementApi.ViewModels.Tree; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; -public class ItemsDictionaryItemTreeController : DictionaryItemTreeControllerBase +public class ItemsDictionaryTreeController : DictionaryTreeControllerBase { - public ItemsDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService) + public ItemsDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService) : base(entityService, localizationService) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/RootDictionaryItemTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs similarity index 82% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/RootDictionaryItemTreeController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs index 18abf7a728..65981cb518 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/RootDictionaryItemTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs @@ -6,11 +6,11 @@ using Umbraco.Cms.ManagementApi.Services.Paging; using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.Tree; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; -public class RootDictionaryItemTreeController : DictionaryItemTreeControllerBase +public class RootDictionaryTreeController : DictionaryTreeControllerBase { - public RootDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService) + public RootDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService) : base(entityService, localizationService) { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs index 747b6b3296..106c636fe1 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.RecycleBin; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/recycle-bin")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Document}")] [RequireDocumentTreeRootAccess] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [OpenApiTag(nameof(Constants.UdiEntityType.Document))] diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs index a20237537d..b69af50c31 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs @@ -16,7 +16,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/tree")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Document}")] [OpenApiTag(nameof(Constants.UdiEntityType.Document))] public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs index a18dfea069..a9dd8326d6 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs @@ -21,7 +21,7 @@ public class ItemsDocumentTreeController : DocumentTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null, string? culture = null) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs index c6247da3a9..1fe034b7b5 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs @@ -12,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentBlueprint.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentBlueprint}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DocumentBlueprint))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentBlueprint}")] +[OpenApiTag("Document Blueprint")] public class DocumentBlueprintTreeControllerBase : EntityTreeControllerBase { private readonly IContentTypeService _contentTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs index 6b7edb6fab..e077d7eb49 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs @@ -13,7 +13,7 @@ public class ItemsDocumentBlueprintTreeController : DocumentBlueprintTreeControl { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs index cc4c224ef5..a80d69e729 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs @@ -12,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DocumentType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentType}")] +[OpenApiTag("Document Type")] public class DocumentTypeTreeControllerBase : FolderTreeControllerBase { private readonly IContentTypeService _contentTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs index e19bf249c6..28da775a58 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs @@ -12,7 +12,7 @@ public class ItemsDocumentTypeTreeController : DocumentTypeTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs index 157e1099de..abd4c9c6bf 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.RecycleBin; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/recycle-bin")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Media}")] [RequireMediaTreeRootAccess] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [OpenApiTag(nameof(Constants.UdiEntityType.Media))] diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs index 2ebf1a559f..cc6074d49b 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs @@ -20,7 +20,7 @@ public class ItemsMediaTreeController : MediaTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs index c03f05c71d..44fb4464c7 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs @@ -15,7 +15,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/tree")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Media}")] [OpenApiTag(nameof(Constants.UdiEntityType.Media))] public class MediaTreeControllerBase : UserStartNodeTreeControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs index 363751fee7..309711cd7d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs @@ -12,7 +12,7 @@ public class ItemsMediaTypeTreeController : MediaTypeTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs index 5b06c46439..043434d0ab 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs @@ -12,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MediaType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MediaType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.MediaType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MediaType}")] +[OpenApiTag("Media Type")] public class MediaTypeTreeControllerBase : FolderTreeControllerBase { private readonly IMediaTypeService _mediaTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs index 94db46be4e..d3be627d30 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs @@ -12,7 +12,7 @@ public class ItemsMemberGroupTreeController : MemberGroupTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs index b3ee033a25..6c4e5eb25a 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs @@ -12,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberGroup.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberGroup}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.MemberGroup))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberGroup}")] +[OpenApiTag("Member Group")] public class MemberGroupTreeControllerBase : EntityTreeControllerBase { public MemberGroupTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs index 249cdd5d67..e70ca8f0ee 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs @@ -12,7 +12,7 @@ public class ItemsMemberTypeTreeController : MemberTypeTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs index 88183dfd58..f52980b4ff 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs @@ -12,8 +12,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.MemberType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberType}")] +[OpenApiTag("Member Type")] public class MemberTypeTreeControllerBase : EntityTreeControllerBase { public MemberTypeTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/BuildModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/BuildModelsBuilderController.cs similarity index 96% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/BuildModelsBuilderController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/BuildModelsBuilderController.cs index c219a5128e..7fcf5b5888 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/BuildModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/BuildModelsBuilderController.cs @@ -8,7 +8,7 @@ using Umbraco.Cms.Infrastructure.ModelsBuilder; using Umbraco.Cms.Infrastructure.ModelsBuilder.Building; using Umbraco.Extensions; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; public class BuildModelsBuilderController : ModelsBuilderControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/GetModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs similarity index 89% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/GetModelsBuilderController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs index aa993cd1f4..09e5fcc1ac 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/GetModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.ManagementApi.Factories; using Umbraco.Cms.ManagementApi.ViewModels.ModelsBuilderDashboard; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; public class GetModelsBuilderController : ModelsBuilderControllerBase { @@ -12,7 +12,7 @@ public class GetModelsBuilderController : ModelsBuilderControllerBase public GetModelsBuilderController(IModelsBuilderViewModelFactory modelsBuilderViewModelFactory) => _modelsBuilderViewModelFactory = modelsBuilderViewModelFactory; - [HttpGet] + [HttpGet("dashboard")] [ProducesResponseType(typeof(CreatedResult), StatusCodes.Status200OK)] [MapToApiVersion("1.0")] public async Task> GetDashboard() => await Task.FromResult(Ok(_modelsBuilderViewModelFactory.Create())); diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/ModelsBuilderControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs similarity index 72% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/ModelsBuilderControllerBase.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs index 4871d3b9d6..6a09074c94 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/ModelsBuilderControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs @@ -2,11 +2,11 @@ using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; [ApiController] [VersionedApiBackOfficeRoute("models-builder")] -[OpenApiTag("ModelsBuilder")] +[OpenApiTag("Models Builder")] [ApiVersion("1.0")] public class ModelsBuilderControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/StatusModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/StatusModelsBuilderController.cs similarity index 94% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/StatusModelsBuilderController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/StatusModelsBuilderController.cs index 9b2cb22e0a..1f130ffb5c 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/StatusModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/StatusModelsBuilderController.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Infrastructure.ModelsBuilder; using Umbraco.Cms.ManagementApi.ViewModels.ModelsBuilderDashboard; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; public class StatusModelsBuilderController : ModelsBuilderControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs index d6107a844a..5a1567028c 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs @@ -12,7 +12,7 @@ public class ItemsPartialViewTreeController : PartialViewTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs index 95ad0eb6cf..d74afbda9d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs @@ -9,8 +9,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.PartialView.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.PartialView}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.PartialView))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.PartialView}")] +[OpenApiTag("Partial View")] public class PartialViewTreeControllerBase : FileSystemTreeControllerBase { public PartialViewTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs index 78dcd539f4..3fe18847f6 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs @@ -7,7 +7,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute("published-cache")] -[OpenApiTag("PublishedCache")] +[OpenApiTag("Published Cache")] public class PublishedCacheControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs index 4639fa7bea..8d1e88c806 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs @@ -22,7 +22,7 @@ public class ByChildRelationController : RelationControllerBase _relationViewModelFactory = relationViewModelFactory; } - [HttpGet("child-relations/{childId:int}")] + [HttpGet("child-relation/{childId:int}")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] public async Task> ByChild(int childId, int skip, int take, string? relationTypeAlias = "") diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs index 7e2054a594..ca3f6ff7de 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs @@ -14,7 +14,7 @@ public class ItemsRelationTypeTreeController : RelationTypeTreeControllerBase : base(entityService) => _relationService = relationService; - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs index c90c124686..6f5f69e8bd 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs @@ -11,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.RelationType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.RelationType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.RelationType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.RelationType}")] +[OpenApiTag("Relation Type")] // NOTE: at the moment relation types aren't supported by EntityService, so we have little use of the // tree controller base. We'll keep it though, in the hope that we can mend EntityService. public class RelationTypeTreeControllerBase : EntityTreeControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs index 99cd6d990e..642fcb63be 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs @@ -12,7 +12,7 @@ public class ItemsScriptTreeController : ScriptTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs index 4e204da4ee..d8266f8226 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs @@ -9,7 +9,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Script.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Script}/tree")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Script}")] [OpenApiTag(nameof(Constants.UdiEntityType.Script))] public class ScriptTreeControllerBase : FileSystemTreeControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs index 205f92d94f..1b8bb4b8b8 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs @@ -12,7 +12,7 @@ public class ItemsStaticFileTreeController : StaticFileTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs index ec50a54495..410559af56 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs @@ -9,8 +9,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.StaticFile.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute("static-file/tree")] -[OpenApiTag("StaticFile")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/static-file")] +[OpenApiTag("Static File")] public class StaticFileTreeControllerBase : FileSystemTreeControllerBase { private static readonly string[] _allowedRootFolders = { "App_Plugins", "wwwroot" }; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs index de2e779ba1..12b04a9c08 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs @@ -12,7 +12,7 @@ public class ItemsStylesheetTreeController : StylesheetTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs index b529752293..6ffe4052de 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs @@ -9,7 +9,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Stylesheet.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Stylesheet}/tree")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Stylesheet}")] [OpenApiTag(nameof(Constants.UdiEntityType.Stylesheet))] public class StylesheetTreeControllerBase : FileSystemTreeControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs new file mode 100644 index 0000000000..5520d20414 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.ManagementApi.ViewModels.Pagination; +using Umbraco.Cms.ManagementApi.ViewModels.Telemetry; + +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; + +public class AllTelemetryController : TelemetryControllerBase +{ + [HttpGet] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task> GetAll(int skip, int take) + { + TelemetryLevel[] levels = Enum.GetValues(); + return await Task.FromResult(new PagedViewModel + { + Total = levels.Length, + Items = levels.Skip(skip).Take(take).Select(level => new TelemetryLevelViewModel { TelemetryLevel = level }), + }); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs new file mode 100644 index 0000000000..007554389e --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.ManagementApi.ViewModels.Telemetry; + +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; + +public class GetTelemetryController : TelemetryControllerBase +{ + private readonly IMetricsConsentService _metricsConsentService; + + public GetTelemetryController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; + + [HttpGet("level")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(TelemetryLevelViewModel), StatusCodes.Status200OK)] + public async Task Get() => await Task.FromResult(new TelemetryLevelViewModel { TelemetryLevel = _metricsConsentService.GetConsentLevel() }); +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs similarity index 51% rename from src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs index cfdd1e8b4f..5c9002335c 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs @@ -1,36 +1,35 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.ManagementApi.ViewModels.Analytics; -using Umbraco.Cms.ManagementApi.ViewModels.Server; +using Umbraco.Cms.ManagementApi.ViewModels.Telemetry; -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; -public class SetAnalyticsController : AnalyticsControllerBase +public class SetTelemetryController : TelemetryControllerBase { private readonly IMetricsConsentService _metricsConsentService; - public SetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; + public SetTelemetryController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; - [HttpPost] + [HttpPost("level")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task SetConsentLevel(AnalyticsLevelViewModel analyticsLevelViewModel) + public async Task SetConsentLevel(TelemetryLevelViewModel telemetryLevelViewModel) { - if (!Enum.IsDefined(analyticsLevelViewModel.AnalyticsLevel)) + if (!Enum.IsDefined(telemetryLevelViewModel.TelemetryLevel)) { var invalidModelProblem = new ProblemDetails { - Title = "Invalid AnalyticsLevel value", - Detail = "The provided value for AnalyticsLevel is not valid", + Title = "Invalid TelemetryLevel value", + Detail = "The provided value for TelemetryLevel is not valid", Status = StatusCodes.Status400BadRequest, Type = "Error", }; return BadRequest(invalidModelProblem); } - _metricsConsentService.SetConsentLevel(analyticsLevelViewModel.AnalyticsLevel); + _metricsConsentService.SetConsentLevel(telemetryLevelViewModel.TelemetryLevel); return await Task.FromResult(Ok()); } } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs new file mode 100644 index 0000000000..b95e35a7e8 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; +using NSwag.Annotations; +using Umbraco.New.Cms.Web.Common.Routing; + +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; + +[ApiController] +[VersionedApiBackOfficeRoute("telemetry")] +[OpenApiTag("Telemetry")] +[ApiVersion("1.0")] +public abstract class TelemetryControllerBase : ManagementApiControllerBase +{ +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs index fb4a29c621..877f740df7 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs @@ -12,7 +12,7 @@ public class ItemsTemplateTreeController : TemplateTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs index be885f26be..1ca105c6aa 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Template.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Template}/tree")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Template}")] [OpenApiTag(nameof(Constants.UdiEntityType.Template))] public class TemplateTreeControllerBase : EntityTreeControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/ForItemTrackedReferencesController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ByIdTrackedReferenceController.cs similarity index 89% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/ForItemTrackedReferencesController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ByIdTrackedReferenceController.cs index a415cf08bf..3c1998a7e4 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/ForItemTrackedReferencesController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ByIdTrackedReferenceController.cs @@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; -public class ForItemTrackedReferencesController : TrackedReferencesControllerBase +public class ByIdTrackedReferenceController : TrackedReferenceControllerBase { private readonly ITrackedReferencesService _trackedReferencesService; private readonly IUmbracoMapper _umbracoMapper; - public ForItemTrackedReferencesController(ITrackedReferencesService trackedReferencesService, IUmbracoMapper umbracoMapper) + public ByIdTrackedReferenceController(ITrackedReferencesService trackedReferencesService, IUmbracoMapper umbracoMapper) { _trackedReferencesService = trackedReferencesService; _umbracoMapper = umbracoMapper; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/DescendantsTrackedReferencesController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/DescendantsTrackedReferenceController.cs similarity index 89% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/DescendantsTrackedReferencesController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/DescendantsTrackedReferenceController.cs index ecac832a84..20f119d173 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/DescendantsTrackedReferencesController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/DescendantsTrackedReferenceController.cs @@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; -public class DescendantsTrackedReferencesController : TrackedReferencesControllerBase +public class DescendantsTrackedReferenceController : TrackedReferenceControllerBase { private readonly ITrackedReferencesService _trackedReferencesSkipTakeService; private readonly IUmbracoMapper _umbracoMapper; - public DescendantsTrackedReferencesController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) + public DescendantsTrackedReferenceController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) { _trackedReferencesSkipTakeService = trackedReferencesSkipTakeService; _umbracoMapper = umbracoMapper; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/MultipleTrackedReferencesController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ItemsTrackedReferenceController.cs similarity index 87% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/MultipleTrackedReferencesController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ItemsTrackedReferenceController.cs index 041d208915..39ad9c2560 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/MultipleTrackedReferencesController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ItemsTrackedReferenceController.cs @@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; -public class MultipleTrackedReferencesController : TrackedReferencesControllerBase +public class ItemsTrackedReferenceController : TrackedReferenceControllerBase { private readonly ITrackedReferencesService _trackedReferencesSkipTakeService; private readonly IUmbracoMapper _umbracoMapper; - public MultipleTrackedReferencesController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) + public ItemsTrackedReferenceController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) { _trackedReferencesSkipTakeService = trackedReferencesSkipTakeService; _umbracoMapper = umbracoMapper; @@ -27,7 +27,7 @@ public class MultipleTrackedReferencesController : TrackedReferencesControllerBa /// Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view). /// This is basically finding children of relations. /// - [HttpGet("multiple")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] public async Task>> GetPagedReferencedItems([FromQuery]int[] ids, long skip, long take, bool? filterMustBeIsDependency) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs similarity index 54% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs index be40c80bf1..f473976c80 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs @@ -2,12 +2,12 @@ using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; [ApiController] -[VersionedApiBackOfficeRoute("tracked-references")] -[OpenApiTag("TrackedReferences")] +[VersionedApiBackOfficeRoute("tracked-reference")] +[OpenApiTag("Tracked Reference")] [ApiVersion("1.0")] -public abstract class TrackedReferencesControllerBase : ManagementApiControllerBase +public abstract class TrackedReferenceControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi.json b/src/Umbraco.Cms.ManagementApi/OpenApi.json index b821f26e24..e352c4d7da 100644 --- a/src/Umbraco.Cms.ManagementApi/OpenApi.json +++ b/src/Umbraco.Cms.ManagementApi/OpenApi.json @@ -75,75 +75,14 @@ } } }, - "/umbraco/management/api/v1/tracked-references/descendants/{parentId}": { + "/umbraco/management/api/v1/tracked-reference/{id}": { "get": { "tags": [ - "TrackedReferences" - ], - "summary": "Gets a page list of the child nodes of the current item used in any kind of relation.", - "description": "Used when deleting and unpublishing a single item to check if this item has any descending items that are in any\nkind of relation.\nThis is basically finding the descending items which are children in relations.", - "operationId": "DescendantsTrackedReferences_Descendants", - "parameters": [ - { - "name": "parentId", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 3 - }, - { - "name": "filterMustBeIsDependency", - "in": "query", - "schema": { - "type": "boolean", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tracked-references/{id}": { - "get": { - "tags": [ - "TrackedReferences" + "Tracked Reference" ], "summary": "Gets a page list of tracked references for the current item, so you can see where an item is being used.", "description": "Used by info tabs on content, media etc. and for the delete and unpublish of single items.\nThis is basically finding parents of relations.", - "operationId": "ForItemTrackedReferences_Get", + "operationId": "ByIdTrackedReference_Get", "parameters": [ { "name": "id", @@ -197,14 +136,75 @@ } } }, - "/umbraco/management/api/v1/tracked-references/multiple": { + "/umbraco/management/api/v1/tracked-reference/descendants/{parentId}": { "get": { "tags": [ - "TrackedReferences" + "Tracked Reference" + ], + "summary": "Gets a page list of the child nodes of the current item used in any kind of relation.", + "description": "Used when deleting and unpublishing a single item to check if this item has any descending items that are in any\nkind of relation.\nThis is basically finding the descending items which are children in relations.", + "operationId": "DescendantsTrackedReference_Descendants", + "parameters": [ + { + "name": "parentId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + }, + "x-position": 1 + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + }, + "x-position": 2 + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + }, + "x-position": 3 + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean", + "nullable": true + }, + "x-position": 4 + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tracked-reference/item": { + "get": { + "tags": [ + "Tracked Reference" ], "summary": "Gets a page list of the items used in any kind of relation from selected integer ids.", "description": "Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view).\nThis is basically finding children of relations.", - "operationId": "MultipleTrackedReferences_GetPagedReferencedItems", + "operationId": "ItemsTrackedReference_GetPagedReferencedItems", "parameters": [ { "name": "ids", @@ -263,7 +263,7 @@ } } }, - "/umbraco/management/api/v1/template/tree/children": { + "/umbraco/management/api/v1/tree/template/children": { "get": { "tags": [ "Template" @@ -314,7 +314,7 @@ } } }, - "/umbraco/management/api/v1/template/tree/items": { + "/umbraco/management/api/v1/tree/template/item": { "get": { "tags": [ "Template" @@ -355,7 +355,7 @@ } } }, - "/umbraco/management/api/v1/template/tree/root": { + "/umbraco/management/api/v1/tree/template/root": { "get": { "tags": [ "Template" @@ -397,7 +397,100 @@ } } }, - "/umbraco/management/api/v1/stylesheet/tree/children": { + "/umbraco/management/api/v1/telemetry": { + "get": { + "tags": [ + "Telemetry" + ], + "operationId": "AllTelemetry_GetAll", + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + }, + "x-position": 1 + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + }, + "x-position": 2 + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedViewModelOfTelemetryLevelViewModel" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/telemetry/level": { + "get": { + "tags": [ + "Telemetry" + ], + "operationId": "GetTelemetry_Get", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TelemetryLevelViewModel" + } + } + } + } + } + }, + "post": { + "tags": [ + "Telemetry" + ], + "operationId": "SetTelemetry_SetConsentLevel", + "requestBody": { + "x-name": "telemetryLevelViewModel", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TelemetryLevelViewModel" + } + } + }, + "required": true, + "x-position": 1 + }, + "responses": { + "400": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "" + } + } + } + }, + "/umbraco/management/api/v1/tree/stylesheet/children": { "get": { "tags": [ "Stylesheet" @@ -448,7 +541,7 @@ } } }, - "/umbraco/management/api/v1/stylesheet/tree/items": { + "/umbraco/management/api/v1/tree/stylesheet/item": { "get": { "tags": [ "Stylesheet" @@ -488,7 +581,7 @@ } } }, - "/umbraco/management/api/v1/stylesheet/tree/root": { + "/umbraco/management/api/v1/tree/stylesheet/root": { "get": { "tags": [ "Stylesheet" @@ -530,10 +623,10 @@ } } }, - "/umbraco/management/api/v1/static-file/tree/children": { + "/umbraco/management/api/v1/tree/static-file/children": { "get": { "tags": [ - "StaticFile" + "Static File" ], "operationId": "ChildrenStaticFileTree_Children", "parameters": [ @@ -581,10 +674,10 @@ } } }, - "/umbraco/management/api/v1/static-file/tree/items": { + "/umbraco/management/api/v1/tree/static-file/item": { "get": { "tags": [ - "StaticFile" + "Static File" ], "operationId": "ItemsStaticFileTree_Items", "parameters": [ @@ -621,10 +714,10 @@ } } }, - "/umbraco/management/api/v1/static-file/tree/root": { + "/umbraco/management/api/v1/tree/static-file/root": { "get": { "tags": [ - "StaticFile" + "Static File" ], "operationId": "RootStaticFileTree_Root", "parameters": [ @@ -962,7 +1055,7 @@ } } }, - "/umbraco/management/api/v1/script/tree/children": { + "/umbraco/management/api/v1/tree/script/children": { "get": { "tags": [ "Script" @@ -1013,7 +1106,7 @@ } } }, - "/umbraco/management/api/v1/script/tree/items": { + "/umbraco/management/api/v1/tree/script/item": { "get": { "tags": [ "Script" @@ -1053,7 +1146,7 @@ } } }, - "/umbraco/management/api/v1/script/tree/root": { + "/umbraco/management/api/v1/tree/script/root": { "get": { "tags": [ "Script" @@ -1095,7 +1188,7 @@ } } }, - "/umbraco/management/api/v1/relation/child-relations/{childId}": { + "/umbraco/management/api/v1/relation/child-relation/{childId}": { "get": { "tags": [ "Relation" @@ -1198,10 +1291,10 @@ } } }, - "/umbraco/management/api/v1/relation-type/tree/items": { + "/umbraco/management/api/v1/tree/relation-type/item": { "get": { "tags": [ - "RelationType" + "Relation Type" ], "operationId": "ItemsRelationTypeTree_Items", "parameters": [ @@ -1239,10 +1332,10 @@ } } }, - "/umbraco/management/api/v1/relation-type/tree/root": { + "/umbraco/management/api/v1/tree/relation-type/root": { "get": { "tags": [ - "RelationType" + "Relation Type" ], "operationId": "RootRelationTypeTree_Root", "parameters": [ @@ -1284,7 +1377,7 @@ "/umbraco/management/api/v1/published-cache/collect": { "post": { "tags": [ - "PublishedCache" + "Published Cache" ], "operationId": "CollectPublishedCache_Collect", "responses": { @@ -1297,7 +1390,7 @@ "/umbraco/management/api/v1/published-cache/rebuild": { "post": { "tags": [ - "PublishedCache" + "Published Cache" ], "operationId": "RebuildPublishedCache_Collect", "responses": { @@ -1310,7 +1403,7 @@ "/umbraco/management/api/v1/published-cache/reload": { "post": { "tags": [ - "PublishedCache" + "Published Cache" ], "operationId": "ReloadPublishedCache_Reload", "responses": { @@ -1323,7 +1416,7 @@ "/umbraco/management/api/v1/published-cache/status": { "get": { "tags": [ - "PublishedCache" + "Published Cache" ], "operationId": "StatusPublishedCache_Status", "responses": { @@ -1360,10 +1453,10 @@ } } }, - "/umbraco/management/api/v1/partial-view/tree/children": { + "/umbraco/management/api/v1/tree/partial-view/children": { "get": { "tags": [ - "PartialView" + "Partial View" ], "operationId": "ChildrenPartialViewTree_Children", "parameters": [ @@ -1411,10 +1504,10 @@ } } }, - "/umbraco/management/api/v1/partial-view/tree/items": { + "/umbraco/management/api/v1/tree/partial-view/item": { "get": { "tags": [ - "PartialView" + "Partial View" ], "operationId": "ItemsPartialViewTree_Items", "parameters": [ @@ -1451,10 +1544,10 @@ } } }, - "/umbraco/management/api/v1/partial-view/tree/root": { + "/umbraco/management/api/v1/tree/partial-view/root": { "get": { "tags": [ - "PartialView" + "Partial View" ], "operationId": "RootPartialViewTree_Root", "parameters": [ @@ -1496,7 +1589,7 @@ "/umbraco/management/api/v1/models-builder/build": { "post": { "tags": [ - "ModelsBuilder" + "Models Builder" ], "operationId": "BuildModelsBuilder_BuildModels", "responses": { @@ -1524,10 +1617,10 @@ } } }, - "/umbraco/management/api/v1/models-builder": { + "/umbraco/management/api/v1/models-builder/dashboard": { "get": { "tags": [ - "ModelsBuilder" + "Models Builder" ], "operationId": "GetModelsBuilder_GetDashboard", "responses": { @@ -1548,7 +1641,7 @@ "/umbraco/management/api/v1/models-builder/status": { "get": { "tags": [ - "ModelsBuilder" + "Models Builder" ], "operationId": "StatusModelsBuilder_GetModelsOutOfDateStatus", "responses": { @@ -1565,10 +1658,10 @@ } } }, - "/umbraco/management/api/v1/member-type/tree/items": { + "/umbraco/management/api/v1/tree/member-type/item": { "get": { "tags": [ - "MemberType" + "Member Type" ], "operationId": "ItemsMemberTypeTree_Items", "parameters": [ @@ -1606,10 +1699,10 @@ } } }, - "/umbraco/management/api/v1/member-type/tree/root": { + "/umbraco/management/api/v1/tree/member-type/root": { "get": { "tags": [ - "MemberType" + "Member Type" ], "operationId": "RootMemberTypeTree_Root", "parameters": [ @@ -1648,10 +1741,10 @@ } } }, - "/umbraco/management/api/v1/member-group/tree/items": { + "/umbraco/management/api/v1/tree/member-group/item": { "get": { "tags": [ - "MemberGroup" + "Member Group" ], "operationId": "ItemsMemberGroupTree_Items", "parameters": [ @@ -1689,10 +1782,10 @@ } } }, - "/umbraco/management/api/v1/member-group/tree/root": { + "/umbraco/management/api/v1/tree/member-group/root": { "get": { "tags": [ - "MemberGroup" + "Member Group" ], "operationId": "RootMemberGroupTree_Root", "parameters": [ @@ -1731,7 +1824,7 @@ } } }, - "/umbraco/management/api/v1/media/tree/children": { + "/umbraco/management/api/v1/tree/media/children": { "get": { "tags": [ "Media" @@ -1792,7 +1885,7 @@ } } }, - "/umbraco/management/api/v1/media/tree/items": { + "/umbraco/management/api/v1/tree/media/item": { "get": { "tags": [ "Media" @@ -1843,7 +1936,7 @@ } } }, - "/umbraco/management/api/v1/media/tree/root": { + "/umbraco/management/api/v1/tree/media/root": { "get": { "tags": [ "Media" @@ -1895,7 +1988,7 @@ } } }, - "/umbraco/management/api/v1/media/recycle-bin/children": { + "/umbraco/management/api/v1/recycle-bin/media/children": { "get": { "tags": [ "Media" @@ -1956,7 +2049,7 @@ } } }, - "/umbraco/management/api/v1/media/recycle-bin/root": { + "/umbraco/management/api/v1/recycle-bin/media/root": { "get": { "tags": [ "Media" @@ -2008,10 +2101,10 @@ } } }, - "/umbraco/management/api/v1/media-type/tree/children": { + "/umbraco/management/api/v1/tree/media-type/children": { "get": { "tags": [ - "MediaType" + "Media Type" ], "operationId": "ChildrenMediaTypeTree_Children", "parameters": [ @@ -2068,10 +2161,10 @@ } } }, - "/umbraco/management/api/v1/media-type/tree/items": { + "/umbraco/management/api/v1/tree/media-type/item": { "get": { "tags": [ - "MediaType" + "Media Type" ], "operationId": "ItemsMediaTypeTree_Items", "parameters": [ @@ -2109,10 +2202,10 @@ } } }, - "/umbraco/management/api/v1/media-type/tree/root": { + "/umbraco/management/api/v1/tree/media-type/root": { "get": { "tags": [ - "MediaType" + "Media Type" ], "operationId": "RootMediaTypeTree_Root", "parameters": [ @@ -2569,7 +2662,7 @@ } } }, - "/umbraco/management/api/v1/document/tree/children": { + "/umbraco/management/api/v1/tree/document/children": { "get": { "tags": [ "Document" @@ -2639,7 +2732,7 @@ } } }, - "/umbraco/management/api/v1/document/tree/items": { + "/umbraco/management/api/v1/tree/document/item": { "get": { "tags": [ "Document" @@ -2699,7 +2792,7 @@ } } }, - "/umbraco/management/api/v1/document/tree/root": { + "/umbraco/management/api/v1/tree/document/root": { "get": { "tags": [ "Document" @@ -2760,7 +2853,7 @@ } } }, - "/umbraco/management/api/v1/document/recycle-bin/children": { + "/umbraco/management/api/v1/recycle-bin/document/children": { "get": { "tags": [ "Document" @@ -2821,7 +2914,7 @@ } } }, - "/umbraco/management/api/v1/document/recycle-bin/root": { + "/umbraco/management/api/v1/recycle-bin/document/root": { "get": { "tags": [ "Document" @@ -2873,10 +2966,10 @@ } } }, - "/umbraco/management/api/v1/document-type/tree/children": { + "/umbraco/management/api/v1/tree/document-type/children": { "get": { "tags": [ - "DocumentType" + "Document Type" ], "operationId": "ChildrenDocumentTypeTree_Children", "parameters": [ @@ -2933,10 +3026,10 @@ } } }, - "/umbraco/management/api/v1/document-type/tree/items": { + "/umbraco/management/api/v1/tree/document-type/item": { "get": { "tags": [ - "DocumentType" + "Document Type" ], "operationId": "ItemsDocumentTypeTree_Items", "parameters": [ @@ -2974,10 +3067,10 @@ } } }, - "/umbraco/management/api/v1/document-type/tree/root": { + "/umbraco/management/api/v1/tree/document-type/root": { "get": { "tags": [ - "DocumentType" + "Document Type" ], "operationId": "RootDocumentTypeTree_Root", "parameters": [ @@ -3025,10 +3118,10 @@ } } }, - "/umbraco/management/api/v1/document-blueprint/tree/items": { + "/umbraco/management/api/v1/tree/document-blueprint/item": { "get": { "tags": [ - "DocumentBlueprint" + "Document Blueprint" ], "operationId": "ItemsDocumentBlueprintTree_Items", "parameters": [ @@ -3066,10 +3159,10 @@ } } }, - "/umbraco/management/api/v1/document-blueprint/tree/root": { + "/umbraco/management/api/v1/tree/document-blueprint/root": { "get": { "tags": [ - "DocumentBlueprint" + "Document Blueprint" ], "operationId": "RootDocumentBlueprintTree_Root", "parameters": [ @@ -3486,12 +3579,12 @@ } } }, - "/umbraco/management/api/v1/dictionary-item/tree/children": { + "/umbraco/management/api/v1/tree/dictionary/children": { "get": { "tags": [ - "DictionaryItem" + "Dictionary" ], - "operationId": "ChildrenDictionaryItemTree_Children", + "operationId": "ChildrenDictionaryTree_Children", "parameters": [ { "name": "parentKey", @@ -3537,12 +3630,12 @@ } } }, - "/umbraco/management/api/v1/dictionary-item/tree/items": { + "/umbraco/management/api/v1/tree/dictionary/item": { "get": { "tags": [ - "DictionaryItem" + "Dictionary" ], - "operationId": "ItemsDictionaryItemTree_Items", + "operationId": "ItemsDictionaryTree_Items", "parameters": [ { "name": "key", @@ -3578,12 +3671,12 @@ } } }, - "/umbraco/management/api/v1/dictionary-item/tree/root": { + "/umbraco/management/api/v1/tree/dictionary/root": { "get": { "tags": [ - "DictionaryItem" + "Dictionary" ], - "operationId": "RootDictionaryItemTree_Root", + "operationId": "RootDictionaryTree_Root", "parameters": [ { "name": "skip", @@ -3620,10 +3713,10 @@ } } }, - "/umbraco/management/api/v1/data-type/tree/children": { + "/umbraco/management/api/v1/tree/data-type/children": { "get": { "tags": [ - "DataType" + "Data Type" ], "operationId": "ChildrenDataTypeTree_Children", "parameters": [ @@ -3680,10 +3773,10 @@ } } }, - "/umbraco/management/api/v1/data-type/tree/items": { + "/umbraco/management/api/v1/tree/data-type/item": { "get": { "tags": [ - "DataType" + "Data Type" ], "operationId": "ItemsDataTypeTree_Items", "parameters": [ @@ -3721,10 +3814,10 @@ } } }, - "/umbraco/management/api/v1/data-type/tree/root": { + "/umbraco/management/api/v1/tree/data-type/root": { "get": { "tags": [ - "DataType" + "Data Type" ], "operationId": "RootDataTypeTree_Root", "parameters": [ @@ -3812,99 +3905,6 @@ } } } - }, - "/umbraco/management/api/v1/analytics/all": { - "get": { - "tags": [ - "Analytics" - ], - "operationId": "AllAnalytics_GetAll", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfTelemetryLevel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/analytics": { - "get": { - "tags": [ - "Analytics" - ], - "operationId": "GetAnalytics_Get", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AnalyticsLevelViewModel" - } - } - } - } - } - }, - "post": { - "tags": [ - "Analytics" - ], - "operationId": "SetAnalytics_SetConsentLevel", - "requestBody": { - "x-name": "analyticsLevelViewModel", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AnalyticsLevelViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } } }, "components": { @@ -4074,6 +4074,45 @@ } } }, + "PagedViewModelOfTelemetryLevelViewModel": { + "type": "object", + "additionalProperties": false, + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TelemetryLevelViewModel" + } + } + } + }, + "TelemetryLevelViewModel": { + "type": "object", + "additionalProperties": false, + "properties": { + "telemetryLevel": { + "$ref": "#/components/schemas/TelemetryLevel" + } + } + }, + "TelemetryLevel": { + "type": "string", + "description": "", + "x-enumNames": [ + "Minimal", + "Basic", + "Detailed" + ], + "enum": [ + "Minimal", + "Basic", + "Detailed" + ] + }, "PagedViewModelOfFileSystemTreeItemViewModel": { "type": "object", "additionalProperties": false, @@ -4583,20 +4622,6 @@ } } }, - "TelemetryLevel": { - "type": "string", - "description": "", - "x-enumNames": [ - "Minimal", - "Basic", - "Detailed" - ], - "enum": [ - "Minimal", - "Basic", - "Detailed" - ] - }, "DatabaseSettingsViewModel": { "type": "object", "additionalProperties": false, @@ -5220,72 +5245,27 @@ "type": "string" } } - }, - "PagedViewModelOfTelemetryLevel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TelemetryLevel2" - } - } - } - }, - "TelemetryLevel2": { - "type": "integer", - "description": "", - "x-enumNames": [ - "Minimal", - "Basic", - "Detailed" - ], - "enum": [ - 0, - 1, - 2 - ] - }, - "AnalyticsLevelViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "analyticsLevel": { - "$ref": "#/components/schemas/TelemetryLevel" - } - } } } }, "tags": [ - { - "name": "Analytics" - }, { "name": "Culture" }, { - "name": "DataType" + "name": "Data Type" }, { "name": "Dictionary" }, - { - "name": "DictionaryItem" - }, { "name": "Document" }, { - "name": "DocumentBlueprint" + "name": "Document Blueprint" }, { - "name": "DocumentType" + "name": "Document Type" }, { "name": "Help" @@ -5300,31 +5280,31 @@ "name": "Media" }, { - "name": "MediaType" + "name": "Media Type" }, { - "name": "MemberGroup" + "name": "Member Group" }, { - "name": "MemberType" + "name": "Member Type" }, { - "name": "ModelsBuilder" + "name": "Models Builder" }, { - "name": "PartialView" + "name": "Partial View" }, { "name": "Profiling" }, { - "name": "PublishedCache" + "name": "Published Cache" }, { "name": "Relation" }, { - "name": "RelationType" + "name": "Relation Type" }, { "name": "Script" @@ -5336,16 +5316,19 @@ "name": "Server" }, { - "name": "StaticFile" + "name": "Static File" }, { "name": "Stylesheet" }, + { + "name": "Telemetry" + }, { "name": "Template" }, { - "name": "TrackedReferences" + "name": "Tracked Reference" }, { "name": "Upgrade" diff --git a/src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs b/src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs deleted file mode 100644 index 73d442992e..0000000000 --- a/src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.ManagementApi.ViewModels.Analytics; - -public class AnalyticsLevelViewModel -{ - [JsonConverter(typeof(JsonStringEnumConverter))] - public TelemetryLevel AnalyticsLevel { get; set; } -} diff --git a/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryLevelViewModel.cs b/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryLevelViewModel.cs new file mode 100644 index 0000000000..d387be92e9 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryLevelViewModel.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.ManagementApi.ViewModels.Telemetry; + +public class TelemetryLevelViewModel +{ + [JsonConverter(typeof(JsonStringEnumConverter))] + public TelemetryLevel TelemetryLevel { get; set; } +} diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index bbeae780d8..6eb184fb40 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -63,6 +63,12 @@ public static partial class Constants public const string AreaToken = "area"; } + public static class RoutePath + { + public const string Tree = "tree"; + public const string RecycleBin = "recycle-bin"; + } + public static class AttributeRouting { public const string BackOfficeToken = "umbracoBackOffice"; diff --git a/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs b/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs index 9178234e74..a869f63bd3 100644 --- a/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs +++ b/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs @@ -1,15 +1,11 @@ -using System; -using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Tests.Integration.TestServerTest; -using Umbraco.Extensions; namespace Umbraco.Cms.Tests.Integration.NewBackoffice; From 746ab4bb232c66fd36ec9143a54d2335f5b59a78 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 31 Oct 2022 08:25:06 +0100 Subject: [PATCH 09/39] Updated Smidge, Npoco and MailKit (#13310) * Updated Smidge, Npoco and MailKit * Added missing command (after breaking interface in npoco) --- .../Umbraco.Cms.Persistence.SqlServer.csproj | 2 +- src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj | 4 ++-- src/Umbraco.Web.Common/Umbraco.Web.Common.csproj | 4 ++-- tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj index 4431cf2558..7d477069ed 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj +++ b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 19ac4606d1..6b922545bb 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -14,7 +14,7 @@ - + @@ -25,7 +25,7 @@ - + diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 7d5bc5ca74..00feb6d8b6 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs b/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs index 26cc13949c..2d387dcdff 100644 --- a/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs +++ b/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs @@ -78,6 +78,7 @@ public class TestDatabase : IUmbracoDatabase public DbTransaction Transaction { get; } public IDictionary Data { get; } + public int CommandTimeout { get; set; } public ISqlContext SqlContext { get; } From 68676db0829f8e3a477e822cf2c82fd80816e744 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 31 Oct 2022 08:25:06 +0100 Subject: [PATCH 10/39] Updated Smidge, Npoco and MailKit (#13310) * Updated Smidge, Npoco and MailKit * Added missing command (after breaking interface in npoco) --- .../Umbraco.Cms.Persistence.SqlServer.csproj | 2 +- src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj | 4 ++-- src/Umbraco.Web.Common/Umbraco.Web.Common.csproj | 4 ++-- tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj index 4431cf2558..7d477069ed 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj +++ b/src/Umbraco.Cms.Persistence.SqlServer/Umbraco.Cms.Persistence.SqlServer.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 19ac4606d1..6b922545bb 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -14,7 +14,7 @@ - + @@ -25,7 +25,7 @@ - + diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 7d5bc5ca74..00feb6d8b6 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs b/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs index 26cc13949c..2d387dcdff 100644 --- a/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs +++ b/tests/Umbraco.Tests.Common/TestHelpers/TestDatabase.cs @@ -78,6 +78,7 @@ public class TestDatabase : IUmbracoDatabase public DbTransaction Transaction { get; } public IDictionary Data { get; } + public int CommandTimeout { get; set; } public ISqlContext SqlContext { get; } From 174874eb11ac10d7ef654544ca3d01a74948bdad Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 1 Nov 2022 08:41:50 +0100 Subject: [PATCH 11/39] Fix casing in fileSystemBasePath (#13306) --- src/Umbraco.Web.BackOffice/Controllers/BlockGridSampleHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/BlockGridSampleHelper.cs b/src/Umbraco.Web.BackOffice/Controllers/BlockGridSampleHelper.cs index c1e976204b..44030cdaf0 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BlockGridSampleHelper.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BlockGridSampleHelper.cs @@ -172,7 +172,7 @@ public sealed class BlockGridSampleHelper internal void CreateSamplePartialViews() { var embeddedBasePath = $"{_partialViewPopulator.CoreEmbeddedPath}.BlockGrid.Components"; - var fileSystemBasePath = "/Views/partials/blockgrid/Components"; + var fileSystemBasePath = "/Views/Partials/blockgrid/Components"; var filesToMove = new[] { "umbBlockGridDemoHeadlineBlock.cshtml", From dc9d4155a35459f9a33a9c9da852f194a23ea019 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 1 Nov 2022 11:15:31 +0100 Subject: [PATCH 12/39] OpenId Connect authentication for new management API (#13318) * First attempt at OpenIddict * Making headway and more TODOs * Redo current policies for multiple schemas + clean up auth controller * Fix bad merge * Clean up some more test code * Fix spacing * Include AddAuthentication() in OpenIddict addition * A little more clean-up * Move application creation to its own implementation + prepare for middleware to handle valid callback URL * Enable refresh token flow * Fix bad merge from v11/dev * Support auth for Swagger and Postman in non-production environments + use default login screen for back-office logins * Add workaround to client side login handling so the OAuth return URL is not corrupted before redirection * Add temporary configuration handling for new backoffice * Restructure the code somewhat, move singular responsibility from management API project * Add recurring task for cleaning up old tokens in the DB * Fix bad merge + make auth controller align with the new management API structure * Explicitly handle the new management API path as a backoffice path (NOTE: this is potentially behaviorally breaking!) * Redo handle the new management API requests as backoffice requests, this time in a non-breaking way * Add/update TODOs * Revert duplication of current auth policies for OpenIddict (as it breaks everything for V11 without the new management APIs) and introduce a dedicated PoC policy setup for OpenIddict. * Fix failing unit tests * Update src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Update src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Update src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Update src/Umbraco.Core/Routing/UmbracoRequestPaths.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> --- .../Security/BackOfficeController.cs | 77 +++++++++ .../Controllers/Security/Paths.cs | 12 ++ .../BackOfficeAuthBuilderExtensions.cs | 146 ++++++++++++++++++ .../ServicesBuilderExtensions.cs | 8 + .../ManagementApiComposer.cs | 37 ++++- ...ceAuthorizationInitializationMiddleware.cs | 62 ++++++++ src/Umbraco.Cms.ManagementApi/OpenApi.json | 61 ++++++++ .../Security/BackOfficeApplicationManager.cs | 138 +++++++++++++++++ .../Security/ClientSecretManager.cs | 16 ++ .../Security/IClientSecretManager.cs | 6 + .../Umbraco.Cms.ManagementApi.csproj | 3 + .../UmbracoBuilder.Configuration.cs | 2 +- .../Routing/UmbracoRequestPaths.cs | 17 +- .../Routing/UmbracoRequestPathsOptions.cs | 10 ++ src/Umbraco.Core/Umbraco.Core.csproj | 4 + .../Constants-OauthClientIds.cs | 23 +++ .../Configuration/NewBackOfficeSettings.cs | 10 ++ .../NewBackOfficeSettingsValidator.cs | 25 +++ .../HostedServices/OpenIddictCleanup.cs | 56 +++++++ .../Security/IBackOfficeApplicationManager.cs | 6 + .../Umbraco.New.Cms.Infrastructure.csproj | 4 + .../src/views/common/login.controller.js | 6 +- .../Objects/TestUmbracoContextFactory.cs | 10 +- .../Routing/UmbracoRequestPathsTests.cs | 26 +++- .../Security/BackOfficeCookieManagerTests.cs | 15 +- 25 files changed, 763 insertions(+), 17 deletions(-) create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs create mode 100644 src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Security/ClientSecretManager.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Security/IClientSecretManager.cs create mode 100644 src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs create mode 100644 src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs create mode 100644 src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs create mode 100644 src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs create mode 100644 src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs create mode 100644 src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs new file mode 100644 index 0000000000..efdeede8f5 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs @@ -0,0 +1,77 @@ +using System.Security.Claims; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using NSwag.Annotations; +using OpenIddict.Abstractions; +using OpenIddict.Server.AspNetCore; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Web.BackOffice.Security; +using Umbraco.Extensions; +using Umbraco.New.Cms.Web.Common.Routing; + +namespace Umbraco.Cms.ManagementApi.Controllers.Security; + +[ApiController] +[VersionedApiBackOfficeRoute(Paths.BackOfficeApiEndpointTemplate)] +[OpenApiTag("Security")] +public class BackOfficeController : ManagementApiControllerBase +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IBackOfficeSignInManager _backOfficeSignInManager; + private readonly IBackOfficeUserManager _backOfficeUserManager; + + public BackOfficeController(IHttpContextAccessor httpContextAccessor, IBackOfficeSignInManager backOfficeSignInManager, IBackOfficeUserManager backOfficeUserManager) + { + _httpContextAccessor = httpContextAccessor; + _backOfficeSignInManager = backOfficeSignInManager; + _backOfficeUserManager = backOfficeUserManager; + } + + [HttpGet("authorize")] + [HttpPost("authorize")] + [MapToApiVersion("1.0")] + public async Task Authorize() + { + HttpContext context = _httpContextAccessor.GetRequiredHttpContext(); + OpenIddictRequest? request = context.GetOpenIddictServerRequest(); + if (request == null) + { + return BadRequest("Unable to obtain OpenID data from the current request"); + } + + // retrieve the user principal stored in the authentication cookie. + AuthenticateResult cookieAuthResult = await HttpContext.AuthenticateAsync(Constants.Security.BackOfficeAuthenticationType); + if (cookieAuthResult.Succeeded && cookieAuthResult.Principal?.Identity?.Name != null) + { + BackOfficeIdentityUser? backOfficeUser = await _backOfficeUserManager.FindByNameAsync(cookieAuthResult.Principal.Identity.Name); + if (backOfficeUser != null) + { + ClaimsPrincipal backOfficePrincipal = await _backOfficeSignInManager.CreateUserPrincipalAsync(backOfficeUser); + backOfficePrincipal.SetClaim(OpenIddictConstants.Claims.Subject, backOfficeUser.Key.ToString()); + + // TODO: it is not optimal to append all claims to the token. + // the token size grows with each claim, although it is still smaller than the old cookie. + // see if we can find a better way so we do not risk leaking sensitive data in bearer tokens. + // maybe work with scopes instead? + Claim[] backOfficeClaims = backOfficePrincipal.Claims.ToArray(); + foreach (Claim backOfficeClaim in backOfficeClaims) + { + backOfficeClaim.SetDestinations(OpenIddictConstants.Destinations.AccessToken); + } + + if (request.GetScopes().Contains(OpenIddictConstants.Scopes.OfflineAccess)) + { + // "offline_access" scope is required to use refresh tokens + backOfficePrincipal.SetScopes(OpenIddictConstants.Scopes.OfflineAccess); + } + + return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, backOfficePrincipal); + } + } + + return new ChallengeResult(new[] { Constants.Security.BackOfficeAuthenticationType }); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs new file mode 100644 index 0000000000..3ce1b7c3c6 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Cms.ManagementApi.Controllers.Security; + +public static class Paths +{ + public const string BackOfficeApiEndpointTemplate = "security/back-office"; + + public static string BackOfficeApiAuthorizationEndpoint = BackOfficeApiEndpointPath($"{BackOfficeApiEndpointTemplate}/authorize"); + + public static string BackOfficeApiTokenEndpoint = BackOfficeApiEndpointPath($"{BackOfficeApiEndpointTemplate}/token"); + + private static string BackOfficeApiEndpointPath(string relativePath) => $"/umbraco/management/api/v1.0/{relativePath}"; +} diff --git a/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs b/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs new file mode 100644 index 0000000000..ab132cbef2 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs @@ -0,0 +1,146 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using OpenIddict.Validation.AspNetCore; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.ManagementApi.Middleware; +using Umbraco.Cms.ManagementApi.Security; +using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.New.Cms.Infrastructure.HostedServices; +using Umbraco.New.Cms.Infrastructure.Security; + +namespace Umbraco.Cms.ManagementApi.DependencyInjection; + +public static class BackOfficeAuthBuilderExtensions +{ + public static IUmbracoBuilder AddBackOfficeAuthentication(this IUmbracoBuilder builder) + { + builder + .AddDbContext() + .AddOpenIddict(); + + return builder; + } + + private static IUmbracoBuilder AddDbContext(this IUmbracoBuilder builder) + { + builder.Services.AddDbContext(options => + { + // Configure the DB context + // TODO: use actual Umbraco DbContext once EF is implemented - and remove dependency on Microsoft.EntityFrameworkCore.InMemory + options.UseInMemoryDatabase(nameof(DbContext)); + + // Register the entity sets needed by OpenIddict. + options.UseOpenIddict(); + }); + + return builder; + } + + private static IUmbracoBuilder AddOpenIddict(this IUmbracoBuilder builder) + { + builder.Services.AddAuthentication(); + builder.Services.AddAuthorization(CreatePolicies); + + builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + options + .UseEntityFrameworkCore() + .UseDbContext(); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the authorization and token endpoints. + options + .SetAuthorizationEndpointUris(Controllers.Security.Paths.BackOfficeApiAuthorizationEndpoint) + .SetTokenEndpointUris(Controllers.Security.Paths.BackOfficeApiTokenEndpoint); + + // Enable authorization code flow with PKCE + options + .AllowAuthorizationCodeFlow() + .RequireProofKeyForCodeExchange() + .AllowRefreshTokenFlow(); + + // Register the encryption and signing credentials. + // - see https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html + options + // TODO: use actual certificates here, see docs above + .AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate() + .DisableAccessTokenEncryption(); + + // Register the ASP.NET Core host and configure for custom authentication endpoint. + options + .UseAspNetCore() + .EnableAuthorizationEndpointPassthrough(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + + builder.Services.AddHostedService(); + builder.Services.AddHostedService(); + + return builder; + } + + // TODO: remove this once EF is implemented + public class DatabaseManager : IHostedService + { + private readonly IServiceProvider _serviceProvider; + + public DatabaseManager(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; + + public async Task StartAsync(CancellationToken cancellationToken) + { + using IServiceScope scope = _serviceProvider.CreateScope(); + + DbContext context = scope.ServiceProvider.GetRequiredService(); + await context.Database.EnsureCreatedAsync(cancellationToken); + + // TODO: add BackOfficeAuthorizationInitializationMiddleware before UseAuthorization (to make it run for unauthorized API requests) and remove this + IBackOfficeApplicationManager backOfficeApplicationManager = scope.ServiceProvider.GetRequiredService(); + await backOfficeApplicationManager.EnsureBackOfficeApplicationAsync(new Uri("https://localhost:44331/"), cancellationToken); + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } + + // TODO: move this to an appropriate location and implement the policy scheme that should be used for the new management APIs + private static void CreatePolicies(AuthorizationOptions options) + { + void AddPolicy(string policyName, string claimType, params string[] allowedClaimValues) + { + options.AddPolicy($"New{policyName}", policy => + { + policy.AuthenticationSchemes.Add(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + policy.RequireClaim(claimType, allowedClaimValues); + }); + } + + // NOTE: these are ONLY sample policies that allow us to test the new management APIs + AddPolicy(AuthorizationPolicies.SectionAccessContent, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content); + AddPolicy(AuthorizationPolicies.SectionAccessForContentTree, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content); + AddPolicy(AuthorizationPolicies.SectionAccessForMediaTree, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Media); + AddPolicy(AuthorizationPolicies.SectionAccessMedia, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Media); + AddPolicy(AuthorizationPolicies.SectionAccessContentOrMedia, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content, Constants.Applications.Media); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs b/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs index cb739478c5..9a05c17bfe 100644 --- a/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs +++ b/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs @@ -1,7 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.ManagementApi.Serialization; using Umbraco.Cms.ManagementApi.Services; +using Umbraco.Extensions; using Umbraco.New.Cms.Core.Services.Installer; using Umbraco.New.Cms.Core.Services.Languages; @@ -17,6 +19,12 @@ public static class ServicesBuilderExtensions builder.Services.AddTransient(); builder.Services.AddTransient(); + // TODO: handle new management API path in core UmbracoRequestPaths (it's a behavioural breaking change so it goes here for now) + builder.Services.Configure(options => + { + options.IsBackOfficeRequest = urlPath => urlPath.InvariantStartsWith($"/umbraco/management/api/"); + }); + return builder; } } diff --git a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs index 071b6de63a..8140a7906a 100644 --- a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs +++ b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs @@ -8,17 +8,18 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; +using NSwag; using NSwag.AspNetCore; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.ManagementApi.Configuration; using Umbraco.Cms.ManagementApi.DependencyInjection; +using Umbraco.Cms.ManagementApi.Security; using Umbraco.Cms.Web.Common.ApplicationBuilder; using Umbraco.Extensions; +using Umbraco.New.Cms.Core; +using Umbraco.New.Cms.Core.Models.Configuration; using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.ManagementApi; @@ -44,7 +45,8 @@ public class ManagementApiComposer : IComposer .AddTrees() .AddFactories() .AddServices() - .AddMappers(); + .AddMappers() + .AddBackOfficeAuthentication(); services.AddApiVersioning(options => { @@ -65,6 +67,20 @@ public class ManagementApiComposer : IComposer { document.Tags = document.Tags.OrderBy(tag => tag.Name).ToList(); }; + + options.AddSecurity("Bearer", Enumerable.Empty(), new OpenApiSecurityScheme + { + Name = "Umbraco", + Type = OpenApiSecuritySchemeType.OAuth2, + Description = "Umbraco Authentication", + Flow = OpenApiOAuth2Flow.AccessCode, + AuthorizationUrl = Controllers.Security.Paths.BackOfficeApiAuthorizationEndpoint, + TokenUrl = Controllers.Security.Paths.BackOfficeApiTokenEndpoint, + Scopes = new Dictionary(), + }); + // this is documented in OAuth2 setup for swagger, but does not seem to be necessary at the moment. + // it is worth try it if operation authentication starts failing. + // options.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("Bearer")); }); services.AddVersionedApiExplorer(options => @@ -78,6 +94,10 @@ public class ManagementApiComposer : IComposer services.AddControllers(); builder.Services.ConfigureOptions(); + // TODO: when this is moved to core, make the AddUmbracoOptions extension private again and remove core InternalsVisibleTo for Umbraco.Cms.ManagementApi + builder.AddUmbracoOptions(); + builder.Services.AddSingleton, NewBackOfficeSettingsValidator>(); + builder.Services.Configure(options => { options.AddFilter(new UmbracoPipelineFilter( @@ -125,6 +145,7 @@ public class ManagementApiComposer : IComposer { GlobalSettings? settings = provider.GetRequiredService>().Value; IHostingEnvironment hostingEnvironment = provider.GetRequiredService(); + IClientSecretManager clientSecretManager = provider.GetRequiredService(); var officePath = settings.GetBackOfficePath(hostingEnvironment); // serve documents (same as app.UseSwagger()) applicationBuilder.UseOpenApi(config => @@ -141,6 +162,14 @@ public class ManagementApiComposer : IComposer config.SwaggerRoutes.Add(new SwaggerUi3Route(ApiAllName, swaggerPath)); config.OperationsSorter = "alpha"; config.TagsSorter = "alpha"; + + config.OAuth2Client = new OAuth2ClientSettings + { + AppName = "Umbraco", + UsePkceWithAuthorizationCodeGrant = true, + ClientId = Constants.OauthClientIds.Swagger, + ClientSecret = clientSecretManager.Get(Constants.OauthClientIds.Swagger) + }; }); } }, diff --git a/src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs b/src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs new file mode 100644 index 0000000000..6ecebb3362 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.Routing; +using Umbraco.New.Cms.Infrastructure.Security; + +namespace Umbraco.Cms.ManagementApi.Middleware; + +public class BackOfficeAuthorizationInitializationMiddleware : IMiddleware +{ + private static bool _firstBackOfficeRequest; + private static SemaphoreSlim _firstBackOfficeRequestLocker = new(1); + + private readonly UmbracoRequestPaths _umbracoRequestPaths; + private readonly IServiceProvider _serviceProvider; + + public BackOfficeAuthorizationInitializationMiddleware(UmbracoRequestPaths umbracoRequestPaths, IServiceProvider serviceProvider) + { + _umbracoRequestPaths = umbracoRequestPaths; + _serviceProvider = serviceProvider; + } + + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + await InitializeBackOfficeAuthorizationOnceAsync(context); + await next(context); + } + + private async Task InitializeBackOfficeAuthorizationOnceAsync(HttpContext context) + { + if (_firstBackOfficeRequest) + { + return; + } + + if (_umbracoRequestPaths.IsBackOfficeRequest(context.Request.Path) == false) + { + return; + } + + await _firstBackOfficeRequestLocker.WaitAsync(); + if (_firstBackOfficeRequest == false) + { + using IServiceScope scope = _serviceProvider.CreateScope(); + IBackOfficeApplicationManager backOfficeApplicationManager = scope.ServiceProvider.GetRequiredService(); + await backOfficeApplicationManager.EnsureBackOfficeApplicationAsync(new Uri(context.Request.GetDisplayUrl())); + _firstBackOfficeRequest = true; + } + + _firstBackOfficeRequestLocker.Release(); + } +} + +// TODO: remove this (used for testing BackOfficeAuthorizationInitializationMiddleware until it can be added to the existing UseBackOffice extension) +// public static class UmbracoApplicationBuilderExtensions +// { +// public static IUmbracoApplicationBuilderContext UseNewBackOffice(this IUmbracoApplicationBuilderContext builder) +// { +// builder.AppBuilder.UseMiddleware(); +// return builder; +// } +// } diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi.json b/src/Umbraco.Cms.ManagementApi/OpenApi.json index e352c4d7da..d71ee73460 100644 --- a/src/Umbraco.Cms.ManagementApi/OpenApi.json +++ b/src/Umbraco.Cms.ManagementApi/OpenApi.json @@ -816,6 +816,46 @@ } } }, + "/umbraco/management/api/v1/security/back-office/authorize": { + "get": { + "tags": [ + "Security" + ], + "operationId": "BackOffice_AuthorizeGET", + "responses": { + "200": { + "description": "", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + }, + "post": { + "tags": [ + "Security" + ], + "operationId": "BackOffice_AuthorizePOST", + "responses": { + "200": { + "description": "", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, "/umbraco/management/api/v1/search/index/{indexName}": { "get": { "tags": [ @@ -5246,8 +5286,26 @@ } } } + }, + "securitySchemes": { + "Bearer": { + "type": "oauth2", + "description": "Umbraco Authentication", + "name": "Umbraco", + "flows": { + "authorizationCode": { + "authorizationUrl": "/umbraco/management/api/v1.0/security/back-office/authorize", + "tokenUrl": "/umbraco/management/api/v1.0/security/back-office/token" + } + } + } } }, + "security": [ + { + "Bearer": [] + } + ], "tags": [ { "name": "Culture" @@ -5312,6 +5370,9 @@ { "name": "Search" }, + { + "name": "Security" + }, { "name": "Server" }, diff --git a/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs b/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs new file mode 100644 index 0000000000..1c0ea342e6 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs @@ -0,0 +1,138 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using OpenIddict.Abstractions; +using Umbraco.New.Cms.Core; +using Umbraco.New.Cms.Core.Models.Configuration; +using Umbraco.New.Cms.Infrastructure.Security; + +namespace Umbraco.Cms.ManagementApi.Security; + +public class BackOfficeApplicationManager : IBackOfficeApplicationManager +{ + private readonly IOpenIddictApplicationManager _applicationManager; + private readonly IWebHostEnvironment _webHostEnvironment; + private readonly IClientSecretManager _clientSecretManager; + private readonly Uri? _backOfficeHost; + + public BackOfficeApplicationManager( + IOpenIddictApplicationManager applicationManager, + IWebHostEnvironment webHostEnvironment, + IClientSecretManager clientSecretManager, + IOptionsMonitor securitySettingsMonitor) + { + _applicationManager = applicationManager; + _webHostEnvironment = webHostEnvironment; + _clientSecretManager = clientSecretManager; + _backOfficeHost = securitySettingsMonitor.CurrentValue.BackOfficeHost; + } + + public async Task EnsureBackOfficeApplicationAsync(Uri backOfficeUrl, CancellationToken cancellationToken = default) + { + if (backOfficeUrl.IsAbsoluteUri is false) + { + throw new ArgumentException($"Expected an absolute URL, got: {backOfficeUrl}", nameof(backOfficeUrl)); + } + + await CreateOrUpdate( + new OpenIddictApplicationDescriptor + { + DisplayName = "Umbraco back-office access", + ClientId = Constants.OauthClientIds.BackOffice, + RedirectUris = + { + CallbackUrlFor(_backOfficeHost ?? backOfficeUrl, "/umbraco/login/callback/") + }, + Type = OpenIddictConstants.ClientTypes.Public, + Permissions = + { + OpenIddictConstants.Permissions.Endpoints.Authorization, + OpenIddictConstants.Permissions.Endpoints.Token, + OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, + OpenIddictConstants.Permissions.GrantTypes.RefreshToken, + OpenIddictConstants.Permissions.ResponseTypes.Code + } + }, + cancellationToken); + + if (_webHostEnvironment.IsProduction()) + { + await Delete(Constants.OauthClientIds.Swagger, cancellationToken); + await Delete(Constants.OauthClientIds.Postman, cancellationToken); + } + else + { + await CreateOrUpdate( + new OpenIddictApplicationDescriptor + { + DisplayName = "Umbraco Swagger access", + ClientId = Constants.OauthClientIds.Swagger, + // TODO: investigate the necessity of client secrets for Swagger + // this is necessary with NSwag - or maybe it's a SwaggerUI3 requirement? investigate if client + // secrets are even necessary if we switch to Swashbuckle + ClientSecret = _clientSecretManager.Get(Constants.OauthClientIds.Swagger), + RedirectUris = + { + CallbackUrlFor(backOfficeUrl, "/umbraco/swagger/oauth2-redirect.html") + }, + Type = OpenIddictConstants.ClientTypes.Confidential, + Permissions = + { + OpenIddictConstants.Permissions.Endpoints.Authorization, + OpenIddictConstants.Permissions.Endpoints.Token, + OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, + OpenIddictConstants.Permissions.ResponseTypes.Code + } + }, + cancellationToken); + + await CreateOrUpdate( + new OpenIddictApplicationDescriptor + { + DisplayName = "Umbraco Postman access", + ClientId = Constants.OauthClientIds.Postman, + RedirectUris = + { + new Uri("https://oauth.pstmn.io/v1/callback") + }, + Type = OpenIddictConstants.ClientTypes.Public, + Permissions = + { + OpenIddictConstants.Permissions.Endpoints.Authorization, + OpenIddictConstants.Permissions.Endpoints.Token, + OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, + OpenIddictConstants.Permissions.ResponseTypes.Code + } + }, + cancellationToken); + } + } + + private async Task CreateOrUpdate(OpenIddictApplicationDescriptor clientDescriptor, CancellationToken cancellationToken) + { + var identifier = clientDescriptor.ClientId ?? + throw new ApplicationException($"ClientId is missing for application: {clientDescriptor.DisplayName ?? "(no name)"}"); + var client = await _applicationManager.FindByClientIdAsync(identifier, cancellationToken); + if (client is null) + { + await _applicationManager.CreateAsync(clientDescriptor, cancellationToken); + } + else + { + await _applicationManager.UpdateAsync(client, clientDescriptor, cancellationToken); + } + } + + private async Task Delete(string identifier, CancellationToken cancellationToken) + { + var client = await _applicationManager.FindByClientIdAsync(identifier, cancellationToken); + if (client is null) + { + return; + } + + await _applicationManager.DeleteAsync(client, cancellationToken); + } + + private static Uri CallbackUrlFor(Uri url, string relativePath) => new Uri( $"{url.GetLeftPart(UriPartial.Authority)}/{relativePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}"); +} diff --git a/src/Umbraco.Cms.ManagementApi/Security/ClientSecretManager.cs b/src/Umbraco.Cms.ManagementApi/Security/ClientSecretManager.cs new file mode 100644 index 0000000000..cbea254ed4 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Security/ClientSecretManager.cs @@ -0,0 +1,16 @@ +namespace Umbraco.Cms.ManagementApi.Security; + +public class ClientSecretManager : IClientSecretManager +{ + private Dictionary _secretsByClientId = new(); + + public string Get(string clientId) + { + if (_secretsByClientId.ContainsKey(clientId) == false) + { + _secretsByClientId[clientId] = Guid.NewGuid().ToString("N"); + } + + return _secretsByClientId[clientId]; + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Security/IClientSecretManager.cs b/src/Umbraco.Cms.ManagementApi/Security/IClientSecretManager.cs new file mode 100644 index 0000000000..7744169b7a --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Security/IClientSecretManager.cs @@ -0,0 +1,6 @@ +namespace Umbraco.Cms.ManagementApi.Security; + +public interface IClientSecretManager +{ + string Get(string clientId); +} diff --git a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj index 592f328ec2..372aeb1995 100644 --- a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj +++ b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj @@ -10,7 +10,10 @@ + + + diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs index 31ef06c400..3be3815afa 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Core.DependencyInjection; /// public static partial class UmbracoBuilderExtensions { - private static IUmbracoBuilder AddUmbracoOptions(this IUmbracoBuilder builder, Action>? configure = null) + internal static IUmbracoBuilder AddUmbracoOptions(this IUmbracoBuilder builder, Action>? configure = null) where TOptions : class { UmbracoOptionsAttribute? umbracoOptionsAttribute = typeof(TOptions).GetCustomAttribute(); diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs index fe1e83d254..02b13cf986 100644 --- a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs +++ b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs @@ -1,6 +1,8 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Routing; @@ -19,11 +21,18 @@ public class UmbracoRequestPaths private readonly string _mvcArea; private readonly string _previewMvcPath; private readonly string _surfaceMvcPath; + private readonly IOptions _umbracoRequestPathsOptions; + + [Obsolete("Use constructor that takes IOptions - Will be removed in Umbraco 13")] + public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment) + : this(globalSettings, hostingEnvironment, StaticServiceProvider.Instance.GetRequiredService>()) + { + } /// /// Initializes a new instance of the class. /// - public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment) + public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment, IOptions umbracoRequestPathsOptions) { var applicationPath = hostingEnvironment.ApplicationVirtualPath; _appPath = applicationPath.TrimStart(Constants.CharArrays.ForwardSlash); @@ -38,6 +47,7 @@ public class UmbracoRequestPaths _surfaceMvcPath = "/" + _mvcArea + "/Surface/"; _apiMvcPath = "/" + _mvcArea + "/Api/"; _installPath = hostingEnvironment.ToAbsolute(Constants.SystemDirectories.Install); + _umbracoRequestPathsOptions = umbracoRequestPathsOptions; } /// @@ -99,6 +109,11 @@ public class UmbracoRequestPaths return false; } + if (_umbracoRequestPathsOptions.Value.IsBackOfficeRequest(urlPath)) + { + return true; + } + // if its none of the above, we will have to try to detect if it's a PluginController route, we can detect this by // checking how many parts the route has, for example, all PluginController routes will be routed like // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id} diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs b/src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs new file mode 100644 index 0000000000..91f13eab3b --- /dev/null +++ b/src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Core.Routing; + +public class UmbracoRequestPathsOptions +{ + /// + /// Gets the delegate that allows us to handle additional URLs as back-office requests. + /// This returns false by default and can be overwritten in Startup.cs. + /// + public Func IsBackOfficeRequest { get; set; } = _ => false; +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 03a292c880..dc480b160f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -47,6 +47,10 @@ <_Parameter1>DynamicProxyGenAssembly2 + + + <_Parameter1>Umbraco.Cms.ManagementApi + diff --git a/src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs b/src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs new file mode 100644 index 0000000000..2fdc54e011 --- /dev/null +++ b/src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs @@ -0,0 +1,23 @@ +namespace Umbraco.New.Cms.Core; + +// TODO: move this class to Umbraco.Cms.Core as a partial class +public static class Constants +{ + public static partial class OauthClientIds + { + /// + /// Client ID used for default back-office access + /// + public const string BackOffice = "umbraco-back-office"; + + /// + /// Client ID used for Swagger API access + /// + public const string Swagger = "umbraco-swagger"; + + /// + /// Client ID used for Postman API access + /// + public const string Postman = "umbraco-postman"; + } +} diff --git a/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs new file mode 100644 index 0000000000..cad7b8868d --- /dev/null +++ b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs @@ -0,0 +1,10 @@ +using Umbraco.Cms.Core.Configuration.Models; + +namespace Umbraco.New.Cms.Core.Models.Configuration; + +// TODO: merge this class with relevant existing settings from Core and clean up +[UmbracoOptions($"{Umbraco.Cms.Core.Constants.Configuration.ConfigPrefix}NewBackOffice")] +public class NewBackOfficeSettings +{ + public Uri? BackOfficeHost { get; set; } = null; +} diff --git a/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs new file mode 100644 index 0000000000..bb1a2eda3d --- /dev/null +++ b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models.Validation; + +namespace Umbraco.New.Cms.Core.Models.Configuration; + +// TODO: merge this class with relevant existing settings validators from Core and clean up +public class NewBackOfficeSettingsValidator : ConfigurationValidatorBase, IValidateOptions +{ + public ValidateOptionsResult Validate(string? name, NewBackOfficeSettings options) + { + if (options.BackOfficeHost != null) + { + if (options.BackOfficeHost.IsAbsoluteUri == false) + { + return ValidateOptionsResult.Fail($"{nameof(NewBackOfficeSettings.BackOfficeHost)} must be an absolute URL"); + } + if (options.BackOfficeHost.PathAndQuery != "/") + { + return ValidateOptionsResult.Fail($"{nameof(NewBackOfficeSettings.BackOfficeHost)} must not have any path or query"); + } + } + + return ValidateOptionsResult.Success; + } +} diff --git a/src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs b/src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs new file mode 100644 index 0000000000..37a6e4caa6 --- /dev/null +++ b/src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs @@ -0,0 +1,56 @@ +using System.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OpenIddict.Abstractions; +using Umbraco.Cms.Infrastructure.HostedServices; + +namespace Umbraco.New.Cms.Infrastructure.HostedServices; + +// port of the OpenIddict Quartz job for cleaning up - see https://github.com/openiddict/openiddict-core/tree/dev/src/OpenIddict.Quartz +public class OpenIddictCleanup : RecurringHostedServiceBase +{ + // keep tokens and authorizations in the database for 7 days + // - NOTE: this is NOT the same as access token lifetime, which is likely very short + private const int LifespanInSeconds = 7 * 24 * 60 * 60; + + private readonly ILogger _logger; + private readonly IServiceProvider _provider; + + public OpenIddictCleanup( + ILogger logger, IServiceProvider provider) + : base(logger, TimeSpan.FromHours(1), TimeSpan.FromMinutes(5)) + { + _logger = logger; + _provider = provider; + } + + public override async Task PerformExecuteAsync(object? state) + { + // hosted services are registered as singletons, but this particular one consumes scoped services... so + // we have to fetch the service dependencies manually using a new scope per invocation. + IServiceScope scope = _provider.CreateScope(); + DateTimeOffset threshold = DateTimeOffset.UtcNow - TimeSpan.FromSeconds(LifespanInSeconds); + + try + { + IOpenIddictTokenManager tokenManager = scope.ServiceProvider.GetService() + ?? throw new ConfigurationErrorsException($"Could not retrieve an {nameof(IOpenIddictTokenManager)} service from the current scope"); + await tokenManager.PruneAsync(threshold); + } + catch (Exception exception) + { + _logger.LogError(exception, "Unable to prune OpenIddict tokens"); + } + + try + { + IOpenIddictAuthorizationManager authorizationManager = scope.ServiceProvider.GetService() + ?? throw new ConfigurationErrorsException($"Could not retrieve an {nameof(IOpenIddictAuthorizationManager)} service from the current scope"); + await authorizationManager.PruneAsync(threshold); + } + catch (Exception exception) + { + _logger.LogError(exception, "Unable to prune OpenIddict authorizations"); + } + } +} diff --git a/src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs b/src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs new file mode 100644 index 0000000000..068f5df472 --- /dev/null +++ b/src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs @@ -0,0 +1,6 @@ +namespace Umbraco.New.Cms.Infrastructure.Security; + +public interface IBackOfficeApplicationManager +{ + Task EnsureBackOfficeApplicationAsync(Uri backOfficeUrl, CancellationToken cancellationToken = default); +} diff --git a/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj b/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj index 8dc5d3cc00..82159079a4 100644 --- a/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj +++ b/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj @@ -10,4 +10,8 @@ + + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js index 5827f7e530..13ca4cb193 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js @@ -11,7 +11,11 @@ angular.module('umbraco').controller("Umbraco.LoginController", function (events //check if there's a returnPath query string, if so redirect to it var locationObj = $location.search(); if (locationObj.returnPath) { - path = decodeURIComponent(locationObj.returnPath); + // decodeURIComponent(...) does not play nice with OAuth redirect URLs, so until we have a + // dedicated login screen for the new back-office, we need to hardcode this exception + path = locationObj.returnPath.indexOf("/security/back-office/authorize") > 0 + ? locationObj.returnPath + : decodeURIComponent(locationObj.returnPath); } // Ensure path is not absolute diff --git a/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs b/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs index 94a64b31c9..2099d1d537 100644 --- a/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs +++ b/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs @@ -23,7 +23,8 @@ public class TestUmbracoContextFactory GlobalSettings globalSettings = null, IUmbracoContextAccessor umbracoContextAccessor = null, IHttpContextAccessor httpContextAccessor = null, - IPublishedUrlProvider publishedUrlProvider = null) + IPublishedUrlProvider publishedUrlProvider = null, + UmbracoRequestPathsOptions umbracoRequestPathsOptions = null) { if (globalSettings == null) { @@ -45,6 +46,11 @@ public class TestUmbracoContextFactory publishedUrlProvider = Mock.Of(); } + if (umbracoRequestPathsOptions == null) + { + umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); + } + var contentCache = new Mock(); var mediaCache = new Mock(); var snapshot = new Mock(); @@ -58,7 +64,7 @@ public class TestUmbracoContextFactory var umbracoContextFactory = new UmbracoContextFactory( umbracoContextAccessor, snapshotService.Object, - new UmbracoRequestPaths(Options.Create(globalSettings), hostingEnvironment), + new UmbracoRequestPaths(Options.Create(globalSettings), hostingEnvironment, Options.Create(umbracoRequestPathsOptions)), hostingEnvironment, new UriUtility(hostingEnvironment), new AspNetCoreCookieManager(httpContextAccessor), diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs index 744febae67..4ed6ce842c 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs @@ -18,10 +18,12 @@ public class UmbracoRequestPathsTests { _hostEnvironment = Mock.Of(); _globalSettings = new GlobalSettings(); + _umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); } private IWebHostEnvironment _hostEnvironment; private GlobalSettings _globalSettings; + private UmbracoRequestPathsOptions _umbracoRequestPathsOptions; private IHostingEnvironment CreateHostingEnvironment(string virtualPath = "") { @@ -49,7 +51,7 @@ public class UmbracoRequestPathsTests public void Is_Client_Side_Request(string url, bool assert) { var hostingEnvironment = CreateHostingEnvironment(); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); var uri = new Uri("http://test.com" + url); var result = umbracoRequestPaths.IsClientSideRequest(uri.AbsolutePath); @@ -60,7 +62,7 @@ public class UmbracoRequestPathsTests public void Is_Client_Side_Request_InvalidPath_ReturnFalse() { var hostingEnvironment = CreateHostingEnvironment(); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); // This URL is invalid. Default to false when the extension cannot be determined var uri = new Uri("http://test.com/installing-modules+foobar+\"yipee\""); @@ -85,11 +87,13 @@ public class UmbracoRequestPathsTests [TestCase("http://www.domain.com/myvdir/umbraco/api/blah", "myvdir", false)] [TestCase("http://www.domain.com/MyVdir/umbraco/api/blah", "/myvdir", false)] [TestCase("http://www.domain.com/MyVdir/Umbraco/", "myvdir", true)] + // NOTE: this test case is false for now - will be true once the IsBackOfficeRequest tweak from the new management API is put into UmbracoRequestPaths + [TestCase("http://www.domain.com/umbraco/management/api/v1.0/my/controller/action/", "", false)] public void Is_Back_Office_Request(string input, string virtualPath, bool expected) { var source = new Uri(input); var hostingEnvironment = CreateHostingEnvironment(virtualPath); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); Assert.AreEqual(expected, umbracoRequestPaths.IsBackOfficeRequest(source.AbsolutePath)); } @@ -106,7 +110,21 @@ public class UmbracoRequestPathsTests { var source = new Uri(input); var hostingEnvironment = CreateHostingEnvironment(); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); Assert.AreEqual(expected, umbracoRequestPaths.IsInstallerRequest(source.AbsolutePath)); } + + [TestCase("http://www.domain.com/some/path", false)] + [TestCase("http://www.domain.com/umbraco/surface/blah", false)] + [TestCase("http://www.domain.com/umbraco/api/blah", false)] + [TestCase("http://www.domain.com/umbraco/management/api/v1.0/my/controller/action/", true)] + public void Force_Back_Office_Request_With_Request_Paths_Options(string input, bool expected) + { + var source = new Uri(input); + var hostingEnvironment = CreateHostingEnvironment(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); + umbracoRequestPathsOptions.IsBackOfficeRequest = _ => true; + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(umbracoRequestPathsOptions)); + Assert.AreEqual(expected, umbracoRequestPaths.IsBackOfficeRequest(source.AbsolutePath)); + } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs index b1881c132e..c59f9db68f 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs @@ -24,12 +24,13 @@ public class BackOfficeCookieManagerTests public void ShouldAuthenticateRequest_When_Not_Configured() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); var mgr = new BackOfficeCookieManager( Mock.Of(), runtime, - new UmbracoRequestPaths(Options.Create(globalSettings), TestHelper.GetHostingEnvironment()), + new UmbracoRequestPaths(Options.Create(globalSettings), TestHelper.GetHostingEnvironment(), Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest("/umbraco"); @@ -41,6 +42,7 @@ public class BackOfficeCookieManagerTests public void ShouldAuthenticateRequest_When_Configured() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); var mgr = new BackOfficeCookieManager( @@ -49,7 +51,8 @@ public class BackOfficeCookieManagerTests new UmbracoRequestPaths( Options.Create(globalSettings), Mock.Of(x => - x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco")), + x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco"), + Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest("/umbraco"); @@ -61,6 +64,7 @@ public class BackOfficeCookieManagerTests public void ShouldAuthenticateRequest_Is_Back_Office() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); @@ -73,7 +77,8 @@ public class BackOfficeCookieManagerTests Options.Create(globalSettings), Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && - x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")), + x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"), + Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest(remainingTimeoutSecondsPath); @@ -87,6 +92,7 @@ public class BackOfficeCookieManagerTests public void ShouldAuthenticateRequest_Not_Back_Office() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); @@ -97,7 +103,8 @@ public class BackOfficeCookieManagerTests Options.Create(globalSettings), Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && - x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")), + x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"), + Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest("/notbackoffice"); From 8243a66deec3747318bc8a9424f43676d6e1f12a Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Wed, 2 Nov 2022 13:09:55 +0100 Subject: [PATCH 13/39] Add guard statement (#13340) --- src/Umbraco.Core/Extensions/ContentExtensions.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Extensions/ContentExtensions.cs b/src/Umbraco.Core/Extensions/ContentExtensions.cs index df0e58d878..ba736c5b13 100644 --- a/src/Umbraco.Core/Extensions/ContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/ContentExtensions.cs @@ -269,11 +269,17 @@ public static class ContentExtensions /// /// to retrieve ancestors for /// An Enumerable list of integer ids - public static IEnumerable? GetAncestorIds(this IContent content) => - content.Path?.Split(Constants.CharArrays.Comma) + public static IEnumerable? GetAncestorIds(this IContent content) + { + if (string.IsNullOrWhiteSpace(content.Path)) + { + return null; + } + + return content.Path.Split(Constants.CharArrays.Comma) .Where(x => x != Constants.System.RootString && x != content.Id.ToString(CultureInfo.InvariantCulture)) - .Select(s => - int.Parse(s, CultureInfo.InvariantCulture)); + .Select(s => int.Parse(s, CultureInfo.InvariantCulture)); + } #endregion From 897cf4ca195927e7bf9932f740fd25461b68a226 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Wed, 2 Nov 2022 15:26:07 +0100 Subject: [PATCH 14/39] V11: Using IFileProvider to access assets added from packages (#13141) * Creating a FileProviderFactory for getting the package.manifest and grid.editors.config.js files through a file provider * Collecting the package.manifest-s from different sources * Searching different sources for grid.editors.config.js * Using an IFileProvider to collect all tours * Refactoring IconService.cs * Typo * Optimizations when looping through the file system * Moving WebRootFileProviderFactory to Umbraco.Web.Common proj * Removes double registering * pluginLangFileSources includes the localPluginFileSources * Comments * Remove linq from foreach * Change workflow for grid.editors.config.js so we check first physical file, then RCL, then Embedded * Clean up * Check if config dir exists * Discover nested package.manifest files * Fix IFileInfo.PhysicalPath check * Revert 712810e1fd995720047832ee689f804185ea69d6 as that way files in content root are preferred over those in web root * Adding comments * Refactoring * Remove PhysicalPath check * Fix registration of WebRootFileProviderFactory --- .../Configuration/Grid/GridConfig.cs | 25 ++- .../Configuration/Grid/GridEditorsConfig.cs | 86 ++++++-- .../IGridEditorsConfigFileProviderFactory.cs | 10 + .../IO/IManifestFileProviderFactory.cs | 10 + .../Manifest/ManifestParser.cs | 92 +++++++- .../Controllers/TourController.cs | 197 ++++++++++++------ .../UmbracoBuilder.LocalizedText.cs | 26 +-- .../Services/IconService.cs | 55 +++-- .../Trees/MemberGroupTreeController.cs | 3 +- .../UmbracoBuilderExtensions.cs | 8 +- .../WebRootFileProviderFactory.cs | 27 +++ .../Testing/UmbracoIntegrationTest.cs | 5 - .../Manifest/ManifestParserTests.cs | 3 +- 13 files changed, 398 insertions(+), 149 deletions(-) create mode 100644 src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs create mode 100644 src/Umbraco.Core/IO/IManifestFileProviderFactory.cs create mode 100644 src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs index 44c9c37dfd..be13776da8 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs @@ -1,8 +1,11 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Core.Configuration.Grid; @@ -13,9 +16,27 @@ public class GridConfig : IGridConfig IManifestParser manifestParser, IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment, - ILoggerFactory loggerFactory) + ILoggerFactory loggerFactory, + IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory) => EditorsConfig = - new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, loggerFactory.CreateLogger()); + new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, loggerFactory.CreateLogger(), gridEditorsConfigFileProviderFactory); + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public GridConfig( + AppCaches appCaches, + IManifestParser manifestParser, + IJsonSerializer jsonSerializer, + IHostingEnvironment hostingEnvironment, + ILoggerFactory loggerFactory) + : this( + appCaches, + manifestParser, + jsonSerializer, + hostingEnvironment, + loggerFactory, + StaticServiceProvider.Instance.GetRequiredService()) + { + } public IGridEditorsConfig EditorsConfig { get; } } diff --git a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs index 11ae329192..ab0ec8b182 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs @@ -1,12 +1,15 @@ -using System.Reflection; using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; +using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.Core.Configuration.Grid; @@ -17,6 +20,7 @@ internal class GridEditorsConfig : IGridEditorsConfig private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; + private readonly IGridEditorsConfigFileProviderFactory _gridEditorsConfigFileProviderFactory; private readonly IManifestParser _manifestParser; public GridEditorsConfig( @@ -24,13 +28,32 @@ internal class GridEditorsConfig : IGridEditorsConfig IHostingEnvironment hostingEnvironment, IManifestParser manifestParser, IJsonSerializer jsonSerializer, - ILogger logger) + ILogger logger, + IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory) { _appCaches = appCaches; _hostingEnvironment = hostingEnvironment; _manifestParser = manifestParser; _jsonSerializer = jsonSerializer; _logger = logger; + _gridEditorsConfigFileProviderFactory = gridEditorsConfigFileProviderFactory; + } + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public GridEditorsConfig( + AppCaches appCaches, + IHostingEnvironment hostingEnvironment, + IManifestParser manifestParser, + IJsonSerializer jsonSerializer, + ILogger logger) + : this( + appCaches, + hostingEnvironment, + manifestParser, + jsonSerializer, + logger, + StaticServiceProvider.Instance.GetRequiredService()) + { } public IEnumerable Editors @@ -39,13 +62,37 @@ internal class GridEditorsConfig : IGridEditorsConfig { List GetResult() { - var configFolder = - new DirectoryInfo(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Config)); + IFileInfo? gridConfig = null; var editors = new List(); - var gridConfig = Path.Combine(configFolder.FullName, "grid.editors.config.js"); - if (File.Exists(gridConfig)) + var configPath = Constants.SystemDirectories.Config.TrimStart(Constants.CharArrays.Tilde); + + // Get physical file if it exists + var configPhysicalDirPath = _hostingEnvironment.MapPathContentRoot(configPath); + + if (Directory.Exists(configPhysicalDirPath) == true) { - var sourceString = File.ReadAllText(gridConfig); + var physicalFileProvider = new PhysicalFileProvider(configPhysicalDirPath); + gridConfig = GetConfigFile(physicalFileProvider, string.Empty); + } + + // If there is no physical file, check in RCLs + if (gridConfig is null) + { + IFileProvider? compositeFileProvider = _gridEditorsConfigFileProviderFactory.Create(); + + if (compositeFileProvider is null) + { + throw new ArgumentNullException(nameof(compositeFileProvider)); + } + + gridConfig = GetConfigFile(compositeFileProvider, configPath); + } + + if (gridConfig is not null) + { + using Stream stream = gridConfig.CreateReadStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var sourceString = reader.ReadToEnd(); try { @@ -63,19 +110,16 @@ internal class GridEditorsConfig : IGridEditorsConfig // Read default from embedded file else { - Assembly assembly = GetType().Assembly; - Stream? resourceStream = assembly.GetManifestResourceStream( - "Umbraco.Cms.Core.EmbeddedResources.Grid.grid.editors.config.js"); + IFileProvider configFileProvider = new EmbeddedFileProvider(GetType().Assembly, "Umbraco.Cms.Core.EmbeddedResources.Grid"); + IFileInfo embeddedConfig = configFileProvider.GetFileInfo("grid.editors.config.js"); - if (resourceStream is not null) - { - using var reader = new StreamReader(resourceStream, Encoding.UTF8); - var sourceString = reader.ReadToEnd(); - editors.AddRange(_jsonSerializer.Deserialize>(sourceString)!); - } + using Stream stream = embeddedConfig.CreateReadStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var sourceString = reader.ReadToEnd(); + editors.AddRange(_jsonSerializer.Deserialize>(sourceString)!); } - // add manifest editors, skip duplicates + // Add manifest editors, skip duplicates foreach (GridEditor gridEditor in _manifestParser.CombinedManifest.GridEditors) { if (editors.Contains(gridEditor) == false) @@ -95,4 +139,10 @@ internal class GridEditorsConfig : IGridEditorsConfig return result!; } } + + private static IFileInfo? GetConfigFile(IFileProvider fileProvider, string path) + { + IFileInfo fileInfo = fileProvider.GetFileInfo($"{path}/grid.editors.config.js"); + return fileInfo.Exists ? fileInfo : null; + } } diff --git a/src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs b/src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs new file mode 100644 index 0000000000..2c401a3f99 --- /dev/null +++ b/src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.FileProviders; + +namespace Umbraco.Cms.Core.IO; + +/// +/// Factory for creating instances for providing the grid.editors.config.js file. +/// +public interface IGridEditorsConfigFileProviderFactory : IFileProviderFactory +{ +} diff --git a/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs b/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs new file mode 100644 index 0000000000..982b029c27 --- /dev/null +++ b/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.FileProviders; + +namespace Umbraco.Cms.Core.IO; + +/// +/// Factory for creating instances for providing the package.manifest file. +/// +public interface IManifestFileProviderFactory : IFileProviderFactory +{ +} diff --git a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs index 4dbd6abd40..887ac05dc4 100644 --- a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs +++ b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs @@ -1,13 +1,17 @@ using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Manifest; @@ -21,6 +25,7 @@ public class ManifestParser : IManifestParser private readonly IAppPolicyCache _cache; private readonly IDataValueEditorFactory _dataValueEditorFactory; + private readonly IManifestFileProviderFactory _manifestFileProviderFactory; private readonly ManifestFilterCollection _filters; private readonly IHostingEnvironment _hostingEnvironment; @@ -46,7 +51,8 @@ public class ManifestParser : IManifestParser IJsonSerializer jsonSerializer, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, - IDataValueEditorFactory dataValueEditorFactory) + IDataValueEditorFactory dataValueEditorFactory, + IManifestFileProviderFactory manifestFileProviderFactory) { if (appCaches == null) { @@ -64,6 +70,34 @@ public class ManifestParser : IManifestParser _localizedTextService = localizedTextService; _shortStringHelper = shortStringHelper; _dataValueEditorFactory = dataValueEditorFactory; + _manifestFileProviderFactory = manifestFileProviderFactory; + } + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public ManifestParser( + AppCaches appCaches, + ManifestValueValidatorCollection validators, + ManifestFilterCollection filters, + ILogger logger, + IIOHelper ioHelper, + IHostingEnvironment hostingEnvironment, + IJsonSerializer jsonSerializer, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + IDataValueEditorFactory dataValueEditorFactory) + : this( + appCaches, + validators, + filters, + logger, + ioHelper, + hostingEnvironment, + jsonSerializer, + localizedTextService, + shortStringHelper, + dataValueEditorFactory, + StaticServiceProvider.Instance.GetRequiredService()) + { } public string AppPluginsPath @@ -89,12 +123,20 @@ public class ManifestParser : IManifestParser public IEnumerable GetManifests() { var manifests = new List(); + IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create(); - foreach (var path in GetManifestFiles()) + if (manifestFileProvider is null) + { + throw new ArgumentNullException(nameof(manifestFileProvider)); + } + + foreach (IFileInfo file in GetManifestFiles(manifestFileProvider, Constants.SystemDirectories.AppPlugins)) { try { - var text = File.ReadAllText(path); + using Stream stream = file.CreateReadStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var text = reader.ReadToEnd(); text = TrimPreamble(text); if (string.IsNullOrWhiteSpace(text)) { @@ -102,12 +144,12 @@ public class ManifestParser : IManifestParser } PackageManifest manifest = ParseManifest(text); - manifest.Source = path; + manifest.Source = file.PhysicalPath!; // We assure that the PhysicalPath is not null in GetManifestFiles() manifests.Add(manifest); } catch (Exception e) { - _logger.LogError(e, "Failed to parse manifest at '{Path}', ignoring.", path); + _logger.LogError(e, "Failed to parse manifest at '{Path}', ignoring.", file.PhysicalPath); } } @@ -242,14 +284,44 @@ public class ManifestParser : IManifestParser return text; } - // gets all manifest files (recursively) - private IEnumerable GetManifestFiles() + // Gets all manifest files + private static IEnumerable GetManifestFiles(IFileProvider fileProvider, string path) { - if (Directory.Exists(_path) == false) + var manifestFiles = new List(); + IEnumerable pluginFolders = fileProvider.GetDirectoryContents(path); + + foreach (IFileInfo pluginFolder in pluginFolders) { - return Array.Empty(); + if (!pluginFolder.IsDirectory) + { + continue; + } + + manifestFiles.AddRange(GetNestedManifestFiles(fileProvider, $"{path}/{pluginFolder.Name}")); } - return Directory.GetFiles(_path, "package.manifest", SearchOption.AllDirectories); + return manifestFiles; + } + + // Helper method to get all nested package.manifest files (recursively) + private static IEnumerable GetNestedManifestFiles(IFileProvider fileProvider, string path) + { + foreach (IFileInfo file in fileProvider.GetDirectoryContents(path)) + { + if (file.IsDirectory) + { + var virtualPath = WebPath.Combine(path, file.Name); + + // Recursively find nested package.manifest files + foreach (IFileInfo nested in GetNestedManifestFiles(fileProvider, virtualPath)) + { + yield return nested; + } + } + else if (file.Name.InvariantEquals("package.manifest") && !string.IsNullOrEmpty(file.PhysicalPath)) + { + yield return file; + } + } } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/TourController.cs b/src/Umbraco.Web.BackOffice/Controllers/TourController.cs index ae31876ac5..4d708b4fc8 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TourController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TourController.cs @@ -1,16 +1,21 @@ using System.Text; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Tour; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; +using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.Web.BackOffice.Controllers; @@ -20,22 +25,44 @@ public class TourController : UmbracoAuthorizedJsonController private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; private readonly IContentTypeService _contentTypeService; private readonly TourFilterCollection _filters; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IWebHostEnvironment _webHostEnvironment; private readonly TourSettings _tourSettings; + // IHostingEnvironment is still injected as when removing it, the number of + // parameters matches with the obsolete ctor and the two ctors become ambiguous + // [ActivatorUtilitiesConstructor] won't solve the problem in this case + // IHostingEnvironment can be removed when the obsolete ctor is removed + [ActivatorUtilitiesConstructor] + public TourController( + TourFilterCollection filters, + IHostingEnvironment hostingEnvironment, + IOptionsSnapshot tourSettings, + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + IContentTypeService contentTypeService, + IWebHostEnvironment webHostEnvironment) + { + _filters = filters; + _tourSettings = tourSettings.Value; + _backofficeSecurityAccessor = backofficeSecurityAccessor; + _contentTypeService = contentTypeService; + _webHostEnvironment = webHostEnvironment; + } + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] public TourController( TourFilterCollection filters, IHostingEnvironment hostingEnvironment, IOptionsSnapshot tourSettings, IBackOfficeSecurityAccessor backofficeSecurityAccessor, IContentTypeService contentTypeService) + : this( + filters, + hostingEnvironment, + tourSettings, + backofficeSecurityAccessor, + contentTypeService, + StaticServiceProvider.Instance.GetRequiredService()) { - _filters = filters; - _hostingEnvironment = hostingEnvironment; - - _tourSettings = tourSettings.Value; - _backofficeSecurityAccessor = backofficeSecurityAccessor; - _contentTypeService = contentTypeService; } public async Task> GetTours() @@ -53,69 +80,55 @@ public class TourController : UmbracoAuthorizedJsonController return result; } - //get all filters that will be applied to all tour aliases + // Get all filters that will be applied to all tour aliases var aliasOnlyFilters = _filters.Where(x => x.PluginName == null && x.TourFileName == null).ToList(); - //don't pass in any filters for core tours that have a plugin name assigned + // Don't pass in any filters for core tours that have a plugin name assigned var nonPluginFilters = _filters.Where(x => x.PluginName == null).ToList(); - //add core tour files - IEnumerable embeddedTourNames = GetType() - .Assembly - .GetManifestResourceNames() - .Where(x => x.StartsWith("Umbraco.Cms.Web.BackOffice.EmbeddedResources.Tours.")); - foreach (var embeddedTourName in embeddedTourNames) + // Get core tour files + IFileProvider toursProvider = new EmbeddedFileProvider(GetType().Assembly, "Umbraco.Cms.Web.BackOffice.EmbeddedResources.Tours"); + + IEnumerable embeddedTourFiles = toursProvider.GetDirectoryContents(string.Empty) + .Where(x => !x.IsDirectory && x.Name.EndsWith(".json")); + + foreach (var embeddedTour in embeddedTourFiles) { - await TryParseTourFile(embeddedTourName, result, nonPluginFilters, aliasOnlyFilters, async x => await GetContentFromEmbeddedResource(x)); + using Stream stream = embeddedTour.CreateReadStream(); + await TryParseTourFile(embeddedTour.Name, result, nonPluginFilters, aliasOnlyFilters, stream); } + // Collect all tour files from packages - /App_Plugins physical or virtual locations + IEnumerable> toursFromPackages = GetTourFiles(_webHostEnvironment.WebRootFileProvider, Constants.SystemDirectories.AppPlugins); - //collect all tour files in packages - var appPlugins = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins); - if (Directory.Exists(appPlugins)) + foreach (var tuple in toursFromPackages) { - foreach (var plugin in Directory.EnumerateDirectories(appPlugins)) + string pluginName = tuple.Item2; + var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)).ToList(); + + // Combine matched package filters with filters not specific to a package + var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList(); + + IFileInfo tourFile = tuple.Item1; + using (Stream stream = tourFile.CreateReadStream()) { - var pluginName = Path.GetFileName(plugin.TrimEnd(Constants.CharArrays.Backslash)); - var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)) - .ToList(); - - //If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely - var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null); - if (isPluginFiltered) - { - continue; - } - - //combine matched package filters with filters not specific to a package - var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList(); - - foreach (var backofficeDir in Directory.EnumerateDirectories(plugin, "backoffice")) - { - foreach (var tourDir in Directory.EnumerateDirectories(backofficeDir, "tours")) - { - foreach (var tourFile in Directory.EnumerateFiles(tourDir, "*.json")) - { - await TryParseTourFile( - tourFile, - result, - combinedFilters, - aliasOnlyFilters, - async x => await System.IO.File.ReadAllTextAsync(x), - pluginName); - } - } - } + await TryParseTourFile( + tourFile.Name, + result, + combinedFilters, + aliasOnlyFilters, + stream, + pluginName); } } - //Get all allowed sections for the current user + // Get all allowed sections for the current user var allowedSections = user.AllowedSections.ToList(); var toursToBeRemoved = new List(); - //Checking to see if the user has access to the required tour sections, else we remove the tour + // Checking to see if the user has access to the required tour sections, else we remove the tour foreach (BackOfficeTourFile backOfficeTourFile in result) { if (backOfficeTourFile.Tours != null) @@ -140,21 +153,70 @@ public class TourController : UmbracoAuthorizedJsonController return result.Except(toursToBeRemoved).OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase); } - private async Task GetContentFromEmbeddedResource(string fileName) + private IEnumerable> GetTourFiles(IFileProvider fileProvider, string folder) { - Stream? resourceStream = GetType().Assembly.GetManifestResourceStream(fileName); + IEnumerable pluginFolders = fileProvider.GetDirectoryContents(folder).Where(x => x.IsDirectory); - if (resourceStream is null) + foreach (IFileInfo pluginFolder in pluginFolders) { - return string.Empty; - } + var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginFolder.Name)).ToList(); - using var reader = new StreamReader(resourceStream, Encoding.UTF8); - return await reader.ReadToEndAsync(); + // If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely + var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null); + if (isPluginFiltered) + { + continue; + } + + // get the full virtual path for the plugin folder + var pluginFolderPath = WebPath.Combine(folder, pluginFolder.Name); + + // loop through the folder(s) in order to find tours + // - there could be multiple on case sensitive file system + // Hard-coding the "backoffice" directory name to gain a better performance when traversing the App_Plugins directory + foreach (var subDir in GetToursFolderPaths(fileProvider, pluginFolderPath, "backoffice")) + { + IEnumerable tourFiles = fileProvider + .GetDirectoryContents(subDir) + .Where(x => x.Name.InvariantEndsWith(".json")); + + foreach (IFileInfo file in tourFiles) + { + yield return new Tuple(file, pluginFolder.Name); + } + } + } + } + + private static IEnumerable GetToursFolderPaths(IFileProvider fileProvider, string path, string subDirName) + { + // Hard-coding the "tours" directory name to gain a better performance when traversing the sub directories + const string toursDirName = "tours"; + + // It is necessary to iterate through the subfolders because on Linux we'll get casing issues when + // we try to access {path}/{pluginDirectory.Name}/backoffice/tours directly + foreach (IFileInfo subDir in fileProvider.GetDirectoryContents(path)) + { + // InvariantEquals({dirName}) is used to gain a better performance when looking for the tours folder + if (subDir.IsDirectory && subDir.Name.InvariantEquals(subDirName)) + { + var virtualPath = WebPath.Combine(path, subDir.Name); + + if (subDir.Name.InvariantEquals(toursDirName)) + { + yield return virtualPath; + } + + foreach (var nested in GetToursFolderPaths(fileProvider, virtualPath, toursDirName)) + { + yield return nested; + } + } + } } /// - /// Gets a tours for a specific doctype + /// Gets a tours for a specific doctype. /// /// The documenttype alias /// A @@ -190,7 +252,7 @@ public class TourController : UmbracoAuthorizedJsonController ICollection result, List filters, List aliasOnlyFilters, - Func> fileNameToFileContent, + Stream fileStream, string? pluginName = null) { var fileName = Path.GetFileNameWithoutExtension(tourFile); @@ -199,24 +261,25 @@ public class TourController : UmbracoAuthorizedJsonController return; } - //get the filters specific to this file + // Get the filters specific to this file var fileFilters = filters.Where(x => x.TourFileName != null && x.TourFileName.IsMatch(fileName)).ToList(); - //If there is any filter applied to match the file only (no tour alias) then ignore the file entirely + // If there is any filter applied to match the file only (no tour alias) then ignore the file entirely var isFileFiltered = fileFilters.Any(x => x.TourAlias == null); if (isFileFiltered) { return; } - //now combine all aliases to filter below + // Now combine all aliases to filter below var aliasFilters = aliasOnlyFilters.Concat(filters.Where(x => x.TourAlias != null)) .Select(x => x.TourAlias) .ToList(); try { - var contents = await fileNameToFileContent(tourFile); + using var reader = new StreamReader(fileStream, Encoding.UTF8); + var contents = reader.ReadToEnd(); BackOfficeTour[]? tours = JsonConvert.DeserializeObject(contents); IEnumerable? backOfficeTours = tours?.Where(x => @@ -234,7 +297,7 @@ public class TourController : UmbracoAuthorizedJsonController Tours = localizedTours ?? new List() }; - //don't add if all of the tours are filtered + // Don't add if all of the tours are filtered if (tour.Tours.Any()) { result.Add(tour); diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs index 60a946a553..7a92a77b3c 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs @@ -1,8 +1,6 @@ -using System.IO; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; -using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -31,7 +29,7 @@ namespace Umbraco.Extensions /// - /// Loads the suplimentary localization files from plugins and user config + /// Loads the suplimentary localization files from plugins and user config. /// private static IEnumerable GetSupplementaryFileSources( IWebHostEnvironment webHostEnvironment) @@ -41,10 +39,10 @@ namespace Umbraco.Extensions IEnumerable localPluginFileSources = GetPluginLanguageFileSources(contentFileProvider, Cms.Core.Constants.SystemDirectories.AppPlugins, false); - // gets all langs files in /app_plugins real or virtual locations + // Gets all language files in /app_plugins real or virtual locations IEnumerable pluginLangFileSources = GetPluginLanguageFileSources(webFileProvider, Cms.Core.Constants.SystemDirectories.AppPlugins, false); - // user defined language files that overwrite the default, these should not be used by plugin creators + // User defined language files that overwrite the default, these should not be used by plugin creators var userConfigLangFolder = Cms.Core.Constants.SystemDirectories.Config .TrimStart(Cms.Core.Constants.CharArrays.Tilde); @@ -56,7 +54,7 @@ namespace Umbraco.Extensions { foreach (IFileInfo langFile in contentFileProvider.GetDirectoryContents($"{userConfigLangFolder}/{langFileSource.Name}")) { - if (langFile.Name.InvariantEndsWith(".xml") && langFile.PhysicalPath is not null) + if (langFile.Name.InvariantEndsWith(".xml")) { configLangFileSources.Add(new LocalizedTextServiceSupplementaryFileSource(langFile, true)); } @@ -64,10 +62,9 @@ namespace Umbraco.Extensions } } - return - localPluginFileSources - .Concat(pluginLangFileSources) - .Concat(configLangFileSources); + return localPluginFileSources + .Concat(pluginLangFileSources) + .Concat(configLangFileSources); } @@ -75,7 +72,7 @@ namespace Umbraco.Extensions /// Loads the suplimentary localaization files via the file provider. /// /// - /// locates all *.xml files in the lang folder of any sub folder of the one provided. + /// Locates all *.xml files in the lang folder of any sub folder of the one provided. /// e.g /app_plugins/plugin-name/lang/*.xml /// private static IEnumerable GetPluginLanguageFileSources( @@ -87,17 +84,16 @@ namespace Umbraco.Extensions foreach (IFileInfo pluginFolder in pluginFolders) { - // get the full virtual path for the plugin folder + // Get the full virtual path for the plugin folder var pluginFolderPath = WebPath.Combine(folder, pluginFolder.Name); - // loop through the lang folder(s) + // Loop through the lang folder(s) // - there could be multiple on case sensitive file system foreach (var langFolder in GetLangFolderPaths(fileProvider, pluginFolderPath)) { - // request all the files out of the path, these will have physicalPath set. + // Request all the files out of the path IEnumerable localizationFiles = fileProvider .GetDirectoryContents(langFolder) - .Where(x => !string.IsNullOrEmpty(x.PhysicalPath)) .Where(x => x.Name.InvariantEndsWith(".xml")); foreach (IFileInfo file in localizationFiles) diff --git a/src/Umbraco.Web.BackOffice/Services/IconService.cs b/src/Umbraco.Web.BackOffice/Services/IconService.cs index ac7ae2455e..068db52be6 100644 --- a/src/Umbraco.Web.BackOffice/Services/IconService.cs +++ b/src/Umbraco.Web.BackOffice/Services/IconService.cs @@ -34,7 +34,6 @@ public class IconService : IIconService { } - [Obsolete("Use other ctor - Will be removed in Umbraco 12")] public IconService( IOptionsMonitor globalSettings, IHostingEnvironment hostingEnvironment, @@ -101,6 +100,7 @@ public class IconService : IIconService } } + // TODO: Refactor to return IEnumerable private IEnumerable GetAllIconsFiles() { var icons = new HashSet(new CaseInsensitiveFileInfoComparer()); @@ -161,46 +161,45 @@ public class IconService : IIconService /// A collection of representing the found SVG icon files. private static IEnumerable GetIconsFiles(IFileProvider fileProvider, string path) { - // Iterate through all plugin folders, this is necessary because on Linux we'll get casing issues when + // Iterate through all plugin folders and their subfolders, this is necessary because on Linux we'll get casing issues when // we directly try to access {path}/{pluginDirectory.Name}/{Constants.SystemDirectories.PluginIcons} foreach (IFileInfo pluginDirectory in fileProvider.GetDirectoryContents(path)) { - // Ideally there shouldn't be any files, but we'd better check to be sure if (!pluginDirectory.IsDirectory) { continue; } - // Iterate through the sub directories of each plugin folder + // Iterate through the sub directories of each plugin folder in order to support case insensitive paths (for Linux) foreach (IFileInfo subDir1 in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}")) { - // Skip files again - if (!subDir1.IsDirectory) + // Hard-coding the "backoffice" directory name to gain a better performance when traversing the pluginDirectory directories + if (subDir1.IsDirectory && subDir1.Name.InvariantEquals("backoffice")) { - continue; - } - - // Iterate through second level sub directories - foreach (IFileInfo subDir2 in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}")) - { - // Skip files again - if (!subDir2.IsDirectory) + // Iterate through second level sub directories in order to support case insensitive paths (for Linux) + foreach (IFileInfo subDir2 in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}")) { - continue; - } - - // Does the directory match the plugin icons folder? (case insensitive for legacy support) - if (!$"/{subDir1.Name}/{subDir2.Name}".InvariantEquals(Constants.SystemDirectories.PluginIcons)) - { - continue; - } - - // Iterate though the files of the second level sub directory. This should be where the SVG files are located :D - foreach (IFileInfo file in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}/{subDir2.Name}")) - { - if (file.Name.InvariantEndsWith(".svg") && file.PhysicalPath != null) + if (!subDir2.IsDirectory) { - yield return new FileInfo(file.PhysicalPath); + continue; + } + + // Does the directory match the plugin icons folder? (case insensitive for legacy support) + if (!$"/{subDir1.Name}/{subDir2.Name}".InvariantEquals(Constants.SystemDirectories.PluginIcons)) + { + continue; + } + + // Iterate though the files of the second level sub directory. This should be where the SVG files are located :D + foreach (IFileInfo file in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}/{subDir2.Name}")) + { + // TODO: Refactor to work with IFileInfo, then we can also remove the .PhysicalPath check + // as this won't work for files that aren't located on a physical file system + // (e.g. embedded resource, Azure Blob Storage, etc.) + if (file.Name.InvariantEndsWith(".svg") && !string.IsNullOrEmpty(file.PhysicalPath)) + { + yield return new FileInfo(file.PhysicalPath); + } } } } diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs index af49c43e90..275e28d9aa 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs @@ -20,8 +20,7 @@ public class MemberGroupTreeController : MemberTypeAndGroupTreeControllerBase { private readonly IMemberGroupService _memberGroupService; - [ - ActivatorUtilitiesConstructor] + [ActivatorUtilitiesConstructor] public MemberGroupTreeController( ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 500bc22447..edfe999d20 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -6,7 +6,6 @@ using Microsoft.AspNetCore.DataProtection.Infrastructure; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ApplicationModels; -using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -27,6 +26,7 @@ using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Diagnostics; using Umbraco.Cms.Core.Extensions; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Macros; using Umbraco.Cms.Core.Net; @@ -48,6 +48,7 @@ using Umbraco.Cms.Web.Common.ApplicationModels; using Umbraco.Cms.Web.Common.AspNetCore; using Umbraco.Cms.Web.Common.Controllers; using Umbraco.Cms.Web.Common.DependencyInjection; +using Umbraco.Cms.Web.Common.FileProviders; using Umbraco.Cms.Web.Common.Localization; using Umbraco.Cms.Web.Common.Macros; using Umbraco.Cms.Web.Common.Middleware; @@ -146,6 +147,11 @@ public static partial class UmbracoBuilderExtensions builder.Services.TryAddEnumerable(ServiceDescriptor .Singleton()); + // WebRootFileProviderFactory is just a wrapper around the IWebHostEnvironment.WebRootFileProvider, + // therefore no need to register it as singleton + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + // Must be added here because DbProviderFactories is netstandard 2.1 so cannot exist in Infra for now builder.Services.AddSingleton(factory => new DbProviderFactoryCreator( DbProviderFactories.GetFactory, diff --git a/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs b/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs new file mode 100644 index 0000000000..64824dd090 --- /dev/null +++ b/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.FileProviders; +using Umbraco.Cms.Core.IO; + +namespace Umbraco.Cms.Web.Common.FileProviders; + +public class WebRootFileProviderFactory : IManifestFileProviderFactory, IGridEditorsConfigFileProviderFactory +{ + private readonly IWebHostEnvironment _webHostEnvironment; + + /// + /// Initializes a new instance of the class. + /// + /// The web hosting environment an application is running in. + public WebRootFileProviderFactory(IWebHostEnvironment webHostEnvironment) + { + _webHostEnvironment = webHostEnvironment; + } + + /// + /// Creates a new instance, pointing at . + /// + /// + /// The newly created instance. + /// + public IFileProvider Create() => _webHostEnvironment.WebRootFileProvider; +} diff --git a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 5babe9aa0b..a174476acd 100644 --- a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -9,7 +7,6 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -22,8 +19,6 @@ using Umbraco.Cms.Persistence.SqlServer; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Integration.DependencyInjection; using Umbraco.Cms.Tests.Integration.Extensions; -using Umbraco.Cms.Web.Common.Hosting; -using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.Integration.Testing; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs index 03f635b2cd..71d1a25228 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs @@ -47,7 +47,8 @@ public class ManifestParserTests new JsonNetSerializer(), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of(), + Mock.Of()); } private ManifestParser _parser; From f4eac20eafcdeb054f1df19818e0d553869553e0 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Wed, 2 Nov 2022 15:26:07 +0100 Subject: [PATCH 15/39] V11: Using IFileProvider to access assets added from packages (#13141) * Creating a FileProviderFactory for getting the package.manifest and grid.editors.config.js files through a file provider * Collecting the package.manifest-s from different sources * Searching different sources for grid.editors.config.js * Using an IFileProvider to collect all tours * Refactoring IconService.cs * Typo * Optimizations when looping through the file system * Moving WebRootFileProviderFactory to Umbraco.Web.Common proj * Removes double registering * pluginLangFileSources includes the localPluginFileSources * Comments * Remove linq from foreach * Change workflow for grid.editors.config.js so we check first physical file, then RCL, then Embedded * Clean up * Check if config dir exists * Discover nested package.manifest files * Fix IFileInfo.PhysicalPath check * Revert 712810e1fd995720047832ee689f804185ea69d6 as that way files in content root are preferred over those in web root * Adding comments * Refactoring * Remove PhysicalPath check * Fix registration of WebRootFileProviderFactory --- .../Configuration/Grid/GridConfig.cs | 25 ++- .../Configuration/Grid/GridEditorsConfig.cs | 86 ++++++-- .../IGridEditorsConfigFileProviderFactory.cs | 10 + .../IO/IManifestFileProviderFactory.cs | 10 + .../Manifest/ManifestParser.cs | 92 +++++++- .../Controllers/TourController.cs | 197 ++++++++++++------ .../UmbracoBuilder.LocalizedText.cs | 26 +-- .../Services/IconService.cs | 55 +++-- .../Trees/MemberGroupTreeController.cs | 3 +- .../UmbracoBuilderExtensions.cs | 8 +- .../WebRootFileProviderFactory.cs | 27 +++ .../Testing/UmbracoIntegrationTest.cs | 5 - .../Manifest/ManifestParserTests.cs | 3 +- 13 files changed, 398 insertions(+), 149 deletions(-) create mode 100644 src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs create mode 100644 src/Umbraco.Core/IO/IManifestFileProviderFactory.cs create mode 100644 src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs index 44c9c37dfd..be13776da8 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs @@ -1,8 +1,11 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Core.Configuration.Grid; @@ -13,9 +16,27 @@ public class GridConfig : IGridConfig IManifestParser manifestParser, IJsonSerializer jsonSerializer, IHostingEnvironment hostingEnvironment, - ILoggerFactory loggerFactory) + ILoggerFactory loggerFactory, + IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory) => EditorsConfig = - new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, loggerFactory.CreateLogger()); + new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, loggerFactory.CreateLogger(), gridEditorsConfigFileProviderFactory); + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public GridConfig( + AppCaches appCaches, + IManifestParser manifestParser, + IJsonSerializer jsonSerializer, + IHostingEnvironment hostingEnvironment, + ILoggerFactory loggerFactory) + : this( + appCaches, + manifestParser, + jsonSerializer, + hostingEnvironment, + loggerFactory, + StaticServiceProvider.Instance.GetRequiredService()) + { + } public IGridEditorsConfig EditorsConfig { get; } } diff --git a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs index 11ae329192..ab0ec8b182 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs @@ -1,12 +1,15 @@ -using System.Reflection; using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; +using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.Core.Configuration.Grid; @@ -17,6 +20,7 @@ internal class GridEditorsConfig : IGridEditorsConfig private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; + private readonly IGridEditorsConfigFileProviderFactory _gridEditorsConfigFileProviderFactory; private readonly IManifestParser _manifestParser; public GridEditorsConfig( @@ -24,13 +28,32 @@ internal class GridEditorsConfig : IGridEditorsConfig IHostingEnvironment hostingEnvironment, IManifestParser manifestParser, IJsonSerializer jsonSerializer, - ILogger logger) + ILogger logger, + IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory) { _appCaches = appCaches; _hostingEnvironment = hostingEnvironment; _manifestParser = manifestParser; _jsonSerializer = jsonSerializer; _logger = logger; + _gridEditorsConfigFileProviderFactory = gridEditorsConfigFileProviderFactory; + } + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public GridEditorsConfig( + AppCaches appCaches, + IHostingEnvironment hostingEnvironment, + IManifestParser manifestParser, + IJsonSerializer jsonSerializer, + ILogger logger) + : this( + appCaches, + hostingEnvironment, + manifestParser, + jsonSerializer, + logger, + StaticServiceProvider.Instance.GetRequiredService()) + { } public IEnumerable Editors @@ -39,13 +62,37 @@ internal class GridEditorsConfig : IGridEditorsConfig { List GetResult() { - var configFolder = - new DirectoryInfo(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Config)); + IFileInfo? gridConfig = null; var editors = new List(); - var gridConfig = Path.Combine(configFolder.FullName, "grid.editors.config.js"); - if (File.Exists(gridConfig)) + var configPath = Constants.SystemDirectories.Config.TrimStart(Constants.CharArrays.Tilde); + + // Get physical file if it exists + var configPhysicalDirPath = _hostingEnvironment.MapPathContentRoot(configPath); + + if (Directory.Exists(configPhysicalDirPath) == true) { - var sourceString = File.ReadAllText(gridConfig); + var physicalFileProvider = new PhysicalFileProvider(configPhysicalDirPath); + gridConfig = GetConfigFile(physicalFileProvider, string.Empty); + } + + // If there is no physical file, check in RCLs + if (gridConfig is null) + { + IFileProvider? compositeFileProvider = _gridEditorsConfigFileProviderFactory.Create(); + + if (compositeFileProvider is null) + { + throw new ArgumentNullException(nameof(compositeFileProvider)); + } + + gridConfig = GetConfigFile(compositeFileProvider, configPath); + } + + if (gridConfig is not null) + { + using Stream stream = gridConfig.CreateReadStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var sourceString = reader.ReadToEnd(); try { @@ -63,19 +110,16 @@ internal class GridEditorsConfig : IGridEditorsConfig // Read default from embedded file else { - Assembly assembly = GetType().Assembly; - Stream? resourceStream = assembly.GetManifestResourceStream( - "Umbraco.Cms.Core.EmbeddedResources.Grid.grid.editors.config.js"); + IFileProvider configFileProvider = new EmbeddedFileProvider(GetType().Assembly, "Umbraco.Cms.Core.EmbeddedResources.Grid"); + IFileInfo embeddedConfig = configFileProvider.GetFileInfo("grid.editors.config.js"); - if (resourceStream is not null) - { - using var reader = new StreamReader(resourceStream, Encoding.UTF8); - var sourceString = reader.ReadToEnd(); - editors.AddRange(_jsonSerializer.Deserialize>(sourceString)!); - } + using Stream stream = embeddedConfig.CreateReadStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var sourceString = reader.ReadToEnd(); + editors.AddRange(_jsonSerializer.Deserialize>(sourceString)!); } - // add manifest editors, skip duplicates + // Add manifest editors, skip duplicates foreach (GridEditor gridEditor in _manifestParser.CombinedManifest.GridEditors) { if (editors.Contains(gridEditor) == false) @@ -95,4 +139,10 @@ internal class GridEditorsConfig : IGridEditorsConfig return result!; } } + + private static IFileInfo? GetConfigFile(IFileProvider fileProvider, string path) + { + IFileInfo fileInfo = fileProvider.GetFileInfo($"{path}/grid.editors.config.js"); + return fileInfo.Exists ? fileInfo : null; + } } diff --git a/src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs b/src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs new file mode 100644 index 0000000000..2c401a3f99 --- /dev/null +++ b/src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.FileProviders; + +namespace Umbraco.Cms.Core.IO; + +/// +/// Factory for creating instances for providing the grid.editors.config.js file. +/// +public interface IGridEditorsConfigFileProviderFactory : IFileProviderFactory +{ +} diff --git a/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs b/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs new file mode 100644 index 0000000000..982b029c27 --- /dev/null +++ b/src/Umbraco.Core/IO/IManifestFileProviderFactory.cs @@ -0,0 +1,10 @@ +using Microsoft.Extensions.FileProviders; + +namespace Umbraco.Cms.Core.IO; + +/// +/// Factory for creating instances for providing the package.manifest file. +/// +public interface IManifestFileProviderFactory : IFileProviderFactory +{ +} diff --git a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs index 4dbd6abd40..887ac05dc4 100644 --- a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs +++ b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs @@ -1,13 +1,17 @@ using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Manifest; @@ -21,6 +25,7 @@ public class ManifestParser : IManifestParser private readonly IAppPolicyCache _cache; private readonly IDataValueEditorFactory _dataValueEditorFactory; + private readonly IManifestFileProviderFactory _manifestFileProviderFactory; private readonly ManifestFilterCollection _filters; private readonly IHostingEnvironment _hostingEnvironment; @@ -46,7 +51,8 @@ public class ManifestParser : IManifestParser IJsonSerializer jsonSerializer, ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, - IDataValueEditorFactory dataValueEditorFactory) + IDataValueEditorFactory dataValueEditorFactory, + IManifestFileProviderFactory manifestFileProviderFactory) { if (appCaches == null) { @@ -64,6 +70,34 @@ public class ManifestParser : IManifestParser _localizedTextService = localizedTextService; _shortStringHelper = shortStringHelper; _dataValueEditorFactory = dataValueEditorFactory; + _manifestFileProviderFactory = manifestFileProviderFactory; + } + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] + public ManifestParser( + AppCaches appCaches, + ManifestValueValidatorCollection validators, + ManifestFilterCollection filters, + ILogger logger, + IIOHelper ioHelper, + IHostingEnvironment hostingEnvironment, + IJsonSerializer jsonSerializer, + ILocalizedTextService localizedTextService, + IShortStringHelper shortStringHelper, + IDataValueEditorFactory dataValueEditorFactory) + : this( + appCaches, + validators, + filters, + logger, + ioHelper, + hostingEnvironment, + jsonSerializer, + localizedTextService, + shortStringHelper, + dataValueEditorFactory, + StaticServiceProvider.Instance.GetRequiredService()) + { } public string AppPluginsPath @@ -89,12 +123,20 @@ public class ManifestParser : IManifestParser public IEnumerable GetManifests() { var manifests = new List(); + IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create(); - foreach (var path in GetManifestFiles()) + if (manifestFileProvider is null) + { + throw new ArgumentNullException(nameof(manifestFileProvider)); + } + + foreach (IFileInfo file in GetManifestFiles(manifestFileProvider, Constants.SystemDirectories.AppPlugins)) { try { - var text = File.ReadAllText(path); + using Stream stream = file.CreateReadStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var text = reader.ReadToEnd(); text = TrimPreamble(text); if (string.IsNullOrWhiteSpace(text)) { @@ -102,12 +144,12 @@ public class ManifestParser : IManifestParser } PackageManifest manifest = ParseManifest(text); - manifest.Source = path; + manifest.Source = file.PhysicalPath!; // We assure that the PhysicalPath is not null in GetManifestFiles() manifests.Add(manifest); } catch (Exception e) { - _logger.LogError(e, "Failed to parse manifest at '{Path}', ignoring.", path); + _logger.LogError(e, "Failed to parse manifest at '{Path}', ignoring.", file.PhysicalPath); } } @@ -242,14 +284,44 @@ public class ManifestParser : IManifestParser return text; } - // gets all manifest files (recursively) - private IEnumerable GetManifestFiles() + // Gets all manifest files + private static IEnumerable GetManifestFiles(IFileProvider fileProvider, string path) { - if (Directory.Exists(_path) == false) + var manifestFiles = new List(); + IEnumerable pluginFolders = fileProvider.GetDirectoryContents(path); + + foreach (IFileInfo pluginFolder in pluginFolders) { - return Array.Empty(); + if (!pluginFolder.IsDirectory) + { + continue; + } + + manifestFiles.AddRange(GetNestedManifestFiles(fileProvider, $"{path}/{pluginFolder.Name}")); } - return Directory.GetFiles(_path, "package.manifest", SearchOption.AllDirectories); + return manifestFiles; + } + + // Helper method to get all nested package.manifest files (recursively) + private static IEnumerable GetNestedManifestFiles(IFileProvider fileProvider, string path) + { + foreach (IFileInfo file in fileProvider.GetDirectoryContents(path)) + { + if (file.IsDirectory) + { + var virtualPath = WebPath.Combine(path, file.Name); + + // Recursively find nested package.manifest files + foreach (IFileInfo nested in GetNestedManifestFiles(fileProvider, virtualPath)) + { + yield return nested; + } + } + else if (file.Name.InvariantEquals("package.manifest") && !string.IsNullOrEmpty(file.PhysicalPath)) + { + yield return file; + } + } } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/TourController.cs b/src/Umbraco.Web.BackOffice/Controllers/TourController.cs index ae31876ac5..4d708b4fc8 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TourController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TourController.cs @@ -1,16 +1,21 @@ using System.Text; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Tour; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; +using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.Web.BackOffice.Controllers; @@ -20,22 +25,44 @@ public class TourController : UmbracoAuthorizedJsonController private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; private readonly IContentTypeService _contentTypeService; private readonly TourFilterCollection _filters; - private readonly IHostingEnvironment _hostingEnvironment; + private readonly IWebHostEnvironment _webHostEnvironment; private readonly TourSettings _tourSettings; + // IHostingEnvironment is still injected as when removing it, the number of + // parameters matches with the obsolete ctor and the two ctors become ambiguous + // [ActivatorUtilitiesConstructor] won't solve the problem in this case + // IHostingEnvironment can be removed when the obsolete ctor is removed + [ActivatorUtilitiesConstructor] + public TourController( + TourFilterCollection filters, + IHostingEnvironment hostingEnvironment, + IOptionsSnapshot tourSettings, + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + IContentTypeService contentTypeService, + IWebHostEnvironment webHostEnvironment) + { + _filters = filters; + _tourSettings = tourSettings.Value; + _backofficeSecurityAccessor = backofficeSecurityAccessor; + _contentTypeService = contentTypeService; + _webHostEnvironment = webHostEnvironment; + } + + [Obsolete("Use other ctor - Will be removed in Umbraco 13")] public TourController( TourFilterCollection filters, IHostingEnvironment hostingEnvironment, IOptionsSnapshot tourSettings, IBackOfficeSecurityAccessor backofficeSecurityAccessor, IContentTypeService contentTypeService) + : this( + filters, + hostingEnvironment, + tourSettings, + backofficeSecurityAccessor, + contentTypeService, + StaticServiceProvider.Instance.GetRequiredService()) { - _filters = filters; - _hostingEnvironment = hostingEnvironment; - - _tourSettings = tourSettings.Value; - _backofficeSecurityAccessor = backofficeSecurityAccessor; - _contentTypeService = contentTypeService; } public async Task> GetTours() @@ -53,69 +80,55 @@ public class TourController : UmbracoAuthorizedJsonController return result; } - //get all filters that will be applied to all tour aliases + // Get all filters that will be applied to all tour aliases var aliasOnlyFilters = _filters.Where(x => x.PluginName == null && x.TourFileName == null).ToList(); - //don't pass in any filters for core tours that have a plugin name assigned + // Don't pass in any filters for core tours that have a plugin name assigned var nonPluginFilters = _filters.Where(x => x.PluginName == null).ToList(); - //add core tour files - IEnumerable embeddedTourNames = GetType() - .Assembly - .GetManifestResourceNames() - .Where(x => x.StartsWith("Umbraco.Cms.Web.BackOffice.EmbeddedResources.Tours.")); - foreach (var embeddedTourName in embeddedTourNames) + // Get core tour files + IFileProvider toursProvider = new EmbeddedFileProvider(GetType().Assembly, "Umbraco.Cms.Web.BackOffice.EmbeddedResources.Tours"); + + IEnumerable embeddedTourFiles = toursProvider.GetDirectoryContents(string.Empty) + .Where(x => !x.IsDirectory && x.Name.EndsWith(".json")); + + foreach (var embeddedTour in embeddedTourFiles) { - await TryParseTourFile(embeddedTourName, result, nonPluginFilters, aliasOnlyFilters, async x => await GetContentFromEmbeddedResource(x)); + using Stream stream = embeddedTour.CreateReadStream(); + await TryParseTourFile(embeddedTour.Name, result, nonPluginFilters, aliasOnlyFilters, stream); } + // Collect all tour files from packages - /App_Plugins physical or virtual locations + IEnumerable> toursFromPackages = GetTourFiles(_webHostEnvironment.WebRootFileProvider, Constants.SystemDirectories.AppPlugins); - //collect all tour files in packages - var appPlugins = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins); - if (Directory.Exists(appPlugins)) + foreach (var tuple in toursFromPackages) { - foreach (var plugin in Directory.EnumerateDirectories(appPlugins)) + string pluginName = tuple.Item2; + var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)).ToList(); + + // Combine matched package filters with filters not specific to a package + var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList(); + + IFileInfo tourFile = tuple.Item1; + using (Stream stream = tourFile.CreateReadStream()) { - var pluginName = Path.GetFileName(plugin.TrimEnd(Constants.CharArrays.Backslash)); - var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)) - .ToList(); - - //If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely - var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null); - if (isPluginFiltered) - { - continue; - } - - //combine matched package filters with filters not specific to a package - var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList(); - - foreach (var backofficeDir in Directory.EnumerateDirectories(plugin, "backoffice")) - { - foreach (var tourDir in Directory.EnumerateDirectories(backofficeDir, "tours")) - { - foreach (var tourFile in Directory.EnumerateFiles(tourDir, "*.json")) - { - await TryParseTourFile( - tourFile, - result, - combinedFilters, - aliasOnlyFilters, - async x => await System.IO.File.ReadAllTextAsync(x), - pluginName); - } - } - } + await TryParseTourFile( + tourFile.Name, + result, + combinedFilters, + aliasOnlyFilters, + stream, + pluginName); } } - //Get all allowed sections for the current user + // Get all allowed sections for the current user var allowedSections = user.AllowedSections.ToList(); var toursToBeRemoved = new List(); - //Checking to see if the user has access to the required tour sections, else we remove the tour + // Checking to see if the user has access to the required tour sections, else we remove the tour foreach (BackOfficeTourFile backOfficeTourFile in result) { if (backOfficeTourFile.Tours != null) @@ -140,21 +153,70 @@ public class TourController : UmbracoAuthorizedJsonController return result.Except(toursToBeRemoved).OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase); } - private async Task GetContentFromEmbeddedResource(string fileName) + private IEnumerable> GetTourFiles(IFileProvider fileProvider, string folder) { - Stream? resourceStream = GetType().Assembly.GetManifestResourceStream(fileName); + IEnumerable pluginFolders = fileProvider.GetDirectoryContents(folder).Where(x => x.IsDirectory); - if (resourceStream is null) + foreach (IFileInfo pluginFolder in pluginFolders) { - return string.Empty; - } + var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginFolder.Name)).ToList(); - using var reader = new StreamReader(resourceStream, Encoding.UTF8); - return await reader.ReadToEndAsync(); + // If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely + var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null); + if (isPluginFiltered) + { + continue; + } + + // get the full virtual path for the plugin folder + var pluginFolderPath = WebPath.Combine(folder, pluginFolder.Name); + + // loop through the folder(s) in order to find tours + // - there could be multiple on case sensitive file system + // Hard-coding the "backoffice" directory name to gain a better performance when traversing the App_Plugins directory + foreach (var subDir in GetToursFolderPaths(fileProvider, pluginFolderPath, "backoffice")) + { + IEnumerable tourFiles = fileProvider + .GetDirectoryContents(subDir) + .Where(x => x.Name.InvariantEndsWith(".json")); + + foreach (IFileInfo file in tourFiles) + { + yield return new Tuple(file, pluginFolder.Name); + } + } + } + } + + private static IEnumerable GetToursFolderPaths(IFileProvider fileProvider, string path, string subDirName) + { + // Hard-coding the "tours" directory name to gain a better performance when traversing the sub directories + const string toursDirName = "tours"; + + // It is necessary to iterate through the subfolders because on Linux we'll get casing issues when + // we try to access {path}/{pluginDirectory.Name}/backoffice/tours directly + foreach (IFileInfo subDir in fileProvider.GetDirectoryContents(path)) + { + // InvariantEquals({dirName}) is used to gain a better performance when looking for the tours folder + if (subDir.IsDirectory && subDir.Name.InvariantEquals(subDirName)) + { + var virtualPath = WebPath.Combine(path, subDir.Name); + + if (subDir.Name.InvariantEquals(toursDirName)) + { + yield return virtualPath; + } + + foreach (var nested in GetToursFolderPaths(fileProvider, virtualPath, toursDirName)) + { + yield return nested; + } + } + } } /// - /// Gets a tours for a specific doctype + /// Gets a tours for a specific doctype. /// /// The documenttype alias /// A @@ -190,7 +252,7 @@ public class TourController : UmbracoAuthorizedJsonController ICollection result, List filters, List aliasOnlyFilters, - Func> fileNameToFileContent, + Stream fileStream, string? pluginName = null) { var fileName = Path.GetFileNameWithoutExtension(tourFile); @@ -199,24 +261,25 @@ public class TourController : UmbracoAuthorizedJsonController return; } - //get the filters specific to this file + // Get the filters specific to this file var fileFilters = filters.Where(x => x.TourFileName != null && x.TourFileName.IsMatch(fileName)).ToList(); - //If there is any filter applied to match the file only (no tour alias) then ignore the file entirely + // If there is any filter applied to match the file only (no tour alias) then ignore the file entirely var isFileFiltered = fileFilters.Any(x => x.TourAlias == null); if (isFileFiltered) { return; } - //now combine all aliases to filter below + // Now combine all aliases to filter below var aliasFilters = aliasOnlyFilters.Concat(filters.Where(x => x.TourAlias != null)) .Select(x => x.TourAlias) .ToList(); try { - var contents = await fileNameToFileContent(tourFile); + using var reader = new StreamReader(fileStream, Encoding.UTF8); + var contents = reader.ReadToEnd(); BackOfficeTour[]? tours = JsonConvert.DeserializeObject(contents); IEnumerable? backOfficeTours = tours?.Where(x => @@ -234,7 +297,7 @@ public class TourController : UmbracoAuthorizedJsonController Tours = localizedTours ?? new List() }; - //don't add if all of the tours are filtered + // Don't add if all of the tours are filtered if (tour.Tours.Any()) { result.Add(tour); diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs index 60a946a553..7a92a77b3c 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.LocalizedText.cs @@ -1,8 +1,6 @@ -using System.IO; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; -using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -31,7 +29,7 @@ namespace Umbraco.Extensions /// - /// Loads the suplimentary localization files from plugins and user config + /// Loads the suplimentary localization files from plugins and user config. /// private static IEnumerable GetSupplementaryFileSources( IWebHostEnvironment webHostEnvironment) @@ -41,10 +39,10 @@ namespace Umbraco.Extensions IEnumerable localPluginFileSources = GetPluginLanguageFileSources(contentFileProvider, Cms.Core.Constants.SystemDirectories.AppPlugins, false); - // gets all langs files in /app_plugins real or virtual locations + // Gets all language files in /app_plugins real or virtual locations IEnumerable pluginLangFileSources = GetPluginLanguageFileSources(webFileProvider, Cms.Core.Constants.SystemDirectories.AppPlugins, false); - // user defined language files that overwrite the default, these should not be used by plugin creators + // User defined language files that overwrite the default, these should not be used by plugin creators var userConfigLangFolder = Cms.Core.Constants.SystemDirectories.Config .TrimStart(Cms.Core.Constants.CharArrays.Tilde); @@ -56,7 +54,7 @@ namespace Umbraco.Extensions { foreach (IFileInfo langFile in contentFileProvider.GetDirectoryContents($"{userConfigLangFolder}/{langFileSource.Name}")) { - if (langFile.Name.InvariantEndsWith(".xml") && langFile.PhysicalPath is not null) + if (langFile.Name.InvariantEndsWith(".xml")) { configLangFileSources.Add(new LocalizedTextServiceSupplementaryFileSource(langFile, true)); } @@ -64,10 +62,9 @@ namespace Umbraco.Extensions } } - return - localPluginFileSources - .Concat(pluginLangFileSources) - .Concat(configLangFileSources); + return localPluginFileSources + .Concat(pluginLangFileSources) + .Concat(configLangFileSources); } @@ -75,7 +72,7 @@ namespace Umbraco.Extensions /// Loads the suplimentary localaization files via the file provider. /// /// - /// locates all *.xml files in the lang folder of any sub folder of the one provided. + /// Locates all *.xml files in the lang folder of any sub folder of the one provided. /// e.g /app_plugins/plugin-name/lang/*.xml /// private static IEnumerable GetPluginLanguageFileSources( @@ -87,17 +84,16 @@ namespace Umbraco.Extensions foreach (IFileInfo pluginFolder in pluginFolders) { - // get the full virtual path for the plugin folder + // Get the full virtual path for the plugin folder var pluginFolderPath = WebPath.Combine(folder, pluginFolder.Name); - // loop through the lang folder(s) + // Loop through the lang folder(s) // - there could be multiple on case sensitive file system foreach (var langFolder in GetLangFolderPaths(fileProvider, pluginFolderPath)) { - // request all the files out of the path, these will have physicalPath set. + // Request all the files out of the path IEnumerable localizationFiles = fileProvider .GetDirectoryContents(langFolder) - .Where(x => !string.IsNullOrEmpty(x.PhysicalPath)) .Where(x => x.Name.InvariantEndsWith(".xml")); foreach (IFileInfo file in localizationFiles) diff --git a/src/Umbraco.Web.BackOffice/Services/IconService.cs b/src/Umbraco.Web.BackOffice/Services/IconService.cs index ac7ae2455e..068db52be6 100644 --- a/src/Umbraco.Web.BackOffice/Services/IconService.cs +++ b/src/Umbraco.Web.BackOffice/Services/IconService.cs @@ -34,7 +34,6 @@ public class IconService : IIconService { } - [Obsolete("Use other ctor - Will be removed in Umbraco 12")] public IconService( IOptionsMonitor globalSettings, IHostingEnvironment hostingEnvironment, @@ -101,6 +100,7 @@ public class IconService : IIconService } } + // TODO: Refactor to return IEnumerable private IEnumerable GetAllIconsFiles() { var icons = new HashSet(new CaseInsensitiveFileInfoComparer()); @@ -161,46 +161,45 @@ public class IconService : IIconService /// A collection of representing the found SVG icon files. private static IEnumerable GetIconsFiles(IFileProvider fileProvider, string path) { - // Iterate through all plugin folders, this is necessary because on Linux we'll get casing issues when + // Iterate through all plugin folders and their subfolders, this is necessary because on Linux we'll get casing issues when // we directly try to access {path}/{pluginDirectory.Name}/{Constants.SystemDirectories.PluginIcons} foreach (IFileInfo pluginDirectory in fileProvider.GetDirectoryContents(path)) { - // Ideally there shouldn't be any files, but we'd better check to be sure if (!pluginDirectory.IsDirectory) { continue; } - // Iterate through the sub directories of each plugin folder + // Iterate through the sub directories of each plugin folder in order to support case insensitive paths (for Linux) foreach (IFileInfo subDir1 in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}")) { - // Skip files again - if (!subDir1.IsDirectory) + // Hard-coding the "backoffice" directory name to gain a better performance when traversing the pluginDirectory directories + if (subDir1.IsDirectory && subDir1.Name.InvariantEquals("backoffice")) { - continue; - } - - // Iterate through second level sub directories - foreach (IFileInfo subDir2 in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}")) - { - // Skip files again - if (!subDir2.IsDirectory) + // Iterate through second level sub directories in order to support case insensitive paths (for Linux) + foreach (IFileInfo subDir2 in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}")) { - continue; - } - - // Does the directory match the plugin icons folder? (case insensitive for legacy support) - if (!$"/{subDir1.Name}/{subDir2.Name}".InvariantEquals(Constants.SystemDirectories.PluginIcons)) - { - continue; - } - - // Iterate though the files of the second level sub directory. This should be where the SVG files are located :D - foreach (IFileInfo file in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}/{subDir2.Name}")) - { - if (file.Name.InvariantEndsWith(".svg") && file.PhysicalPath != null) + if (!subDir2.IsDirectory) { - yield return new FileInfo(file.PhysicalPath); + continue; + } + + // Does the directory match the plugin icons folder? (case insensitive for legacy support) + if (!$"/{subDir1.Name}/{subDir2.Name}".InvariantEquals(Constants.SystemDirectories.PluginIcons)) + { + continue; + } + + // Iterate though the files of the second level sub directory. This should be where the SVG files are located :D + foreach (IFileInfo file in fileProvider.GetDirectoryContents($"{path}/{pluginDirectory.Name}/{subDir1.Name}/{subDir2.Name}")) + { + // TODO: Refactor to work with IFileInfo, then we can also remove the .PhysicalPath check + // as this won't work for files that aren't located on a physical file system + // (e.g. embedded resource, Azure Blob Storage, etc.) + if (file.Name.InvariantEndsWith(".svg") && !string.IsNullOrEmpty(file.PhysicalPath)) + { + yield return new FileInfo(file.PhysicalPath); + } } } } diff --git a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs index af49c43e90..275e28d9aa 100644 --- a/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs @@ -20,8 +20,7 @@ public class MemberGroupTreeController : MemberTypeAndGroupTreeControllerBase { private readonly IMemberGroupService _memberGroupService; - [ - ActivatorUtilitiesConstructor] + [ActivatorUtilitiesConstructor] public MemberGroupTreeController( ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 500bc22447..edfe999d20 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -6,7 +6,6 @@ using Microsoft.AspNetCore.DataProtection.Infrastructure; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ApplicationModels; -using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -27,6 +26,7 @@ using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Diagnostics; using Umbraco.Cms.Core.Extensions; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Macros; using Umbraco.Cms.Core.Net; @@ -48,6 +48,7 @@ using Umbraco.Cms.Web.Common.ApplicationModels; using Umbraco.Cms.Web.Common.AspNetCore; using Umbraco.Cms.Web.Common.Controllers; using Umbraco.Cms.Web.Common.DependencyInjection; +using Umbraco.Cms.Web.Common.FileProviders; using Umbraco.Cms.Web.Common.Localization; using Umbraco.Cms.Web.Common.Macros; using Umbraco.Cms.Web.Common.Middleware; @@ -146,6 +147,11 @@ public static partial class UmbracoBuilderExtensions builder.Services.TryAddEnumerable(ServiceDescriptor .Singleton()); + // WebRootFileProviderFactory is just a wrapper around the IWebHostEnvironment.WebRootFileProvider, + // therefore no need to register it as singleton + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + // Must be added here because DbProviderFactories is netstandard 2.1 so cannot exist in Infra for now builder.Services.AddSingleton(factory => new DbProviderFactoryCreator( DbProviderFactories.GetFactory, diff --git a/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs b/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs new file mode 100644 index 0000000000..64824dd090 --- /dev/null +++ b/src/Umbraco.Web.Common/FileProviders/WebRootFileProviderFactory.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.FileProviders; +using Umbraco.Cms.Core.IO; + +namespace Umbraco.Cms.Web.Common.FileProviders; + +public class WebRootFileProviderFactory : IManifestFileProviderFactory, IGridEditorsConfigFileProviderFactory +{ + private readonly IWebHostEnvironment _webHostEnvironment; + + /// + /// Initializes a new instance of the class. + /// + /// The web hosting environment an application is running in. + public WebRootFileProviderFactory(IWebHostEnvironment webHostEnvironment) + { + _webHostEnvironment = webHostEnvironment; + } + + /// + /// Creates a new instance, pointing at . + /// + /// + /// The newly created instance. + /// + public IFileProvider Create() => _webHostEnvironment.WebRootFileProvider; +} diff --git a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 5babe9aa0b..a174476acd 100644 --- a/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/tests/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -9,7 +7,6 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -22,8 +19,6 @@ using Umbraco.Cms.Persistence.SqlServer; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Integration.DependencyInjection; using Umbraco.Cms.Tests.Integration.Extensions; -using Umbraco.Cms.Web.Common.Hosting; -using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.Integration.Testing; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs index 03f635b2cd..71d1a25228 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs @@ -47,7 +47,8 @@ public class ManifestParserTests new JsonNetSerializer(), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of(), + Mock.Of()); } private ManifestParser _parser; From 0ed3121eaa09b5f337d4ebc6fcb2938964656fb1 Mon Sep 17 00:00:00 2001 From: nikolajlauridsen Date: Thu, 3 Nov 2022 09:38:33 +0100 Subject: [PATCH 16/39] Bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index b36746fe7c..4043ae827d 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "11.0.0-rc2", + "version": "11.0.0-rc3", "assemblyVersion": { "precision": "build" }, From 2a5c70a28b7d5103d59cffedc70e6521fbe8a008 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 4 Nov 2022 14:46:35 +0100 Subject: [PATCH 17/39] Use Swashbuckle instead of NSwag (#13350) * First attempt at OpenIddict * Making headway and more TODOs * Redo current policies for multiple schemas + clean up auth controller * Fix bad merge * Clean up some more test code * Fix spacing * Include AddAuthentication() in OpenIddict addition * A little more clean-up * Move application creation to its own implementation + prepare for middleware to handle valid callback URL * Enable refresh token flow * Fix bad merge from v11/dev * Support auth for Swagger and Postman in non-production environments + use default login screen for back-office logins * Add workaround to client side login handling so the OAuth return URL is not corrupted before redirection * Add temporary configuration handling for new backoffice * Restructure the code somewhat, move singular responsibility from management API project * Add recurring task for cleaning up old tokens in the DB * Fix bad merge + make auth controller align with the new management API structure * Explicitly handle the new management API path as a backoffice path (NOTE: this is potentially behaviorally breaking!) * Redo handle the new management API requests as backoffice requests, this time in a non-breaking way * Add/update TODOs * Replace NSwag with Swashbuckle and clean up unnecessary client secret workaround * Revert duplication of current auth policies for OpenIddict (as it breaks everything for V11 without the new management APIs) and introduce a dedicated PoC policy setup for OpenIddict. * Fix failing unit tests * A little niceness + export new OpenApi.json and fix path in contract unit test * Redo after merge with v11/dev + filter out unwanted mime types * Remove CreatedResult and NotFoundObjectResult where possible * Custom schema IDs - no more "ViewModel" postfix and make generic lists look less clunky too * A little more explanation for generic schema ID generation * Force Swashbuckle to use enum string names * Update OpenApi.json to match new enum string values * Add clarifying comment about weird looking construct --- .../Culture/CultureControllerBase.cs | 3 +- .../Tree/DataTypeTreeControllerBase.cs | 3 +- .../Dictionary/DictionaryControllerBase.cs | 3 +- .../Dictionary/ExportDictionaryController.cs | 4 +- .../Tree/DictionaryTreeControllerBase.cs | 3 +- .../DocumentRecycleBinControllerBase.cs | 3 +- .../Tree/DocumentTreeControllerBase.cs | 3 +- .../DocumentBlueprintTreeControllerBase.cs | 3 +- .../Tree/DocumentTypeTreeControllerBase.cs | 3 +- .../Controllers/Help/HelpControllerBase.cs | 3 +- .../Install/InstallControllerBase.cs | 3 +- .../Language/LanguageControllerBase.cs | 3 +- .../MediaRecycleBinControllerBase.cs | 3 +- .../Media/Tree/MediaTreeControllerBase.cs | 6 +- .../Tree/MediaTypeTreeControllerBase.cs | 3 +- .../Tree/MemberGroupTreeControllerBase.cs | 3 +- .../Tree/MemberTypeTreeControllerBase.cs | 3 +- .../GetModelsBuilderController.cs | 5 +- .../ModelsBuilderControllerBase.cs | 3 +- .../Tree/PartialViewTreeControllerBase.cs | 3 +- .../Profiling/ProfilingControllerBase.cs | 3 +- .../PublishedCacheControllerBase.cs | 3 +- .../Relation/RelationControllerBase.cs | 3 +- .../Tree/RelationTypeTreeControllerBase.cs | 3 +- .../Script/Tree/ScriptTreeControllerBase.cs | 3 +- .../Search/SearchControllerBase.cs | 3 +- .../Security/BackOfficeController.cs | 3 +- .../Server/ServerControllerBase.cs | 3 +- .../Tree/StaticFileTreeControllerBase.cs | 3 +- .../Tree/StylesheetTreeControllerBase.cs | 3 +- .../Telemetry/AllTelemetryController.cs | 8 +- .../Telemetry/GetTelemetryController.cs | 4 +- .../Telemetry/SetTelemetryController.cs | 6 +- .../Telemetry/TelemetryControllerBase.cs | 3 +- .../Tree/TemplateTreeControllerBase.cs | 3 +- .../TrackedReferencesControllerBase.cs | 3 +- .../Upgrade/UpgradeControllerBase.cs | 3 +- .../BackOfficeAuthBuilderExtensions.cs | 1 - .../ManagementApiComposer.cs | 105 +- src/Umbraco.Cms.ManagementApi/OpenApi.json | 10881 +++++++++------- .../OpenApi/EnumSchemaFilter.cs | 23 + .../OpenApi/MimeTypeDocumentFilter.cs | 33 + .../OpenApi/SchemaIdGenerator.cs | 26 + .../Security/BackOfficeApplicationManager.cs | 9 +- .../Security/ClientSecretManager.cs | 16 - .../Security/IClientSecretManager.cs | 6 - .../Umbraco.Cms.ManagementApi.csproj | 4 +- ...evelViewModel.cs => TelemetryViewModel.cs} | 2 +- .../Routing/UmbracoRequestPaths.cs | 1 + .../NewBackoffice/OpenAPIContractTest.cs | 2 +- 50 files changed, 6430 insertions(+), 4805 deletions(-) create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Security/ClientSecretManager.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Security/IClientSecretManager.cs rename src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/{TelemetryLevelViewModel.cs => TelemetryViewModel.cs} (86%) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs index 7ea0b77ae2..3ab1257824 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Culture; [ApiController] [VersionedApiBackOfficeRoute("culture")] -[OpenApiTag("Culture")] +[ApiExplorerSettings(GroupName = "Culture")] [ApiVersion("1.0")] public abstract class CultureControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs index 8c0c6598ae..e5948770db 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -13,7 +12,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DataType.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DataType}")] -[OpenApiTag("Data Type")] +[ApiExplorerSettings(GroupName = "Data Type")] public class DataTypeTreeControllerBase : FolderTreeControllerBase { private readonly IDataTypeService _dataTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs index f102d497ac..cfdba7cec1 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary; [ApiController] [VersionedApiBackOfficeRoute("dictionary")] -[OpenApiTag("Dictionary")] +[ApiExplorerSettings(GroupName = "Dictionary")] [ApiVersion("1.0")] // TODO: Add authentication public abstract class DictionaryControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs index f4944220d5..5145db1076 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs @@ -23,13 +23,13 @@ public class ExportDictionaryController : DictionaryControllerBase [HttpGet("export/{key:guid}")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(NotFoundObjectResult), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)] public async Task ExportDictionary(Guid key, bool includeChildren = false) { IDictionaryItem? dictionaryItem = _localizationService.GetDictionaryItemById(key); if (dictionaryItem is null) { - return await Task.FromResult(NotFound("No dictionary item found with id ")); + return await Task.FromResult(NotFound()); } XElement xml = _entityXmlSerializer.Serialize(dictionaryItem, includeChildren); diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs index c91a267c9f..4959789809 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -12,7 +11,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/dictionary")] -[OpenApiTag("Dictionary")] +[ApiExplorerSettings(GroupName = "Dictionary")] // NOTE: at the moment dictionary items (renamed to dictionary tree) aren't supported by EntityService, so we have little use of the // tree controller base. We'll keep it though, in the hope that we can mend EntityService. public class DictionaryTreeControllerBase : EntityTreeControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs index 106c636fe1..2edcb07a39 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -17,7 +16,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.RecycleBin; [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Document}")] [RequireDocumentTreeRootAccess] [ProducesResponseType(StatusCodes.Status401Unauthorized)] -[OpenApiTag(nameof(Constants.UdiEntityType.Document))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))] public class DocumentRecycleBinControllerBase : RecycleBinControllerBase { public DocumentRecycleBinControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs index b69af50c31..b838c03790 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Models; @@ -17,7 +16,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Document}")] -[OpenApiTag(nameof(Constants.UdiEntityType.Document))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))] public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBase { private readonly IPublicAccessService _publicAccessService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs index 1fe034b7b5..ad4e32cd87 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -13,7 +12,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentBlueprint.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentBlueprint}")] -[OpenApiTag("Document Blueprint")] +[ApiExplorerSettings(GroupName = "Document Blueprint")] public class DocumentBlueprintTreeControllerBase : EntityTreeControllerBase { private readonly IContentTypeService _contentTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs index a80d69e729..91676b9f80 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -13,7 +12,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentType.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentType}")] -[OpenApiTag("Document Type")] +[ApiExplorerSettings(GroupName = "Document Type")] public class DocumentTypeTreeControllerBase : FolderTreeControllerBase { private readonly IContentTypeService _contentTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs index 7220b8738b..9a50d45186 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Help; [ApiController] [VersionedApiBackOfficeRoute("help")] -[OpenApiTag("Help")] +[ApiExplorerSettings(GroupName = "Help")] [ApiVersion("1.0")] public abstract class HelpControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs index 359e62b4b8..04cd4885bb 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.ManagementApi.Filters; using Umbraco.New.Cms.Web.Common.Routing; @@ -8,7 +7,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Install; [ApiController] [VersionedApiBackOfficeRoute("install")] -[OpenApiTag("Install")] +[ApiExplorerSettings(GroupName = "Install")] [RequireRuntimeLevel(RuntimeLevel.Install)] public abstract class InstallControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs index b84088906c..c316486fed 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Language; [ApiController] [VersionedApiBackOfficeRoute("language")] -[OpenApiTag("Language")] +[ApiExplorerSettings(GroupName = "Language")] [ApiVersion("1.0")] public abstract class LanguageControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs index abd4c9c6bf..787a12f957 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -17,7 +16,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.RecycleBin; [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Media}")] [RequireMediaTreeRootAccess] [ProducesResponseType(StatusCodes.Status401Unauthorized)] -[OpenApiTag(nameof(Constants.UdiEntityType.Media))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Media))] public class MediaRecycleBinControllerBase : RecycleBinControllerBase { public MediaRecycleBinControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs index 44fb4464c7..67e90efe8e 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs @@ -1,5 +1,5 @@ -using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Models; @@ -16,7 +16,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Media}")] -[OpenApiTag(nameof(Constants.UdiEntityType.Media))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Media))] public class MediaTreeControllerBase : UserStartNodeTreeControllerBase { private readonly AppCaches _appCaches; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs index 043434d0ab..d47691df4e 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -13,7 +12,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MediaType.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MediaType}")] -[OpenApiTag("Media Type")] +[ApiExplorerSettings(GroupName = "Media Type")] public class MediaTypeTreeControllerBase : FolderTreeControllerBase { private readonly IMediaTypeService _mediaTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs index 6c4e5eb25a..455fcf64f7 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -13,7 +12,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberGroup.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberGroup}")] -[OpenApiTag("Member Group")] +[ApiExplorerSettings(GroupName = "Member Group")] public class MemberGroupTreeControllerBase : EntityTreeControllerBase { public MemberGroupTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs index f52980b4ff..2924ad3ba1 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -13,7 +12,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberType.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberType}")] -[OpenApiTag("Member Type")] +[ApiExplorerSettings(GroupName = "Member Type")] public class MemberTypeTreeControllerBase : EntityTreeControllerBase { public MemberTypeTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs index 09e5fcc1ac..b2080ee610 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs @@ -1,5 +1,4 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.ManagementApi.Factories; using Umbraco.Cms.ManagementApi.ViewModels.ModelsBuilderDashboard; @@ -13,7 +12,7 @@ public class GetModelsBuilderController : ModelsBuilderControllerBase public GetModelsBuilderController(IModelsBuilderViewModelFactory modelsBuilderViewModelFactory) => _modelsBuilderViewModelFactory = modelsBuilderViewModelFactory; [HttpGet("dashboard")] - [ProducesResponseType(typeof(CreatedResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ModelsBuilderViewModel), StatusCodes.Status200OK)] [MapToApiVersion("1.0")] public async Task> GetDashboard() => await Task.FromResult(Ok(_modelsBuilderViewModelFactory.Create())); } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs index 6a09074c94..aa428d6005 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; [ApiController] [VersionedApiBackOfficeRoute("models-builder")] -[OpenApiTag("Models Builder")] +[ApiExplorerSettings(GroupName = "Models Builder")] [ApiVersion("1.0")] public class ModelsBuilderControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs index d74afbda9d..ba2420e851 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -10,7 +9,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.PartialView.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.PartialView}")] -[OpenApiTag("Partial View")] +[ApiExplorerSettings(GroupName = "Partial View")] public class PartialViewTreeControllerBase : FileSystemTreeControllerBase { public PartialViewTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs index c081f74fe0..f1919b2501 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Profiling; @@ -7,7 +6,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Profiling; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute("profiling")] -[OpenApiTag("Profiling")] +[ApiExplorerSettings(GroupName = "Profiling")] public class ProfilingControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs index 3fe18847f6..914775a563 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache; @@ -7,7 +6,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute("published-cache")] -[OpenApiTag("Published Cache")] +[ApiExplorerSettings(GroupName = "Published Cache")] public class PublishedCacheControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs index 7ec26735d1..65266ef356 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Relation; [ApiController] [VersionedApiBackOfficeRoute("relation")] -[OpenApiTag("Relation")] +[ApiExplorerSettings(GroupName = "Relation")] [ApiVersion("1.0")] // TODO: Implement Authentication public abstract class RelationControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs index 6f5f69e8bd..c3497e7a1d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -12,7 +11,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.RelationType.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.RelationType}")] -[OpenApiTag("Relation Type")] +[ApiExplorerSettings(GroupName = "Relation Type")] // NOTE: at the moment relation types aren't supported by EntityService, so we have little use of the // tree controller base. We'll keep it though, in the hope that we can mend EntityService. public class RelationTypeTreeControllerBase : EntityTreeControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs index d8266f8226..f0f7398141 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -10,7 +9,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Script.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Script}")] -[OpenApiTag(nameof(Constants.UdiEntityType.Script))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Script))] public class ScriptTreeControllerBase : FileSystemTreeControllerBase { public ScriptTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs index 2ab98aa2ff..9d3befc022 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Search; [ApiController] [VersionedApiBackOfficeRoute("search")] -[OpenApiTag("Search")] +[ApiExplorerSettings(GroupName = "Search")] public class SearchControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs index efdeede8f5..74ce67ed03 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using OpenIddict.Abstractions; using OpenIddict.Server.AspNetCore; using Umbraco.Cms.Core; @@ -16,7 +15,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Security; [ApiController] [VersionedApiBackOfficeRoute(Paths.BackOfficeApiEndpointTemplate)] -[OpenApiTag("Security")] +[ApiExplorerSettings(GroupName = "Security")] public class BackOfficeController : ManagementApiControllerBase { private readonly IHttpContextAccessor _httpContextAccessor; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs index 9dd6b3a192..11674dcc23 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Server; [ApiController] [VersionedApiBackOfficeRoute("server")] -[OpenApiTag("Server")] +[ApiExplorerSettings(GroupName = "Server")] public abstract class ServerControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs index 410559af56..a66898619a 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -10,7 +9,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.StaticFile.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/static-file")] -[OpenApiTag("Static File")] +[ApiExplorerSettings(GroupName = "Static File")] public class StaticFileTreeControllerBase : FileSystemTreeControllerBase { private static readonly string[] _allowedRootFolders = { "App_Plugins", "wwwroot" }; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs index 6ffe4052de..c0fe829806 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -10,7 +9,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Stylesheet.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Stylesheet}")] -[OpenApiTag(nameof(Constants.UdiEntityType.Stylesheet))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Stylesheet))] public class StylesheetTreeControllerBase : FileSystemTreeControllerBase { public StylesheetTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs index 5520d20414..7e3eed886b 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs @@ -10,14 +10,14 @@ public class AllTelemetryController : TelemetryControllerBase { [HttpGet] [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task> GetAll(int skip, int take) + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task> GetAll(int skip, int take) { TelemetryLevel[] levels = Enum.GetValues(); - return await Task.FromResult(new PagedViewModel + return await Task.FromResult(new PagedViewModel { Total = levels.Length, - Items = levels.Skip(skip).Take(take).Select(level => new TelemetryLevelViewModel { TelemetryLevel = level }), + Items = levels.Skip(skip).Take(take).Select(level => new TelemetryViewModel { TelemetryLevel = level }), }); } } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs index 007554389e..3e6323343b 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs @@ -13,6 +13,6 @@ public class GetTelemetryController : TelemetryControllerBase [HttpGet("level")] [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(TelemetryLevelViewModel), StatusCodes.Status200OK)] - public async Task Get() => await Task.FromResult(new TelemetryLevelViewModel { TelemetryLevel = _metricsConsentService.GetConsentLevel() }); + [ProducesResponseType(typeof(TelemetryViewModel), StatusCodes.Status200OK)] + public async Task Get() => await Task.FromResult(new TelemetryViewModel { TelemetryLevel = _metricsConsentService.GetConsentLevel() }); } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs index 5c9002335c..0687997ff0 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs @@ -15,9 +15,9 @@ public class SetTelemetryController : TelemetryControllerBase [MapToApiVersion("1.0")] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task SetConsentLevel(TelemetryLevelViewModel telemetryLevelViewModel) + public async Task SetConsentLevel(TelemetryViewModel telemetryViewModel) { - if (!Enum.IsDefined(telemetryLevelViewModel.TelemetryLevel)) + if (!Enum.IsDefined(telemetryViewModel.TelemetryLevel)) { var invalidModelProblem = new ProblemDetails { @@ -29,7 +29,7 @@ public class SetTelemetryController : TelemetryControllerBase return BadRequest(invalidModelProblem); } - _metricsConsentService.SetConsentLevel(telemetryLevelViewModel.TelemetryLevel); + _metricsConsentService.SetConsentLevel(telemetryViewModel.TelemetryLevel); return await Task.FromResult(Ok()); } } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs index b95e35a7e8..ea5835b2ca 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; [ApiController] [VersionedApiBackOfficeRoute("telemetry")] -[OpenApiTag("Telemetry")] +[ApiExplorerSettings(GroupName = "Telemetry")] [ApiVersion("1.0")] public abstract class TelemetryControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs index 1ca105c6aa..c27206c665 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -13,7 +12,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Template.Tree; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Template}")] -[OpenApiTag(nameof(Constants.UdiEntityType.Template))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Template))] public class TemplateTreeControllerBase : EntityTreeControllerBase { public TemplateTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs index f473976c80..3b62a0fe69 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; [ApiController] [VersionedApiBackOfficeRoute("tracked-reference")] -[OpenApiTag("Tracked Reference")] +[ApiExplorerSettings(GroupName = "Tracked Reference")] [ApiVersion("1.0")] public abstract class TrackedReferenceControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs index 084515aba2..6e8e056280 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.ManagementApi.Filters; using Umbraco.New.Cms.Web.Common.Routing; @@ -11,7 +10,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Upgrade; [ApiController] [RequireRuntimeLevel(RuntimeLevel.Upgrade)] [VersionedApiBackOfficeRoute("upgrade")] -[OpenApiTag("Upgrade")] +[ApiExplorerSettings(GroupName = "Upgrade")] public abstract class UpgradeControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs b/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs index ab132cbef2..5331acaaa9 100644 --- a/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs +++ b/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs @@ -93,7 +93,6 @@ public static class BackOfficeAuthBuilderExtensions }); builder.Services.AddTransient(); - builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHostedService(); diff --git a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs index 8140a7906a..fa1d34a30a 100644 --- a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs +++ b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs @@ -3,19 +3,19 @@ using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; -using NSwag; -using NSwag.AspNetCore; +using Microsoft.OpenApi.Models; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.ManagementApi.Configuration; using Umbraco.Cms.ManagementApi.DependencyInjection; -using Umbraco.Cms.ManagementApi.Security; +using Umbraco.Cms.ManagementApi.OpenApi; using Umbraco.Cms.Web.Common.ApplicationBuilder; using Umbraco.Extensions; using Umbraco.New.Cms.Core; @@ -27,7 +27,7 @@ namespace Umbraco.Cms.ManagementApi; public class ManagementApiComposer : IComposer { private const string ApiTitle = "Umbraco Backoffice API"; - private const string ApiAllName = "All"; + private const string ApiDefaultDocumentName = "v1"; private ApiVersion DefaultApiVersion => new(1, 0); @@ -57,30 +57,63 @@ public class ManagementApiComposer : IComposer options.UseApiBehavior = false; }); - services.AddOpenApiDocument(options => + services.AddSwaggerGen(swaggerGenOptions => { - options.Title = ApiTitle; - options.Version = ApiAllName; - options.DocumentName = ApiAllName; - options.Description = "This shows all APIs available in this version of Umbraco - Including all the legacy apis that is available for backward compatibility"; - options.PostProcess = document => - { - document.Tags = document.Tags.OrderBy(tag => tag.Name).ToList(); - }; + swaggerGenOptions.SwaggerDoc( + ApiDefaultDocumentName, + new OpenApiInfo + { + Title = ApiTitle, + Version = DefaultApiVersion.ToString(), + Description = "This shows all APIs available in this version of Umbraco - including all the legacy apis that are available for backward compatibility" + }); - options.AddSecurity("Bearer", Enumerable.Empty(), new OpenApiSecurityScheme + swaggerGenOptions.DocInclusionPredicate((_, api) => !string.IsNullOrWhiteSpace(api.GroupName)); + + swaggerGenOptions.TagActionsBy(api => new [] { api.GroupName }); + + // see https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-operation-sort-order-eg-for-ui-sorting + string ActionSortKeySelector(ApiDescription apiDesc) + => $"{apiDesc.GroupName}_{apiDesc.ActionDescriptor.AttributeRouteInfo?.Template ?? apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.ActionDescriptor.RouteValues["action"]}_{apiDesc.HttpMethod}"; + swaggerGenOptions.OrderActionsBy(ActionSortKeySelector); + + swaggerGenOptions.AddSecurityDefinition("OAuth", new OpenApiSecurityScheme { + In = ParameterLocation.Header, Name = "Umbraco", - Type = OpenApiSecuritySchemeType.OAuth2, + Type = SecuritySchemeType.OAuth2, Description = "Umbraco Authentication", - Flow = OpenApiOAuth2Flow.AccessCode, - AuthorizationUrl = Controllers.Security.Paths.BackOfficeApiAuthorizationEndpoint, - TokenUrl = Controllers.Security.Paths.BackOfficeApiTokenEndpoint, - Scopes = new Dictionary(), + Flows = new OpenApiOAuthFlows + { + AuthorizationCode = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri(Controllers.Security.Paths.BackOfficeApiAuthorizationEndpoint, UriKind.Relative), + TokenUrl = new Uri(Controllers.Security.Paths.BackOfficeApiTokenEndpoint, UriKind.Relative) + } + } }); - // this is documented in OAuth2 setup for swagger, but does not seem to be necessary at the moment. - // it is worth try it if operation authentication starts failing. - // options.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("Bearer")); + + swaggerGenOptions.AddSecurityRequirement(new OpenApiSecurityRequirement + { + // this weird looking construct works because OpenApiSecurityRequirement + // is a specialization of Dictionary<,> + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Id = "OAuth", + Type = ReferenceType.SecurityScheme + } + }, + new List { } + } + }); + + swaggerGenOptions.DocumentFilter(); + swaggerGenOptions.SchemaFilter(); + + swaggerGenOptions.CustomSchemaIds(SchemaIdGenerator.Generate); }); services.AddVersionedApiExplorer(options => @@ -145,31 +178,19 @@ public class ManagementApiComposer : IComposer { GlobalSettings? settings = provider.GetRequiredService>().Value; IHostingEnvironment hostingEnvironment = provider.GetRequiredService(); - IClientSecretManager clientSecretManager = provider.GetRequiredService(); var officePath = settings.GetBackOfficePath(hostingEnvironment); - // serve documents (same as app.UseSwagger()) - applicationBuilder.UseOpenApi(config => + + applicationBuilder.UseSwagger(swaggerOptions => { - config.Path = $"{officePath}/swagger/{{documentName}}/swagger.json"; + swaggerOptions.RouteTemplate = $"{officePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}/swagger/{{documentName}}/swagger.json"; }); - - // Serve Swagger UI - applicationBuilder.UseSwaggerUi3(config => + applicationBuilder.UseSwaggerUI(swaggerUiOptions => { - config.Path = officePath + "/swagger"; - config.SwaggerRoutes.Clear(); - var swaggerPath = $"{officePath}/swagger/{ApiAllName}/swagger.json"; - config.SwaggerRoutes.Add(new SwaggerUi3Route(ApiAllName, swaggerPath)); - config.OperationsSorter = "alpha"; - config.TagsSorter = "alpha"; + swaggerUiOptions.SwaggerEndpoint($"{officePath}/swagger/v1/swagger.json", $"{ApiTitle} {DefaultApiVersion}"); + swaggerUiOptions.RoutePrefix = $"{officePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}/swagger"; - config.OAuth2Client = new OAuth2ClientSettings - { - AppName = "Umbraco", - UsePkceWithAuthorizationCodeGrant = true, - ClientId = Constants.OauthClientIds.Swagger, - ClientSecret = clientSecretManager.Get(Constants.OauthClientIds.Swagger) - }; + swaggerUiOptions.OAuthClientId(Constants.OauthClientIds.Swagger); + swaggerUiOptions.OAuthUsePkce(); }); } }, diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi.json b/src/Umbraco.Cms.ManagementApi/OpenApi.json index d71ee73460..c0b9f4ef0b 100644 --- a/src/Umbraco.Cms.ManagementApi/OpenApi.json +++ b/src/Umbraco.Cms.ManagementApi/OpenApi.json @@ -1,195 +1,41 @@ { - "x-generator": "NSwag v13.17.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))", - "openapi": "3.0.0", + "openapi": "3.0.1", "info": { "title": "Umbraco Backoffice API", - "description": "This shows all APIs available in this version of Umbraco - Including all the legacy apis that is available for backward compatibility", - "version": "All" + "description": "This shows all APIs available in this version of Umbraco - including all the legacy apis that are available for backward compatibility", + "version": "1.0" }, - "servers": [ - { - "url": "https://localhost:44331" - } - ], "paths": { - "/umbraco/management/api/v1/upgrade/authorize": { - "post": { - "tags": [ - "Upgrade" - ], - "operationId": "AuthorizeUpgrade_Authorize", - "responses": { - "200": { - "description": "" - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/upgrade/settings": { + "/umbraco/management/api/v1/culture": { "get": { "tags": [ - "Upgrade" + "Culture" ], - "operationId": "SettingsUpgrade_Settings", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpgradeSettingsViewModel" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tracked-reference/{id}": { - "get": { - "tags": [ - "Tracked Reference" - ], - "summary": "Gets a page list of tracked references for the current item, so you can see where an item is being used.", - "description": "Used by info tabs on content, media etc. and for the delete and unpublish of single items.\nThis is basically finding parents of relations.", - "operationId": "ByIdTrackedReference_Get", "parameters": [ { - "name": "id", - "in": "path", - "required": true, + "name": "skip", + "in": "query", "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 2 + } }, { "name": "take", "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 3 - }, - { - "name": "filterMustBeIsDependency", - "in": "query", - "schema": { - "type": "boolean", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tracked-reference/descendants/{parentId}": { - "get": { - "tags": [ - "Tracked Reference" - ], - "summary": "Gets a page list of the child nodes of the current item used in any kind of relation.", - "description": "Used when deleting and unpublishing a single item to check if this item has any descending items that are in any\nkind of relation.\nThis is basically finding the descending items which are children in relations.", - "operationId": "DescendantsTrackedReference_Descendants", - "parameters": [ - { - "name": "parentId", - "in": "path", - "required": true, "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 3 - }, - { - "name": "filterMustBeIsDependency", - "in": "query", - "schema": { - "type": "boolean", - "nullable": true - }, - "x-position": 4 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" + "$ref": "#/components/schemas/PagedCulture" } } } @@ -197,87 +43,19 @@ } } }, - "/umbraco/management/api/v1/tracked-reference/item": { + "/umbraco/management/api/v1/tree/data-type/children": { "get": { "tags": [ - "Tracked Reference" + "Data Type" ], - "summary": "Gets a page list of the items used in any kind of relation from selected integer ids.", - "description": "Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view).\nThis is basically finding children of relations.", - "operationId": "ItemsTrackedReference_GetPagedReferencedItems", - "parameters": [ - { - "name": "ids", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "integer", - "format": "int32" - } - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 3 - }, - { - "name": "filterMustBeIsDependency", - "in": "query", - "schema": { - "type": "boolean", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/template/children": { - "get": { - "tags": [ - "Template" - ], - "operationId": "ChildrenTemplateTree_Children", "parameters": [ { "name": "parentKey", "in": "query", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "skip", @@ -286,8 +64,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -296,1886 +73,7 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } } - } - } - } - }, - "/umbraco/management/api/v1/tree/template/item": { - "get": { - "tags": [ - "Template" - ], - "operationId": "ItemsTemplateTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/template/root": { - "get": { - "tags": [ - "Template" - ], - "operationId": "RootTemplateTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/telemetry": { - "get": { - "tags": [ - "Telemetry" - ], - "operationId": "AllTelemetry_GetAll", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfTelemetryLevelViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/telemetry/level": { - "get": { - "tags": [ - "Telemetry" - ], - "operationId": "GetTelemetry_Get", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TelemetryLevelViewModel" - } - } - } - } - } - }, - "post": { - "tags": [ - "Telemetry" - ], - "operationId": "SetTelemetry_SetConsentLevel", - "requestBody": { - "x-name": "telemetryLevelViewModel", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TelemetryLevelViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/tree/stylesheet/children": { - "get": { - "tags": [ - "Stylesheet" - ], - "operationId": "ChildrenStylesheetTree_Children", - "parameters": [ - { - "name": "path", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/stylesheet/item": { - "get": { - "tags": [ - "Stylesheet" - ], - "operationId": "ItemsStylesheetTree_Items", - "parameters": [ - { - "name": "path", - "x-originalName": "paths", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/stylesheet/root": { - "get": { - "tags": [ - "Stylesheet" - ], - "operationId": "RootStylesheetTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/static-file/children": { - "get": { - "tags": [ - "Static File" - ], - "operationId": "ChildrenStaticFileTree_Children", - "parameters": [ - { - "name": "path", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/static-file/item": { - "get": { - "tags": [ - "Static File" - ], - "operationId": "ItemsStaticFileTree_Items", - "parameters": [ - { - "name": "path", - "x-originalName": "paths", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/static-file/root": { - "get": { - "tags": [ - "Static File" - ], - "operationId": "RootStaticFileTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/server/status": { - "get": { - "tags": [ - "Server" - ], - "operationId": "StatusServer_Get", - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServerStatusViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/server/version": { - "get": { - "tags": [ - "Server" - ], - "operationId": "VersionServer_Get", - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VersionViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/security/back-office/authorize": { - "get": { - "tags": [ - "Security" - ], - "operationId": "BackOffice_AuthorizeGET", - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "post": { - "tags": [ - "Security" - ], - "operationId": "BackOffice_AuthorizePOST", - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/index/{indexName}": { - "get": { - "tags": [ - "Search" - ], - "summary": "Check if the index has been rebuilt", - "description": "This is kind of rudimentary since there's no way we can know that the index has rebuilt, we\nhave a listener for the index op complete so we'll just check if that key is no longer there in the runtime cache", - "operationId": "IndexDetailsSearch_Index", - "parameters": [ - { - "name": "indexName", - "in": "path", - "required": true, - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - } - ], - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/IndexViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/index": { - "get": { - "tags": [ - "Search" - ], - "summary": "Get the details for indexers", - "operationId": "IndexListSearch_Indexes", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfIndexViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/index/{indexName}/rebuild": { - "post": { - "tags": [ - "Search" - ], - "summary": "Rebuilds the index", - "operationId": "IndexRebuildSearch_Rebuild", - "parameters": [ - { - "name": "indexName", - "in": "path", - "required": true, - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - } - ], - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/searcher": { - "get": { - "tags": [ - "Search" - ], - "summary": "Get the details for searchers", - "operationId": "SearcherListSearch_Searchers", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfSearcherViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/searcher/{searcherName}/search": { - "get": { - "tags": [ - "Search" - ], - "operationId": "SearcherSearchSearch_GetSearchResults", - "parameters": [ - { - "name": "searcherName", - "in": "path", - "required": true, - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "query", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 2 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 3 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfPagedViewModelOfSearchResultViewModel" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/script/children": { - "get": { - "tags": [ - "Script" - ], - "operationId": "ChildrenScriptTree_Children", - "parameters": [ - { - "name": "path", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/script/item": { - "get": { - "tags": [ - "Script" - ], - "operationId": "ItemsScriptTree_Items", - "parameters": [ - { - "name": "path", - "x-originalName": "paths", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/script/root": { - "get": { - "tags": [ - "Script" - ], - "operationId": "RootScriptTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/relation/child-relation/{childId}": { - "get": { - "tags": [ - "Relation" - ], - "operationId": "ByChildRelation_ByChild", - "parameters": [ - { - "name": "childId", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 3 - }, - { - "name": "relationTypeAlias", - "in": "query", - "schema": { - "type": "string", - "default": "", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/relation/{id}": { - "get": { - "tags": [ - "Relation" - ], - "operationId": "ByIdRelation_ById", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RelationViewModel" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/relation-type/item": { - "get": { - "tags": [ - "Relation Type" - ], - "operationId": "ItemsRelationTypeTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/relation-type/root": { - "get": { - "tags": [ - "Relation Type" - ], - "operationId": "RootRelationTypeTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/published-cache/collect": { - "post": { - "tags": [ - "Published Cache" - ], - "operationId": "CollectPublishedCache_Collect", - "responses": { - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/published-cache/rebuild": { - "post": { - "tags": [ - "Published Cache" - ], - "operationId": "RebuildPublishedCache_Collect", - "responses": { - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/published-cache/reload": { - "post": { - "tags": [ - "Published Cache" - ], - "operationId": "ReloadPublishedCache_Reload", - "responses": { - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/published-cache/status": { - "get": { - "tags": [ - "Published Cache" - ], - "operationId": "StatusPublishedCache_Status", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/profiling/status": { - "get": { - "tags": [ - "Profiling" - ], - "operationId": "StatusProfiling_Status", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProfilingStatusViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/partial-view/children": { - "get": { - "tags": [ - "Partial View" - ], - "operationId": "ChildrenPartialViewTree_Children", - "parameters": [ - { - "name": "path", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/partial-view/item": { - "get": { - "tags": [ - "Partial View" - ], - "operationId": "ItemsPartialViewTree_Items", - "parameters": [ - { - "name": "path", - "x-originalName": "paths", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/partial-view/root": { - "get": { - "tags": [ - "Partial View" - ], - "operationId": "RootPartialViewTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/models-builder/build": { - "post": { - "tags": [ - "Models Builder" - ], - "operationId": "BuildModelsBuilder_BuildModels", - "responses": { - "201": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/models-builder/dashboard": { - "get": { - "tags": [ - "Models Builder" - ], - "operationId": "GetModelsBuilder_GetDashboard", - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/models-builder/status": { - "get": { - "tags": [ - "Models Builder" - ], - "operationId": "StatusModelsBuilder_GetModelsOutOfDateStatus", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OutOfDateStatusViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/member-type/item": { - "get": { - "tags": [ - "Member Type" - ], - "operationId": "ItemsMemberTypeTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/member-type/root": { - "get": { - "tags": [ - "Member Type" - ], - "operationId": "RootMemberTypeTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/member-group/item": { - "get": { - "tags": [ - "Member Group" - ], - "operationId": "ItemsMemberGroupTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/member-group/root": { - "get": { - "tags": [ - "Member Group" - ], - "operationId": "RootMemberGroupTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/media/children": { - "get": { - "tags": [ - "Media" - ], - "operationId": "ChildrenMediaTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfContentTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/media/item": { - "get": { - "tags": [ - "Media" - ], - "operationId": "ItemsMediaTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ContentTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/media/root": { - "get": { - "tags": [ - "Media" - ], - "operationId": "RootMediaTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfContentTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/recycle-bin/media/children": { - "get": { - "tags": [ - "Media" - ], - "operationId": "ChildrenMediaRecycleBin_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/recycle-bin/media/root": { - "get": { - "tags": [ - "Media" - ], - "operationId": "RootMediaRecycleBin_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/media-type/children": { - "get": { - "tags": [ - "Media Type" - ], - "operationId": "ChildrenMediaTypeTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 }, { "name": "foldersOnly", @@ -2183,17 +81,16 @@ "schema": { "type": "boolean", "default": false - }, - "x-position": 4 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" + "$ref": "#/components/schemas/PagedFolderTreeItem" } } } @@ -2201,39 +98,33 @@ } } }, - "/umbraco/management/api/v1/tree/media-type/item": { + "/umbraco/management/api/v1/tree/data-type/item": { "get": { "tags": [ - "Media Type" + "Data Type" ], - "operationId": "ItemsMediaTypeTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "$ref": "#/components/schemas/FolderTreeItem" } } } @@ -2242,12 +133,11 @@ } } }, - "/umbraco/management/api/v1/tree/media-type/root": { + "/umbraco/management/api/v1/tree/data-type/root": { "get": { "tags": [ - "Media Type" + "Data Type" ], - "operationId": "RootMediaTypeTree_Root", "parameters": [ { "name": "skip", @@ -2256,8 +146,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -2266,8 +155,7 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } }, { "name": "foldersOnly", @@ -2275,965 +163,16 @@ "schema": { "type": "boolean", "default": false - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/language": { - "get": { - "tags": [ - "Language" - ], - "summary": "1\n Returns all currently configured languages.", - "operationId": "AllLanguage_GetAll", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfLanguageViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/language/{id}": { - "get": { - "tags": [ - "Language" - ], - "operationId": "ByIdLanguage_ById", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LanguageViewModel" - } - } - } - } - } - }, - "delete": { - "tags": [ - "Language" - ], - "summary": "Deletes a language with a given ID", - "operationId": "DeleteLanguage_Delete", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/language/create": { - "post": { - "tags": [ - "Language" - ], - "summary": "Creates or saves a language", - "operationId": "CreateLanguage_Create", - "requestBody": { - "x-name": "language", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LanguageViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "201": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/language/update": { - "put": { - "tags": [ - "Language" - ], - "summary": "Updates a language", - "operationId": "UpdateLanguage_Update", - "requestBody": { - "x-name": "language", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LanguageViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/install/settings": { - "get": { - "tags": [ - "Install" - ], - "operationId": "SettingsInstall_Settings", - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InstallSettingsViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/install/setup": { - "post": { - "tags": [ - "Install" - ], - "operationId": "SetupInstall_Setup", - "requestBody": { - "x-name": "installData", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InstallViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/install/validate-database": { - "post": { - "tags": [ - "Install" - ], - "operationId": "ValidateDatabaseInstall_ValidateDatabase", - "requestBody": { - "x-name": "viewModel", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DatabaseInstallViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/help": { - "get": { - "tags": [ - "Help" - ], - "operationId": "GetHelp_Get", - "parameters": [ - { - "name": "section", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "tree", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 2 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 3 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 4 - }, - { - "name": "baseUrl", - "in": "query", - "schema": { - "type": "string", - "default": "https://our.umbraco.com", - "nullable": true - }, - "x-position": 5 - } - ], - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfHelpPageViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/document/children": { - "get": { - "tags": [ - "Document" - ], - "operationId": "ChildrenDocumentTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 4 - }, - { - "name": "culture", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 5 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/document/item": { - "get": { - "tags": [ - "Document" - ], - "operationId": "ItemsDocumentTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 2 - }, - { - "name": "culture", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/document/root": { - "get": { - "tags": [ - "Document" - ], - "operationId": "RootDocumentTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 3 - }, - { - "name": "culture", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/recycle-bin/document/children": { - "get": { - "tags": [ - "Document" - ], - "operationId": "ChildrenDocumentRecycleBin_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/recycle-bin/document/root": { - "get": { - "tags": [ - "Document" - ], - "operationId": "RootDocumentRecycleBin_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/document-type/children": { - "get": { - "tags": [ - "Document Type" - ], - "operationId": "ChildrenDocumentTypeTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTypeTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/document-type/item": { - "get": { - "tags": [ - "Document Type" - ], - "operationId": "ItemsDocumentTypeTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentTypeTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/document-type/root": { - "get": { - "tags": [ - "Document Type" - ], - "operationId": "RootDocumentTypeTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTypeTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/document-blueprint/item": { - "get": { - "tags": [ - "Document Blueprint" - ], - "operationId": "ItemsDocumentBlueprintTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentBlueprintTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/document-blueprint/root": { - "get": { - "tags": [ - "Document Blueprint" - ], - "operationId": "RootDocumentBlueprintTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentBlueprintTreeItemViewModel" + "$ref": "#/components/schemas/PagedFolderTreeItem" } } } @@ -3246,8 +185,6 @@ "tags": [ "Dictionary" ], - "summary": "Retrieves a list with all dictionary items", - "operationId": "AllDictionary_All", "parameters": [ { "name": "skip", @@ -3255,8 +192,7 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 + } }, { "name": "take", @@ -3264,17 +200,68 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 2 + } } ], "responses": { "200": { - "description": "The IEnumerable`1.\n ", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDictionaryOverviewViewModel" + "$ref": "#/components/schemas/PagedDictionaryOverview" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/dictionary/{id}": { + "patch": { + "tags": [ + "Dictionary" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/JsonPatch" + } + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentResult" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3287,39 +274,34 @@ "tags": [ "Dictionary" ], - "summary": "Gets a dictionary item by guid", - "operationId": "ByIdDictionary_ByKey", "parameters": [ { "name": "key", "in": "path", "required": true, - "description": "The id.\n ", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } } ], "responses": { "200": { - "description": "The DictionaryDisplay. Returns a not found response when dictionary item does not exist\n ", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DictionaryViewModel" + "$ref": "#/components/schemas/Dictionary" } } } }, "404": { - "description": "", + "description": "Not Found", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3330,32 +312,27 @@ "tags": [ "Dictionary" ], - "summary": "Deletes a data type with a given ID", - "operationId": "DeleteDictionary_Delete", "parameters": [ { "name": "key", "in": "path", "required": true, - "description": "The key of the dictionary item to delete", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } } ], "responses": { "200": { - "description": "HttpResponseMessage\n " + "description": "Success" }, "404": { - "description": "", + "description": "Not Found", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3368,35 +345,28 @@ "tags": [ "Dictionary" ], - "summary": "Creates a new dictionary item", - "operationId": "CreateDictionary_Create", "requestBody": { - "x-name": "dictionaryViewModel", - "description": "The viewmodel to pass to the action", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DictionaryItemViewModel" + "$ref": "#/components/schemas/DictionaryItem" } } - }, - "required": true, - "x-position": 1 + } }, "responses": { "201": { - "description": "The HttpResponseMessage.\n ", + "description": "Created", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/CreatedResult" } } } }, "400": { - "description": "", + "description": "Bad Request", "content": { "application/json": { "schema": { @@ -3413,7 +383,6 @@ "tags": [ "Dictionary" ], - "operationId": "ExportDictionary_ExportDictionary", "parameters": [ { "name": "key", @@ -3421,9 +390,8 @@ "required": true, "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "includeChildren", @@ -3431,15 +399,14 @@ "schema": { "type": "boolean", "default": false - }, - "x-position": 2 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { - "application/octet-stream": { + "application/json": { "schema": { "type": "string", "format": "binary" @@ -3448,12 +415,11 @@ } }, "404": { - "description": "", + "description": "Not Found", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3466,106 +432,40 @@ "tags": [ "Dictionary" ], - "operationId": "ImportDictionary_ImportDictionary", "parameters": [ { "name": "file", "in": "query", "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 + "type": "string" + } }, { "name": "parentId", "in": "query", "schema": { "type": "integer", - "format": "int32", - "nullable": true - }, - "x-position": 2 + "format": "int32" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ContentResult" } } } }, "404": { - "description": "", + "description": "Not Found", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/dictionary/{id}": { - "patch": { - "tags": [ - "Dictionary" - ], - "operationId": "UpdateDictionary_Update", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - } - ], - "requestBody": { - "x-name": "updateViewModel", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/JsonPatchViewModel" - } - } - } - }, - "required": true, - "x-position": 2 - }, - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3578,36 +478,22 @@ "tags": [ "Dictionary" ], - "operationId": "UploadDictionary_Upload", "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary", - "nullable": true - } - } - } - } - } + "content": { } }, "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DictionaryImportViewModel" + "$ref": "#/components/schemas/DictionaryImport" } } } }, "400": { - "description": "", + "description": "Bad Request", "content": { "application/json": { "schema": { @@ -3624,16 +510,14 @@ "tags": [ "Dictionary" ], - "operationId": "ChildrenDictionaryTree_Children", "parameters": [ { "name": "parentKey", "in": "query", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "skip", @@ -3642,8 +526,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -3652,17 +535,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -3675,34 +557,28 @@ "tags": [ "Dictionary" ], - "operationId": "ItemsDictionaryTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "$ref": "#/components/schemas/FolderTreeItem" } } } @@ -3716,7 +592,6 @@ "tags": [ "Dictionary" ], - "operationId": "RootDictionaryTree_Root", "parameters": [ { "name": "skip", @@ -3725,8 +600,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -3735,17 +609,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -3753,99 +626,33 @@ } } }, - "/umbraco/management/api/v1/tree/data-type/children": { + "/umbraco/management/api/v1/tree/document-blueprint/item": { "get": { "tags": [ - "Data Type" + "Document Blueprint" ], - "operationId": "ChildrenDataTypeTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tree/data-type/item": { - "get": { - "tags": [ - "Data Type" - ], - "operationId": "ItemsDataTypeTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "$ref": "#/components/schemas/DocumentBlueprintTreeItem" } } } @@ -3854,12 +661,11 @@ } } }, - "/umbraco/management/api/v1/tree/data-type/root": { + "/umbraco/management/api/v1/tree/document-blueprint/root": { "get": { "tags": [ - "Data Type" + "Document Blueprint" ], - "operationId": "RootDataTypeTree_Root", "parameters": [ { "name": "skip", @@ -3868,8 +674,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -3878,26 +683,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" + "$ref": "#/components/schemas/PagedDocumentBlueprintTreeItem" } } } @@ -3905,22 +700,442 @@ } } }, - "/umbraco/management/api/v1/culture": { + "/umbraco/management/api/v1/tree/document-type/children": { "get": { "tags": [ - "Culture" + "Document Type" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedDocumentTypeTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document-type/item": { + "get": { + "tags": [ + "Document Type" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTypeTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document-type/root": { + "get": { + "tags": [ + "Document Type" ], - "summary": "Returns all cultures available for creating languages.", - "operationId": "AllCulture_GetAll", "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedDocumentTypeTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/recycle-bin/document/children": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRecycleBinItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/recycle-bin/document/root": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRecycleBinItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document/children": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "culture", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedDocumentTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document/item": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "culture", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document/root": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "culture", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedDocumentTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/help": { + "get": { + "tags": [ + "Help" + ], + "parameters": [ + { + "name": "section", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "tree", + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "skip", "in": "query", "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 + } }, { "name": "take", @@ -3928,17 +1143,2378 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 2 + } + }, + { + "name": "baseUrl", + "in": "query", + "schema": { + "type": "string", + "default": "https://our.umbraco.com" + } + } + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedHelpPage" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/install/settings": { + "get": { + "tags": [ + "Install" + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InstallSettings" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/install/setup": { + "post": { + "tags": [ + "Install" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Install" + } + } + } + }, + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/install/validate-database": { + "post": { + "tags": [ + "Install" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DatabaseInstall" + } + } + } + }, + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/language": { + "get": { + "tags": [ + "Language" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfCultureViewModel" + "$ref": "#/components/schemas/PagedLanguage" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/language/{id}": { + "get": { + "tags": [ + "Language" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Language" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Language" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/language/create": { + "post": { + "tags": [ + "Language" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Language" + } + } + } + }, + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "201": { + "description": "Created" + } + } + } + }, + "/umbraco/management/api/v1/language/update": { + "put": { + "tags": [ + "Language" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Language" + } + } + } + }, + "responses": { + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/tree/media-type/children": { + "get": { + "tags": [ + "Media Type" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFolderTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media-type/item": { + "get": { + "tags": [ + "Media Type" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FolderTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media-type/root": { + "get": { + "tags": [ + "Media Type" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFolderTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/recycle-bin/media/children": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRecycleBinItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/recycle-bin/media/root": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRecycleBinItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media/children": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedContentTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media/item": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContentTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media/root": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedContentTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/member-group/item": { + "get": { + "tags": [ + "Member Group" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/member-group/root": { + "get": { + "tags": [ + "Member Group" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/member-type/item": { + "get": { + "tags": [ + "Member Type" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/member-type/root": { + "get": { + "tags": [ + "Member Type" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/models-builder/build": { + "post": { + "tags": [ + "Models Builder" + ], + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatedResult" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/models-builder/dashboard": { + "get": { + "tags": [ + "Models Builder" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelsBuilder" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/models-builder/status": { + "get": { + "tags": [ + "Models Builder" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OutOfDateStatus" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/partial-view/children": { + "get": { + "tags": [ + "Partial View" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/partial-view/item": { + "get": { + "tags": [ + "Partial View" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/partial-view/root": { + "get": { + "tags": [ + "Partial View" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/profiling/status": { + "get": { + "tags": [ + "Profiling" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfilingStatus" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/published-cache/collect": { + "post": { + "tags": [ + "Published Cache" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/published-cache/rebuild": { + "post": { + "tags": [ + "Published Cache" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/published-cache/reload": { + "post": { + "tags": [ + "Published Cache" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/published-cache/status": { + "get": { + "tags": [ + "Published Cache" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/relation-type/item": { + "get": { + "tags": [ + "Relation Type" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FolderTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/relation-type/root": { + "get": { + "tags": [ + "Relation Type" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/relation/{id}": { + "get": { + "tags": [ + "Relation" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Relation" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/relation/child-relation/{childId}": { + "get": { + "tags": [ + "Relation" + ], + "parameters": [ + { + "name": "childId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "relationTypeAlias", + "in": "query", + "schema": { + "type": "string", + "default": "" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRelation" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/script/children": { + "get": { + "tags": [ + "Script" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/script/item": { + "get": { + "tags": [ + "Script" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/script/root": { + "get": { + "tags": [ + "Script" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/index": { + "get": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedIndex" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/index/{indexName}": { + "get": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "indexName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Index" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/index/{indexName}/rebuild": { + "post": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "indexName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OkResult" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/searcher": { + "get": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedSearcher" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/searcher/{searcherName}/search": { + "get": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "searcherName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedPaged" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/security/back-office/authorize": { + "get": { + "tags": [ + "Security" + ], + "responses": { + "200": { + "description": "Success" + } + } + }, + "post": { + "tags": [ + "Security" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/server/status": { + "get": { + "tags": [ + "Server" + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerStatus" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/server/version": { + "get": { + "tags": [ + "Server" + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Version" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/static-file/children": { + "get": { + "tags": [ + "Static File" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/static-file/item": { + "get": { + "tags": [ + "Static File" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/static-file/root": { + "get": { + "tags": [ + "Static File" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/stylesheet/children": { + "get": { + "tags": [ + "Stylesheet" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/stylesheet/item": { + "get": { + "tags": [ + "Stylesheet" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/stylesheet/root": { + "get": { + "tags": [ + "Stylesheet" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/telemetry": { + "get": { + "tags": [ + "Telemetry" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedTelemetry" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/telemetry/level": { + "get": { + "tags": [ + "Telemetry" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Telemetry" + } + } + } + } + } + }, + "post": { + "tags": [ + "Telemetry" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Telemetry" + } + } + } + }, + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/tree/template/children": { + "get": { + "tags": [ + "Template" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/template/item": { + "get": { + "tags": [ + "Template" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/template/root": { + "get": { + "tags": [ + "Template" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tracked-reference/{id}": { + "get": { + "tags": [ + "Tracked Reference" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRelationItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tracked-reference/descendants/{parentId}": { + "get": { + "tags": [ + "Tracked Reference" + ], + "parameters": [ + { + "name": "parentId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRelationItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tracked-reference/item": { + "get": { + "tags": [ + "Tracked Reference" + ], + "parameters": [ + { + "name": "ids", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRelationItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/upgrade/authorize": { + "post": { + "tags": [ + "Upgrade" + ], + "responses": { + "200": { + "description": "Success" + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/upgrade/settings": { + "get": { + "tags": [ + "Upgrade" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpgradeSettings" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" } } } @@ -3949,11 +3525,2317 @@ }, "components": { "schemas": { + "Assembly": { + "type": "object", + "properties": { + "definedTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TypeInfo" + }, + "nullable": true, + "readOnly": true + }, + "exportedTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "codeBase": { + "type": "string", + "nullable": true, + "readOnly": true, + "deprecated": true + }, + "entryPoint": { + "$ref": "#/components/schemas/MethodInfo" + }, + "fullName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "imageRuntimeVersion": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "isDynamic": { + "type": "boolean", + "readOnly": true + }, + "location": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "reflectionOnly": { + "type": "boolean", + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "isFullyTrusted": { + "type": "boolean", + "readOnly": true + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "escapedCodeBase": { + "type": "string", + "nullable": true, + "readOnly": true, + "deprecated": true + }, + "manifestModule": { + "$ref": "#/components/schemas/Module" + }, + "modules": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Module" + }, + "nullable": true, + "readOnly": true + }, + "globalAssemblyCache": { + "type": "boolean", + "readOnly": true, + "deprecated": true + }, + "hostContext": { + "type": "integer", + "format": "int64", + "readOnly": true + }, + "securityRuleSet": { + "$ref": "#/components/schemas/SecurityRuleSet" + } + }, + "additionalProperties": false + }, + "BackOfficeNotification": { + "type": "object", + "properties": { + "header": { + "type": "string", + "nullable": true + }, + "message": { + "type": "string", + "nullable": true + }, + "notificationType": { + "$ref": "#/components/schemas/NotificationStyle" + } + }, + "additionalProperties": false + }, + "CallingConventions": { + "enum": [ + "Standard", + "VarArgs", + "Any", + "HasThis", + "ExplicitThis" + ], + "type": "integer", + "format": "int32" + }, + "ConsentLevel": { + "type": "object", + "properties": { + "level": { + "$ref": "#/components/schemas/TelemetryLevel" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ConstructorInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "attributes": { + "$ref": "#/components/schemas/MethodAttributes" + }, + "methodImplementationFlags": { + "$ref": "#/components/schemas/MethodImplAttributes" + }, + "callingConvention": { + "$ref": "#/components/schemas/CallingConventions" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isConstructor": { + "type": "boolean", + "readOnly": true + }, + "isFinal": { + "type": "boolean", + "readOnly": true + }, + "isHideBySig": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isVirtual": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodDefinition": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "methodHandle": { + "$ref": "#/components/schemas/RuntimeMethodHandle" + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + } + }, + "additionalProperties": false + }, + "ContentApp": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "alias": { + "type": "string", + "nullable": true + }, + "weight": { + "type": "integer", + "format": "int32" + }, + "icon": { + "type": "string", + "nullable": true + }, + "view": { + "type": "string", + "nullable": true + }, + "viewModel": { + "nullable": true + }, + "active": { + "type": "boolean" + }, + "badge": { + "$ref": "#/components/schemas/ContentAppBadge" + } + }, + "additionalProperties": false + }, + "ContentAppBadge": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "format": "int32" + }, + "type": { + "$ref": "#/components/schemas/ContentAppBadgeType" + } + }, + "additionalProperties": false + }, + "ContentAppBadgeType": { + "enum": [ + "default", + "warning", + "alert" + ], + "type": "integer", + "format": "int32" + }, + "ContentResult": { + "type": "object", + "properties": { + "content": { + "type": "string", + "nullable": true + }, + "contentType": { + "type": "string", + "nullable": true + }, + "statusCode": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "ContentTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "noAccess": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CreatedResult": { + "type": "object", + "properties": { + "value": { + "nullable": true + }, + "formatters": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IOutputFormatter" + }, + "nullable": true + }, + "contentTypes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "declaredType": { + "$ref": "#/components/schemas/Type" + }, + "statusCode": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "location": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "Culture": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "englishName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CustomAttributeData": { + "type": "object", + "properties": { + "attributeType": { + "$ref": "#/components/schemas/Type" + }, + "constructor": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "constructorArguments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeTypedArgument" + }, + "nullable": true, + "readOnly": true + }, + "namedArguments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeNamedArgument" + }, + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "CustomAttributeNamedArgument": { + "type": "object", + "properties": { + "memberInfo": { + "$ref": "#/components/schemas/MemberInfo" + }, + "typedValue": { + "$ref": "#/components/schemas/CustomAttributeTypedArgument" + }, + "memberName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "isField": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "CustomAttributeTypedArgument": { + "type": "object", + "properties": { + "argumentType": { + "$ref": "#/components/schemas/Type" + }, + "value": { + "nullable": true + } + }, + "additionalProperties": false + }, + "DatabaseInstall": { + "required": [ + "id", + "providerName" + ], + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "providerName": { + "minLength": 1, + "type": "string" + }, + "server": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "username": { + "type": "string", + "nullable": true + }, + "password": { + "type": "string", + "nullable": true + }, + "useIntegratedAuthentication": { + "type": "boolean" + }, + "connectionString": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DatabaseSettings": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "displayName": { + "type": "string", + "nullable": true + }, + "defaultDatabaseName": { + "type": "string", + "nullable": true + }, + "providerName": { + "type": "string", + "nullable": true + }, + "isConfigured": { + "type": "boolean" + }, + "requiresServer": { + "type": "boolean" + }, + "serverPlaceholder": { + "type": "string", + "nullable": true + }, + "requiresCredentials": { + "type": "boolean" + }, + "supportsIntegratedAuthentication": { + "type": "boolean" + }, + "requiresConnectionTest": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "Dictionary": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "parentId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "translations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryTranslation" + }, + "nullable": true + }, + "contentApps": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContentApp" + }, + "nullable": true + }, + "notifications": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BackOfficeNotification" + }, + "nullable": true, + "readOnly": true + }, + "name": { + "minLength": 1, + "type": "string" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "path": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DictionaryImport": { + "type": "object", + "properties": { + "dictionaryItems": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryItemsImport" + }, + "nullable": true + }, + "tempFileName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DictionaryItem": { + "type": "object", + "properties": { + "parentId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "key": { + "type": "string", + "format": "uuid" + } + }, + "additionalProperties": false + }, + "DictionaryItemsImport": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "level": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "DictionaryOverview": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "key": { + "type": "string", + "format": "uuid" + }, + "level": { + "type": "integer", + "format": "int32" + }, + "translations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryTranslationOverview" + }, + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "DictionaryTranslation": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "displayName": { + "type": "string", + "nullable": true + }, + "isoCode": { + "type": "string", + "nullable": true + }, + "translation": { + "type": "string", + "nullable": true + }, + "languageId": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "DictionaryTranslationOverview": { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "nullable": true + }, + "hasTranslation": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DocumentBlueprintTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "documentTypeKey": { + "type": "string", + "format": "uuid" + }, + "documentTypeAlias": { + "type": "string", + "nullable": true + }, + "documentTypeName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DocumentTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "noAccess": { + "type": "boolean" + }, + "isProtected": { + "type": "boolean" + }, + "isPublished": { + "type": "boolean" + }, + "isEdited": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DocumentTypeTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "isFolder": { + "type": "boolean" + }, + "isElement": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "EntityTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + } + }, + "additionalProperties": false + }, + "EventAttributes": { + "enum": [ + "None", + "SpecialName", + "RTSpecialName", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "EventInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "attributes": { + "$ref": "#/components/schemas/EventAttributes" + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "addMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "removeMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "raiseMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "isMulticast": { + "type": "boolean", + "readOnly": true + }, + "eventHandlerType": { + "$ref": "#/components/schemas/Type" + } + }, + "additionalProperties": false + }, + "Field": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "values": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "FieldAttributes": { + "enum": [ + "PrivateScope", + "Private", + "FamANDAssem", + "Assembly", + "Family", + "FamORAssem", + "Public", + "FieldAccessMask", + "Static", + "InitOnly", + "Literal", + "NotSerialized", + "HasFieldRVA", + "SpecialName", + "RTSpecialName", + "HasFieldMarshal", + "PinvokeImpl", + "HasDefault", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "FieldInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "attributes": { + "$ref": "#/components/schemas/FieldAttributes" + }, + "fieldType": { + "$ref": "#/components/schemas/Type" + }, + "isInitOnly": { + "type": "boolean", + "readOnly": true + }, + "isLiteral": { + "type": "boolean", + "readOnly": true + }, + "isNotSerialized": { + "type": "boolean", + "readOnly": true + }, + "isPinvokeImpl": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "fieldHandle": { + "$ref": "#/components/schemas/RuntimeFieldHandle" + } + }, + "additionalProperties": false + }, + "FileSystemTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "path": { + "type": "string", + "nullable": true + }, + "isFolder": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "FolderTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "isFolder": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "GenericParameterAttributes": { + "enum": [ + "None", + "Covariant", + "Contravariant", + "VarianceMask", + "ReferenceTypeConstraint", + "NotNullableValueTypeConstraint", + "DefaultConstructorConstraint", + "SpecialConstraintMask" + ], + "type": "integer", + "format": "int32" + }, + "HelpPage": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ICustomAttributeProvider": { + "type": "object", + "additionalProperties": false + }, + "IOutputFormatter": { + "type": "object", + "additionalProperties": false + }, + "Index": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "healthStatus": { + "type": "string", + "nullable": true + }, + "isHealthy": { + "type": "boolean", + "readOnly": true + }, + "canRebuild": { + "type": "boolean" + }, + "searcherName": { + "type": "string", + "nullable": true + }, + "documentCount": { + "type": "integer", + "format": "int64" + }, + "fieldCount": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "Install": { + "required": [ + "database", + "user" + ], + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/UserInstall" + }, + "database": { + "$ref": "#/components/schemas/DatabaseInstall" + }, + "telemetryLevel": { + "$ref": "#/components/schemas/TelemetryLevel" + } + }, + "additionalProperties": false + }, + "InstallSettings": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/UserSettings" + }, + "databases": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DatabaseSettings" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "IntPtr": { + "type": "object", + "additionalProperties": false + }, + "JsonPatch": { + "type": "object", + "properties": { + "op": { + "type": "string", + "nullable": true + }, + "path": { + "type": "string", + "nullable": true + }, + "value": { + "nullable": true + } + }, + "additionalProperties": false + }, + "Language": { + "required": [ + "isoCode" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "isoCode": { + "minLength": 1, + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "isDefault": { + "type": "boolean" + }, + "isMandatory": { + "type": "boolean" + }, + "fallbackLanguageId": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "LayoutKind": { + "enum": [ + "Sequential", + "Explicit", + "Auto" + ], + "type": "integer", + "format": "int32" + }, + "MemberInfo": { + "type": "object", + "properties": { + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "MemberTypes": { + "enum": [ + "Constructor", + "Event", + "Field", + "Method", + "Property", + "TypeInfo", + "Custom", + "NestedType", + "All" + ], + "type": "integer", + "format": "int32" + }, + "MethodAttributes": { + "enum": [ + "ReuseSlot", + "PrivateScope", + "Private", + "FamANDAssem", + "Assembly", + "Family", + "FamORAssem", + "Public", + "MemberAccessMask", + "UnmanagedExport", + "Static", + "Final", + "Virtual", + "HideBySig", + "NewSlot", + "VtableLayoutMask", + "CheckAccessOnOverride", + "Abstract", + "SpecialName", + "RTSpecialName", + "PinvokeImpl", + "HasSecurity", + "RequireSecObject", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "MethodBase": { + "type": "object", + "properties": { + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "attributes": { + "$ref": "#/components/schemas/MethodAttributes" + }, + "methodImplementationFlags": { + "$ref": "#/components/schemas/MethodImplAttributes" + }, + "callingConvention": { + "$ref": "#/components/schemas/CallingConventions" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isConstructor": { + "type": "boolean", + "readOnly": true + }, + "isFinal": { + "type": "boolean", + "readOnly": true + }, + "isHideBySig": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isVirtual": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodDefinition": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "methodHandle": { + "$ref": "#/components/schemas/RuntimeMethodHandle" + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "MethodImplAttributes": { + "enum": [ + "IL", + "Managed", + "Native", + "OPTIL", + "Runtime", + "CodeTypeMask", + "Unmanaged", + "ManagedMask", + "NoInlining", + "ForwardRef", + "Synchronized", + "NoOptimization", + "PreserveSig", + "AggressiveInlining", + "AggressiveOptimization", + "InternalCall", + "MaxMethodImplVal" + ], + "type": "integer", + "format": "int32" + }, + "MethodInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "attributes": { + "$ref": "#/components/schemas/MethodAttributes" + }, + "methodImplementationFlags": { + "$ref": "#/components/schemas/MethodImplAttributes" + }, + "callingConvention": { + "$ref": "#/components/schemas/CallingConventions" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isConstructor": { + "type": "boolean", + "readOnly": true + }, + "isFinal": { + "type": "boolean", + "readOnly": true + }, + "isHideBySig": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isVirtual": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodDefinition": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "methodHandle": { + "$ref": "#/components/schemas/RuntimeMethodHandle" + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "returnParameter": { + "$ref": "#/components/schemas/ParameterInfo" + }, + "returnType": { + "$ref": "#/components/schemas/Type" + }, + "returnTypeCustomAttributes": { + "$ref": "#/components/schemas/ICustomAttributeProvider" + } + }, + "additionalProperties": false + }, + "ModelsBuilder": { + "type": "object", + "properties": { + "mode": { + "$ref": "#/components/schemas/ModelsMode" + }, + "canGenerate": { + "type": "boolean" + }, + "outOfDateModels": { + "type": "boolean" + }, + "lastError": { + "type": "string", + "nullable": true + }, + "version": { + "type": "string", + "nullable": true + }, + "modelsNamespace": { + "type": "string", + "nullable": true + }, + "trackingOutOfDateModels": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "ModelsMode": { + "enum": [ + "Nothing", + "InMemoryAuto", + "SourceCodeManual", + "SourceCodeAuto" + ], + "type": "integer", + "format": "int32" + }, + "Module": { + "type": "object", + "properties": { + "assembly": { + "$ref": "#/components/schemas/Assembly" + }, + "fullyQualifiedName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "mdStreamVersion": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "moduleVersionId": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "scopeName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "moduleHandle": { + "$ref": "#/components/schemas/ModuleHandle" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "ModuleHandle": { + "type": "object", + "properties": { + "mdStreamVersion": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "NotFoundResult": { + "type": "object", + "properties": { + "statusCode": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "NotificationStyle": { + "enum": [ + "Save", + "Info", + "Error", + "Success", + "Warning" + ], + "type": "integer", + "format": "int32" + }, + "OkResult": { + "type": "object", + "properties": { + "statusCode": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "OutOfDateStatus": { + "type": "object", + "properties": { + "status": { + "$ref": "#/components/schemas/OutOfDateType" + } + }, + "additionalProperties": false + }, + "OutOfDateType": { + "enum": [ + "OutOfDate", + "Current", + "Unknown" + ], + "type": "integer", + "format": "int32" + }, + "PagedContentTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContentTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedCulture": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Culture" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDictionaryOverview": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryOverview" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDocumentBlueprintTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentBlueprintTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDocumentTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDocumentTypeTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTypeTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedEntityTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedFileSystemTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedFolderTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FolderTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedHelpPage": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HelpPage" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedIndex": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Index" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedLanguage": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Language" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedPaged": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PagedSearchResult" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedRecycleBinItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecycleBinItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedRelation": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Relation" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedRelationItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedSearchResult": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SearchResult" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedSearcher": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Searcher" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedTelemetry": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Telemetry" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ParameterAttributes": { + "enum": [ + "None", + "In", + "Out", + "Lcid", + "Retval", + "Optional", + "HasDefault", + "HasFieldMarshal", + "Reserved3", + "Reserved4", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "ParameterInfo": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/components/schemas/ParameterAttributes" + }, + "member": { + "$ref": "#/components/schemas/MemberInfo" + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "parameterType": { + "$ref": "#/components/schemas/Type" + }, + "position": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "isIn": { + "type": "boolean", + "readOnly": true + }, + "isLcid": { + "type": "boolean", + "readOnly": true + }, + "isOptional": { + "type": "boolean", + "readOnly": true + }, + "isOut": { + "type": "boolean", + "readOnly": true + }, + "isRetval": { + "type": "boolean", + "readOnly": true + }, + "defaultValue": { + "nullable": true, + "readOnly": true + }, + "rawDefaultValue": { + "nullable": true, + "readOnly": true + }, + "hasDefaultValue": { + "type": "boolean", + "readOnly": true + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, "ProblemDetails": { "type": "object", - "additionalProperties": { - "nullable": true - }, "properties": { "type": { "type": "string", @@ -3976,52 +5858,165 @@ "type": "string", "nullable": true } - } + }, + "additionalProperties": { } }, - "UpgradeSettingsViewModel": { + "ProfilingStatus": { "type": "object", - "additionalProperties": false, "properties": { - "currentState": { - "type": "string" - }, - "newState": { - "type": "string" - }, - "newVersion": { - "type": "string" - }, - "oldVersion": { - "type": "string" - }, - "reportUrl": { - "type": "string" + "enabled": { + "type": "boolean" } - } + }, + "additionalProperties": false }, - "PagedViewModelOfRelationItemViewModel": { + "PropertyAttributes": { + "enum": [ + "None", + "SpecialName", + "RTSpecialName", + "HasDefault", + "Reserved2", + "Reserved3", + "Reserved4", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "PropertyInfo": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" + "name": { + "type": "string", + "nullable": true, + "readOnly": true }, - "items": { + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { "type": "array", "items": { - "$ref": "#/components/schemas/RelationItemViewModel" - } + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "propertyType": { + "$ref": "#/components/schemas/Type" + }, + "attributes": { + "$ref": "#/components/schemas/PropertyAttributes" + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "canRead": { + "type": "boolean", + "readOnly": true + }, + "canWrite": { + "type": "boolean", + "readOnly": true + }, + "getMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "setMethod": { + "$ref": "#/components/schemas/MethodInfo" } - } + }, + "additionalProperties": false }, - "RelationItemViewModel": { + "RecycleBinItem": { + "type": "object", + "properties": { + "key": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + } + }, + "additionalProperties": false + }, + "Relation": { + "type": "object", + "properties": { + "parentId": { + "type": "integer", + "format": "int32" + }, + "parentName": { + "type": "string", + "nullable": true + }, + "childId": { + "type": "integer", + "format": "int32" + }, + "childName": { + "type": "string", + "nullable": true + }, + "createDate": { + "type": "string", + "format": "date-time" + }, + "comment": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "RelationItem": { "type": "object", - "additionalProperties": false, "properties": { "nodeKey": { "type": "string", - "format": "guid" + "format": "uuid" }, "nodeName": { "type": "string", @@ -4053,161 +6048,19 @@ "relationTypeIsDependency": { "type": "boolean" } - } + }, + "additionalProperties": false }, - "PagedViewModelOfEntityTreeItemViewModel": { + "RuntimeFieldHandle": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } + "value": { + "$ref": "#/components/schemas/IntPtr" } - } - }, - "EntityTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/TreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string", - "format": "guid" - }, - "isContainer": { - "type": "boolean" - }, - "parentKey": { - "type": "string", - "format": "guid", - "nullable": true - } - } - } - ] - }, - "TreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "hasChildren": { - "type": "boolean" - } - } - }, - "PagedViewModelOfTelemetryLevelViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TelemetryLevelViewModel" - } - } - } - }, - "TelemetryLevelViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "telemetryLevel": { - "$ref": "#/components/schemas/TelemetryLevel" - } - } - }, - "TelemetryLevel": { - "type": "string", - "description": "", - "x-enumNames": [ - "Minimal", - "Basic", - "Detailed" - ], - "enum": [ - "Minimal", - "Basic", - "Detailed" - ] - }, - "PagedViewModelOfFileSystemTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - }, - "FileSystemTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/TreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "path": { - "type": "string" - }, - "isFolder": { - "type": "boolean" - } - } - } - ] - }, - "ServerStatusViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "serverStatus": { - "$ref": "#/components/schemas/RuntimeLevel" - } - } + }, + "additionalProperties": false }, "RuntimeLevel": { - "type": "string", - "description": "Describes the levels in which the runtime can run.\n ", - "x-enumNames": [ - "Unknown", - "Boot", - "Install", - "Upgrade", - "Run", - "BootFailed" - ], "enum": [ "Unknown", "Boot", @@ -4215,126 +6068,34 @@ "Upgrade", "Run", "BootFailed" - ] + ], + "type": "integer", + "format": "int32" }, - "VersionViewModel": { + "RuntimeMethodHandle": { "type": "object", - "additionalProperties": false, "properties": { - "version": { - "type": "string" + "value": { + "$ref": "#/components/schemas/IntPtr" } - } + }, + "additionalProperties": false }, - "IndexViewModel": { + "RuntimeTypeHandle": { "type": "object", - "additionalProperties": false, "properties": { - "name": { - "type": "string" - }, - "healthStatus": { - "type": "string", - "nullable": true - }, - "isHealthy": { - "type": "boolean" - }, - "canRebuild": { - "type": "boolean" - }, - "searcherName": { - "type": "string" - }, - "documentCount": { - "type": "integer", - "format": "int64" - }, - "fieldCount": { - "type": "integer", - "format": "int32" + "value": { + "$ref": "#/components/schemas/IntPtr" } - } + }, + "additionalProperties": false }, - "PagedViewModelOfIndexViewModel": { + "SearchResult": { "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IndexViewModel" - } - } - } - }, - "PagedViewModelOfSearcherViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearcherViewModel" - } - } - } - }, - "SearcherViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - } - } - }, - "PagedViewModelOfPagedViewModelOfSearchResultViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PagedViewModelOfSearchResultViewModel" - } - } - } - }, - "PagedViewModelOfSearchResultViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearchResultViewModel" - } - } - } - }, - "SearchResultViewModel": { - "type": "object", - "additionalProperties": false, "properties": { "id": { - "type": "string" + "type": "string", + "nullable": true }, "score": { "type": "number", @@ -4342,297 +6103,830 @@ }, "fieldCount": { "type": "integer", - "format": "int32" + "format": "int32", + "readOnly": true }, "fields": { "type": "array", "items": { - "$ref": "#/components/schemas/FieldViewModel" - } + "$ref": "#/components/schemas/Field" + }, + "nullable": true } - } + }, + "additionalProperties": false }, - "FieldViewModel": { + "Searcher": { "type": "object", - "additionalProperties": false, "properties": { "name": { - "type": "string" - }, - "values": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "PagedViewModelOfRelationViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationViewModel" - } - } - } - }, - "RelationViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "parentId": { - "type": "integer", - "readOnly": true, - "description": "Gets or sets the Parent Id of the Relation (Source).\n ", - "format": "int32" - }, - "parentName": { "type": "string", - "readOnly": true, - "description": "Gets or sets the Parent Name of the relation (Source).\n ", - "nullable": true - }, - "childId": { - "type": "integer", - "readOnly": true, - "description": "Gets or sets the Child Id of the Relation (Destination).\n ", - "format": "int32" - }, - "childName": { - "type": "string", - "readOnly": true, - "description": "Gets or sets the Child Name of the relation (Destination).\n ", - "nullable": true - }, - "createDate": { - "type": "string", - "readOnly": true, - "description": "Gets or sets the date when the Relation was created.\n ", - "format": "date-time" - }, - "comment": { - "type": "string", - "readOnly": true, - "description": "Gets or sets a comment for the Relation.\n ", "nullable": true } - } + }, + "additionalProperties": false }, - "FolderTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "isFolder": { - "type": "boolean" - } - } - } - ] - }, - "ProfilingStatusViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "OutOfDateStatusViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "status": { - "$ref": "#/components/schemas/OutOfDateType" - } - } - }, - "OutOfDateType": { - "type": "integer", - "description": "", - "x-enumNames": [ - "OutOfDate", - "Current", - "Unknown" - ], + "SecurityRuleSet": { "enum": [ - 0, - 1, - 100 - ] - }, - "PagedViewModelOfContentTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ContentTreeItemViewModel" - } - } - } - }, - "ContentTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "noAccess": { - "type": "boolean" - } - } - } - ] - }, - "PagedViewModelOfRecycleBinItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RecycleBinItemViewModel" - } - } - } - }, - "RecycleBinItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string", - "format": "guid" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "hasChildren": { - "type": "boolean" - }, - "isContainer": { - "type": "boolean" - }, - "parentKey": { - "type": "string", - "format": "guid", - "nullable": true - } - } - }, - "PagedViewModelOfFolderTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" - } - } - } - }, - "PagedViewModelOfLanguageViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LanguageViewModel" - } - } - } - }, - "LanguageViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "isoCode" + "None", + "Level1", + "Level2" ], + "type": "integer", + "format": "int32" + }, + "ServerStatus": { + "type": "object", "properties": { - "id": { - "type": "integer", - "format": "int32" - }, - "isoCode": { - "type": "string", - "minLength": 1 + "serverStatus": { + "$ref": "#/components/schemas/RuntimeLevel" + } + }, + "additionalProperties": false + }, + "StructLayoutAttribute": { + "type": "object", + "properties": { + "typeId": { + "nullable": true, + "readOnly": true }, + "value": { + "$ref": "#/components/schemas/LayoutKind" + } + }, + "additionalProperties": false + }, + "Telemetry": { + "type": "object", + "properties": { + "telemetryLevel": { + "$ref": "#/components/schemas/TelemetryLevel" + } + }, + "additionalProperties": false + }, + "TelemetryLevel": { + "enum": [ + "Minimal", + "Basic", + "Detailed" + ], + "type": "integer", + "format": "int32" + }, + "Type": { + "type": "object", + "properties": { "name": { "type": "string", - "nullable": true + "nullable": true, + "readOnly": true }, - "isDefault": { - "type": "boolean" + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true }, - "isMandatory": { - "type": "boolean" + "isCollectible": { + "type": "boolean", + "readOnly": true }, - "fallbackLanguageId": { + "metadataToken": { "type": "integer", "format": "int32", - "nullable": true - } - } - }, - "InstallSettingsViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "user": { - "$ref": "#/components/schemas/UserSettingsViewModel" + "readOnly": true }, - "databases": { + "isInterface": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "namespace": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assemblyQualifiedName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "fullName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assembly": { + "$ref": "#/components/schemas/Assembly" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "isNested": { + "type": "boolean", + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "declaringMethod": { + "$ref": "#/components/schemas/MethodBase" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "underlyingSystemType": { + "$ref": "#/components/schemas/Type" + }, + "isTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isArray": { + "type": "boolean", + "readOnly": true + }, + "isByRef": { + "type": "boolean", + "readOnly": true + }, + "isPointer": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isSZArray": { + "type": "boolean", + "readOnly": true + }, + "isVariableBoundArray": { + "type": "boolean", + "readOnly": true + }, + "isByRefLike": { + "type": "boolean", + "readOnly": true + }, + "hasElementType": { + "type": "boolean", + "readOnly": true + }, + "genericTypeArguments": { "type": "array", "items": { - "$ref": "#/components/schemas/DatabaseSettingsViewModel" - } + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "genericParameterPosition": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "genericParameterAttributes": { + "$ref": "#/components/schemas/GenericParameterAttributes" + }, + "attributes": { + "$ref": "#/components/schemas/TypeAttributes" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isImport": { + "type": "boolean", + "readOnly": true + }, + "isSealed": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isClass": { + "type": "boolean", + "readOnly": true + }, + "isNestedAssembly": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamANDAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamily": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamORAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedPrivate": { + "type": "boolean", + "readOnly": true + }, + "isNestedPublic": { + "type": "boolean", + "readOnly": true + }, + "isNotPublic": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isAutoLayout": { + "type": "boolean", + "readOnly": true + }, + "isExplicitLayout": { + "type": "boolean", + "readOnly": true + }, + "isLayoutSequential": { + "type": "boolean", + "readOnly": true + }, + "isAnsiClass": { + "type": "boolean", + "readOnly": true + }, + "isAutoClass": { + "type": "boolean", + "readOnly": true + }, + "isUnicodeClass": { + "type": "boolean", + "readOnly": true + }, + "isCOMObject": { + "type": "boolean", + "readOnly": true + }, + "isContextful": { + "type": "boolean", + "readOnly": true + }, + "isEnum": { + "type": "boolean", + "readOnly": true + }, + "isMarshalByRef": { + "type": "boolean", + "readOnly": true + }, + "isPrimitive": { + "type": "boolean", + "readOnly": true + }, + "isValueType": { + "type": "boolean", + "readOnly": true + }, + "isSignatureType": { + "type": "boolean", + "readOnly": true + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "structLayoutAttribute": { + "$ref": "#/components/schemas/StructLayoutAttribute" + }, + "typeInitializer": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "typeHandle": { + "$ref": "#/components/schemas/RuntimeTypeHandle" + }, + "guid": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "baseType": { + "$ref": "#/components/schemas/Type" + }, + "isSerializable": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "isVisible": { + "type": "boolean", + "readOnly": true } - } + }, + "additionalProperties": false }, - "UserSettingsViewModel": { + "TypeAttributes": { + "enum": [ + "NotPublic", + "AutoLayout", + "AnsiClass", + "Class", + "Public", + "NestedPublic", + "NestedPrivate", + "NestedFamily", + "NestedAssembly", + "NestedFamANDAssem", + "NestedFamORAssem", + "VisibilityMask", + "SequentialLayout", + "ExplicitLayout", + "LayoutMask", + "Interface", + "ClassSemanticsMask", + "Abstract", + "Sealed", + "SpecialName", + "RTSpecialName", + "Import", + "Serializable", + "WindowsRuntime", + "UnicodeClass", + "AutoClass", + "CustomFormatClass", + "StringFormatMask", + "HasSecurity", + "ReservedMask", + "BeforeFieldInit", + "CustomFormatMask" + ], + "type": "integer", + "format": "int32" + }, + "TypeInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "isInterface": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "namespace": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assemblyQualifiedName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "fullName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assembly": { + "$ref": "#/components/schemas/Assembly" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "isNested": { + "type": "boolean", + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "declaringMethod": { + "$ref": "#/components/schemas/MethodBase" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "underlyingSystemType": { + "$ref": "#/components/schemas/Type" + }, + "isTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isArray": { + "type": "boolean", + "readOnly": true + }, + "isByRef": { + "type": "boolean", + "readOnly": true + }, + "isPointer": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isSZArray": { + "type": "boolean", + "readOnly": true + }, + "isVariableBoundArray": { + "type": "boolean", + "readOnly": true + }, + "isByRefLike": { + "type": "boolean", + "readOnly": true + }, + "hasElementType": { + "type": "boolean", + "readOnly": true + }, + "genericTypeArguments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "genericParameterPosition": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "genericParameterAttributes": { + "$ref": "#/components/schemas/GenericParameterAttributes" + }, + "attributes": { + "$ref": "#/components/schemas/TypeAttributes" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isImport": { + "type": "boolean", + "readOnly": true + }, + "isSealed": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isClass": { + "type": "boolean", + "readOnly": true + }, + "isNestedAssembly": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamANDAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamily": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamORAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedPrivate": { + "type": "boolean", + "readOnly": true + }, + "isNestedPublic": { + "type": "boolean", + "readOnly": true + }, + "isNotPublic": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isAutoLayout": { + "type": "boolean", + "readOnly": true + }, + "isExplicitLayout": { + "type": "boolean", + "readOnly": true + }, + "isLayoutSequential": { + "type": "boolean", + "readOnly": true + }, + "isAnsiClass": { + "type": "boolean", + "readOnly": true + }, + "isAutoClass": { + "type": "boolean", + "readOnly": true + }, + "isUnicodeClass": { + "type": "boolean", + "readOnly": true + }, + "isCOMObject": { + "type": "boolean", + "readOnly": true + }, + "isContextful": { + "type": "boolean", + "readOnly": true + }, + "isEnum": { + "type": "boolean", + "readOnly": true + }, + "isMarshalByRef": { + "type": "boolean", + "readOnly": true + }, + "isPrimitive": { + "type": "boolean", + "readOnly": true + }, + "isValueType": { + "type": "boolean", + "readOnly": true + }, + "isSignatureType": { + "type": "boolean", + "readOnly": true + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "structLayoutAttribute": { + "$ref": "#/components/schemas/StructLayoutAttribute" + }, + "typeInitializer": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "typeHandle": { + "$ref": "#/components/schemas/RuntimeTypeHandle" + }, + "guid": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "baseType": { + "$ref": "#/components/schemas/Type" + }, + "isSerializable": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "isVisible": { + "type": "boolean", + "readOnly": true + }, + "genericTypeParameters": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "declaredConstructors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredEvents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredFields": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FieldInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredMembers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredMethods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MethodInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredNestedTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TypeInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PropertyInfo" + }, + "nullable": true, + "readOnly": true + }, + "implementedInterfaces": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "UpgradeSettings": { + "type": "object", + "properties": { + "currentState": { + "type": "string", + "nullable": true + }, + "newState": { + "type": "string", + "nullable": true + }, + "newVersion": { + "type": "string", + "nullable": true + }, + "oldVersion": { + "type": "string", + "nullable": true + }, + "reportUrl": { + "type": "string", + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "UserInstall": { + "required": [ + "email", + "name", + "password" + ], + "type": "object", + "properties": { + "name": { + "maxLength": 255, + "minLength": 0, + "type": "string" + }, + "email": { + "minLength": 1, + "type": "string", + "format": "email" + }, + "password": { + "minLength": 1, + "type": "string" + }, + "subscribeToNewsletter": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "UserSettings": { "type": "object", - "additionalProperties": false, "properties": { "minCharLength": { "type": "integer", @@ -4645,657 +6939,33 @@ "consentLevels": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsentLevelViewModel" - } - } - } - }, - "ConsentLevelViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "level": { - "$ref": "#/components/schemas/TelemetryLevel" - }, - "description": { - "type": "string" - } - } - }, - "DatabaseSettingsViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "id": { - "type": "string", - "format": "guid" - }, - "sortOrder": { - "type": "integer", - "format": "int32" - }, - "displayName": { - "type": "string" - }, - "defaultDatabaseName": { - "type": "string" - }, - "providerName": { - "type": "string" - }, - "isConfigured": { - "type": "boolean" - }, - "requiresServer": { - "type": "boolean" - }, - "serverPlaceholder": { - "type": "string" - }, - "requiresCredentials": { - "type": "boolean" - }, - "supportsIntegratedAuthentication": { - "type": "boolean" - }, - "requiresConnectionTest": { - "type": "boolean" - } - } - }, - "InstallViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "user", - "database" - ], - "properties": { - "user": { - "$ref": "#/components/schemas/UserInstallViewModel" - }, - "database": { - "$ref": "#/components/schemas/DatabaseInstallViewModel" - }, - "telemetryLevel": { - "$ref": "#/components/schemas/TelemetryLevel" - } - } - }, - "UserInstallViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "email", - "password" - ], - "properties": { - "name": { - "type": "string", - "maxLength": 255, - "minLength": 0 - }, - "email": { - "type": "string", - "format": "email", - "minLength": 1 - }, - "password": { - "type": "string", - "minLength": 1 - }, - "subscribeToNewsletter": { - "type": "boolean" - } - } - }, - "DatabaseInstallViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "id", - "providerName" - ], - "properties": { - "id": { - "type": "string", - "format": "guid", - "minLength": 1 - }, - "providerName": { - "type": "string", - "minLength": 1 - }, - "server": { - "type": "string", + "$ref": "#/components/schemas/ConsentLevel" + }, "nullable": true - }, - "name": { - "type": "string", - "nullable": true - }, - "username": { - "type": "string", - "nullable": true - }, - "password": { - "type": "string", - "nullable": true - }, - "useIntegratedAuthentication": { - "type": "boolean" - }, - "connectionString": { + } + }, + "additionalProperties": false + }, + "Version": { + "type": "object", + "properties": { + "version": { "type": "string", "nullable": true } - } - }, - "PagedViewModelOfHelpPageViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/HelpPageViewModel" - } - } - } - }, - "HelpPageViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "nullable": true - }, - "description": { - "type": "string", - "nullable": true - }, - "url": { - "type": "string", - "nullable": true - }, - "type": { - "type": "string", - "nullable": true - } - } - }, - "PagedViewModelOfDocumentTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentTreeItemViewModel" - } - } - } - }, - "DocumentTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/ContentTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "isProtected": { - "type": "boolean" - }, - "isPublished": { - "type": "boolean" - }, - "isEdited": { - "type": "boolean" - } - } - } - ] - }, - "PagedViewModelOfDocumentTypeTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentTypeTreeItemViewModel" - } - } - } - }, - "DocumentTypeTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/FolderTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "isElement": { - "type": "boolean" - } - } - } - ] - }, - "DocumentBlueprintTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "documentTypeKey": { - "type": "string", - "format": "guid" - }, - "documentTypeAlias": { - "type": "string" - }, - "documentTypeName": { - "type": "string", - "nullable": true - } - } - } - ] - }, - "PagedViewModelOfDocumentBlueprintTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentBlueprintTreeItemViewModel" - } - } - } - }, - "PagedViewModelOfDictionaryOverviewViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DictionaryOverviewViewModel" - } - } - } - }, - "DictionaryOverviewViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "description": "Gets or sets the key.\n ", - "nullable": true - }, - "key": { - "type": "string", - "description": "Gets or sets the key.\n ", - "format": "guid" - }, - "level": { - "type": "integer", - "description": "Gets or sets the level.\n ", - "format": "int32" - }, - "translations": { - "type": "array", - "description": "Sets the translations.\n ", - "items": { - "$ref": "#/components/schemas/DictionaryTranslationOverviewViewModel" - } - } - } - }, - "DictionaryTranslationOverviewViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "displayName": { - "type": "string", - "description": "Gets or sets the display name.\n ", - "nullable": true - }, - "hasTranslation": { - "type": "boolean", - "description": "Gets or sets a value indicating whether has translation.\n " - } - } - }, - "DictionaryViewModel": { - "type": "object", - "description": "The dictionary display model\n ", - "additionalProperties": false, - "required": [ - "name" - ], - "properties": { - "parentId": { - "type": "string", - "description": "Gets or sets the parent id.\n ", - "format": "guid", - "nullable": true - }, - "translations": { - "type": "array", - "description": "Gets or sets the translations.\n ", - "items": { - "$ref": "#/components/schemas/DictionaryTranslationViewModel" - } - }, - "contentApps": { - "type": "array", - "description": "Apps for the dictionary item\n ", - "items": { - "$ref": "#/components/schemas/ContentApp" - } - }, - "notifications": { - "type": "array", - "description": "This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.\n ", - "items": { - "$ref": "#/components/schemas/BackOfficeNotification" - } - }, - "name": { - "type": "string", - "minLength": 1 - }, - "key": { - "type": "string", - "description": "Gets or sets the Key for the object\n ", - "format": "guid" - }, - "path": { - "type": "string", - "description": "The path of the entity\n " - } - } - }, - "DictionaryTranslationViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "id": { - "type": "integer", - "format": "int32" - }, - "key": { - "type": "string", - "format": "guid" - }, - "displayName": { - "type": "string", - "description": "Gets or sets the display name.\n ", - "nullable": true - }, - "isoCode": { - "type": "string", - "description": "Gets or sets the ISO code.\n ", - "nullable": true - }, - "translation": { - "type": "string", - "description": "Gets or sets the translation.\n " - }, - "languageId": { - "type": "integer", - "description": "Gets or sets the language id.\n ", - "format": "int32" - } - } - }, - "ContentApp": { - "type": "object", - "description": "Represents a content app.\n ", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "description": "Gets the name of the content app.\n ", - "nullable": true - }, - "alias": { - "type": "string", - "description": "Gets the unique alias of the content app.\n ", - "nullable": true - }, - "weight": { - "type": "integer", - "description": "Gets or sets the weight of the content app.\n ", - "format": "int32" - }, - "icon": { - "type": "string", - "description": "Gets the icon of the content app.\n ", - "nullable": true - }, - "view": { - "type": "string", - "description": "Gets the view for rendering the content app.\n ", - "nullable": true - }, - "viewModel": { - "description": "The view model specific to this app\n ", - "nullable": true - }, - "active": { - "type": "boolean", - "description": "Gets a value indicating whether the app is active.\n " - }, - "badge": { - "description": "Gets or sets the content app badge.\n ", - "nullable": true, - "oneOf": [ - { - "$ref": "#/components/schemas/ContentAppBadge" - } - ] - } - } - }, - "ContentAppBadge": { - "type": "object", - "description": "Represents a content app badge\n ", - "additionalProperties": false, - "properties": { - "count": { - "type": "integer", - "description": "Gets or sets the number displayed in the badge\n ", - "format": "int32" - }, - "type": { - "description": "Gets or sets the type of badge to display\n ", - "oneOf": [ - { - "$ref": "#/components/schemas/ContentAppBadgeType" - } - ] - } - } - }, - "ContentAppBadgeType": { - "type": "integer", - "description": "Represent the content app badge types\n ", - "x-enumNames": [ - "Default", - "Warning", - "Alert" - ], - "enum": [ - 0, - 1, - 2 - ] - }, - "BackOfficeNotification": { - "type": "object", - "additionalProperties": false, - "properties": { - "header": { - "type": "string", - "nullable": true - }, - "message": { - "type": "string", - "nullable": true - }, - "notificationType": { - "$ref": "#/components/schemas/NotificationStyle" - } - } - }, - "NotificationStyle": { - "type": "integer", - "description": "", - "x-enumNames": [ - "Save", - "Info", - "Error", - "Success", - "Warning" - ], - "enum": [ - 0, - 1, - 2, - 3, - 4 - ] - }, - "DictionaryItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "parentId": { - "type": "string", - "format": "guid", - "nullable": true - }, - "key": { - "type": "string", - "format": "guid" - } - } - }, - "JsonPatchViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "op": { - "type": "string" - }, - "path": { - "type": "string" - }, - "value": {} - } - }, - "DictionaryImportViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "dictionaryItems": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DictionaryItemsImportViewModel" - } - }, - "tempFileName": { - "type": "string", - "nullable": true - } - } - }, - "DictionaryItemsImportViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "nullable": true - }, - "level": { - "type": "integer", - "format": "int32" - } - } - }, - "PagedViewModelOfCultureViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CultureViewModel" - } - } - } - }, - "CultureViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "englishName": { - "type": "string" - } - } + }, + "additionalProperties": false } }, "securitySchemes": { - "Bearer": { + "OAuth": { "type": "oauth2", "description": "Umbraco Authentication", - "name": "Umbraco", "flows": { "authorizationCode": { "authorizationUrl": "/umbraco/management/api/v1.0/security/back-office/authorize", - "tokenUrl": "/umbraco/management/api/v1.0/security/back-office/token" + "tokenUrl": "/umbraco/management/api/v1.0/security/back-office/token", + "scopes": { } } } } @@ -5303,96 +6973,7 @@ }, "security": [ { - "Bearer": [] - } - ], - "tags": [ - { - "name": "Culture" - }, - { - "name": "Data Type" - }, - { - "name": "Dictionary" - }, - { - "name": "Document" - }, - { - "name": "Document Blueprint" - }, - { - "name": "Document Type" - }, - { - "name": "Help" - }, - { - "name": "Install" - }, - { - "name": "Language" - }, - { - "name": "Media" - }, - { - "name": "Media Type" - }, - { - "name": "Member Group" - }, - { - "name": "Member Type" - }, - { - "name": "Models Builder" - }, - { - "name": "Partial View" - }, - { - "name": "Profiling" - }, - { - "name": "Published Cache" - }, - { - "name": "Relation" - }, - { - "name": "Relation Type" - }, - { - "name": "Script" - }, - { - "name": "Search" - }, - { - "name": "Security" - }, - { - "name": "Server" - }, - { - "name": "Static File" - }, - { - "name": "Stylesheet" - }, - { - "name": "Telemetry" - }, - { - "name": "Template" - }, - { - "name": "Tracked Reference" - }, - { - "name": "Upgrade" + "OAuth": [ ] } ] } diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs new file mode 100644 index 0000000000..2b8dd3f469 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Runtime.Serialization; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +public class EnumSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema model, SchemaFilterContext context) + { + if (context.Type.IsEnum) + { + model.Enum.Clear(); + foreach (var name in Enum.GetNames(context.Type)) + { + var actualName = context.Type.GetField(name)?.GetCustomAttribute()?.Value ?? name; + model.Enum.Add(new OpenApiString(actualName)); + } + } + } +} diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs new file mode 100644 index 0000000000..f49a89e22f --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs @@ -0,0 +1,33 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using Umbraco.Extensions; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +/// +/// This filter explicitly removes all other mime types than application/json from the produced OpenAPI document +/// +public class MimeTypeDocumentFilter : IDocumentFilter +{ + public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) + { + OpenApiOperation[] operations = swaggerDoc.Paths + .SelectMany(path => path.Value.Operations.Values) + .ToArray(); + + void RemoveUnwantedMimeTypes(IDictionary content) => + content.RemoveAll(r => r.Key != "application/json"); + + OpenApiRequestBody[] requestBodies = operations.Select(operation => operation.RequestBody).WhereNotNull().ToArray(); + foreach (OpenApiRequestBody requestBody in requestBodies) + { + RemoveUnwantedMimeTypes(requestBody.Content); + } + + OpenApiResponse[] responses = operations.SelectMany(operation => operation.Responses.Values).WhereNotNull().ToArray(); + foreach (OpenApiResponse response in responses) + { + RemoveUnwantedMimeTypes(response.Content); + } + } +} diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs new file mode 100644 index 0000000000..71c15620fd --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs @@ -0,0 +1,26 @@ +using System.Text.RegularExpressions; +using Umbraco.Extensions; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +internal static class SchemaIdGenerator +{ + public static string Generate(Type type) + { + string SanitizedTypeName(Type t) => t.Name + // first grab the "non generic" part of any generic type name (i.e. "PagedViewModel`1" becomes "PagedViewModel") + .Split('`').First() + // then remove the "ViewModel" postfix from type names + .TrimEnd("ViewModel"); + + var name = SanitizedTypeName(type); + if (type.IsGenericType) + { + // append the generic type names, ultimately turning i.e. "PagedViewModel" into "PagedRelationItem" + name += string.Join(string.Empty, type.GenericTypeArguments.Select(SanitizedTypeName)); + } + + // make absolutely sure we don't pass any invalid named by removing all non-word chars + return Regex.Replace(name, @"[^\w]", string.Empty); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs b/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs index 1c0ea342e6..87f63f2f6e 100644 --- a/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs +++ b/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs @@ -12,18 +12,15 @@ public class BackOfficeApplicationManager : IBackOfficeApplicationManager { private readonly IOpenIddictApplicationManager _applicationManager; private readonly IWebHostEnvironment _webHostEnvironment; - private readonly IClientSecretManager _clientSecretManager; private readonly Uri? _backOfficeHost; public BackOfficeApplicationManager( IOpenIddictApplicationManager applicationManager, IWebHostEnvironment webHostEnvironment, - IClientSecretManager clientSecretManager, IOptionsMonitor securitySettingsMonitor) { _applicationManager = applicationManager; _webHostEnvironment = webHostEnvironment; - _clientSecretManager = clientSecretManager; _backOfficeHost = securitySettingsMonitor.CurrentValue.BackOfficeHost; } @@ -67,15 +64,11 @@ public class BackOfficeApplicationManager : IBackOfficeApplicationManager { DisplayName = "Umbraco Swagger access", ClientId = Constants.OauthClientIds.Swagger, - // TODO: investigate the necessity of client secrets for Swagger - // this is necessary with NSwag - or maybe it's a SwaggerUI3 requirement? investigate if client - // secrets are even necessary if we switch to Swashbuckle - ClientSecret = _clientSecretManager.Get(Constants.OauthClientIds.Swagger), RedirectUris = { CallbackUrlFor(backOfficeUrl, "/umbraco/swagger/oauth2-redirect.html") }, - Type = OpenIddictConstants.ClientTypes.Confidential, + Type = OpenIddictConstants.ClientTypes.Public, Permissions = { OpenIddictConstants.Permissions.Endpoints.Authorization, diff --git a/src/Umbraco.Cms.ManagementApi/Security/ClientSecretManager.cs b/src/Umbraco.Cms.ManagementApi/Security/ClientSecretManager.cs deleted file mode 100644 index cbea254ed4..0000000000 --- a/src/Umbraco.Cms.ManagementApi/Security/ClientSecretManager.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Umbraco.Cms.ManagementApi.Security; - -public class ClientSecretManager : IClientSecretManager -{ - private Dictionary _secretsByClientId = new(); - - public string Get(string clientId) - { - if (_secretsByClientId.ContainsKey(clientId) == false) - { - _secretsByClientId[clientId] = Guid.NewGuid().ToString("N"); - } - - return _secretsByClientId[clientId]; - } -} diff --git a/src/Umbraco.Cms.ManagementApi/Security/IClientSecretManager.cs b/src/Umbraco.Cms.ManagementApi/Security/IClientSecretManager.cs deleted file mode 100644 index 7744169b7a..0000000000 --- a/src/Umbraco.Cms.ManagementApi/Security/IClientSecretManager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Umbraco.Cms.ManagementApi.Security; - -public interface IClientSecretManager -{ - string Get(string clientId); -} diff --git a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj index 372aeb1995..37c95e98ee 100644 --- a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj +++ b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj @@ -11,9 +11,11 @@ - + + + diff --git a/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryLevelViewModel.cs b/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs similarity index 86% rename from src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryLevelViewModel.cs rename to src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs index d387be92e9..574ae86360 100644 --- a/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryLevelViewModel.cs +++ b/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs @@ -3,7 +3,7 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.ManagementApi.ViewModels.Telemetry; -public class TelemetryLevelViewModel +public class TelemetryViewModel { [JsonConverter(typeof(JsonStringEnumConverter))] public TelemetryLevel TelemetryLevel { get; set; } diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs index 02b13cf986..960be851ff 100644 --- a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs +++ b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs @@ -61,6 +61,7 @@ public class UmbracoRequestPaths /// These are def back office: /// /Umbraco/BackOffice = back office /// /Umbraco/Preview = back office + /// /Umbraco/Management/Api = back office /// /// /// If it's not any of the above then we cannot determine if it's back office or front-end diff --git a/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs b/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs index a869f63bd3..c4c0bad3a9 100644 --- a/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs +++ b/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs @@ -35,7 +35,7 @@ internal sealed class OpenAPIContractTest : UmbracoTestServerTestBase var officePath = GlobalSettings.GetBackOfficePath(HostingEnvironment); var urlToContract = $"{officePath}/management/api/openapi.json"; - var swaggerPath = $"{officePath}/swagger/All/swagger.json"; + var swaggerPath = $"{officePath}/swagger/v1/swagger.json"; var apiContract = JObject.Parse(await Client.GetStringAsync(urlToContract)); var generatedJsonString = await Client.GetStringAsync(swaggerPath); From 8074c3fb628eb843a2a97f63506f483c77590d0b Mon Sep 17 00:00:00 2001 From: Nathan Woulfe Date: Tue, 8 Nov 2022 01:01:11 +1000 Subject: [PATCH 18/39] add workflow to schema (#13349) * add workflow to schema * add licenses to CMSDefinition - intentionally only adding to schema, not registered as options --- src/JsonSchema/AppSettings.cs | 9 ++++++--- src/JsonSchema/appsettings-schema.json | 11 +++++++++++ .../Configuration/Models/LicensesSettings.cs | 11 +++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Core/Configuration/Models/LicensesSettings.cs diff --git a/src/JsonSchema/AppSettings.cs b/src/JsonSchema/AppSettings.cs index a8ea2f5dbb..f9c7c224be 100644 --- a/src/JsonSchema/AppSettings.cs +++ b/src/JsonSchema/AppSettings.cs @@ -12,11 +12,12 @@ internal class AppSettings public CmsDefinition? CMS { get; set; } /// - /// Configurations for the Umbraco CMS + /// Configurations for the Umbraco CMS /// public class CmsDefinition { public ContentSettings? Content { get; set; } + public CoreDebugSettings? Debug { get; set; } public ExceptionFilterSettings? ExceptionFilter { get; set; } @@ -37,7 +38,7 @@ internal class AppSettings public LoggingSettings? Logging { get; set; } - public NuCacheSettings? NuCache { get; set; } + public NuCacheSettings? NuCache { get; set; } public RequestHandlerSettings? RequestHandler { get; set; } @@ -49,7 +50,7 @@ internal class AppSettings public TypeFinderSettings? TypeFinder { get; set; } - public WebRoutingSettings? WebRouting { get; set; } + public WebRoutingSettings? WebRouting { get; set; } public UmbracoPluginSettings? Plugins { get; set; } @@ -72,5 +73,7 @@ internal class AppSettings public InstallDefaultDataSettings? DefaultDataCreation { get; set; } public DataTypesSettings? DataTypes { get; set; } + + public LicensesSettings? Licenses { get; set; } } } diff --git a/src/JsonSchema/appsettings-schema.json b/src/JsonSchema/appsettings-schema.json index 95c9a9b4c1..44b006b853 100644 --- a/src/JsonSchema/appsettings-schema.json +++ b/src/JsonSchema/appsettings-schema.json @@ -41,6 +41,17 @@ "$ref": "appsettings-schema.Umbraco.Deploy.json#/definitions/JsonSchemaDeployDefinition" } ] + }, + "Workflow": { + "description": "Configuration of Umbraco Workflow", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "appsettings-schema.Umbraco.Workflow.json#/definitions/JsonSchemaWorkflowDefinition" + } + ] } } } diff --git a/src/Umbraco.Core/Configuration/Models/LicensesSettings.cs b/src/Umbraco.Core/Configuration/Models/LicensesSettings.cs new file mode 100644 index 0000000000..f8de1d7265 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/LicensesSettings.cs @@ -0,0 +1,11 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Configuration.Models; + +/// +/// Typed configuration options for license settings. +/// +public class LicensesSettings : Dictionary +{ +} From 65803e26456d374572a24223b6438b7f01818bdc Mon Sep 17 00:00:00 2001 From: Nathan Woulfe Date: Tue, 8 Nov 2022 01:01:11 +1000 Subject: [PATCH 19/39] add workflow to schema (#13349) * add workflow to schema * add licenses to CMSDefinition - intentionally only adding to schema, not registered as options --- src/JsonSchema/AppSettings.cs | 9 ++++++--- src/JsonSchema/appsettings-schema.json | 11 +++++++++++ .../Configuration/Models/LicensesSettings.cs | 11 +++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Core/Configuration/Models/LicensesSettings.cs diff --git a/src/JsonSchema/AppSettings.cs b/src/JsonSchema/AppSettings.cs index a8ea2f5dbb..f9c7c224be 100644 --- a/src/JsonSchema/AppSettings.cs +++ b/src/JsonSchema/AppSettings.cs @@ -12,11 +12,12 @@ internal class AppSettings public CmsDefinition? CMS { get; set; } /// - /// Configurations for the Umbraco CMS + /// Configurations for the Umbraco CMS /// public class CmsDefinition { public ContentSettings? Content { get; set; } + public CoreDebugSettings? Debug { get; set; } public ExceptionFilterSettings? ExceptionFilter { get; set; } @@ -37,7 +38,7 @@ internal class AppSettings public LoggingSettings? Logging { get; set; } - public NuCacheSettings? NuCache { get; set; } + public NuCacheSettings? NuCache { get; set; } public RequestHandlerSettings? RequestHandler { get; set; } @@ -49,7 +50,7 @@ internal class AppSettings public TypeFinderSettings? TypeFinder { get; set; } - public WebRoutingSettings? WebRouting { get; set; } + public WebRoutingSettings? WebRouting { get; set; } public UmbracoPluginSettings? Plugins { get; set; } @@ -72,5 +73,7 @@ internal class AppSettings public InstallDefaultDataSettings? DefaultDataCreation { get; set; } public DataTypesSettings? DataTypes { get; set; } + + public LicensesSettings? Licenses { get; set; } } } diff --git a/src/JsonSchema/appsettings-schema.json b/src/JsonSchema/appsettings-schema.json index 95c9a9b4c1..44b006b853 100644 --- a/src/JsonSchema/appsettings-schema.json +++ b/src/JsonSchema/appsettings-schema.json @@ -41,6 +41,17 @@ "$ref": "appsettings-schema.Umbraco.Deploy.json#/definitions/JsonSchemaDeployDefinition" } ] + }, + "Workflow": { + "description": "Configuration of Umbraco Workflow", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "appsettings-schema.Umbraco.Workflow.json#/definitions/JsonSchemaWorkflowDefinition" + } + ] } } } diff --git a/src/Umbraco.Core/Configuration/Models/LicensesSettings.cs b/src/Umbraco.Core/Configuration/Models/LicensesSettings.cs new file mode 100644 index 0000000000..f8de1d7265 --- /dev/null +++ b/src/Umbraco.Core/Configuration/Models/LicensesSettings.cs @@ -0,0 +1,11 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +namespace Umbraco.Cms.Core.Configuration.Models; + +/// +/// Typed configuration options for license settings. +/// +public class LicensesSettings : Dictionary +{ +} From c1e9775f29978ace2d3bcd0d35a932dc888395d0 Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 8 Nov 2022 12:48:46 +0100 Subject: [PATCH 20/39] Merge new backoffice changes to release branch (#13368) * New backoffice: Cleanup management API routes (#13296) * Rename ModelsBuilderDashboard folder to ModelsBuilder * Fix modelsbuilder paths and related naming * Rename analytics route to telemetry * Fix controller bases - routes and tags * Fix items route * Fix more controllerbase routes * Fix route * Fix OpenApi file * Merging DictionaryItem and Dictionary * Fix TrackedReferences naming * Update OpenApi file * Rename Analytics* related types to Telemetry* * New Backoffice: Return AnalyticsLevelViewModel from Telemetry/ (#13298) * Return TelemetryLevelViewModel instead of TelemetryLevel * Fix schema * Change telemetry/current to telemetry/level (cherry picked from commit f2b8494c669cbbf04b623753abbf1be211973aa9) * Add contants for tree and recycle-bin subpaths (cherry picked from commit 4449f56bc00832ea6d357a3854b454791c80e0e2) Co-authored-by: Mole * OpenId Connect authentication for new management API (#13318) * First attempt at OpenIddict * Making headway and more TODOs * Redo current policies for multiple schemas + clean up auth controller * Fix bad merge * Clean up some more test code * Fix spacing * Include AddAuthentication() in OpenIddict addition * A little more clean-up * Move application creation to its own implementation + prepare for middleware to handle valid callback URL * Enable refresh token flow * Fix bad merge from v11/dev * Support auth for Swagger and Postman in non-production environments + use default login screen for back-office logins * Add workaround to client side login handling so the OAuth return URL is not corrupted before redirection * Add temporary configuration handling for new backoffice * Restructure the code somewhat, move singular responsibility from management API project * Add recurring task for cleaning up old tokens in the DB * Fix bad merge + make auth controller align with the new management API structure * Explicitly handle the new management API path as a backoffice path (NOTE: this is potentially behaviorally breaking!) * Redo handle the new management API requests as backoffice requests, this time in a non-breaking way * Add/update TODOs * Revert duplication of current auth policies for OpenIddict (as it breaks everything for V11 without the new management APIs) and introduce a dedicated PoC policy setup for OpenIddict. * Fix failing unit tests * Update src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Update src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Update src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Update src/Umbraco.Core/Routing/UmbracoRequestPaths.cs Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> * Use Swashbuckle instead of NSwag (#13350) * First attempt at OpenIddict * Making headway and more TODOs * Redo current policies for multiple schemas + clean up auth controller * Fix bad merge * Clean up some more test code * Fix spacing * Include AddAuthentication() in OpenIddict addition * A little more clean-up * Move application creation to its own implementation + prepare for middleware to handle valid callback URL * Enable refresh token flow * Fix bad merge from v11/dev * Support auth for Swagger and Postman in non-production environments + use default login screen for back-office logins * Add workaround to client side login handling so the OAuth return URL is not corrupted before redirection * Add temporary configuration handling for new backoffice * Restructure the code somewhat, move singular responsibility from management API project * Add recurring task for cleaning up old tokens in the DB * Fix bad merge + make auth controller align with the new management API structure * Explicitly handle the new management API path as a backoffice path (NOTE: this is potentially behaviorally breaking!) * Redo handle the new management API requests as backoffice requests, this time in a non-breaking way * Add/update TODOs * Replace NSwag with Swashbuckle and clean up unnecessary client secret workaround * Revert duplication of current auth policies for OpenIddict (as it breaks everything for V11 without the new management APIs) and introduce a dedicated PoC policy setup for OpenIddict. * Fix failing unit tests * A little niceness + export new OpenApi.json and fix path in contract unit test * Redo after merge with v11/dev + filter out unwanted mime types * Remove CreatedResult and NotFoundObjectResult where possible * Custom schema IDs - no more "ViewModel" postfix and make generic lists look less clunky too * A little more explanation for generic schema ID generation * Force Swashbuckle to use enum string names * Update OpenApi.json to match new enum string values * Add clarifying comment about weird looking construct Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Co-authored-by: Kenn Jacobsen Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> --- .../Analytics/AllAnalyticsController.cs | 22 - .../Analytics/AnalyticsControllerBase.cs | 13 - .../Analytics/GetAnalyticsController.cs | 18 - .../Culture/CultureControllerBase.cs | 3 +- .../Tree/DataTypeTreeControllerBase.cs | 5 +- .../Tree/ItemsDataTypeTreeController.cs | 2 +- .../Dictionary/DictionaryControllerBase.cs | 3 +- .../Dictionary/ExportDictionaryController.cs | 4 +- .../Tree/ChildrenDictionaryTreeController.cs} | 6 +- .../Tree/DictionaryTreeControllerBase.cs} | 13 +- .../Tree/ItemsDictionaryTreeController.cs} | 8 +- .../Tree/RootDictionaryTreeController.cs} | 6 +- .../DocumentRecycleBinControllerBase.cs | 5 +- .../Tree/DocumentTreeControllerBase.cs | 5 +- .../Tree/ItemsDocumentTreeController.cs | 2 +- .../DocumentBlueprintTreeControllerBase.cs | 5 +- .../ItemsDocumentBlueprintTreeController.cs | 2 +- .../Tree/DocumentTypeTreeControllerBase.cs | 5 +- .../Tree/ItemsDocumentTypeTreeController.cs | 2 +- .../Controllers/Help/HelpControllerBase.cs | 3 +- .../Install/InstallControllerBase.cs | 3 +- .../Language/LanguageControllerBase.cs | 3 +- .../MediaRecycleBinControllerBase.cs | 5 +- .../Media/Tree/ItemsMediaTreeController.cs | 2 +- .../Media/Tree/MediaTreeControllerBase.cs | 8 +- .../Tree/ItemsMediaTypeTreeController.cs | 2 +- .../Tree/MediaTypeTreeControllerBase.cs | 5 +- .../Tree/ItemsMemberGroupTreeController.cs | 2 +- .../Tree/MemberGroupTreeControllerBase.cs | 5 +- .../Tree/ItemsMemberTypeTreeController.cs | 2 +- .../Tree/MemberTypeTreeControllerBase.cs | 5 +- .../BuildModelsBuilderController.cs | 2 +- .../GetModelsBuilderController.cs | 9 +- .../ModelsBuilderControllerBase.cs | 5 +- .../StatusModelsBuilderController.cs | 2 +- .../Tree/ItemsPartialViewTreeController.cs | 2 +- .../Tree/PartialViewTreeControllerBase.cs | 5 +- .../Profiling/ProfilingControllerBase.cs | 3 +- .../PublishedCacheControllerBase.cs | 3 +- .../Relation/ByChildRelationController.cs | 2 +- .../Relation/RelationControllerBase.cs | 3 +- .../Tree/ItemsRelationTypeTreeController.cs | 2 +- .../Tree/RelationTypeTreeControllerBase.cs | 5 +- .../Script/Tree/ItemsScriptTreeController.cs | 2 +- .../Script/Tree/ScriptTreeControllerBase.cs | 5 +- .../Search/SearchControllerBase.cs | 3 +- .../Security/BackOfficeController.cs | 76 + .../Controllers/Security/Paths.cs | 12 + .../Server/ServerControllerBase.cs | 3 +- .../Tree/ItemsStaticFileTreeController.cs | 2 +- .../Tree/StaticFileTreeControllerBase.cs | 5 +- .../Tree/ItemsStylesheetTreeController.cs | 2 +- .../Tree/StylesheetTreeControllerBase.cs | 5 +- .../Telemetry/AllTelemetryController.cs | 23 + .../Telemetry/GetTelemetryController.cs | 18 + .../SetTelemetryController.cs} | 21 +- .../Telemetry/TelemetryControllerBase.cs | 12 + .../Tree/ItemsTemplateTreeController.cs | 2 +- .../Tree/TemplateTreeControllerBase.cs | 5 +- .../ByIdTrackedReferenceController.cs} | 6 +- .../DescendantsTrackedReferenceController.cs} | 6 +- .../ItemsTrackedReferenceController.cs} | 8 +- .../TrackedReferencesControllerBase.cs | 12 + .../TrackedReferencesControllerBase.cs | 13 - .../Upgrade/UpgradeControllerBase.cs | 3 +- .../BackOfficeAuthBuilderExtensions.cs | 145 + .../ServicesBuilderExtensions.cs | 8 + .../ManagementApiComposer.cs | 104 +- ...ceAuthorizationInitializationMiddleware.cs | 62 + src/Umbraco.Cms.ManagementApi/OpenApi.json | 10717 +++++++++------- .../OpenApi/EnumSchemaFilter.cs | 23 + .../OpenApi/MimeTypeDocumentFilter.cs | 33 + .../OpenApi/SchemaIdGenerator.cs | 26 + .../Security/BackOfficeApplicationManager.cs | 131 + .../Umbraco.Cms.ManagementApi.csproj | 7 +- .../Analytics/AnalyticsLevelViewModel.cs | 10 - .../Telemetry/TelemetryViewModel.cs | 10 + src/Umbraco.Core/Constants-Web.cs | 6 + .../UmbracoBuilder.Configuration.cs | 2 +- .../Routing/UmbracoRequestPaths.cs | 18 +- .../Routing/UmbracoRequestPathsOptions.cs | 10 + src/Umbraco.Core/Umbraco.Core.csproj | 4 + .../Constants-OauthClientIds.cs | 23 + .../Configuration/NewBackOfficeSettings.cs | 10 + .../NewBackOfficeSettingsValidator.cs | 25 + .../HostedServices/OpenIddictCleanup.cs | 56 + .../Security/IBackOfficeApplicationManager.cs | 6 + .../Umbraco.New.Cms.Infrastructure.csproj | 4 + .../src/views/common/login.controller.js | 6 +- .../NewBackoffice/OpenAPIContractTest.cs | 6 +- .../Objects/TestUmbracoContextFactory.cs | 10 +- .../Routing/UmbracoRequestPathsTests.cs | 26 +- .../Security/BackOfficeCookieManagerTests.cs | 15 +- 93 files changed, 7160 insertions(+), 4804 deletions(-) delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs => Dictionary/Tree/ChildrenDictionaryTreeController.cs} (82%) rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs => Dictionary/Tree/DictionaryTreeControllerBase.cs} (77%) rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs => Dictionary/Tree/ItemsDictionaryTreeController.cs} (72%) rename src/Umbraco.Cms.ManagementApi/Controllers/{DictionaryItem/Tree/RootDictionaryItemTreeController.cs => Dictionary/Tree/RootDictionaryTreeController.cs} (82%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/BuildModelsBuilderController.cs (96%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/GetModelsBuilderController.cs (73%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/ModelsBuilderControllerBase.cs (65%) rename src/Umbraco.Cms.ManagementApi/Controllers/{ModelsBuilderDashboard => ModelsBuilder}/StatusModelsBuilderController.cs (94%) create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs rename src/Umbraco.Cms.ManagementApi/Controllers/{Analytics/SetAnalyticsController.cs => Telemetry/SetTelemetryController.cs} (51%) create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences/ForItemTrackedReferencesController.cs => TrackedReference/ByIdTrackedReferenceController.cs} (89%) rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences/DescendantsTrackedReferencesController.cs => TrackedReference/DescendantsTrackedReferenceController.cs} (89%) rename src/Umbraco.Cms.ManagementApi/Controllers/{TrackedReferences/MultipleTrackedReferencesController.cs => TrackedReference/ItemsTrackedReferenceController.cs} (87%) create mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs create mode 100644 src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs create mode 100644 src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs delete mode 100644 src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs create mode 100644 src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs create mode 100644 src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs create mode 100644 src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs create mode 100644 src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs create mode 100644 src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs create mode 100644 src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs create mode 100644 src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs deleted file mode 100644 index 780d6f90d3..0000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AllAnalyticsController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.ManagementApi.ViewModels.Pagination; - -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; - -public class AllAnalyticsController : AnalyticsControllerBase -{ - [HttpGet("all")] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task> GetAll(int skip, int take) - { - TelemetryLevel[] levels = Enum.GetValues(); - return await Task.FromResult(new PagedViewModel - { - Total = levels.Length, - Items = levels.Skip(skip).Take(take), - }); - } -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs deleted file mode 100644 index 1ce29ea5c6..0000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/AnalyticsControllerBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; -using Umbraco.New.Cms.Web.Common.Routing; - -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; - -[ApiController] -[VersionedApiBackOfficeRoute("analytics")] -[OpenApiTag("Analytics")] -[ApiVersion("1.0")] -public abstract class AnalyticsControllerBase : ManagementApiControllerBase -{ -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs deleted file mode 100644 index 1c45e41e71..0000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/GetAnalyticsController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.ManagementApi.ViewModels.Analytics; - -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; - -public class GetAnalyticsController : AnalyticsControllerBase -{ - private readonly IMetricsConsentService _metricsConsentService; - - public GetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; - - [HttpGet] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(AnalyticsLevelViewModel), StatusCodes.Status200OK)] - public async Task Get() => await Task.FromResult(new AnalyticsLevelViewModel { AnalyticsLevel = _metricsConsentService.GetConsentLevel() }); -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs index 7ea0b77ae2..3ab1257824 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Culture/CultureControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Culture; [ApiController] [VersionedApiBackOfficeRoute("culture")] -[OpenApiTag("Culture")] +[ApiExplorerSettings(GroupName = "Culture")] [ApiVersion("1.0")] public abstract class CultureControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs index 4bdae5a2fa..e5948770db 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/DataTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DataType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DataType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DataType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DataType}")] +[ApiExplorerSettings(GroupName = "Data Type")] public class DataTypeTreeControllerBase : FolderTreeControllerBase { private readonly IDataTypeService _dataTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs index 82eabcb6f4..34779cca3d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DataType/Tree/ItemsDataTypeTreeController.cs @@ -12,7 +12,7 @@ public class ItemsDataTypeTreeController : DataTypeTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs index f102d497ac..cfdba7cec1 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/DictionaryControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary; [ApiController] [VersionedApiBackOfficeRoute("dictionary")] -[OpenApiTag("Dictionary")] +[ApiExplorerSettings(GroupName = "Dictionary")] [ApiVersion("1.0")] // TODO: Add authentication public abstract class DictionaryControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs index f4944220d5..5145db1076 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/ExportDictionaryController.cs @@ -23,13 +23,13 @@ public class ExportDictionaryController : DictionaryControllerBase [HttpGet("export/{key:guid}")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] - [ProducesResponseType(typeof(NotFoundObjectResult), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(NotFoundResult), StatusCodes.Status404NotFound)] public async Task ExportDictionary(Guid key, bool includeChildren = false) { IDictionaryItem? dictionaryItem = _localizationService.GetDictionaryItemById(key); if (dictionaryItem is null) { - return await Task.FromResult(NotFound("No dictionary item found with id ")); + return await Task.FromResult(NotFound()); } XElement xml = _entityXmlSerializer.Serialize(dictionaryItem, includeChildren); diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs similarity index 82% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs index 0969b808c9..8b2e6b2328 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ChildrenDictionaryItemTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ChildrenDictionaryTreeController.cs @@ -6,11 +6,11 @@ using Umbraco.Cms.ManagementApi.Services.Paging; using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.Tree; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; -public class ChildrenDictionaryItemTreeController : DictionaryItemTreeControllerBase +public class ChildrenDictionaryTreeController : DictionaryTreeControllerBase { - public ChildrenDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService) + public ChildrenDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService) : base(entityService, localizationService) { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs similarity index 77% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs index 0d6a513e2d..4959789809 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/DictionaryItemTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/DictionaryTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -7,17 +6,17 @@ using Umbraco.Cms.ManagementApi.Controllers.Tree; using Umbraco.Cms.ManagementApi.ViewModels.Tree; using Umbraco.New.Cms.Web.Common.Routing; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DictionaryItem}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DictionaryItem))] -// NOTE: at the moment dictionary items aren't supported by EntityService, so we have little use of the +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/dictionary")] +[ApiExplorerSettings(GroupName = "Dictionary")] +// NOTE: at the moment dictionary items (renamed to dictionary tree) aren't supported by EntityService, so we have little use of the // tree controller base. We'll keep it though, in the hope that we can mend EntityService. -public class DictionaryItemTreeControllerBase : EntityTreeControllerBase +public class DictionaryTreeControllerBase : EntityTreeControllerBase { - public DictionaryItemTreeControllerBase(IEntityService entityService, ILocalizationService localizationService) + public DictionaryTreeControllerBase(IEntityService entityService, ILocalizationService localizationService) : base(entityService) => LocalizationService = localizationService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ItemsDictionaryTreeController.cs similarity index 72% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ItemsDictionaryTreeController.cs index 69d2fda33d..ea2de2058a 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/ItemsDictionaryItemTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/ItemsDictionaryTreeController.cs @@ -4,16 +4,16 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.ManagementApi.ViewModels.Tree; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; -public class ItemsDictionaryItemTreeController : DictionaryItemTreeControllerBase +public class ItemsDictionaryTreeController : DictionaryTreeControllerBase { - public ItemsDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService) + public ItemsDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService) : base(entityService, localizationService) { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/RootDictionaryItemTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs similarity index 82% rename from src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/RootDictionaryItemTreeController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs index 18abf7a728..65981cb518 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DictionaryItem/Tree/RootDictionaryItemTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Dictionary/Tree/RootDictionaryTreeController.cs @@ -6,11 +6,11 @@ using Umbraco.Cms.ManagementApi.Services.Paging; using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.Tree; -namespace Umbraco.Cms.ManagementApi.Controllers.DictionaryItem.Tree; +namespace Umbraco.Cms.ManagementApi.Controllers.Dictionary.Tree; -public class RootDictionaryItemTreeController : DictionaryItemTreeControllerBase +public class RootDictionaryTreeController : DictionaryTreeControllerBase { - public RootDictionaryItemTreeController(IEntityService entityService, ILocalizationService localizationService) + public RootDictionaryTreeController(IEntityService entityService, ILocalizationService localizationService) : base(entityService, localizationService) { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs index 747b6b3296..2edcb07a39 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/RecycleBin/DocumentRecycleBinControllerBase.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -14,10 +13,10 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.RecycleBin; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/recycle-bin")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Document}")] [RequireDocumentTreeRootAccess] [ProducesResponseType(StatusCodes.Status401Unauthorized)] -[OpenApiTag(nameof(Constants.UdiEntityType.Document))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))] public class DocumentRecycleBinControllerBase : RecycleBinControllerBase { public DocumentRecycleBinControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs index a20237537d..b838c03790 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/DocumentTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Models; @@ -16,8 +15,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Document.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Document))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Document}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Document))] public abstract class DocumentTreeControllerBase : UserStartNodeTreeControllerBase { private readonly IPublicAccessService _publicAccessService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs index a18dfea069..a9dd8326d6 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Document/Tree/ItemsDocumentTreeController.cs @@ -21,7 +21,7 @@ public class ItemsDocumentTreeController : DocumentTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null, string? culture = null) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs index c6247da3a9..ad4e32cd87 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/DocumentBlueprintTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentBlueprint.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentBlueprint}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DocumentBlueprint))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentBlueprint}")] +[ApiExplorerSettings(GroupName = "Document Blueprint")] public class DocumentBlueprintTreeControllerBase : EntityTreeControllerBase { private readonly IContentTypeService _contentTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs index 6b7edb6fab..e077d7eb49 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentBlueprint/Tree/ItemsDocumentBlueprintTreeController.cs @@ -13,7 +13,7 @@ public class ItemsDocumentBlueprintTreeController : DocumentBlueprintTreeControl { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs index cc4c224ef5..91676b9f80 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/DocumentTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.DocumentType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.DocumentType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.DocumentType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.DocumentType}")] +[ApiExplorerSettings(GroupName = "Document Type")] public class DocumentTypeTreeControllerBase : FolderTreeControllerBase { private readonly IContentTypeService _contentTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs index e19bf249c6..28da775a58 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/DocumentType/Tree/ItemsDocumentTypeTreeController.cs @@ -12,7 +12,7 @@ public class ItemsDocumentTypeTreeController : DocumentTypeTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs index 7220b8738b..9a50d45186 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Help/HelpControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Help; [ApiController] [VersionedApiBackOfficeRoute("help")] -[OpenApiTag("Help")] +[ApiExplorerSettings(GroupName = "Help")] [ApiVersion("1.0")] public abstract class HelpControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs index 359e62b4b8..04cd4885bb 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Install/InstallControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.ManagementApi.Filters; using Umbraco.New.Cms.Web.Common.Routing; @@ -8,7 +7,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Install; [ApiController] [VersionedApiBackOfficeRoute("install")] -[OpenApiTag("Install")] +[ApiExplorerSettings(GroupName = "Install")] [RequireRuntimeLevel(RuntimeLevel.Install)] public abstract class InstallControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs index b84088906c..c316486fed 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Language/LanguageControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Language; [ApiController] [VersionedApiBackOfficeRoute("language")] -[OpenApiTag("Language")] +[ApiExplorerSettings(GroupName = "Language")] [ApiVersion("1.0")] public abstract class LanguageControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs index 157e1099de..787a12f957 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/RecycleBin/MediaRecycleBinControllerBase.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -14,10 +13,10 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.RecycleBin; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/recycle-bin")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.RecycleBin}/{Constants.UdiEntityType.Media}")] [RequireMediaTreeRootAccess] [ProducesResponseType(StatusCodes.Status401Unauthorized)] -[OpenApiTag(nameof(Constants.UdiEntityType.Media))] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Media))] public class MediaRecycleBinControllerBase : RecycleBinControllerBase { public MediaRecycleBinControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs index 2ebf1a559f..cc6074d49b 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/ItemsMediaTreeController.cs @@ -20,7 +20,7 @@ public class ItemsMediaTreeController : MediaTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys, Guid? dataTypeKey = null) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs index c03f05c71d..67e90efe8e 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Media/Tree/MediaTreeControllerBase.cs @@ -1,5 +1,5 @@ -using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Models; @@ -15,8 +15,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Media.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Media}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Media))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Media}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Media))] public class MediaTreeControllerBase : UserStartNodeTreeControllerBase { private readonly AppCaches _appCaches; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs index 363751fee7..309711cd7d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/ItemsMediaTypeTreeController.cs @@ -12,7 +12,7 @@ public class ItemsMediaTypeTreeController : MediaTypeTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs index 5b06c46439..d47691df4e 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MediaType/Tree/MediaTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MediaType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MediaType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.MediaType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MediaType}")] +[ApiExplorerSettings(GroupName = "Media Type")] public class MediaTypeTreeControllerBase : FolderTreeControllerBase { private readonly IMediaTypeService _mediaTypeService; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs index 94db46be4e..d3be627d30 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/ItemsMemberGroupTreeController.cs @@ -12,7 +12,7 @@ public class ItemsMemberGroupTreeController : MemberGroupTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs index b3ee033a25..455fcf64f7 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberGroup/Tree/MemberGroupTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberGroup.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberGroup}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.MemberGroup))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberGroup}")] +[ApiExplorerSettings(GroupName = "Member Group")] public class MemberGroupTreeControllerBase : EntityTreeControllerBase { public MemberGroupTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs index 249cdd5d67..e70ca8f0ee 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/ItemsMemberTypeTreeController.cs @@ -12,7 +12,7 @@ public class ItemsMemberTypeTreeController : MemberTypeTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs index 88183dfd58..2924ad3ba1 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/MemberType/Tree/MemberTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.MemberType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.MemberType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.MemberType}")] +[ApiExplorerSettings(GroupName = "Member Type")] public class MemberTypeTreeControllerBase : EntityTreeControllerBase { public MemberTypeTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/BuildModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/BuildModelsBuilderController.cs similarity index 96% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/BuildModelsBuilderController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/BuildModelsBuilderController.cs index c219a5128e..7fcf5b5888 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/BuildModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/BuildModelsBuilderController.cs @@ -8,7 +8,7 @@ using Umbraco.Cms.Infrastructure.ModelsBuilder; using Umbraco.Cms.Infrastructure.ModelsBuilder.Building; using Umbraco.Extensions; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; public class BuildModelsBuilderController : ModelsBuilderControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/GetModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs similarity index 73% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/GetModelsBuilderController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs index aa993cd1f4..b2080ee610 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/GetModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/GetModelsBuilderController.cs @@ -1,10 +1,9 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.ManagementApi.Factories; using Umbraco.Cms.ManagementApi.ViewModels.ModelsBuilderDashboard; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; public class GetModelsBuilderController : ModelsBuilderControllerBase { @@ -12,8 +11,8 @@ public class GetModelsBuilderController : ModelsBuilderControllerBase public GetModelsBuilderController(IModelsBuilderViewModelFactory modelsBuilderViewModelFactory) => _modelsBuilderViewModelFactory = modelsBuilderViewModelFactory; - [HttpGet] - [ProducesResponseType(typeof(CreatedResult), StatusCodes.Status200OK)] + [HttpGet("dashboard")] + [ProducesResponseType(typeof(ModelsBuilderViewModel), StatusCodes.Status200OK)] [MapToApiVersion("1.0")] public async Task> GetDashboard() => await Task.FromResult(Ok(_modelsBuilderViewModelFactory.Create())); } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/ModelsBuilderControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs similarity index 65% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/ModelsBuilderControllerBase.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs index 4871d3b9d6..aa428d6005 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/ModelsBuilderControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/ModelsBuilderControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; [ApiController] [VersionedApiBackOfficeRoute("models-builder")] -[OpenApiTag("ModelsBuilder")] +[ApiExplorerSettings(GroupName = "Models Builder")] [ApiVersion("1.0")] public class ModelsBuilderControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/StatusModelsBuilderController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/StatusModelsBuilderController.cs similarity index 94% rename from src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/StatusModelsBuilderController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/StatusModelsBuilderController.cs index 9b2cb22e0a..1f130ffb5c 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilderDashboard/StatusModelsBuilderController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/ModelsBuilder/StatusModelsBuilderController.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Infrastructure.ModelsBuilder; using Umbraco.Cms.ManagementApi.ViewModels.ModelsBuilderDashboard; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilderDashboard; +namespace Umbraco.Cms.ManagementApi.Controllers.ModelsBuilder; public class StatusModelsBuilderController : ModelsBuilderControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs index d6107a844a..5a1567028c 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/ItemsPartialViewTreeController.cs @@ -12,7 +12,7 @@ public class ItemsPartialViewTreeController : PartialViewTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs index 95ad0eb6cf..ba2420e851 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PartialView/Tree/PartialViewTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -9,8 +8,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.PartialView.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.PartialView}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.PartialView))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.PartialView}")] +[ApiExplorerSettings(GroupName = "Partial View")] public class PartialViewTreeControllerBase : FileSystemTreeControllerBase { public PartialViewTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs index c081f74fe0..f1919b2501 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Profiling/ProfilingControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Profiling; @@ -7,7 +6,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Profiling; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute("profiling")] -[OpenApiTag("Profiling")] +[ApiExplorerSettings(GroupName = "Profiling")] public class ProfilingControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs index 78dcd539f4..914775a563 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/PublishedCacheControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache; @@ -7,7 +6,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.PublishedCache; [ApiVersion("1.0")] [ApiController] [VersionedApiBackOfficeRoute("published-cache")] -[OpenApiTag("PublishedCache")] +[ApiExplorerSettings(GroupName = "Published Cache")] public class PublishedCacheControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs index 4639fa7bea..8d1e88c806 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/ByChildRelationController.cs @@ -22,7 +22,7 @@ public class ByChildRelationController : RelationControllerBase _relationViewModelFactory = relationViewModelFactory; } - [HttpGet("child-relations/{childId:int}")] + [HttpGet("child-relation/{childId:int}")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] public async Task> ByChild(int childId, int skip, int take, string? relationTypeAlias = "") diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs index 7ec26735d1..65266ef356 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Relation/RelationControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Relation; [ApiController] [VersionedApiBackOfficeRoute("relation")] -[OpenApiTag("Relation")] +[ApiExplorerSettings(GroupName = "Relation")] [ApiVersion("1.0")] // TODO: Implement Authentication public abstract class RelationControllerBase : ManagementApiControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs index 7e2054a594..ca3f6ff7de 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/ItemsRelationTypeTreeController.cs @@ -14,7 +14,7 @@ public class ItemsRelationTypeTreeController : RelationTypeTreeControllerBase : base(entityService) => _relationService = relationService; - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs index c90c124686..c3497e7a1d 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/RelationType/Tree/RelationTypeTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; @@ -11,8 +10,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.RelationType.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.RelationType}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.RelationType))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.RelationType}")] +[ApiExplorerSettings(GroupName = "Relation Type")] // NOTE: at the moment relation types aren't supported by EntityService, so we have little use of the // tree controller base. We'll keep it though, in the hope that we can mend EntityService. public class RelationTypeTreeControllerBase : EntityTreeControllerBase diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs index 99cd6d990e..642fcb63be 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ItemsScriptTreeController.cs @@ -12,7 +12,7 @@ public class ItemsScriptTreeController : ScriptTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs index 4e204da4ee..f0f7398141 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Script/Tree/ScriptTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -9,8 +8,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Script.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Script}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Script))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Script}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Script))] public class ScriptTreeControllerBase : FileSystemTreeControllerBase { public ScriptTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs index 2ab98aa2ff..9d3befc022 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Search/SearchControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Search; [ApiController] [VersionedApiBackOfficeRoute("search")] -[OpenApiTag("Search")] +[ApiExplorerSettings(GroupName = "Search")] public class SearchControllerBase : ManagementApiControllerBase { } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs new file mode 100644 index 0000000000..74ce67ed03 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Security/BackOfficeController.cs @@ -0,0 +1,76 @@ +using System.Security.Claims; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using OpenIddict.Abstractions; +using OpenIddict.Server.AspNetCore; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Web.BackOffice.Security; +using Umbraco.Extensions; +using Umbraco.New.Cms.Web.Common.Routing; + +namespace Umbraco.Cms.ManagementApi.Controllers.Security; + +[ApiController] +[VersionedApiBackOfficeRoute(Paths.BackOfficeApiEndpointTemplate)] +[ApiExplorerSettings(GroupName = "Security")] +public class BackOfficeController : ManagementApiControllerBase +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IBackOfficeSignInManager _backOfficeSignInManager; + private readonly IBackOfficeUserManager _backOfficeUserManager; + + public BackOfficeController(IHttpContextAccessor httpContextAccessor, IBackOfficeSignInManager backOfficeSignInManager, IBackOfficeUserManager backOfficeUserManager) + { + _httpContextAccessor = httpContextAccessor; + _backOfficeSignInManager = backOfficeSignInManager; + _backOfficeUserManager = backOfficeUserManager; + } + + [HttpGet("authorize")] + [HttpPost("authorize")] + [MapToApiVersion("1.0")] + public async Task Authorize() + { + HttpContext context = _httpContextAccessor.GetRequiredHttpContext(); + OpenIddictRequest? request = context.GetOpenIddictServerRequest(); + if (request == null) + { + return BadRequest("Unable to obtain OpenID data from the current request"); + } + + // retrieve the user principal stored in the authentication cookie. + AuthenticateResult cookieAuthResult = await HttpContext.AuthenticateAsync(Constants.Security.BackOfficeAuthenticationType); + if (cookieAuthResult.Succeeded && cookieAuthResult.Principal?.Identity?.Name != null) + { + BackOfficeIdentityUser? backOfficeUser = await _backOfficeUserManager.FindByNameAsync(cookieAuthResult.Principal.Identity.Name); + if (backOfficeUser != null) + { + ClaimsPrincipal backOfficePrincipal = await _backOfficeSignInManager.CreateUserPrincipalAsync(backOfficeUser); + backOfficePrincipal.SetClaim(OpenIddictConstants.Claims.Subject, backOfficeUser.Key.ToString()); + + // TODO: it is not optimal to append all claims to the token. + // the token size grows with each claim, although it is still smaller than the old cookie. + // see if we can find a better way so we do not risk leaking sensitive data in bearer tokens. + // maybe work with scopes instead? + Claim[] backOfficeClaims = backOfficePrincipal.Claims.ToArray(); + foreach (Claim backOfficeClaim in backOfficeClaims) + { + backOfficeClaim.SetDestinations(OpenIddictConstants.Destinations.AccessToken); + } + + if (request.GetScopes().Contains(OpenIddictConstants.Scopes.OfflineAccess)) + { + // "offline_access" scope is required to use refresh tokens + backOfficePrincipal.SetScopes(OpenIddictConstants.Scopes.OfflineAccess); + } + + return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, backOfficePrincipal); + } + } + + return new ChallengeResult(new[] { Constants.Security.BackOfficeAuthenticationType }); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs new file mode 100644 index 0000000000..3ce1b7c3c6 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Security/Paths.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Cms.ManagementApi.Controllers.Security; + +public static class Paths +{ + public const string BackOfficeApiEndpointTemplate = "security/back-office"; + + public static string BackOfficeApiAuthorizationEndpoint = BackOfficeApiEndpointPath($"{BackOfficeApiEndpointTemplate}/authorize"); + + public static string BackOfficeApiTokenEndpoint = BackOfficeApiEndpointPath($"{BackOfficeApiEndpointTemplate}/token"); + + private static string BackOfficeApiEndpointPath(string relativePath) => $"/umbraco/management/api/v1.0/{relativePath}"; +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs index 9dd6b3a192..11674dcc23 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Server/ServerControllerBase.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.New.Cms.Web.Common.Routing; namespace Umbraco.Cms.ManagementApi.Controllers.Server; [ApiController] [VersionedApiBackOfficeRoute("server")] -[OpenApiTag("Server")] +[ApiExplorerSettings(GroupName = "Server")] public abstract class ServerControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs index 205f92d94f..1b8bb4b8b8 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/ItemsStaticFileTreeController.cs @@ -12,7 +12,7 @@ public class ItemsStaticFileTreeController : StaticFileTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs index ec50a54495..a66898619a 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/StaticFile/Tree/StaticFileTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -9,8 +8,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.StaticFile.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute("static-file/tree")] -[OpenApiTag("StaticFile")] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/static-file")] +[ApiExplorerSettings(GroupName = "Static File")] public class StaticFileTreeControllerBase : FileSystemTreeControllerBase { private static readonly string[] _allowedRootFolders = { "App_Plugins", "wwwroot" }; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs index de2e779ba1..12b04a9c08 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/ItemsStylesheetTreeController.cs @@ -12,7 +12,7 @@ public class ItemsStylesheetTreeController : StylesheetTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "path")] string[] paths) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs index b529752293..c0fe829806 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Stylesheet/Tree/StylesheetTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.ManagementApi.Controllers.Tree; @@ -9,8 +8,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Stylesheet.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Stylesheet}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Stylesheet))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Stylesheet}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Stylesheet))] public class StylesheetTreeControllerBase : FileSystemTreeControllerBase { public StylesheetTreeControllerBase(FileSystems fileSystems) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs new file mode 100644 index 0000000000..7e3eed886b --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/AllTelemetryController.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.ManagementApi.ViewModels.Pagination; +using Umbraco.Cms.ManagementApi.ViewModels.Telemetry; + +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; + +public class AllTelemetryController : TelemetryControllerBase +{ + [HttpGet] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] + public async Task> GetAll(int skip, int take) + { + TelemetryLevel[] levels = Enum.GetValues(); + return await Task.FromResult(new PagedViewModel + { + Total = levels.Length, + Items = levels.Skip(skip).Take(take).Select(level => new TelemetryViewModel { TelemetryLevel = level }), + }); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs new file mode 100644 index 0000000000..3e6323343b --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/GetTelemetryController.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.ManagementApi.ViewModels.Telemetry; + +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; + +public class GetTelemetryController : TelemetryControllerBase +{ + private readonly IMetricsConsentService _metricsConsentService; + + public GetTelemetryController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; + + [HttpGet("level")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(TelemetryViewModel), StatusCodes.Status200OK)] + public async Task Get() => await Task.FromResult(new TelemetryViewModel { TelemetryLevel = _metricsConsentService.GetConsentLevel() }); +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs similarity index 51% rename from src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs index cfdd1e8b4f..0687997ff0 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Analytics/SetAnalyticsController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/SetTelemetryController.cs @@ -1,36 +1,35 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.ManagementApi.ViewModels.Analytics; -using Umbraco.Cms.ManagementApi.ViewModels.Server; +using Umbraco.Cms.ManagementApi.ViewModels.Telemetry; -namespace Umbraco.Cms.ManagementApi.Controllers.Analytics; +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; -public class SetAnalyticsController : AnalyticsControllerBase +public class SetTelemetryController : TelemetryControllerBase { private readonly IMetricsConsentService _metricsConsentService; - public SetAnalyticsController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; + public SetTelemetryController(IMetricsConsentService metricsConsentService) => _metricsConsentService = metricsConsentService; - [HttpPost] + [HttpPost("level")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task SetConsentLevel(AnalyticsLevelViewModel analyticsLevelViewModel) + public async Task SetConsentLevel(TelemetryViewModel telemetryViewModel) { - if (!Enum.IsDefined(analyticsLevelViewModel.AnalyticsLevel)) + if (!Enum.IsDefined(telemetryViewModel.TelemetryLevel)) { var invalidModelProblem = new ProblemDetails { - Title = "Invalid AnalyticsLevel value", - Detail = "The provided value for AnalyticsLevel is not valid", + Title = "Invalid TelemetryLevel value", + Detail = "The provided value for TelemetryLevel is not valid", Status = StatusCodes.Status400BadRequest, Type = "Error", }; return BadRequest(invalidModelProblem); } - _metricsConsentService.SetConsentLevel(analyticsLevelViewModel.AnalyticsLevel); + _metricsConsentService.SetConsentLevel(telemetryViewModel.TelemetryLevel); return await Task.FromResult(Ok()); } } diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs new file mode 100644 index 0000000000..ea5835b2ca --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Telemetry/TelemetryControllerBase.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Umbraco.New.Cms.Web.Common.Routing; + +namespace Umbraco.Cms.ManagementApi.Controllers.Telemetry; + +[ApiController] +[VersionedApiBackOfficeRoute("telemetry")] +[ApiExplorerSettings(GroupName = "Telemetry")] +[ApiVersion("1.0")] +public abstract class TelemetryControllerBase : ManagementApiControllerBase +{ +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs index fb4a29c621..877f740df7 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/ItemsTemplateTreeController.cs @@ -12,7 +12,7 @@ public class ItemsTemplateTreeController : TemplateTreeControllerBase { } - [HttpGet("items")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] public async Task>> Items([FromQuery(Name = "key")] Guid[] keys) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs index be885f26be..c27206c665 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Template/Tree/TemplateTreeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -12,8 +11,8 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Template.Tree; [ApiVersion("1.0")] [ApiController] -[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Template}/tree")] -[OpenApiTag(nameof(Constants.UdiEntityType.Template))] +[VersionedApiBackOfficeRoute($"{Constants.Web.RoutePath.Tree}/{Constants.UdiEntityType.Template}")] +[ApiExplorerSettings(GroupName = nameof(Constants.UdiEntityType.Template))] public class TemplateTreeControllerBase : EntityTreeControllerBase { public TemplateTreeControllerBase(IEntityService entityService) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/ForItemTrackedReferencesController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ByIdTrackedReferenceController.cs similarity index 89% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/ForItemTrackedReferencesController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ByIdTrackedReferenceController.cs index a415cf08bf..3c1998a7e4 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/ForItemTrackedReferencesController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ByIdTrackedReferenceController.cs @@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; -public class ForItemTrackedReferencesController : TrackedReferencesControllerBase +public class ByIdTrackedReferenceController : TrackedReferenceControllerBase { private readonly ITrackedReferencesService _trackedReferencesService; private readonly IUmbracoMapper _umbracoMapper; - public ForItemTrackedReferencesController(ITrackedReferencesService trackedReferencesService, IUmbracoMapper umbracoMapper) + public ByIdTrackedReferenceController(ITrackedReferencesService trackedReferencesService, IUmbracoMapper umbracoMapper) { _trackedReferencesService = trackedReferencesService; _umbracoMapper = umbracoMapper; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/DescendantsTrackedReferencesController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/DescendantsTrackedReferenceController.cs similarity index 89% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/DescendantsTrackedReferencesController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/DescendantsTrackedReferenceController.cs index ecac832a84..20f119d173 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/DescendantsTrackedReferencesController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/DescendantsTrackedReferenceController.cs @@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; -public class DescendantsTrackedReferencesController : TrackedReferencesControllerBase +public class DescendantsTrackedReferenceController : TrackedReferenceControllerBase { private readonly ITrackedReferencesService _trackedReferencesSkipTakeService; private readonly IUmbracoMapper _umbracoMapper; - public DescendantsTrackedReferencesController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) + public DescendantsTrackedReferenceController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) { _trackedReferencesSkipTakeService = trackedReferencesSkipTakeService; _umbracoMapper = umbracoMapper; diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/MultipleTrackedReferencesController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ItemsTrackedReferenceController.cs similarity index 87% rename from src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/MultipleTrackedReferencesController.cs rename to src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ItemsTrackedReferenceController.cs index 041d208915..39ad9c2560 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/MultipleTrackedReferencesController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/ItemsTrackedReferenceController.cs @@ -7,14 +7,14 @@ using Umbraco.Cms.ManagementApi.ViewModels.Pagination; using Umbraco.Cms.ManagementApi.ViewModels.TrackedReferences; using Umbraco.New.Cms.Core.Models; -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; -public class MultipleTrackedReferencesController : TrackedReferencesControllerBase +public class ItemsTrackedReferenceController : TrackedReferenceControllerBase { private readonly ITrackedReferencesService _trackedReferencesSkipTakeService; private readonly IUmbracoMapper _umbracoMapper; - public MultipleTrackedReferencesController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) + public ItemsTrackedReferenceController(ITrackedReferencesService trackedReferencesSkipTakeService, IUmbracoMapper umbracoMapper) { _trackedReferencesSkipTakeService = trackedReferencesSkipTakeService; _umbracoMapper = umbracoMapper; @@ -27,7 +27,7 @@ public class MultipleTrackedReferencesController : TrackedReferencesControllerBa /// Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view). /// This is basically finding children of relations. /// - [HttpGet("multiple")] + [HttpGet("item")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] public async Task>> GetPagedReferencedItems([FromQuery]int[] ids, long skip, long take, bool? filterMustBeIsDependency) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs new file mode 100644 index 0000000000..3b62a0fe69 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReference/TrackedReferencesControllerBase.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Umbraco.New.Cms.Web.Common.Routing; + +namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReference; + +[ApiController] +[VersionedApiBackOfficeRoute("tracked-reference")] +[ApiExplorerSettings(GroupName = "Tracked Reference")] +[ApiVersion("1.0")] +public abstract class TrackedReferenceControllerBase : ManagementApiControllerBase +{ +} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs deleted file mode 100644 index be40c80bf1..0000000000 --- a/src/Umbraco.Cms.ManagementApi/Controllers/TrackedReferences/TrackedReferencesControllerBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; -using Umbraco.New.Cms.Web.Common.Routing; - -namespace Umbraco.Cms.ManagementApi.Controllers.TrackedReferences; - -[ApiController] -[VersionedApiBackOfficeRoute("tracked-references")] -[OpenApiTag("TrackedReferences")] -[ApiVersion("1.0")] -public abstract class TrackedReferencesControllerBase : ManagementApiControllerBase -{ -} diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs index 084515aba2..6e8e056280 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Upgrade/UpgradeControllerBase.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using NSwag.Annotations; using Umbraco.Cms.Core; using Umbraco.Cms.ManagementApi.Filters; using Umbraco.New.Cms.Web.Common.Routing; @@ -11,7 +10,7 @@ namespace Umbraco.Cms.ManagementApi.Controllers.Upgrade; [ApiController] [RequireRuntimeLevel(RuntimeLevel.Upgrade)] [VersionedApiBackOfficeRoute("upgrade")] -[OpenApiTag("Upgrade")] +[ApiExplorerSettings(GroupName = "Upgrade")] public abstract class UpgradeControllerBase : ManagementApiControllerBase { diff --git a/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs b/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs new file mode 100644 index 0000000000..5331acaaa9 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/DependencyInjection/BackOfficeAuthBuilderExtensions.cs @@ -0,0 +1,145 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using OpenIddict.Validation.AspNetCore; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.ManagementApi.Middleware; +using Umbraco.Cms.ManagementApi.Security; +using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.New.Cms.Infrastructure.HostedServices; +using Umbraco.New.Cms.Infrastructure.Security; + +namespace Umbraco.Cms.ManagementApi.DependencyInjection; + +public static class BackOfficeAuthBuilderExtensions +{ + public static IUmbracoBuilder AddBackOfficeAuthentication(this IUmbracoBuilder builder) + { + builder + .AddDbContext() + .AddOpenIddict(); + + return builder; + } + + private static IUmbracoBuilder AddDbContext(this IUmbracoBuilder builder) + { + builder.Services.AddDbContext(options => + { + // Configure the DB context + // TODO: use actual Umbraco DbContext once EF is implemented - and remove dependency on Microsoft.EntityFrameworkCore.InMemory + options.UseInMemoryDatabase(nameof(DbContext)); + + // Register the entity sets needed by OpenIddict. + options.UseOpenIddict(); + }); + + return builder; + } + + private static IUmbracoBuilder AddOpenIddict(this IUmbracoBuilder builder) + { + builder.Services.AddAuthentication(); + builder.Services.AddAuthorization(CreatePolicies); + + builder.Services.AddOpenIddict() + + // Register the OpenIddict core components. + .AddCore(options => + { + options + .UseEntityFrameworkCore() + .UseDbContext(); + }) + + // Register the OpenIddict server components. + .AddServer(options => + { + // Enable the authorization and token endpoints. + options + .SetAuthorizationEndpointUris(Controllers.Security.Paths.BackOfficeApiAuthorizationEndpoint) + .SetTokenEndpointUris(Controllers.Security.Paths.BackOfficeApiTokenEndpoint); + + // Enable authorization code flow with PKCE + options + .AllowAuthorizationCodeFlow() + .RequireProofKeyForCodeExchange() + .AllowRefreshTokenFlow(); + + // Register the encryption and signing credentials. + // - see https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html + options + // TODO: use actual certificates here, see docs above + .AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate() + .DisableAccessTokenEncryption(); + + // Register the ASP.NET Core host and configure for custom authentication endpoint. + options + .UseAspNetCore() + .EnableAuthorizationEndpointPassthrough(); + }) + + // Register the OpenIddict validation components. + .AddValidation(options => + { + // Import the configuration from the local OpenIddict server instance. + options.UseLocalServer(); + + // Register the ASP.NET Core host. + options.UseAspNetCore(); + }); + + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + + builder.Services.AddHostedService(); + builder.Services.AddHostedService(); + + return builder; + } + + // TODO: remove this once EF is implemented + public class DatabaseManager : IHostedService + { + private readonly IServiceProvider _serviceProvider; + + public DatabaseManager(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider; + + public async Task StartAsync(CancellationToken cancellationToken) + { + using IServiceScope scope = _serviceProvider.CreateScope(); + + DbContext context = scope.ServiceProvider.GetRequiredService(); + await context.Database.EnsureCreatedAsync(cancellationToken); + + // TODO: add BackOfficeAuthorizationInitializationMiddleware before UseAuthorization (to make it run for unauthorized API requests) and remove this + IBackOfficeApplicationManager backOfficeApplicationManager = scope.ServiceProvider.GetRequiredService(); + await backOfficeApplicationManager.EnsureBackOfficeApplicationAsync(new Uri("https://localhost:44331/"), cancellationToken); + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } + + // TODO: move this to an appropriate location and implement the policy scheme that should be used for the new management APIs + private static void CreatePolicies(AuthorizationOptions options) + { + void AddPolicy(string policyName, string claimType, params string[] allowedClaimValues) + { + options.AddPolicy($"New{policyName}", policy => + { + policy.AuthenticationSchemes.Add(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + policy.RequireClaim(claimType, allowedClaimValues); + }); + } + + // NOTE: these are ONLY sample policies that allow us to test the new management APIs + AddPolicy(AuthorizationPolicies.SectionAccessContent, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content); + AddPolicy(AuthorizationPolicies.SectionAccessForContentTree, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content); + AddPolicy(AuthorizationPolicies.SectionAccessForMediaTree, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Media); + AddPolicy(AuthorizationPolicies.SectionAccessMedia, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Media); + AddPolicy(AuthorizationPolicies.SectionAccessContentOrMedia, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content, Constants.Applications.Media); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs b/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs index cb739478c5..9a05c17bfe 100644 --- a/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs +++ b/src/Umbraco.Cms.ManagementApi/DependencyInjection/ServicesBuilderExtensions.cs @@ -1,7 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.ManagementApi.Serialization; using Umbraco.Cms.ManagementApi.Services; +using Umbraco.Extensions; using Umbraco.New.Cms.Core.Services.Installer; using Umbraco.New.Cms.Core.Services.Languages; @@ -17,6 +19,12 @@ public static class ServicesBuilderExtensions builder.Services.AddTransient(); builder.Services.AddTransient(); + // TODO: handle new management API path in core UmbracoRequestPaths (it's a behavioural breaking change so it goes here for now) + builder.Services.Configure(options => + { + options.IsBackOfficeRequest = urlPath => urlPath.InvariantStartsWith($"/umbraco/management/api/"); + }); + return builder; } } diff --git a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs index b73bc40ed2..5f9614ee94 100644 --- a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs +++ b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs @@ -3,22 +3,23 @@ using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; -using NSwag.AspNetCore; +using Microsoft.OpenApi.Models; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.ManagementApi.Configuration; using Umbraco.Cms.ManagementApi.DependencyInjection; +using Umbraco.Cms.ManagementApi.OpenApi; using Umbraco.Cms.Web.Common.ApplicationBuilder; using Umbraco.Extensions; +using Umbraco.New.Cms.Core; +using Umbraco.New.Cms.Core.Models.Configuration; using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.ManagementApi; @@ -26,7 +27,7 @@ namespace Umbraco.Cms.ManagementApi; public class ManagementApiComposer : IComposer { private const string ApiTitle = "Umbraco Backoffice API"; - private const string ApiAllName = "All"; + private const string ApiDefaultDocumentName = "v1"; private ApiVersion DefaultApiVersion => new(1, 0); @@ -44,7 +45,8 @@ public class ManagementApiComposer : IComposer .AddTrees() .AddFactories() .AddServices() - .AddMappers(); + .AddMappers() + .AddBackOfficeAuthentication(); services.AddApiVersioning(options => { @@ -55,16 +57,63 @@ public class ManagementApiComposer : IComposer options.UseApiBehavior = false; }); - services.AddOpenApiDocument(options => + services.AddSwaggerGen(swaggerGenOptions => { - options.Title = ApiTitle; - options.Version = ApiAllName; - options.DocumentName = ApiAllName; - options.Description = "This shows all APIs available in this version of Umbraco - Including all the legacy apis that is available for backward compatibility"; - options.PostProcess = document => + swaggerGenOptions.SwaggerDoc( + ApiDefaultDocumentName, + new OpenApiInfo + { + Title = ApiTitle, + Version = DefaultApiVersion.ToString(), + Description = "This shows all APIs available in this version of Umbraco - including all the legacy apis that are available for backward compatibility" + }); + + swaggerGenOptions.DocInclusionPredicate((_, api) => !string.IsNullOrWhiteSpace(api.GroupName)); + + swaggerGenOptions.TagActionsBy(api => new [] { api.GroupName }); + + // see https://github.com/domaindrivendev/Swashbuckle.AspNetCore#change-operation-sort-order-eg-for-ui-sorting + string ActionSortKeySelector(ApiDescription apiDesc) + => $"{apiDesc.GroupName}_{apiDesc.ActionDescriptor.AttributeRouteInfo?.Template ?? apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.ActionDescriptor.RouteValues["action"]}_{apiDesc.HttpMethod}"; + swaggerGenOptions.OrderActionsBy(ActionSortKeySelector); + + swaggerGenOptions.AddSecurityDefinition("OAuth", new OpenApiSecurityScheme { - document.Tags = document.Tags.OrderBy(tag => tag.Name).ToList(); - }; + In = ParameterLocation.Header, + Name = "Umbraco", + Type = SecuritySchemeType.OAuth2, + Description = "Umbraco Authentication", + Flows = new OpenApiOAuthFlows + { + AuthorizationCode = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri(Controllers.Security.Paths.BackOfficeApiAuthorizationEndpoint, UriKind.Relative), + TokenUrl = new Uri(Controllers.Security.Paths.BackOfficeApiTokenEndpoint, UriKind.Relative) + } + } + }); + + swaggerGenOptions.AddSecurityRequirement(new OpenApiSecurityRequirement + { + // this weird looking construct works because OpenApiSecurityRequirement + // is a specialization of Dictionary<,> + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Id = "OAuth", + Type = ReferenceType.SecurityScheme + } + }, + new List { } + } + }); + + swaggerGenOptions.DocumentFilter(); + swaggerGenOptions.SchemaFilter(); + + swaggerGenOptions.CustomSchemaIds(SchemaIdGenerator.Generate); }); services.AddVersionedApiExplorer(options => @@ -78,6 +127,10 @@ public class ManagementApiComposer : IComposer services.AddControllers(); builder.Services.ConfigureOptions(); + // TODO: when this is moved to core, make the AddUmbracoOptions extension private again and remove core InternalsVisibleTo for Umbraco.Cms.ManagementApi + builder.AddUmbracoOptions(); + builder.Services.AddSingleton, NewBackOfficeSettingsValidator>(); + builder.Services.Configure(options => { options.AddFilter(new UmbracoPipelineFilter( @@ -113,21 +166,18 @@ public class ManagementApiComposer : IComposer GlobalSettings? settings = provider.GetRequiredService>().Value; IHostingEnvironment hostingEnvironment = provider.GetRequiredService(); var officePath = settings.GetBackOfficePath(hostingEnvironment); - // serve documents (same as app.UseSwagger()) - applicationBuilder.UseOpenApi(config => - { - config.Path = $"{officePath}/swagger/{{documentName}}/swagger.json"; - }); - // Serve Swagger UI - applicationBuilder.UseSwaggerUi3(config => + applicationBuilder.UseSwagger(swaggerOptions => { - config.Path = officePath + "/swagger"; - config.SwaggerRoutes.Clear(); - var swaggerPath = $"{officePath}/swagger/{ApiAllName}/swagger.json"; - config.SwaggerRoutes.Add(new SwaggerUi3Route(ApiAllName, swaggerPath)); - config.OperationsSorter = "alpha"; - config.TagsSorter = "alpha"; + swaggerOptions.RouteTemplate = $"{officePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}/swagger/{{documentName}}/swagger.json"; + }); + applicationBuilder.UseSwaggerUI(swaggerUiOptions => + { + swaggerUiOptions.SwaggerEndpoint($"{officePath}/swagger/v1/swagger.json", $"{ApiTitle} {DefaultApiVersion}"); + swaggerUiOptions.RoutePrefix = $"{officePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}/swagger"; + + swaggerUiOptions.OAuthClientId(Constants.OauthClientIds.Swagger); + swaggerUiOptions.OAuthUsePkce(); }); } }, diff --git a/src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs b/src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs new file mode 100644 index 0000000000..6ecebb3362 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Middleware/BackOfficeAuthorizationInitializationMiddleware.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.Routing; +using Umbraco.New.Cms.Infrastructure.Security; + +namespace Umbraco.Cms.ManagementApi.Middleware; + +public class BackOfficeAuthorizationInitializationMiddleware : IMiddleware +{ + private static bool _firstBackOfficeRequest; + private static SemaphoreSlim _firstBackOfficeRequestLocker = new(1); + + private readonly UmbracoRequestPaths _umbracoRequestPaths; + private readonly IServiceProvider _serviceProvider; + + public BackOfficeAuthorizationInitializationMiddleware(UmbracoRequestPaths umbracoRequestPaths, IServiceProvider serviceProvider) + { + _umbracoRequestPaths = umbracoRequestPaths; + _serviceProvider = serviceProvider; + } + + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + await InitializeBackOfficeAuthorizationOnceAsync(context); + await next(context); + } + + private async Task InitializeBackOfficeAuthorizationOnceAsync(HttpContext context) + { + if (_firstBackOfficeRequest) + { + return; + } + + if (_umbracoRequestPaths.IsBackOfficeRequest(context.Request.Path) == false) + { + return; + } + + await _firstBackOfficeRequestLocker.WaitAsync(); + if (_firstBackOfficeRequest == false) + { + using IServiceScope scope = _serviceProvider.CreateScope(); + IBackOfficeApplicationManager backOfficeApplicationManager = scope.ServiceProvider.GetRequiredService(); + await backOfficeApplicationManager.EnsureBackOfficeApplicationAsync(new Uri(context.Request.GetDisplayUrl())); + _firstBackOfficeRequest = true; + } + + _firstBackOfficeRequestLocker.Release(); + } +} + +// TODO: remove this (used for testing BackOfficeAuthorizationInitializationMiddleware until it can be added to the existing UseBackOffice extension) +// public static class UmbracoApplicationBuilderExtensions +// { +// public static IUmbracoApplicationBuilderContext UseNewBackOffice(this IUmbracoApplicationBuilderContext builder) +// { +// builder.AppBuilder.UseMiddleware(); +// return builder; +// } +// } diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi.json b/src/Umbraco.Cms.ManagementApi/OpenApi.json index b821f26e24..c0b9f4ef0b 100644 --- a/src/Umbraco.Cms.ManagementApi/OpenApi.json +++ b/src/Umbraco.Cms.ManagementApi/OpenApi.json @@ -1,195 +1,41 @@ { - "x-generator": "NSwag v13.17.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))", - "openapi": "3.0.0", + "openapi": "3.0.1", "info": { "title": "Umbraco Backoffice API", - "description": "This shows all APIs available in this version of Umbraco - Including all the legacy apis that is available for backward compatibility", - "version": "All" + "description": "This shows all APIs available in this version of Umbraco - including all the legacy apis that are available for backward compatibility", + "version": "1.0" }, - "servers": [ - { - "url": "https://localhost:44331" - } - ], "paths": { - "/umbraco/management/api/v1/upgrade/authorize": { - "post": { - "tags": [ - "Upgrade" - ], - "operationId": "AuthorizeUpgrade_Authorize", - "responses": { - "200": { - "description": "" - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/upgrade/settings": { + "/umbraco/management/api/v1/culture": { "get": { "tags": [ - "Upgrade" + "Culture" ], - "operationId": "SettingsUpgrade_Settings", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpgradeSettingsViewModel" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tracked-references/descendants/{parentId}": { - "get": { - "tags": [ - "TrackedReferences" - ], - "summary": "Gets a page list of the child nodes of the current item used in any kind of relation.", - "description": "Used when deleting and unpublishing a single item to check if this item has any descending items that are in any\nkind of relation.\nThis is basically finding the descending items which are children in relations.", - "operationId": "DescendantsTrackedReferences_Descendants", "parameters": [ { - "name": "parentId", - "in": "path", - "required": true, + "name": "skip", + "in": "query", "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 2 + } }, { "name": "take", "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 3 - }, - { - "name": "filterMustBeIsDependency", - "in": "query", - "schema": { - "type": "boolean", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/tracked-references/{id}": { - "get": { - "tags": [ - "TrackedReferences" - ], - "summary": "Gets a page list of tracked references for the current item, so you can see where an item is being used.", - "description": "Used by info tabs on content, media etc. and for the delete and unpublish of single items.\nThis is basically finding parents of relations.", - "operationId": "ForItemTrackedReferences_Get", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 3 - }, - { - "name": "filterMustBeIsDependency", - "in": "query", - "schema": { - "type": "boolean", - "nullable": true - }, - "x-position": 4 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" + "$ref": "#/components/schemas/PagedCulture" } } } @@ -197,87 +43,19 @@ } } }, - "/umbraco/management/api/v1/tracked-references/multiple": { + "/umbraco/management/api/v1/tree/data-type/children": { "get": { "tags": [ - "TrackedReferences" + "Data Type" ], - "summary": "Gets a page list of the items used in any kind of relation from selected integer ids.", - "description": "Used when bulk deleting content/media and bulk unpublishing content (delete and unpublish on List view).\nThis is basically finding children of relations.", - "operationId": "MultipleTrackedReferences_GetPagedReferencedItems", - "parameters": [ - { - "name": "ids", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "integer", - "format": "int32" - } - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int64" - }, - "x-position": 3 - }, - { - "name": "filterMustBeIsDependency", - "in": "query", - "schema": { - "type": "boolean", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/template/tree/children": { - "get": { - "tags": [ - "Template" - ], - "operationId": "ChildrenTemplateTree_Children", "parameters": [ { "name": "parentKey", "in": "query", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "skip", @@ -286,8 +64,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -296,1753 +73,7 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } } - } - } - } - }, - "/umbraco/management/api/v1/template/tree/items": { - "get": { - "tags": [ - "Template" - ], - "operationId": "ItemsTemplateTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/template/tree/root": { - "get": { - "tags": [ - "Template" - ], - "operationId": "RootTemplateTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/stylesheet/tree/children": { - "get": { - "tags": [ - "Stylesheet" - ], - "operationId": "ChildrenStylesheetTree_Children", - "parameters": [ - { - "name": "path", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/stylesheet/tree/items": { - "get": { - "tags": [ - "Stylesheet" - ], - "operationId": "ItemsStylesheetTree_Items", - "parameters": [ - { - "name": "path", - "x-originalName": "paths", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/stylesheet/tree/root": { - "get": { - "tags": [ - "Stylesheet" - ], - "operationId": "RootStylesheetTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/static-file/tree/children": { - "get": { - "tags": [ - "StaticFile" - ], - "operationId": "ChildrenStaticFileTree_Children", - "parameters": [ - { - "name": "path", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/static-file/tree/items": { - "get": { - "tags": [ - "StaticFile" - ], - "operationId": "ItemsStaticFileTree_Items", - "parameters": [ - { - "name": "path", - "x-originalName": "paths", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/static-file/tree/root": { - "get": { - "tags": [ - "StaticFile" - ], - "operationId": "RootStaticFileTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/server/status": { - "get": { - "tags": [ - "Server" - ], - "operationId": "StatusServer_Get", - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServerStatusViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/server/version": { - "get": { - "tags": [ - "Server" - ], - "operationId": "VersionServer_Get", - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VersionViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/index/{indexName}": { - "get": { - "tags": [ - "Search" - ], - "summary": "Check if the index has been rebuilt", - "description": "This is kind of rudimentary since there's no way we can know that the index has rebuilt, we\nhave a listener for the index op complete so we'll just check if that key is no longer there in the runtime cache", - "operationId": "IndexDetailsSearch_Index", - "parameters": [ - { - "name": "indexName", - "in": "path", - "required": true, - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - } - ], - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/IndexViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/index": { - "get": { - "tags": [ - "Search" - ], - "summary": "Get the details for indexers", - "operationId": "IndexListSearch_Indexes", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfIndexViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/index/{indexName}/rebuild": { - "post": { - "tags": [ - "Search" - ], - "summary": "Rebuilds the index", - "operationId": "IndexRebuildSearch_Rebuild", - "parameters": [ - { - "name": "indexName", - "in": "path", - "required": true, - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - } - ], - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/searcher": { - "get": { - "tags": [ - "Search" - ], - "summary": "Get the details for searchers", - "operationId": "SearcherListSearch_Searchers", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfSearcherViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/search/searcher/{searcherName}/search": { - "get": { - "tags": [ - "Search" - ], - "operationId": "SearcherSearchSearch_GetSearchResults", - "parameters": [ - { - "name": "searcherName", - "in": "path", - "required": true, - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "query", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 2 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 3 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfPagedViewModelOfSearchResultViewModel" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/script/tree/children": { - "get": { - "tags": [ - "Script" - ], - "operationId": "ChildrenScriptTree_Children", - "parameters": [ - { - "name": "path", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/script/tree/items": { - "get": { - "tags": [ - "Script" - ], - "operationId": "ItemsScriptTree_Items", - "parameters": [ - { - "name": "path", - "x-originalName": "paths", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/script/tree/root": { - "get": { - "tags": [ - "Script" - ], - "operationId": "RootScriptTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/relation/child-relations/{childId}": { - "get": { - "tags": [ - "Relation" - ], - "operationId": "ByChildRelation_ByChild", - "parameters": [ - { - "name": "childId", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 3 - }, - { - "name": "relationTypeAlias", - "in": "query", - "schema": { - "type": "string", - "default": "", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRelationViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/relation/{id}": { - "get": { - "tags": [ - "Relation" - ], - "operationId": "ByIdRelation_ById", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RelationViewModel" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/relation-type/tree/items": { - "get": { - "tags": [ - "RelationType" - ], - "operationId": "ItemsRelationTypeTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/relation-type/tree/root": { - "get": { - "tags": [ - "RelationType" - ], - "operationId": "RootRelationTypeTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/published-cache/collect": { - "post": { - "tags": [ - "PublishedCache" - ], - "operationId": "CollectPublishedCache_Collect", - "responses": { - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/published-cache/rebuild": { - "post": { - "tags": [ - "PublishedCache" - ], - "operationId": "RebuildPublishedCache_Collect", - "responses": { - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/published-cache/reload": { - "post": { - "tags": [ - "PublishedCache" - ], - "operationId": "ReloadPublishedCache_Reload", - "responses": { - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/published-cache/status": { - "get": { - "tags": [ - "PublishedCache" - ], - "operationId": "StatusPublishedCache_Status", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/profiling/status": { - "get": { - "tags": [ - "Profiling" - ], - "operationId": "StatusProfiling_Status", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProfilingStatusViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/partial-view/tree/children": { - "get": { - "tags": [ - "PartialView" - ], - "operationId": "ChildrenPartialViewTree_Children", - "parameters": [ - { - "name": "path", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/partial-view/tree/items": { - "get": { - "tags": [ - "PartialView" - ], - "operationId": "ItemsPartialViewTree_Items", - "parameters": [ - { - "name": "path", - "x-originalName": "paths", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/partial-view/tree/root": { - "get": { - "tags": [ - "PartialView" - ], - "operationId": "RootPartialViewTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFileSystemTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/models-builder/build": { - "post": { - "tags": [ - "ModelsBuilder" - ], - "operationId": "BuildModelsBuilder_BuildModels", - "responses": { - "201": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/models-builder": { - "get": { - "tags": [ - "ModelsBuilder" - ], - "operationId": "GetModelsBuilder_GetDashboard", - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/models-builder/status": { - "get": { - "tags": [ - "ModelsBuilder" - ], - "operationId": "StatusModelsBuilder_GetModelsOutOfDateStatus", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OutOfDateStatusViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/member-type/tree/items": { - "get": { - "tags": [ - "MemberType" - ], - "operationId": "ItemsMemberTypeTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/member-type/tree/root": { - "get": { - "tags": [ - "MemberType" - ], - "operationId": "RootMemberTypeTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/member-group/tree/items": { - "get": { - "tags": [ - "MemberGroup" - ], - "operationId": "ItemsMemberGroupTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/member-group/tree/root": { - "get": { - "tags": [ - "MemberGroup" - ], - "operationId": "RootMemberGroupTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/media/tree/children": { - "get": { - "tags": [ - "Media" - ], - "operationId": "ChildrenMediaTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfContentTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/media/tree/items": { - "get": { - "tags": [ - "Media" - ], - "operationId": "ItemsMediaTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ContentTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/media/tree/root": { - "get": { - "tags": [ - "Media" - ], - "operationId": "RootMediaTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfContentTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/media/recycle-bin/children": { - "get": { - "tags": [ - "Media" - ], - "operationId": "ChildrenMediaRecycleBin_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/media/recycle-bin/root": { - "get": { - "tags": [ - "Media" - ], - "operationId": "RootMediaRecycleBin_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/media-type/tree/children": { - "get": { - "tags": [ - "MediaType" - ], - "operationId": "ChildrenMediaTypeTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 }, { "name": "foldersOnly", @@ -2050,17 +81,16 @@ "schema": { "type": "boolean", "default": false - }, - "x-position": 4 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" + "$ref": "#/components/schemas/PagedFolderTreeItem" } } } @@ -2068,39 +98,33 @@ } } }, - "/umbraco/management/api/v1/media-type/tree/items": { + "/umbraco/management/api/v1/tree/data-type/item": { "get": { "tags": [ - "MediaType" + "Data Type" ], - "operationId": "ItemsMediaTypeTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "$ref": "#/components/schemas/FolderTreeItem" } } } @@ -2109,12 +133,11 @@ } } }, - "/umbraco/management/api/v1/media-type/tree/root": { + "/umbraco/management/api/v1/tree/data-type/root": { "get": { "tags": [ - "MediaType" + "Data Type" ], - "operationId": "RootMediaTypeTree_Root", "parameters": [ { "name": "skip", @@ -2123,8 +146,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -2133,8 +155,7 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } }, { "name": "foldersOnly", @@ -2142,965 +163,16 @@ "schema": { "type": "boolean", "default": false - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/language": { - "get": { - "tags": [ - "Language" - ], - "summary": "1\n Returns all currently configured languages.", - "operationId": "AllLanguage_GetAll", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfLanguageViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/language/{id}": { - "get": { - "tags": [ - "Language" - ], - "operationId": "ByIdLanguage_ById", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LanguageViewModel" - } - } - } - } - } - }, - "delete": { - "tags": [ - "Language" - ], - "summary": "Deletes a language with a given ID", - "operationId": "DeleteLanguage_Delete", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 1 - } - ], - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/language/create": { - "post": { - "tags": [ - "Language" - ], - "summary": "Creates or saves a language", - "operationId": "CreateLanguage_Create", - "requestBody": { - "x-name": "language", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LanguageViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "201": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/language/update": { - "put": { - "tags": [ - "Language" - ], - "summary": "Updates a language", - "operationId": "UpdateLanguage_Update", - "requestBody": { - "x-name": "language", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LanguageViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/install/settings": { - "get": { - "tags": [ - "Install" - ], - "operationId": "SettingsInstall_Settings", - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InstallSettingsViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/install/setup": { - "post": { - "tags": [ - "Install" - ], - "operationId": "SetupInstall_Setup", - "requestBody": { - "x-name": "installData", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InstallViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "428": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/install/validate-database": { - "post": { - "tags": [ - "Install" - ], - "operationId": "ValidateDatabaseInstall_ValidateDatabase", - "requestBody": { - "x-name": "viewModel", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DatabaseInstallViewModel" - } - } - }, - "required": true, - "x-position": 1 - }, - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "" - } - } - } - }, - "/umbraco/management/api/v1/help": { - "get": { - "tags": [ - "Help" - ], - "operationId": "GetHelp_Get", - "parameters": [ - { - "name": "section", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 - }, - { - "name": "tree", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 2 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 3 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32" - }, - "x-position": 4 - }, - { - "name": "baseUrl", - "in": "query", - "schema": { - "type": "string", - "default": "https://our.umbraco.com", - "nullable": true - }, - "x-position": 5 - } - ], - "responses": { - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfHelpPageViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document/tree/children": { - "get": { - "tags": [ - "Document" - ], - "operationId": "ChildrenDocumentTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 4 - }, - { - "name": "culture", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 5 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document/tree/items": { - "get": { - "tags": [ - "Document" - ], - "operationId": "ItemsDocumentTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 2 - }, - { - "name": "culture", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document/tree/root": { - "get": { - "tags": [ - "Document" - ], - "operationId": "RootDocumentTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - }, - { - "name": "dataTypeKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid", - "nullable": true - }, - "x-position": 3 - }, - { - "name": "culture", - "in": "query", - "schema": { - "type": "string", - "nullable": true - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document/recycle-bin/children": { - "get": { - "tags": [ - "Document" - ], - "operationId": "ChildrenDocumentRecycleBin_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - } - ], - "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document/recycle-bin/root": { - "get": { - "tags": [ - "Document" - ], - "operationId": "RootDocumentRecycleBin_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "401": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } - }, - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfRecycleBinItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document-type/tree/children": { - "get": { - "tags": [ - "DocumentType" - ], - "operationId": "ChildrenDocumentTypeTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTypeTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document-type/tree/items": { - "get": { - "tags": [ - "DocumentType" - ], - "operationId": "ItemsDocumentTypeTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentTypeTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document-type/tree/root": { - "get": { - "tags": [ - "DocumentType" - ], - "operationId": "RootDocumentTypeTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 3 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentTypeTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document-blueprint/tree/items": { - "get": { - "tags": [ - "DocumentBlueprint" - ], - "operationId": "ItemsDocumentBlueprintTree_Items", - "parameters": [ - { - "name": "key", - "x-originalName": "keys", - "in": "query", - "style": "form", - "explode": true, - "schema": { - "type": "array", - "nullable": true, - "items": { - "type": "string", - "format": "guid" - } - }, - "x-position": 1 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentBlueprintTreeItemViewModel" - } - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/document-blueprint/tree/root": { - "get": { - "tags": [ - "DocumentBlueprint" - ], - "operationId": "RootDocumentBlueprintTree_Root", - "parameters": [ - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 1 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 2 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDocumentBlueprintTreeItemViewModel" + "$ref": "#/components/schemas/PagedFolderTreeItem" } } } @@ -3113,8 +185,6 @@ "tags": [ "Dictionary" ], - "summary": "Retrieves a list with all dictionary items", - "operationId": "AllDictionary_All", "parameters": [ { "name": "skip", @@ -3122,8 +192,7 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 + } }, { "name": "take", @@ -3131,17 +200,68 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 2 + } } ], "responses": { "200": { - "description": "The IEnumerable`1.\n ", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfDictionaryOverviewViewModel" + "$ref": "#/components/schemas/PagedDictionaryOverview" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/dictionary/{id}": { + "patch": { + "tags": [ + "Dictionary" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/JsonPatch" + } + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentResult" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3154,39 +274,34 @@ "tags": [ "Dictionary" ], - "summary": "Gets a dictionary item by guid", - "operationId": "ByIdDictionary_ByKey", "parameters": [ { "name": "key", "in": "path", "required": true, - "description": "The id.\n ", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } } ], "responses": { "200": { - "description": "The DictionaryDisplay. Returns a not found response when dictionary item does not exist\n ", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DictionaryViewModel" + "$ref": "#/components/schemas/Dictionary" } } } }, "404": { - "description": "", + "description": "Not Found", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3197,32 +312,27 @@ "tags": [ "Dictionary" ], - "summary": "Deletes a data type with a given ID", - "operationId": "DeleteDictionary_Delete", "parameters": [ { "name": "key", "in": "path", "required": true, - "description": "The key of the dictionary item to delete", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } } ], "responses": { "200": { - "description": "HttpResponseMessage\n " + "description": "Success" }, "404": { - "description": "", + "description": "Not Found", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3235,35 +345,28 @@ "tags": [ "Dictionary" ], - "summary": "Creates a new dictionary item", - "operationId": "CreateDictionary_Create", "requestBody": { - "x-name": "dictionaryViewModel", - "description": "The viewmodel to pass to the action", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DictionaryItemViewModel" + "$ref": "#/components/schemas/DictionaryItem" } } - }, - "required": true, - "x-position": 1 + } }, "responses": { "201": { - "description": "The HttpResponseMessage.\n ", + "description": "Created", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/CreatedResult" } } } }, "400": { - "description": "", + "description": "Bad Request", "content": { "application/json": { "schema": { @@ -3280,7 +383,6 @@ "tags": [ "Dictionary" ], - "operationId": "ExportDictionary_ExportDictionary", "parameters": [ { "name": "key", @@ -3288,9 +390,8 @@ "required": true, "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "includeChildren", @@ -3298,15 +399,14 @@ "schema": { "type": "boolean", "default": false - }, - "x-position": 2 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { - "application/octet-stream": { + "application/json": { "schema": { "type": "string", "format": "binary" @@ -3315,12 +415,11 @@ } }, "404": { - "description": "", + "description": "Not Found", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3333,106 +432,40 @@ "tags": [ "Dictionary" ], - "operationId": "ImportDictionary_ImportDictionary", "parameters": [ { "name": "file", "in": "query", "schema": { - "type": "string", - "nullable": true - }, - "x-position": 1 + "type": "string" + } }, { "name": "parentId", "in": "query", "schema": { "type": "integer", - "format": "int32", - "nullable": true - }, - "x-position": 2 + "format": "int32" + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ContentResult" } } } }, "404": { - "description": "", + "description": "Not Found", "content": { - "application/octet-stream": { + "application/json": { "schema": { - "type": "string", - "format": "binary" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/dictionary/{id}": { - "patch": { - "tags": [ - "Dictionary" - ], - "operationId": "UpdateDictionary_Update", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - } - ], - "requestBody": { - "x-name": "updateViewModel", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/JsonPatchViewModel" - } - } - } - }, - "required": true, - "x-position": 2 - }, - "responses": { - "200": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/NotFoundResult" } } } @@ -3445,36 +478,22 @@ "tags": [ "Dictionary" ], - "operationId": "UploadDictionary_Upload", "requestBody": { - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary", - "nullable": true - } - } - } - } - } + "content": { } }, "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DictionaryImportViewModel" + "$ref": "#/components/schemas/DictionaryImport" } } } }, "400": { - "description": "", + "description": "Bad Request", "content": { "application/json": { "schema": { @@ -3486,21 +505,19 @@ } } }, - "/umbraco/management/api/v1/dictionary-item/tree/children": { + "/umbraco/management/api/v1/tree/dictionary/children": { "get": { "tags": [ - "DictionaryItem" + "Dictionary" ], - "operationId": "ChildrenDictionaryItemTree_Children", "parameters": [ { "name": "parentKey", "in": "query", "schema": { "type": "string", - "format": "guid" - }, - "x-position": 1 + "format": "uuid" + } }, { "name": "skip", @@ -3509,8 +526,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 2 + } }, { "name": "take", @@ -3519,17 +535,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -3537,39 +552,33 @@ } } }, - "/umbraco/management/api/v1/dictionary-item/tree/items": { + "/umbraco/management/api/v1/tree/dictionary/item": { "get": { "tags": [ - "DictionaryItem" + "Dictionary" ], - "operationId": "ItemsDictionaryItemTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "$ref": "#/components/schemas/FolderTreeItem" } } } @@ -3578,12 +587,11 @@ } } }, - "/umbraco/management/api/v1/dictionary-item/tree/root": { + "/umbraco/management/api/v1/tree/dictionary/root": { "get": { "tags": [ - "DictionaryItem" + "Dictionary" ], - "operationId": "RootDictionaryItemTree_Root", "parameters": [ { "name": "skip", @@ -3592,8 +600,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -3602,17 +609,16 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfEntityTreeItemViewModel" + "$ref": "#/components/schemas/PagedEntityTreeItem" } } } @@ -3620,99 +626,33 @@ } } }, - "/umbraco/management/api/v1/data-type/tree/children": { + "/umbraco/management/api/v1/tree/document-blueprint/item": { "get": { "tags": [ - "DataType" + "Document Blueprint" ], - "operationId": "ChildrenDataTypeTree_Children", - "parameters": [ - { - "name": "parentKey", - "in": "query", - "schema": { - "type": "string", - "format": "guid" - }, - "x-position": 1 - }, - { - "name": "skip", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - }, - "x-position": 2 - }, - { - "name": "take", - "in": "query", - "schema": { - "type": "integer", - "format": "int32", - "default": 100 - }, - "x-position": 3 - }, - { - "name": "foldersOnly", - "in": "query", - "schema": { - "type": "boolean", - "default": false - }, - "x-position": 4 - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" - } - } - } - } - } - } - }, - "/umbraco/management/api/v1/data-type/tree/items": { - "get": { - "tags": [ - "DataType" - ], - "operationId": "ItemsDataTypeTree_Items", "parameters": [ { "name": "key", - "x-originalName": "keys", "in": "query", - "style": "form", - "explode": true, "schema": { "type": "array", - "nullable": true, "items": { "type": "string", - "format": "guid" + "format": "uuid" } - }, - "x-position": 1 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" + "$ref": "#/components/schemas/DocumentBlueprintTreeItem" } } } @@ -3721,12 +661,11 @@ } } }, - "/umbraco/management/api/v1/data-type/tree/root": { + "/umbraco/management/api/v1/tree/document-blueprint/root": { "get": { "tags": [ - "DataType" + "Document Blueprint" ], - "operationId": "RootDataTypeTree_Root", "parameters": [ { "name": "skip", @@ -3735,8 +674,7 @@ "type": "integer", "format": "int32", "default": 0 - }, - "x-position": 1 + } }, { "name": "take", @@ -3745,8 +683,54 @@ "type": "integer", "format": "int32", "default": 100 - }, - "x-position": 2 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedDocumentBlueprintTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document-type/children": { + "get": { + "tags": [ + "Document Type" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } }, { "name": "foldersOnly", @@ -3754,17 +738,16 @@ "schema": { "type": "boolean", "default": false - }, - "x-position": 3 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfFolderTreeItemViewModel" + "$ref": "#/components/schemas/PagedDocumentTypeTreeItem" } } } @@ -3772,22 +755,387 @@ } } }, - "/umbraco/management/api/v1/culture": { + "/umbraco/management/api/v1/tree/document-type/item": { "get": { "tags": [ - "Culture" + "Document Type" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTypeTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document-type/root": { + "get": { + "tags": [ + "Document Type" ], - "summary": "Returns all cultures available for creating languages.", - "operationId": "AllCulture_GetAll", "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedDocumentTypeTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/recycle-bin/document/children": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRecycleBinItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/recycle-bin/document/root": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRecycleBinItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document/children": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "culture", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedDocumentTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document/item": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "culture", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/document/root": { + "get": { + "tags": [ + "Document" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "culture", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedDocumentTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/help": { + "get": { + "tags": [ + "Help" + ], + "parameters": [ + { + "name": "section", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "tree", + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "skip", "in": "query", "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 + } }, { "name": "take", @@ -3795,17 +1143,34 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 2 + } + }, + { + "name": "baseUrl", + "in": "query", + "schema": { + "type": "string", + "default": "https://our.umbraco.com" + } } ], "responses": { - "200": { - "description": "", + "400": { + "description": "Bad Request", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfCultureViewModel" + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedHelpPage" } } } @@ -3813,12 +1178,122 @@ } } }, - "/umbraco/management/api/v1/analytics/all": { + "/umbraco/management/api/v1/install/settings": { "get": { "tags": [ - "Analytics" + "Install" + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InstallSettings" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/install/setup": { + "post": { + "tags": [ + "Install" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Install" + } + } + } + }, + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/install/validate-database": { + "post": { + "tags": [ + "Install" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DatabaseInstall" + } + } + } + }, + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/language": { + "get": { + "tags": [ + "Language" ], - "operationId": "AllAnalytics_GetAll", "parameters": [ { "name": "skip", @@ -3826,8 +1301,7 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 1 + } }, { "name": "take", @@ -3835,17 +1309,16 @@ "schema": { "type": "integer", "format": "int32" - }, - "x-position": 2 + } } ], "responses": { "200": { - "description": "", + "description": "Success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PagedViewModelOfTelemetryLevel" + "$ref": "#/components/schemas/PagedLanguage" } } } @@ -3853,19 +1326,1823 @@ } } }, - "/umbraco/management/api/v1/analytics": { + "/umbraco/management/api/v1/language/{id}": { "get": { "tags": [ - "Analytics" + "Language" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } ], - "operationId": "GetAnalytics_Get", "responses": { - "200": { - "description": "", + "404": { + "description": "Not Found", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsLevelViewModel" + "$ref": "#/components/schemas/NotFoundResult" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Language" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Language" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/language/create": { + "post": { + "tags": [ + "Language" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Language" + } + } + } + }, + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "201": { + "description": "Created" + } + } + } + }, + "/umbraco/management/api/v1/language/update": { + "put": { + "tags": [ + "Language" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Language" + } + } + } + }, + "responses": { + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/tree/media-type/children": { + "get": { + "tags": [ + "Media Type" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFolderTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media-type/item": { + "get": { + "tags": [ + "Media Type" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FolderTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media-type/root": { + "get": { + "tags": [ + "Media Type" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "foldersOnly", + "in": "query", + "schema": { + "type": "boolean", + "default": false + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFolderTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/recycle-bin/media/children": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRecycleBinItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/recycle-bin/media/root": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRecycleBinItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media/children": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedContentTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media/item": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContentTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/media/root": { + "get": { + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + }, + { + "name": "dataTypeKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedContentTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/member-group/item": { + "get": { + "tags": [ + "Member Group" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/member-group/root": { + "get": { + "tags": [ + "Member Group" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/member-type/item": { + "get": { + "tags": [ + "Member Type" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/member-type/root": { + "get": { + "tags": [ + "Member Type" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/models-builder/build": { + "post": { + "tags": [ + "Models Builder" + ], + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatedResult" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/models-builder/dashboard": { + "get": { + "tags": [ + "Models Builder" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelsBuilder" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/models-builder/status": { + "get": { + "tags": [ + "Models Builder" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OutOfDateStatus" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/partial-view/children": { + "get": { + "tags": [ + "Partial View" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/partial-view/item": { + "get": { + "tags": [ + "Partial View" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/partial-view/root": { + "get": { + "tags": [ + "Partial View" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/profiling/status": { + "get": { + "tags": [ + "Profiling" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfilingStatus" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/published-cache/collect": { + "post": { + "tags": [ + "Published Cache" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/published-cache/rebuild": { + "post": { + "tags": [ + "Published Cache" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/published-cache/reload": { + "post": { + "tags": [ + "Published Cache" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/published-cache/status": { + "get": { + "tags": [ + "Published Cache" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/relation-type/item": { + "get": { + "tags": [ + "Relation Type" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FolderTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/relation-type/root": { + "get": { + "tags": [ + "Relation Type" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/relation/{id}": { + "get": { + "tags": [ + "Relation" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Relation" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundResult" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/relation/child-relation/{childId}": { + "get": { + "tags": [ + "Relation" + ], + "parameters": [ + { + "name": "childId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "relationTypeAlias", + "in": "query", + "schema": { + "type": "string", + "default": "" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRelation" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/script/children": { + "get": { + "tags": [ + "Script" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/script/item": { + "get": { + "tags": [ + "Script" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/script/root": { + "get": { + "tags": [ + "Script" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/index": { + "get": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedIndex" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/index/{indexName}": { + "get": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "indexName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Index" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/index/{indexName}/rebuild": { + "post": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "indexName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OkResult" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/searcher": { + "get": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedSearcher" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/search/searcher/{searcherName}/search": { + "get": { + "tags": [ + "Search" + ], + "parameters": [ + { + "name": "searcherName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedPaged" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/security/back-office/authorize": { + "get": { + "tags": [ + "Security" + ], + "responses": { + "200": { + "description": "Success" + } + } + }, + "post": { + "tags": [ + "Security" + ], + "responses": { + "200": { + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/server/status": { + "get": { + "tags": [ + "Server" + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerStatus" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/server/version": { + "get": { + "tags": [ + "Server" + ], + "responses": { + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Version" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/static-file/children": { + "get": { + "tags": [ + "Static File" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/static-file/item": { + "get": { + "tags": [ + "Static File" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/static-file/root": { + "get": { + "tags": [ + "Static File" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/stylesheet/children": { + "get": { + "tags": [ + "Stylesheet" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/stylesheet/item": { + "get": { + "tags": [ + "Stylesheet" + ], + "parameters": [ + { + "name": "path", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/stylesheet/root": { + "get": { + "tags": [ + "Stylesheet" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedFileSystemTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/telemetry": { + "get": { + "tags": [ + "Telemetry" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedTelemetry" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/telemetry/level": { + "get": { + "tags": [ + "Telemetry" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Telemetry" } } } @@ -3874,24 +3151,20 @@ }, "post": { "tags": [ - "Analytics" + "Telemetry" ], - "operationId": "SetAnalytics_SetConsentLevel", "requestBody": { - "x-name": "analyticsLevelViewModel", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsLevelViewModel" + "$ref": "#/components/schemas/Telemetry" } } - }, - "required": true, - "x-position": 1 + } }, "responses": { "400": { - "description": "", + "description": "Bad Request", "content": { "application/json": { "schema": { @@ -3901,7 +3174,350 @@ } }, "200": { - "description": "" + "description": "Success" + } + } + } + }, + "/umbraco/management/api/v1/tree/template/children": { + "get": { + "tags": [ + "Template" + ], + "parameters": [ + { + "name": "parentKey", + "in": "query", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/template/item": { + "get": { + "tags": [ + "Template" + ], + "parameters": [ + { + "name": "key", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + } + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tree/template/root": { + "get": { + "tags": [ + "Template" + ], + "parameters": [ + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 0 + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int32", + "default": 100 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedEntityTreeItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tracked-reference/{id}": { + "get": { + "tags": [ + "Tracked Reference" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRelationItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tracked-reference/descendants/{parentId}": { + "get": { + "tags": [ + "Tracked Reference" + ], + "parameters": [ + { + "name": "parentId", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRelationItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/tracked-reference/item": { + "get": { + "tags": [ + "Tracked Reference" + ], + "parameters": [ + { + "name": "ids", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + } + }, + { + "name": "skip", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "take", + "in": "query", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "filterMustBeIsDependency", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PagedRelationItem" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/upgrade/authorize": { + "post": { + "tags": [ + "Upgrade" + ], + "responses": { + "200": { + "description": "Success" + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/umbraco/management/api/v1/upgrade/settings": { + "get": { + "tags": [ + "Upgrade" + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpgradeSettings" + } + } + } + }, + "428": { + "description": "Client Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } } } } @@ -3909,11 +3525,2317 @@ }, "components": { "schemas": { + "Assembly": { + "type": "object", + "properties": { + "definedTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TypeInfo" + }, + "nullable": true, + "readOnly": true + }, + "exportedTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "codeBase": { + "type": "string", + "nullable": true, + "readOnly": true, + "deprecated": true + }, + "entryPoint": { + "$ref": "#/components/schemas/MethodInfo" + }, + "fullName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "imageRuntimeVersion": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "isDynamic": { + "type": "boolean", + "readOnly": true + }, + "location": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "reflectionOnly": { + "type": "boolean", + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "isFullyTrusted": { + "type": "boolean", + "readOnly": true + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "escapedCodeBase": { + "type": "string", + "nullable": true, + "readOnly": true, + "deprecated": true + }, + "manifestModule": { + "$ref": "#/components/schemas/Module" + }, + "modules": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Module" + }, + "nullable": true, + "readOnly": true + }, + "globalAssemblyCache": { + "type": "boolean", + "readOnly": true, + "deprecated": true + }, + "hostContext": { + "type": "integer", + "format": "int64", + "readOnly": true + }, + "securityRuleSet": { + "$ref": "#/components/schemas/SecurityRuleSet" + } + }, + "additionalProperties": false + }, + "BackOfficeNotification": { + "type": "object", + "properties": { + "header": { + "type": "string", + "nullable": true + }, + "message": { + "type": "string", + "nullable": true + }, + "notificationType": { + "$ref": "#/components/schemas/NotificationStyle" + } + }, + "additionalProperties": false + }, + "CallingConventions": { + "enum": [ + "Standard", + "VarArgs", + "Any", + "HasThis", + "ExplicitThis" + ], + "type": "integer", + "format": "int32" + }, + "ConsentLevel": { + "type": "object", + "properties": { + "level": { + "$ref": "#/components/schemas/TelemetryLevel" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ConstructorInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "attributes": { + "$ref": "#/components/schemas/MethodAttributes" + }, + "methodImplementationFlags": { + "$ref": "#/components/schemas/MethodImplAttributes" + }, + "callingConvention": { + "$ref": "#/components/schemas/CallingConventions" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isConstructor": { + "type": "boolean", + "readOnly": true + }, + "isFinal": { + "type": "boolean", + "readOnly": true + }, + "isHideBySig": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isVirtual": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodDefinition": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "methodHandle": { + "$ref": "#/components/schemas/RuntimeMethodHandle" + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + } + }, + "additionalProperties": false + }, + "ContentApp": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "alias": { + "type": "string", + "nullable": true + }, + "weight": { + "type": "integer", + "format": "int32" + }, + "icon": { + "type": "string", + "nullable": true + }, + "view": { + "type": "string", + "nullable": true + }, + "viewModel": { + "nullable": true + }, + "active": { + "type": "boolean" + }, + "badge": { + "$ref": "#/components/schemas/ContentAppBadge" + } + }, + "additionalProperties": false + }, + "ContentAppBadge": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "format": "int32" + }, + "type": { + "$ref": "#/components/schemas/ContentAppBadgeType" + } + }, + "additionalProperties": false + }, + "ContentAppBadgeType": { + "enum": [ + "default", + "warning", + "alert" + ], + "type": "integer", + "format": "int32" + }, + "ContentResult": { + "type": "object", + "properties": { + "content": { + "type": "string", + "nullable": true + }, + "contentType": { + "type": "string", + "nullable": true + }, + "statusCode": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "ContentTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "noAccess": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "CreatedResult": { + "type": "object", + "properties": { + "value": { + "nullable": true + }, + "formatters": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IOutputFormatter" + }, + "nullable": true + }, + "contentTypes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "declaredType": { + "$ref": "#/components/schemas/Type" + }, + "statusCode": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "location": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "Culture": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "englishName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "CustomAttributeData": { + "type": "object", + "properties": { + "attributeType": { + "$ref": "#/components/schemas/Type" + }, + "constructor": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "constructorArguments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeTypedArgument" + }, + "nullable": true, + "readOnly": true + }, + "namedArguments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeNamedArgument" + }, + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "CustomAttributeNamedArgument": { + "type": "object", + "properties": { + "memberInfo": { + "$ref": "#/components/schemas/MemberInfo" + }, + "typedValue": { + "$ref": "#/components/schemas/CustomAttributeTypedArgument" + }, + "memberName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "isField": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "CustomAttributeTypedArgument": { + "type": "object", + "properties": { + "argumentType": { + "$ref": "#/components/schemas/Type" + }, + "value": { + "nullable": true + } + }, + "additionalProperties": false + }, + "DatabaseInstall": { + "required": [ + "id", + "providerName" + ], + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "providerName": { + "minLength": 1, + "type": "string" + }, + "server": { + "type": "string", + "nullable": true + }, + "name": { + "type": "string", + "nullable": true + }, + "username": { + "type": "string", + "nullable": true + }, + "password": { + "type": "string", + "nullable": true + }, + "useIntegratedAuthentication": { + "type": "boolean" + }, + "connectionString": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DatabaseSettings": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, + "displayName": { + "type": "string", + "nullable": true + }, + "defaultDatabaseName": { + "type": "string", + "nullable": true + }, + "providerName": { + "type": "string", + "nullable": true + }, + "isConfigured": { + "type": "boolean" + }, + "requiresServer": { + "type": "boolean" + }, + "serverPlaceholder": { + "type": "string", + "nullable": true + }, + "requiresCredentials": { + "type": "boolean" + }, + "supportsIntegratedAuthentication": { + "type": "boolean" + }, + "requiresConnectionTest": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "Dictionary": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "parentId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "translations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryTranslation" + }, + "nullable": true + }, + "contentApps": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContentApp" + }, + "nullable": true + }, + "notifications": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BackOfficeNotification" + }, + "nullable": true, + "readOnly": true + }, + "name": { + "minLength": 1, + "type": "string" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "path": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DictionaryImport": { + "type": "object", + "properties": { + "dictionaryItems": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryItemsImport" + }, + "nullable": true + }, + "tempFileName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DictionaryItem": { + "type": "object", + "properties": { + "parentId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "key": { + "type": "string", + "format": "uuid" + } + }, + "additionalProperties": false + }, + "DictionaryItemsImport": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "level": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "DictionaryOverview": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "key": { + "type": "string", + "format": "uuid" + }, + "level": { + "type": "integer", + "format": "int32" + }, + "translations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryTranslationOverview" + }, + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "DictionaryTranslation": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "displayName": { + "type": "string", + "nullable": true + }, + "isoCode": { + "type": "string", + "nullable": true + }, + "translation": { + "type": "string", + "nullable": true + }, + "languageId": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "DictionaryTranslationOverview": { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "nullable": true + }, + "hasTranslation": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DocumentBlueprintTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "documentTypeKey": { + "type": "string", + "format": "uuid" + }, + "documentTypeAlias": { + "type": "string", + "nullable": true + }, + "documentTypeName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "DocumentTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "noAccess": { + "type": "boolean" + }, + "isProtected": { + "type": "boolean" + }, + "isPublished": { + "type": "boolean" + }, + "isEdited": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DocumentTypeTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "isFolder": { + "type": "boolean" + }, + "isElement": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "EntityTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + } + }, + "additionalProperties": false + }, + "EventAttributes": { + "enum": [ + "None", + "SpecialName", + "RTSpecialName", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "EventInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "attributes": { + "$ref": "#/components/schemas/EventAttributes" + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "addMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "removeMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "raiseMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "isMulticast": { + "type": "boolean", + "readOnly": true + }, + "eventHandlerType": { + "$ref": "#/components/schemas/Type" + } + }, + "additionalProperties": false + }, + "Field": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "values": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "FieldAttributes": { + "enum": [ + "PrivateScope", + "Private", + "FamANDAssem", + "Assembly", + "Family", + "FamORAssem", + "Public", + "FieldAccessMask", + "Static", + "InitOnly", + "Literal", + "NotSerialized", + "HasFieldRVA", + "SpecialName", + "RTSpecialName", + "HasFieldMarshal", + "PinvokeImpl", + "HasDefault", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "FieldInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "attributes": { + "$ref": "#/components/schemas/FieldAttributes" + }, + "fieldType": { + "$ref": "#/components/schemas/Type" + }, + "isInitOnly": { + "type": "boolean", + "readOnly": true + }, + "isLiteral": { + "type": "boolean", + "readOnly": true + }, + "isNotSerialized": { + "type": "boolean", + "readOnly": true + }, + "isPinvokeImpl": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "fieldHandle": { + "$ref": "#/components/schemas/RuntimeFieldHandle" + } + }, + "additionalProperties": false + }, + "FileSystemTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "path": { + "type": "string", + "nullable": true + }, + "isFolder": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "FolderTreeItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "key": { + "type": "string", + "format": "uuid" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "isFolder": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "GenericParameterAttributes": { + "enum": [ + "None", + "Covariant", + "Contravariant", + "VarianceMask", + "ReferenceTypeConstraint", + "NotNullableValueTypeConstraint", + "DefaultConstructorConstraint", + "SpecialConstraintMask" + ], + "type": "integer", + "format": "int32" + }, + "HelpPage": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ICustomAttributeProvider": { + "type": "object", + "additionalProperties": false + }, + "IOutputFormatter": { + "type": "object", + "additionalProperties": false + }, + "Index": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + }, + "healthStatus": { + "type": "string", + "nullable": true + }, + "isHealthy": { + "type": "boolean", + "readOnly": true + }, + "canRebuild": { + "type": "boolean" + }, + "searcherName": { + "type": "string", + "nullable": true + }, + "documentCount": { + "type": "integer", + "format": "int64" + }, + "fieldCount": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "Install": { + "required": [ + "database", + "user" + ], + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/UserInstall" + }, + "database": { + "$ref": "#/components/schemas/DatabaseInstall" + }, + "telemetryLevel": { + "$ref": "#/components/schemas/TelemetryLevel" + } + }, + "additionalProperties": false + }, + "InstallSettings": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/UserSettings" + }, + "databases": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DatabaseSettings" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "IntPtr": { + "type": "object", + "additionalProperties": false + }, + "JsonPatch": { + "type": "object", + "properties": { + "op": { + "type": "string", + "nullable": true + }, + "path": { + "type": "string", + "nullable": true + }, + "value": { + "nullable": true + } + }, + "additionalProperties": false + }, + "Language": { + "required": [ + "isoCode" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "isoCode": { + "minLength": 1, + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "isDefault": { + "type": "boolean" + }, + "isMandatory": { + "type": "boolean" + }, + "fallbackLanguageId": { + "type": "integer", + "format": "int32", + "nullable": true + } + }, + "additionalProperties": false + }, + "LayoutKind": { + "enum": [ + "Sequential", + "Explicit", + "Auto" + ], + "type": "integer", + "format": "int32" + }, + "MemberInfo": { + "type": "object", + "properties": { + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "MemberTypes": { + "enum": [ + "Constructor", + "Event", + "Field", + "Method", + "Property", + "TypeInfo", + "Custom", + "NestedType", + "All" + ], + "type": "integer", + "format": "int32" + }, + "MethodAttributes": { + "enum": [ + "ReuseSlot", + "PrivateScope", + "Private", + "FamANDAssem", + "Assembly", + "Family", + "FamORAssem", + "Public", + "MemberAccessMask", + "UnmanagedExport", + "Static", + "Final", + "Virtual", + "HideBySig", + "NewSlot", + "VtableLayoutMask", + "CheckAccessOnOverride", + "Abstract", + "SpecialName", + "RTSpecialName", + "PinvokeImpl", + "HasSecurity", + "RequireSecObject", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "MethodBase": { + "type": "object", + "properties": { + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "attributes": { + "$ref": "#/components/schemas/MethodAttributes" + }, + "methodImplementationFlags": { + "$ref": "#/components/schemas/MethodImplAttributes" + }, + "callingConvention": { + "$ref": "#/components/schemas/CallingConventions" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isConstructor": { + "type": "boolean", + "readOnly": true + }, + "isFinal": { + "type": "boolean", + "readOnly": true + }, + "isHideBySig": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isVirtual": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodDefinition": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "methodHandle": { + "$ref": "#/components/schemas/RuntimeMethodHandle" + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "MethodImplAttributes": { + "enum": [ + "IL", + "Managed", + "Native", + "OPTIL", + "Runtime", + "CodeTypeMask", + "Unmanaged", + "ManagedMask", + "NoInlining", + "ForwardRef", + "Synchronized", + "NoOptimization", + "PreserveSig", + "AggressiveInlining", + "AggressiveOptimization", + "InternalCall", + "MaxMethodImplVal" + ], + "type": "integer", + "format": "int32" + }, + "MethodInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "attributes": { + "$ref": "#/components/schemas/MethodAttributes" + }, + "methodImplementationFlags": { + "$ref": "#/components/schemas/MethodImplAttributes" + }, + "callingConvention": { + "$ref": "#/components/schemas/CallingConventions" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isConstructor": { + "type": "boolean", + "readOnly": true + }, + "isFinal": { + "type": "boolean", + "readOnly": true + }, + "isHideBySig": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isStatic": { + "type": "boolean", + "readOnly": true + }, + "isVirtual": { + "type": "boolean", + "readOnly": true + }, + "isAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamily": { + "type": "boolean", + "readOnly": true + }, + "isFamilyAndAssembly": { + "type": "boolean", + "readOnly": true + }, + "isFamilyOrAssembly": { + "type": "boolean", + "readOnly": true + }, + "isPrivate": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethod": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodDefinition": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "methodHandle": { + "$ref": "#/components/schemas/RuntimeMethodHandle" + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "returnParameter": { + "$ref": "#/components/schemas/ParameterInfo" + }, + "returnType": { + "$ref": "#/components/schemas/Type" + }, + "returnTypeCustomAttributes": { + "$ref": "#/components/schemas/ICustomAttributeProvider" + } + }, + "additionalProperties": false + }, + "ModelsBuilder": { + "type": "object", + "properties": { + "mode": { + "$ref": "#/components/schemas/ModelsMode" + }, + "canGenerate": { + "type": "boolean" + }, + "outOfDateModels": { + "type": "boolean" + }, + "lastError": { + "type": "string", + "nullable": true + }, + "version": { + "type": "string", + "nullable": true + }, + "modelsNamespace": { + "type": "string", + "nullable": true + }, + "trackingOutOfDateModels": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "ModelsMode": { + "enum": [ + "Nothing", + "InMemoryAuto", + "SourceCodeManual", + "SourceCodeAuto" + ], + "type": "integer", + "format": "int32" + }, + "Module": { + "type": "object", + "properties": { + "assembly": { + "$ref": "#/components/schemas/Assembly" + }, + "fullyQualifiedName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "mdStreamVersion": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "moduleVersionId": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "scopeName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "moduleHandle": { + "$ref": "#/components/schemas/ModuleHandle" + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "ModuleHandle": { + "type": "object", + "properties": { + "mdStreamVersion": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, + "NotFoundResult": { + "type": "object", + "properties": { + "statusCode": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "NotificationStyle": { + "enum": [ + "Save", + "Info", + "Error", + "Success", + "Warning" + ], + "type": "integer", + "format": "int32" + }, + "OkResult": { + "type": "object", + "properties": { + "statusCode": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "OutOfDateStatus": { + "type": "object", + "properties": { + "status": { + "$ref": "#/components/schemas/OutOfDateType" + } + }, + "additionalProperties": false + }, + "OutOfDateType": { + "enum": [ + "OutOfDate", + "Current", + "Unknown" + ], + "type": "integer", + "format": "int32" + }, + "PagedContentTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContentTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedCulture": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Culture" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDictionaryOverview": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DictionaryOverview" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDocumentBlueprintTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentBlueprintTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDocumentTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedDocumentTypeTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentTypeTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedEntityTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedFileSystemTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystemTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedFolderTreeItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FolderTreeItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedHelpPage": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HelpPage" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedIndex": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Index" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedLanguage": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Language" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedPaged": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PagedSearchResult" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedRecycleBinItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecycleBinItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedRelation": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Relation" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedRelationItem": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RelationItem" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedSearchResult": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SearchResult" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedSearcher": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Searcher" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "PagedTelemetry": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Telemetry" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ParameterAttributes": { + "enum": [ + "None", + "In", + "Out", + "Lcid", + "Retval", + "Optional", + "HasDefault", + "HasFieldMarshal", + "Reserved3", + "Reserved4", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "ParameterInfo": { + "type": "object", + "properties": { + "attributes": { + "$ref": "#/components/schemas/ParameterAttributes" + }, + "member": { + "$ref": "#/components/schemas/MemberInfo" + }, + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "parameterType": { + "$ref": "#/components/schemas/Type" + }, + "position": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "isIn": { + "type": "boolean", + "readOnly": true + }, + "isLcid": { + "type": "boolean", + "readOnly": true + }, + "isOptional": { + "type": "boolean", + "readOnly": true + }, + "isOut": { + "type": "boolean", + "readOnly": true + }, + "isRetval": { + "type": "boolean", + "readOnly": true + }, + "defaultValue": { + "nullable": true, + "readOnly": true + }, + "rawDefaultValue": { + "nullable": true, + "readOnly": true + }, + "hasDefaultValue": { + "type": "boolean", + "readOnly": true + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + }, "ProblemDetails": { "type": "object", - "additionalProperties": { - "nullable": true - }, "properties": { "type": { "type": "string", @@ -3936,52 +5858,165 @@ "type": "string", "nullable": true } - } + }, + "additionalProperties": { } }, - "UpgradeSettingsViewModel": { + "ProfilingStatus": { "type": "object", - "additionalProperties": false, "properties": { - "currentState": { - "type": "string" - }, - "newState": { - "type": "string" - }, - "newVersion": { - "type": "string" - }, - "oldVersion": { - "type": "string" - }, - "reportUrl": { - "type": "string" + "enabled": { + "type": "boolean" } - } + }, + "additionalProperties": false }, - "PagedViewModelOfRelationItemViewModel": { + "PropertyAttributes": { + "enum": [ + "None", + "SpecialName", + "RTSpecialName", + "HasDefault", + "Reserved2", + "Reserved3", + "Reserved4", + "ReservedMask" + ], + "type": "integer", + "format": "int32" + }, + "PropertyInfo": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" + "name": { + "type": "string", + "nullable": true, + "readOnly": true }, - "items": { + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "customAttributes": { "type": "array", "items": { - "$ref": "#/components/schemas/RelationItemViewModel" - } + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "propertyType": { + "$ref": "#/components/schemas/Type" + }, + "attributes": { + "$ref": "#/components/schemas/PropertyAttributes" + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "canRead": { + "type": "boolean", + "readOnly": true + }, + "canWrite": { + "type": "boolean", + "readOnly": true + }, + "getMethod": { + "$ref": "#/components/schemas/MethodInfo" + }, + "setMethod": { + "$ref": "#/components/schemas/MethodInfo" } - } + }, + "additionalProperties": false }, - "RelationItemViewModel": { + "RecycleBinItem": { + "type": "object", + "properties": { + "key": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + }, + "icon": { + "type": "string", + "nullable": true + }, + "hasChildren": { + "type": "boolean" + }, + "isContainer": { + "type": "boolean" + }, + "parentKey": { + "type": "string", + "format": "uuid", + "nullable": true + } + }, + "additionalProperties": false + }, + "Relation": { + "type": "object", + "properties": { + "parentId": { + "type": "integer", + "format": "int32" + }, + "parentName": { + "type": "string", + "nullable": true + }, + "childId": { + "type": "integer", + "format": "int32" + }, + "childName": { + "type": "string", + "nullable": true + }, + "createDate": { + "type": "string", + "format": "date-time" + }, + "comment": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "RelationItem": { "type": "object", - "additionalProperties": false, "properties": { "nodeKey": { "type": "string", - "format": "guid" + "format": "uuid" }, "nodeName": { "type": "string", @@ -4013,122 +6048,19 @@ "relationTypeIsDependency": { "type": "boolean" } - } + }, + "additionalProperties": false }, - "PagedViewModelOfEntityTreeItemViewModel": { + "RuntimeFieldHandle": { "type": "object", - "additionalProperties": false, "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - } + "value": { + "$ref": "#/components/schemas/IntPtr" } - } - }, - "EntityTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/TreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string", - "format": "guid" - }, - "isContainer": { - "type": "boolean" - }, - "parentKey": { - "type": "string", - "format": "guid", - "nullable": true - } - } - } - ] - }, - "TreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "hasChildren": { - "type": "boolean" - } - } - }, - "PagedViewModelOfFileSystemTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FileSystemTreeItemViewModel" - } - } - } - }, - "FileSystemTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/TreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "path": { - "type": "string" - }, - "isFolder": { - "type": "boolean" - } - } - } - ] - }, - "ServerStatusViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "serverStatus": { - "$ref": "#/components/schemas/RuntimeLevel" - } - } + }, + "additionalProperties": false }, "RuntimeLevel": { - "type": "string", - "description": "Describes the levels in which the runtime can run.\n ", - "x-enumNames": [ - "Unknown", - "Boot", - "Install", - "Upgrade", - "Run", - "BootFailed" - ], "enum": [ "Unknown", "Boot", @@ -4136,126 +6068,34 @@ "Upgrade", "Run", "BootFailed" - ] + ], + "type": "integer", + "format": "int32" }, - "VersionViewModel": { + "RuntimeMethodHandle": { "type": "object", - "additionalProperties": false, "properties": { - "version": { - "type": "string" + "value": { + "$ref": "#/components/schemas/IntPtr" } - } + }, + "additionalProperties": false }, - "IndexViewModel": { + "RuntimeTypeHandle": { "type": "object", - "additionalProperties": false, "properties": { - "name": { - "type": "string" - }, - "healthStatus": { - "type": "string", - "nullable": true - }, - "isHealthy": { - "type": "boolean" - }, - "canRebuild": { - "type": "boolean" - }, - "searcherName": { - "type": "string" - }, - "documentCount": { - "type": "integer", - "format": "int64" - }, - "fieldCount": { - "type": "integer", - "format": "int32" + "value": { + "$ref": "#/components/schemas/IntPtr" } - } + }, + "additionalProperties": false }, - "PagedViewModelOfIndexViewModel": { + "SearchResult": { "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IndexViewModel" - } - } - } - }, - "PagedViewModelOfSearcherViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearcherViewModel" - } - } - } - }, - "SearcherViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - } - } - }, - "PagedViewModelOfPagedViewModelOfSearchResultViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PagedViewModelOfSearchResultViewModel" - } - } - } - }, - "PagedViewModelOfSearchResultViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearchResultViewModel" - } - } - } - }, - "SearchResultViewModel": { - "type": "object", - "additionalProperties": false, "properties": { "id": { - "type": "string" + "type": "string", + "nullable": true }, "score": { "type": "number", @@ -4263,297 +6103,830 @@ }, "fieldCount": { "type": "integer", - "format": "int32" + "format": "int32", + "readOnly": true }, "fields": { "type": "array", "items": { - "$ref": "#/components/schemas/FieldViewModel" - } + "$ref": "#/components/schemas/Field" + }, + "nullable": true } - } + }, + "additionalProperties": false }, - "FieldViewModel": { + "Searcher": { "type": "object", - "additionalProperties": false, "properties": { "name": { - "type": "string" - }, - "values": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "PagedViewModelOfRelationViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RelationViewModel" - } - } - } - }, - "RelationViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "parentId": { - "type": "integer", - "readOnly": true, - "description": "Gets or sets the Parent Id of the Relation (Source).\n ", - "format": "int32" - }, - "parentName": { "type": "string", - "readOnly": true, - "description": "Gets or sets the Parent Name of the relation (Source).\n ", - "nullable": true - }, - "childId": { - "type": "integer", - "readOnly": true, - "description": "Gets or sets the Child Id of the Relation (Destination).\n ", - "format": "int32" - }, - "childName": { - "type": "string", - "readOnly": true, - "description": "Gets or sets the Child Name of the relation (Destination).\n ", - "nullable": true - }, - "createDate": { - "type": "string", - "readOnly": true, - "description": "Gets or sets the date when the Relation was created.\n ", - "format": "date-time" - }, - "comment": { - "type": "string", - "readOnly": true, - "description": "Gets or sets a comment for the Relation.\n ", "nullable": true } - } + }, + "additionalProperties": false }, - "FolderTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "isFolder": { - "type": "boolean" - } - } - } - ] - }, - "ProfilingStatusViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean" - } - } - }, - "OutOfDateStatusViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "status": { - "$ref": "#/components/schemas/OutOfDateType" - } - } - }, - "OutOfDateType": { - "type": "integer", - "description": "", - "x-enumNames": [ - "OutOfDate", - "Current", - "Unknown" - ], + "SecurityRuleSet": { "enum": [ - 0, - 1, - 100 - ] - }, - "PagedViewModelOfContentTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ContentTreeItemViewModel" - } - } - } - }, - "ContentTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "noAccess": { - "type": "boolean" - } - } - } - ] - }, - "PagedViewModelOfRecycleBinItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RecycleBinItemViewModel" - } - } - } - }, - "RecycleBinItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "key": { - "type": "string", - "format": "guid" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "hasChildren": { - "type": "boolean" - }, - "isContainer": { - "type": "boolean" - }, - "parentKey": { - "type": "string", - "format": "guid", - "nullable": true - } - } - }, - "PagedViewModelOfFolderTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FolderTreeItemViewModel" - } - } - } - }, - "PagedViewModelOfLanguageViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LanguageViewModel" - } - } - } - }, - "LanguageViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "isoCode" + "None", + "Level1", + "Level2" ], + "type": "integer", + "format": "int32" + }, + "ServerStatus": { + "type": "object", "properties": { - "id": { - "type": "integer", - "format": "int32" - }, - "isoCode": { - "type": "string", - "minLength": 1 + "serverStatus": { + "$ref": "#/components/schemas/RuntimeLevel" + } + }, + "additionalProperties": false + }, + "StructLayoutAttribute": { + "type": "object", + "properties": { + "typeId": { + "nullable": true, + "readOnly": true }, + "value": { + "$ref": "#/components/schemas/LayoutKind" + } + }, + "additionalProperties": false + }, + "Telemetry": { + "type": "object", + "properties": { + "telemetryLevel": { + "$ref": "#/components/schemas/TelemetryLevel" + } + }, + "additionalProperties": false + }, + "TelemetryLevel": { + "enum": [ + "Minimal", + "Basic", + "Detailed" + ], + "type": "integer", + "format": "int32" + }, + "Type": { + "type": "object", + "properties": { "name": { "type": "string", - "nullable": true + "nullable": true, + "readOnly": true }, - "isDefault": { - "type": "boolean" + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true }, - "isMandatory": { - "type": "boolean" + "isCollectible": { + "type": "boolean", + "readOnly": true }, - "fallbackLanguageId": { + "metadataToken": { "type": "integer", "format": "int32", - "nullable": true - } - } - }, - "InstallSettingsViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "user": { - "$ref": "#/components/schemas/UserSettingsViewModel" + "readOnly": true }, - "databases": { + "isInterface": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "namespace": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assemblyQualifiedName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "fullName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assembly": { + "$ref": "#/components/schemas/Assembly" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "isNested": { + "type": "boolean", + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "declaringMethod": { + "$ref": "#/components/schemas/MethodBase" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "underlyingSystemType": { + "$ref": "#/components/schemas/Type" + }, + "isTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isArray": { + "type": "boolean", + "readOnly": true + }, + "isByRef": { + "type": "boolean", + "readOnly": true + }, + "isPointer": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isSZArray": { + "type": "boolean", + "readOnly": true + }, + "isVariableBoundArray": { + "type": "boolean", + "readOnly": true + }, + "isByRefLike": { + "type": "boolean", + "readOnly": true + }, + "hasElementType": { + "type": "boolean", + "readOnly": true + }, + "genericTypeArguments": { "type": "array", "items": { - "$ref": "#/components/schemas/DatabaseSettingsViewModel" - } + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "genericParameterPosition": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "genericParameterAttributes": { + "$ref": "#/components/schemas/GenericParameterAttributes" + }, + "attributes": { + "$ref": "#/components/schemas/TypeAttributes" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isImport": { + "type": "boolean", + "readOnly": true + }, + "isSealed": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isClass": { + "type": "boolean", + "readOnly": true + }, + "isNestedAssembly": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamANDAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamily": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamORAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedPrivate": { + "type": "boolean", + "readOnly": true + }, + "isNestedPublic": { + "type": "boolean", + "readOnly": true + }, + "isNotPublic": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isAutoLayout": { + "type": "boolean", + "readOnly": true + }, + "isExplicitLayout": { + "type": "boolean", + "readOnly": true + }, + "isLayoutSequential": { + "type": "boolean", + "readOnly": true + }, + "isAnsiClass": { + "type": "boolean", + "readOnly": true + }, + "isAutoClass": { + "type": "boolean", + "readOnly": true + }, + "isUnicodeClass": { + "type": "boolean", + "readOnly": true + }, + "isCOMObject": { + "type": "boolean", + "readOnly": true + }, + "isContextful": { + "type": "boolean", + "readOnly": true + }, + "isEnum": { + "type": "boolean", + "readOnly": true + }, + "isMarshalByRef": { + "type": "boolean", + "readOnly": true + }, + "isPrimitive": { + "type": "boolean", + "readOnly": true + }, + "isValueType": { + "type": "boolean", + "readOnly": true + }, + "isSignatureType": { + "type": "boolean", + "readOnly": true + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "structLayoutAttribute": { + "$ref": "#/components/schemas/StructLayoutAttribute" + }, + "typeInitializer": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "typeHandle": { + "$ref": "#/components/schemas/RuntimeTypeHandle" + }, + "guid": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "baseType": { + "$ref": "#/components/schemas/Type" + }, + "isSerializable": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "isVisible": { + "type": "boolean", + "readOnly": true } - } + }, + "additionalProperties": false }, - "UserSettingsViewModel": { + "TypeAttributes": { + "enum": [ + "NotPublic", + "AutoLayout", + "AnsiClass", + "Class", + "Public", + "NestedPublic", + "NestedPrivate", + "NestedFamily", + "NestedAssembly", + "NestedFamANDAssem", + "NestedFamORAssem", + "VisibilityMask", + "SequentialLayout", + "ExplicitLayout", + "LayoutMask", + "Interface", + "ClassSemanticsMask", + "Abstract", + "Sealed", + "SpecialName", + "RTSpecialName", + "Import", + "Serializable", + "WindowsRuntime", + "UnicodeClass", + "AutoClass", + "CustomFormatClass", + "StringFormatMask", + "HasSecurity", + "ReservedMask", + "BeforeFieldInit", + "CustomFormatMask" + ], + "type": "integer", + "format": "int32" + }, + "TypeInfo": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "customAttributes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomAttributeData" + }, + "nullable": true, + "readOnly": true + }, + "isCollectible": { + "type": "boolean", + "readOnly": true + }, + "metadataToken": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "isInterface": { + "type": "boolean", + "readOnly": true + }, + "memberType": { + "$ref": "#/components/schemas/MemberTypes" + }, + "namespace": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assemblyQualifiedName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "fullName": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "assembly": { + "$ref": "#/components/schemas/Assembly" + }, + "module": { + "$ref": "#/components/schemas/Module" + }, + "isNested": { + "type": "boolean", + "readOnly": true + }, + "declaringType": { + "$ref": "#/components/schemas/Type" + }, + "declaringMethod": { + "$ref": "#/components/schemas/MethodBase" + }, + "reflectedType": { + "$ref": "#/components/schemas/Type" + }, + "underlyingSystemType": { + "$ref": "#/components/schemas/Type" + }, + "isTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isArray": { + "type": "boolean", + "readOnly": true + }, + "isByRef": { + "type": "boolean", + "readOnly": true + }, + "isPointer": { + "type": "boolean", + "readOnly": true + }, + "isConstructedGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericMethodParameter": { + "type": "boolean", + "readOnly": true + }, + "isGenericType": { + "type": "boolean", + "readOnly": true + }, + "isGenericTypeDefinition": { + "type": "boolean", + "readOnly": true + }, + "isSZArray": { + "type": "boolean", + "readOnly": true + }, + "isVariableBoundArray": { + "type": "boolean", + "readOnly": true + }, + "isByRefLike": { + "type": "boolean", + "readOnly": true + }, + "hasElementType": { + "type": "boolean", + "readOnly": true + }, + "genericTypeArguments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "genericParameterPosition": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "genericParameterAttributes": { + "$ref": "#/components/schemas/GenericParameterAttributes" + }, + "attributes": { + "$ref": "#/components/schemas/TypeAttributes" + }, + "isAbstract": { + "type": "boolean", + "readOnly": true + }, + "isImport": { + "type": "boolean", + "readOnly": true + }, + "isSealed": { + "type": "boolean", + "readOnly": true + }, + "isSpecialName": { + "type": "boolean", + "readOnly": true + }, + "isClass": { + "type": "boolean", + "readOnly": true + }, + "isNestedAssembly": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamANDAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamily": { + "type": "boolean", + "readOnly": true + }, + "isNestedFamORAssem": { + "type": "boolean", + "readOnly": true + }, + "isNestedPrivate": { + "type": "boolean", + "readOnly": true + }, + "isNestedPublic": { + "type": "boolean", + "readOnly": true + }, + "isNotPublic": { + "type": "boolean", + "readOnly": true + }, + "isPublic": { + "type": "boolean", + "readOnly": true + }, + "isAutoLayout": { + "type": "boolean", + "readOnly": true + }, + "isExplicitLayout": { + "type": "boolean", + "readOnly": true + }, + "isLayoutSequential": { + "type": "boolean", + "readOnly": true + }, + "isAnsiClass": { + "type": "boolean", + "readOnly": true + }, + "isAutoClass": { + "type": "boolean", + "readOnly": true + }, + "isUnicodeClass": { + "type": "boolean", + "readOnly": true + }, + "isCOMObject": { + "type": "boolean", + "readOnly": true + }, + "isContextful": { + "type": "boolean", + "readOnly": true + }, + "isEnum": { + "type": "boolean", + "readOnly": true + }, + "isMarshalByRef": { + "type": "boolean", + "readOnly": true + }, + "isPrimitive": { + "type": "boolean", + "readOnly": true + }, + "isValueType": { + "type": "boolean", + "readOnly": true + }, + "isSignatureType": { + "type": "boolean", + "readOnly": true + }, + "isSecurityCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecuritySafeCritical": { + "type": "boolean", + "readOnly": true + }, + "isSecurityTransparent": { + "type": "boolean", + "readOnly": true + }, + "structLayoutAttribute": { + "$ref": "#/components/schemas/StructLayoutAttribute" + }, + "typeInitializer": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "typeHandle": { + "$ref": "#/components/schemas/RuntimeTypeHandle" + }, + "guid": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "baseType": { + "$ref": "#/components/schemas/Type" + }, + "isSerializable": { + "type": "boolean", + "readOnly": true + }, + "containsGenericParameters": { + "type": "boolean", + "readOnly": true + }, + "isVisible": { + "type": "boolean", + "readOnly": true + }, + "genericTypeParameters": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + }, + "declaredConstructors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ConstructorInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredEvents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredFields": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FieldInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredMembers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredMethods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MethodInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredNestedTypes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TypeInfo" + }, + "nullable": true, + "readOnly": true + }, + "declaredProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PropertyInfo" + }, + "nullable": true, + "readOnly": true + }, + "implementedInterfaces": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Type" + }, + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "UpgradeSettings": { + "type": "object", + "properties": { + "currentState": { + "type": "string", + "nullable": true + }, + "newState": { + "type": "string", + "nullable": true + }, + "newVersion": { + "type": "string", + "nullable": true + }, + "oldVersion": { + "type": "string", + "nullable": true + }, + "reportUrl": { + "type": "string", + "nullable": true, + "readOnly": true + } + }, + "additionalProperties": false + }, + "UserInstall": { + "required": [ + "email", + "name", + "password" + ], + "type": "object", + "properties": { + "name": { + "maxLength": 255, + "minLength": 0, + "type": "string" + }, + "email": { + "minLength": 1, + "type": "string", + "format": "email" + }, + "password": { + "minLength": 1, + "type": "string" + }, + "subscribeToNewsletter": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "UserSettings": { "type": "object", - "additionalProperties": false, "properties": { "minCharLength": { "type": "integer", @@ -4566,789 +6939,41 @@ "consentLevels": { "type": "array", "items": { - "$ref": "#/components/schemas/ConsentLevelViewModel" - } - } - } - }, - "ConsentLevelViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "level": { - "$ref": "#/components/schemas/TelemetryLevel" - }, - "description": { - "type": "string" - } - } - }, - "TelemetryLevel": { - "type": "string", - "description": "", - "x-enumNames": [ - "Minimal", - "Basic", - "Detailed" - ], - "enum": [ - "Minimal", - "Basic", - "Detailed" - ] - }, - "DatabaseSettingsViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "id": { - "type": "string", - "format": "guid" - }, - "sortOrder": { - "type": "integer", - "format": "int32" - }, - "displayName": { - "type": "string" - }, - "defaultDatabaseName": { - "type": "string" - }, - "providerName": { - "type": "string" - }, - "isConfigured": { - "type": "boolean" - }, - "requiresServer": { - "type": "boolean" - }, - "serverPlaceholder": { - "type": "string" - }, - "requiresCredentials": { - "type": "boolean" - }, - "supportsIntegratedAuthentication": { - "type": "boolean" - }, - "requiresConnectionTest": { - "type": "boolean" - } - } - }, - "InstallViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "user", - "database" - ], - "properties": { - "user": { - "$ref": "#/components/schemas/UserInstallViewModel" - }, - "database": { - "$ref": "#/components/schemas/DatabaseInstallViewModel" - }, - "telemetryLevel": { - "$ref": "#/components/schemas/TelemetryLevel" - } - } - }, - "UserInstallViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "email", - "password" - ], - "properties": { - "name": { - "type": "string", - "maxLength": 255, - "minLength": 0 - }, - "email": { - "type": "string", - "format": "email", - "minLength": 1 - }, - "password": { - "type": "string", - "minLength": 1 - }, - "subscribeToNewsletter": { - "type": "boolean" - } - } - }, - "DatabaseInstallViewModel": { - "type": "object", - "additionalProperties": false, - "required": [ - "id", - "providerName" - ], - "properties": { - "id": { - "type": "string", - "format": "guid", - "minLength": 1 - }, - "providerName": { - "type": "string", - "minLength": 1 - }, - "server": { - "type": "string", + "$ref": "#/components/schemas/ConsentLevel" + }, "nullable": true - }, - "name": { - "type": "string", - "nullable": true - }, - "username": { - "type": "string", - "nullable": true - }, - "password": { - "type": "string", - "nullable": true - }, - "useIntegratedAuthentication": { - "type": "boolean" - }, - "connectionString": { + } + }, + "additionalProperties": false + }, + "Version": { + "type": "object", + "properties": { + "version": { "type": "string", "nullable": true } - } - }, - "PagedViewModelOfHelpPageViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/HelpPageViewModel" - } - } - } - }, - "HelpPageViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "nullable": true - }, - "description": { - "type": "string", - "nullable": true - }, - "url": { - "type": "string", - "nullable": true - }, - "type": { - "type": "string", - "nullable": true - } - } - }, - "PagedViewModelOfDocumentTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentTreeItemViewModel" - } - } - } - }, - "DocumentTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/ContentTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "isProtected": { - "type": "boolean" - }, - "isPublished": { - "type": "boolean" - }, - "isEdited": { - "type": "boolean" - } - } - } - ] - }, - "PagedViewModelOfDocumentTypeTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentTypeTreeItemViewModel" - } - } - } - }, - "DocumentTypeTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/FolderTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "isElement": { - "type": "boolean" - } - } - } - ] - }, - "DocumentBlueprintTreeItemViewModel": { - "allOf": [ - { - "$ref": "#/components/schemas/EntityTreeItemViewModel" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "documentTypeKey": { - "type": "string", - "format": "guid" - }, - "documentTypeAlias": { - "type": "string" - }, - "documentTypeName": { - "type": "string", - "nullable": true - } - } - } - ] - }, - "PagedViewModelOfDocumentBlueprintTreeItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DocumentBlueprintTreeItemViewModel" - } - } - } - }, - "PagedViewModelOfDictionaryOverviewViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DictionaryOverviewViewModel" - } - } - } - }, - "DictionaryOverviewViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "description": "Gets or sets the key.\n ", - "nullable": true - }, - "key": { - "type": "string", - "description": "Gets or sets the key.\n ", - "format": "guid" - }, - "level": { - "type": "integer", - "description": "Gets or sets the level.\n ", - "format": "int32" - }, - "translations": { - "type": "array", - "description": "Sets the translations.\n ", - "items": { - "$ref": "#/components/schemas/DictionaryTranslationOverviewViewModel" - } - } - } - }, - "DictionaryTranslationOverviewViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "displayName": { - "type": "string", - "description": "Gets or sets the display name.\n ", - "nullable": true - }, - "hasTranslation": { - "type": "boolean", - "description": "Gets or sets a value indicating whether has translation.\n " - } - } - }, - "DictionaryViewModel": { - "type": "object", - "description": "The dictionary display model\n ", - "additionalProperties": false, - "required": [ - "name" - ], - "properties": { - "parentId": { - "type": "string", - "description": "Gets or sets the parent id.\n ", - "format": "guid", - "nullable": true - }, - "translations": { - "type": "array", - "description": "Gets or sets the translations.\n ", - "items": { - "$ref": "#/components/schemas/DictionaryTranslationViewModel" - } - }, - "contentApps": { - "type": "array", - "description": "Apps for the dictionary item\n ", - "items": { - "$ref": "#/components/schemas/ContentApp" - } - }, - "notifications": { - "type": "array", - "description": "This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.\n ", - "items": { - "$ref": "#/components/schemas/BackOfficeNotification" - } - }, - "name": { - "type": "string", - "minLength": 1 - }, - "key": { - "type": "string", - "description": "Gets or sets the Key for the object\n ", - "format": "guid" - }, - "path": { - "type": "string", - "description": "The path of the entity\n " - } - } - }, - "DictionaryTranslationViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "id": { - "type": "integer", - "format": "int32" - }, - "key": { - "type": "string", - "format": "guid" - }, - "displayName": { - "type": "string", - "description": "Gets or sets the display name.\n ", - "nullable": true - }, - "isoCode": { - "type": "string", - "description": "Gets or sets the ISO code.\n ", - "nullable": true - }, - "translation": { - "type": "string", - "description": "Gets or sets the translation.\n " - }, - "languageId": { - "type": "integer", - "description": "Gets or sets the language id.\n ", - "format": "int32" - } - } - }, - "ContentApp": { - "type": "object", - "description": "Represents a content app.\n ", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "description": "Gets the name of the content app.\n ", - "nullable": true - }, - "alias": { - "type": "string", - "description": "Gets the unique alias of the content app.\n ", - "nullable": true - }, - "weight": { - "type": "integer", - "description": "Gets or sets the weight of the content app.\n ", - "format": "int32" - }, - "icon": { - "type": "string", - "description": "Gets the icon of the content app.\n ", - "nullable": true - }, - "view": { - "type": "string", - "description": "Gets the view for rendering the content app.\n ", - "nullable": true - }, - "viewModel": { - "description": "The view model specific to this app\n ", - "nullable": true - }, - "active": { - "type": "boolean", - "description": "Gets a value indicating whether the app is active.\n " - }, - "badge": { - "description": "Gets or sets the content app badge.\n ", - "nullable": true, - "oneOf": [ - { - "$ref": "#/components/schemas/ContentAppBadge" - } - ] - } - } - }, - "ContentAppBadge": { - "type": "object", - "description": "Represents a content app badge\n ", - "additionalProperties": false, - "properties": { - "count": { - "type": "integer", - "description": "Gets or sets the number displayed in the badge\n ", - "format": "int32" - }, - "type": { - "description": "Gets or sets the type of badge to display\n ", - "oneOf": [ - { - "$ref": "#/components/schemas/ContentAppBadgeType" - } - ] - } - } - }, - "ContentAppBadgeType": { - "type": "integer", - "description": "Represent the content app badge types\n ", - "x-enumNames": [ - "Default", - "Warning", - "Alert" - ], - "enum": [ - 0, - 1, - 2 - ] - }, - "BackOfficeNotification": { - "type": "object", - "additionalProperties": false, - "properties": { - "header": { - "type": "string", - "nullable": true - }, - "message": { - "type": "string", - "nullable": true - }, - "notificationType": { - "$ref": "#/components/schemas/NotificationStyle" - } - } - }, - "NotificationStyle": { - "type": "integer", - "description": "", - "x-enumNames": [ - "Save", - "Info", - "Error", - "Success", - "Warning" - ], - "enum": [ - 0, - 1, - 2, - 3, - 4 - ] - }, - "DictionaryItemViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "parentId": { - "type": "string", - "format": "guid", - "nullable": true - }, - "key": { - "type": "string", - "format": "guid" - } - } - }, - "JsonPatchViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "op": { - "type": "string" - }, - "path": { - "type": "string" - }, - "value": {} - } - }, - "DictionaryImportViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "dictionaryItems": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DictionaryItemsImportViewModel" - } - }, - "tempFileName": { - "type": "string", - "nullable": true - } - } - }, - "DictionaryItemsImportViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "nullable": true - }, - "level": { - "type": "integer", - "format": "int32" - } - } - }, - "PagedViewModelOfCultureViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CultureViewModel" - } - } - } - }, - "CultureViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "englishName": { - "type": "string" - } - } - }, - "PagedViewModelOfTelemetryLevel": { - "type": "object", - "additionalProperties": false, - "properties": { - "total": { - "type": "integer", - "format": "int64" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TelemetryLevel2" - } - } - } - }, - "TelemetryLevel2": { - "type": "integer", - "description": "", - "x-enumNames": [ - "Minimal", - "Basic", - "Detailed" - ], - "enum": [ - 0, - 1, - 2 - ] - }, - "AnalyticsLevelViewModel": { - "type": "object", - "additionalProperties": false, - "properties": { - "analyticsLevel": { - "$ref": "#/components/schemas/TelemetryLevel" + }, + "additionalProperties": false + } + }, + "securitySchemes": { + "OAuth": { + "type": "oauth2", + "description": "Umbraco Authentication", + "flows": { + "authorizationCode": { + "authorizationUrl": "/umbraco/management/api/v1.0/security/back-office/authorize", + "tokenUrl": "/umbraco/management/api/v1.0/security/back-office/token", + "scopes": { } } } } } }, - "tags": [ + "security": [ { - "name": "Analytics" - }, - { - "name": "Culture" - }, - { - "name": "DataType" - }, - { - "name": "Dictionary" - }, - { - "name": "DictionaryItem" - }, - { - "name": "Document" - }, - { - "name": "DocumentBlueprint" - }, - { - "name": "DocumentType" - }, - { - "name": "Help" - }, - { - "name": "Install" - }, - { - "name": "Language" - }, - { - "name": "Media" - }, - { - "name": "MediaType" - }, - { - "name": "MemberGroup" - }, - { - "name": "MemberType" - }, - { - "name": "ModelsBuilder" - }, - { - "name": "PartialView" - }, - { - "name": "Profiling" - }, - { - "name": "PublishedCache" - }, - { - "name": "Relation" - }, - { - "name": "RelationType" - }, - { - "name": "Script" - }, - { - "name": "Search" - }, - { - "name": "Server" - }, - { - "name": "StaticFile" - }, - { - "name": "Stylesheet" - }, - { - "name": "Template" - }, - { - "name": "TrackedReferences" - }, - { - "name": "Upgrade" + "OAuth": [ ] } ] } diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs new file mode 100644 index 0000000000..2b8dd3f469 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/EnumSchemaFilter.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Runtime.Serialization; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +public class EnumSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema model, SchemaFilterContext context) + { + if (context.Type.IsEnum) + { + model.Enum.Clear(); + foreach (var name in Enum.GetNames(context.Type)) + { + var actualName = context.Type.GetField(name)?.GetCustomAttribute()?.Value ?? name; + model.Enum.Add(new OpenApiString(actualName)); + } + } + } +} diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs new file mode 100644 index 0000000000..f49a89e22f --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/MimeTypeDocumentFilter.cs @@ -0,0 +1,33 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using Umbraco.Extensions; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +/// +/// This filter explicitly removes all other mime types than application/json from the produced OpenAPI document +/// +public class MimeTypeDocumentFilter : IDocumentFilter +{ + public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) + { + OpenApiOperation[] operations = swaggerDoc.Paths + .SelectMany(path => path.Value.Operations.Values) + .ToArray(); + + void RemoveUnwantedMimeTypes(IDictionary content) => + content.RemoveAll(r => r.Key != "application/json"); + + OpenApiRequestBody[] requestBodies = operations.Select(operation => operation.RequestBody).WhereNotNull().ToArray(); + foreach (OpenApiRequestBody requestBody in requestBodies) + { + RemoveUnwantedMimeTypes(requestBody.Content); + } + + OpenApiResponse[] responses = operations.SelectMany(operation => operation.Responses.Values).WhereNotNull().ToArray(); + foreach (OpenApiResponse response in responses) + { + RemoveUnwantedMimeTypes(response.Content); + } + } +} diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs new file mode 100644 index 0000000000..71c15620fd --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/SchemaIdGenerator.cs @@ -0,0 +1,26 @@ +using System.Text.RegularExpressions; +using Umbraco.Extensions; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +internal static class SchemaIdGenerator +{ + public static string Generate(Type type) + { + string SanitizedTypeName(Type t) => t.Name + // first grab the "non generic" part of any generic type name (i.e. "PagedViewModel`1" becomes "PagedViewModel") + .Split('`').First() + // then remove the "ViewModel" postfix from type names + .TrimEnd("ViewModel"); + + var name = SanitizedTypeName(type); + if (type.IsGenericType) + { + // append the generic type names, ultimately turning i.e. "PagedViewModel" into "PagedRelationItem" + name += string.Join(string.Empty, type.GenericTypeArguments.Select(SanitizedTypeName)); + } + + // make absolutely sure we don't pass any invalid named by removing all non-word chars + return Regex.Replace(name, @"[^\w]", string.Empty); + } +} diff --git a/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs b/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs new file mode 100644 index 0000000000..87f63f2f6e --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/Security/BackOfficeApplicationManager.cs @@ -0,0 +1,131 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using OpenIddict.Abstractions; +using Umbraco.New.Cms.Core; +using Umbraco.New.Cms.Core.Models.Configuration; +using Umbraco.New.Cms.Infrastructure.Security; + +namespace Umbraco.Cms.ManagementApi.Security; + +public class BackOfficeApplicationManager : IBackOfficeApplicationManager +{ + private readonly IOpenIddictApplicationManager _applicationManager; + private readonly IWebHostEnvironment _webHostEnvironment; + private readonly Uri? _backOfficeHost; + + public BackOfficeApplicationManager( + IOpenIddictApplicationManager applicationManager, + IWebHostEnvironment webHostEnvironment, + IOptionsMonitor securitySettingsMonitor) + { + _applicationManager = applicationManager; + _webHostEnvironment = webHostEnvironment; + _backOfficeHost = securitySettingsMonitor.CurrentValue.BackOfficeHost; + } + + public async Task EnsureBackOfficeApplicationAsync(Uri backOfficeUrl, CancellationToken cancellationToken = default) + { + if (backOfficeUrl.IsAbsoluteUri is false) + { + throw new ArgumentException($"Expected an absolute URL, got: {backOfficeUrl}", nameof(backOfficeUrl)); + } + + await CreateOrUpdate( + new OpenIddictApplicationDescriptor + { + DisplayName = "Umbraco back-office access", + ClientId = Constants.OauthClientIds.BackOffice, + RedirectUris = + { + CallbackUrlFor(_backOfficeHost ?? backOfficeUrl, "/umbraco/login/callback/") + }, + Type = OpenIddictConstants.ClientTypes.Public, + Permissions = + { + OpenIddictConstants.Permissions.Endpoints.Authorization, + OpenIddictConstants.Permissions.Endpoints.Token, + OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, + OpenIddictConstants.Permissions.GrantTypes.RefreshToken, + OpenIddictConstants.Permissions.ResponseTypes.Code + } + }, + cancellationToken); + + if (_webHostEnvironment.IsProduction()) + { + await Delete(Constants.OauthClientIds.Swagger, cancellationToken); + await Delete(Constants.OauthClientIds.Postman, cancellationToken); + } + else + { + await CreateOrUpdate( + new OpenIddictApplicationDescriptor + { + DisplayName = "Umbraco Swagger access", + ClientId = Constants.OauthClientIds.Swagger, + RedirectUris = + { + CallbackUrlFor(backOfficeUrl, "/umbraco/swagger/oauth2-redirect.html") + }, + Type = OpenIddictConstants.ClientTypes.Public, + Permissions = + { + OpenIddictConstants.Permissions.Endpoints.Authorization, + OpenIddictConstants.Permissions.Endpoints.Token, + OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, + OpenIddictConstants.Permissions.ResponseTypes.Code + } + }, + cancellationToken); + + await CreateOrUpdate( + new OpenIddictApplicationDescriptor + { + DisplayName = "Umbraco Postman access", + ClientId = Constants.OauthClientIds.Postman, + RedirectUris = + { + new Uri("https://oauth.pstmn.io/v1/callback") + }, + Type = OpenIddictConstants.ClientTypes.Public, + Permissions = + { + OpenIddictConstants.Permissions.Endpoints.Authorization, + OpenIddictConstants.Permissions.Endpoints.Token, + OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, + OpenIddictConstants.Permissions.ResponseTypes.Code + } + }, + cancellationToken); + } + } + + private async Task CreateOrUpdate(OpenIddictApplicationDescriptor clientDescriptor, CancellationToken cancellationToken) + { + var identifier = clientDescriptor.ClientId ?? + throw new ApplicationException($"ClientId is missing for application: {clientDescriptor.DisplayName ?? "(no name)"}"); + var client = await _applicationManager.FindByClientIdAsync(identifier, cancellationToken); + if (client is null) + { + await _applicationManager.CreateAsync(clientDescriptor, cancellationToken); + } + else + { + await _applicationManager.UpdateAsync(client, clientDescriptor, cancellationToken); + } + } + + private async Task Delete(string identifier, CancellationToken cancellationToken) + { + var client = await _applicationManager.FindByClientIdAsync(identifier, cancellationToken); + if (client is null) + { + return; + } + + await _applicationManager.DeleteAsync(client, cancellationToken); + } + + private static Uri CallbackUrlFor(Uri url, string relativePath) => new Uri( $"{url.GetLeftPart(UriPartial.Authority)}/{relativePath.TrimStart(Core.Constants.CharArrays.ForwardSlash)}"); +} diff --git a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj index 592f328ec2..37c95e98ee 100644 --- a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj +++ b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj @@ -10,7 +10,12 @@ - + + + + + + diff --git a/src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs b/src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs deleted file mode 100644 index 73d442992e..0000000000 --- a/src/Umbraco.Cms.ManagementApi/ViewModels/Analytics/AnalyticsLevelViewModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.ManagementApi.ViewModels.Analytics; - -public class AnalyticsLevelViewModel -{ - [JsonConverter(typeof(JsonStringEnumConverter))] - public TelemetryLevel AnalyticsLevel { get; set; } -} diff --git a/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs b/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs new file mode 100644 index 0000000000..574ae86360 --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/ViewModels/Telemetry/TelemetryViewModel.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.ManagementApi.ViewModels.Telemetry; + +public class TelemetryViewModel +{ + [JsonConverter(typeof(JsonStringEnumConverter))] + public TelemetryLevel TelemetryLevel { get; set; } +} diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index bbeae780d8..6eb184fb40 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -63,6 +63,12 @@ public static partial class Constants public const string AreaToken = "area"; } + public static class RoutePath + { + public const string Tree = "tree"; + public const string RecycleBin = "recycle-bin"; + } + public static class AttributeRouting { public const string BackOfficeToken = "umbracoBackOffice"; diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs index 31ef06c400..3be3815afa 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Core.DependencyInjection; /// public static partial class UmbracoBuilderExtensions { - private static IUmbracoBuilder AddUmbracoOptions(this IUmbracoBuilder builder, Action>? configure = null) + internal static IUmbracoBuilder AddUmbracoOptions(this IUmbracoBuilder builder, Action>? configure = null) where TOptions : class { UmbracoOptionsAttribute? umbracoOptionsAttribute = typeof(TOptions).GetCustomAttribute(); diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs index fe1e83d254..960be851ff 100644 --- a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs +++ b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs @@ -1,6 +1,8 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Routing; @@ -19,11 +21,18 @@ public class UmbracoRequestPaths private readonly string _mvcArea; private readonly string _previewMvcPath; private readonly string _surfaceMvcPath; + private readonly IOptions _umbracoRequestPathsOptions; + + [Obsolete("Use constructor that takes IOptions - Will be removed in Umbraco 13")] + public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment) + : this(globalSettings, hostingEnvironment, StaticServiceProvider.Instance.GetRequiredService>()) + { + } /// /// Initializes a new instance of the class. /// - public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment) + public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment, IOptions umbracoRequestPathsOptions) { var applicationPath = hostingEnvironment.ApplicationVirtualPath; _appPath = applicationPath.TrimStart(Constants.CharArrays.ForwardSlash); @@ -38,6 +47,7 @@ public class UmbracoRequestPaths _surfaceMvcPath = "/" + _mvcArea + "/Surface/"; _apiMvcPath = "/" + _mvcArea + "/Api/"; _installPath = hostingEnvironment.ToAbsolute(Constants.SystemDirectories.Install); + _umbracoRequestPathsOptions = umbracoRequestPathsOptions; } /// @@ -51,6 +61,7 @@ public class UmbracoRequestPaths /// These are def back office: /// /Umbraco/BackOffice = back office /// /Umbraco/Preview = back office + /// /Umbraco/Management/Api = back office /// /// /// If it's not any of the above then we cannot determine if it's back office or front-end @@ -99,6 +110,11 @@ public class UmbracoRequestPaths return false; } + if (_umbracoRequestPathsOptions.Value.IsBackOfficeRequest(urlPath)) + { + return true; + } + // if its none of the above, we will have to try to detect if it's a PluginController route, we can detect this by // checking how many parts the route has, for example, all PluginController routes will be routed like // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id} diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs b/src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs new file mode 100644 index 0000000000..91f13eab3b --- /dev/null +++ b/src/Umbraco.Core/Routing/UmbracoRequestPathsOptions.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Cms.Core.Routing; + +public class UmbracoRequestPathsOptions +{ + /// + /// Gets the delegate that allows us to handle additional URLs as back-office requests. + /// This returns false by default and can be overwritten in Startup.cs. + /// + public Func IsBackOfficeRequest { get; set; } = _ => false; +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 03a292c880..dc480b160f 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -47,6 +47,10 @@ <_Parameter1>DynamicProxyGenAssembly2 + + + <_Parameter1>Umbraco.Cms.ManagementApi + diff --git a/src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs b/src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs new file mode 100644 index 0000000000..2fdc54e011 --- /dev/null +++ b/src/Umbraco.New.Cms.Core/Constants-OauthClientIds.cs @@ -0,0 +1,23 @@ +namespace Umbraco.New.Cms.Core; + +// TODO: move this class to Umbraco.Cms.Core as a partial class +public static class Constants +{ + public static partial class OauthClientIds + { + /// + /// Client ID used for default back-office access + /// + public const string BackOffice = "umbraco-back-office"; + + /// + /// Client ID used for Swagger API access + /// + public const string Swagger = "umbraco-swagger"; + + /// + /// Client ID used for Postman API access + /// + public const string Postman = "umbraco-postman"; + } +} diff --git a/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs new file mode 100644 index 0000000000..cad7b8868d --- /dev/null +++ b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettings.cs @@ -0,0 +1,10 @@ +using Umbraco.Cms.Core.Configuration.Models; + +namespace Umbraco.New.Cms.Core.Models.Configuration; + +// TODO: merge this class with relevant existing settings from Core and clean up +[UmbracoOptions($"{Umbraco.Cms.Core.Constants.Configuration.ConfigPrefix}NewBackOffice")] +public class NewBackOfficeSettings +{ + public Uri? BackOfficeHost { get; set; } = null; +} diff --git a/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs new file mode 100644 index 0000000000..bb1a2eda3d --- /dev/null +++ b/src/Umbraco.New.Cms.Core/Models/Configuration/NewBackOfficeSettingsValidator.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models.Validation; + +namespace Umbraco.New.Cms.Core.Models.Configuration; + +// TODO: merge this class with relevant existing settings validators from Core and clean up +public class NewBackOfficeSettingsValidator : ConfigurationValidatorBase, IValidateOptions +{ + public ValidateOptionsResult Validate(string? name, NewBackOfficeSettings options) + { + if (options.BackOfficeHost != null) + { + if (options.BackOfficeHost.IsAbsoluteUri == false) + { + return ValidateOptionsResult.Fail($"{nameof(NewBackOfficeSettings.BackOfficeHost)} must be an absolute URL"); + } + if (options.BackOfficeHost.PathAndQuery != "/") + { + return ValidateOptionsResult.Fail($"{nameof(NewBackOfficeSettings.BackOfficeHost)} must not have any path or query"); + } + } + + return ValidateOptionsResult.Success; + } +} diff --git a/src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs b/src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs new file mode 100644 index 0000000000..37a6e4caa6 --- /dev/null +++ b/src/Umbraco.New.Cms.Infrastructure/HostedServices/OpenIddictCleanup.cs @@ -0,0 +1,56 @@ +using System.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OpenIddict.Abstractions; +using Umbraco.Cms.Infrastructure.HostedServices; + +namespace Umbraco.New.Cms.Infrastructure.HostedServices; + +// port of the OpenIddict Quartz job for cleaning up - see https://github.com/openiddict/openiddict-core/tree/dev/src/OpenIddict.Quartz +public class OpenIddictCleanup : RecurringHostedServiceBase +{ + // keep tokens and authorizations in the database for 7 days + // - NOTE: this is NOT the same as access token lifetime, which is likely very short + private const int LifespanInSeconds = 7 * 24 * 60 * 60; + + private readonly ILogger _logger; + private readonly IServiceProvider _provider; + + public OpenIddictCleanup( + ILogger logger, IServiceProvider provider) + : base(logger, TimeSpan.FromHours(1), TimeSpan.FromMinutes(5)) + { + _logger = logger; + _provider = provider; + } + + public override async Task PerformExecuteAsync(object? state) + { + // hosted services are registered as singletons, but this particular one consumes scoped services... so + // we have to fetch the service dependencies manually using a new scope per invocation. + IServiceScope scope = _provider.CreateScope(); + DateTimeOffset threshold = DateTimeOffset.UtcNow - TimeSpan.FromSeconds(LifespanInSeconds); + + try + { + IOpenIddictTokenManager tokenManager = scope.ServiceProvider.GetService() + ?? throw new ConfigurationErrorsException($"Could not retrieve an {nameof(IOpenIddictTokenManager)} service from the current scope"); + await tokenManager.PruneAsync(threshold); + } + catch (Exception exception) + { + _logger.LogError(exception, "Unable to prune OpenIddict tokens"); + } + + try + { + IOpenIddictAuthorizationManager authorizationManager = scope.ServiceProvider.GetService() + ?? throw new ConfigurationErrorsException($"Could not retrieve an {nameof(IOpenIddictAuthorizationManager)} service from the current scope"); + await authorizationManager.PruneAsync(threshold); + } + catch (Exception exception) + { + _logger.LogError(exception, "Unable to prune OpenIddict authorizations"); + } + } +} diff --git a/src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs b/src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs new file mode 100644 index 0000000000..068f5df472 --- /dev/null +++ b/src/Umbraco.New.Cms.Infrastructure/Security/IBackOfficeApplicationManager.cs @@ -0,0 +1,6 @@ +namespace Umbraco.New.Cms.Infrastructure.Security; + +public interface IBackOfficeApplicationManager +{ + Task EnsureBackOfficeApplicationAsync(Uri backOfficeUrl, CancellationToken cancellationToken = default); +} diff --git a/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj b/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj index 8dc5d3cc00..82159079a4 100644 --- a/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj +++ b/src/Umbraco.New.Cms.Infrastructure/Umbraco.New.Cms.Infrastructure.csproj @@ -10,4 +10,8 @@ + + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js index 5827f7e530..13ca4cb193 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/login.controller.js @@ -11,7 +11,11 @@ angular.module('umbraco').controller("Umbraco.LoginController", function (events //check if there's a returnPath query string, if so redirect to it var locationObj = $location.search(); if (locationObj.returnPath) { - path = decodeURIComponent(locationObj.returnPath); + // decodeURIComponent(...) does not play nice with OAuth redirect URLs, so until we have a + // dedicated login screen for the new back-office, we need to hardcode this exception + path = locationObj.returnPath.indexOf("/security/back-office/authorize") > 0 + ? locationObj.returnPath + : decodeURIComponent(locationObj.returnPath); } // Ensure path is not absolute diff --git a/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs b/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs index 9178234e74..c4c0bad3a9 100644 --- a/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs +++ b/tests/Umbraco.Tests.Integration/NewBackoffice/OpenAPIContractTest.cs @@ -1,15 +1,11 @@ -using System; -using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Tests.Integration.TestServerTest; -using Umbraco.Extensions; namespace Umbraco.Cms.Tests.Integration.NewBackoffice; @@ -39,7 +35,7 @@ internal sealed class OpenAPIContractTest : UmbracoTestServerTestBase var officePath = GlobalSettings.GetBackOfficePath(HostingEnvironment); var urlToContract = $"{officePath}/management/api/openapi.json"; - var swaggerPath = $"{officePath}/swagger/All/swagger.json"; + var swaggerPath = $"{officePath}/swagger/v1/swagger.json"; var apiContract = JObject.Parse(await Client.GetStringAsync(urlToContract)); var generatedJsonString = await Client.GetStringAsync(swaggerPath); diff --git a/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs b/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs index 94a64b31c9..2099d1d537 100644 --- a/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs +++ b/tests/Umbraco.Tests.UnitTests/TestHelpers/Objects/TestUmbracoContextFactory.cs @@ -23,7 +23,8 @@ public class TestUmbracoContextFactory GlobalSettings globalSettings = null, IUmbracoContextAccessor umbracoContextAccessor = null, IHttpContextAccessor httpContextAccessor = null, - IPublishedUrlProvider publishedUrlProvider = null) + IPublishedUrlProvider publishedUrlProvider = null, + UmbracoRequestPathsOptions umbracoRequestPathsOptions = null) { if (globalSettings == null) { @@ -45,6 +46,11 @@ public class TestUmbracoContextFactory publishedUrlProvider = Mock.Of(); } + if (umbracoRequestPathsOptions == null) + { + umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); + } + var contentCache = new Mock(); var mediaCache = new Mock(); var snapshot = new Mock(); @@ -58,7 +64,7 @@ public class TestUmbracoContextFactory var umbracoContextFactory = new UmbracoContextFactory( umbracoContextAccessor, snapshotService.Object, - new UmbracoRequestPaths(Options.Create(globalSettings), hostingEnvironment), + new UmbracoRequestPaths(Options.Create(globalSettings), hostingEnvironment, Options.Create(umbracoRequestPathsOptions)), hostingEnvironment, new UriUtility(hostingEnvironment), new AspNetCoreCookieManager(httpContextAccessor), diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs index 744febae67..4ed6ce842c 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UmbracoRequestPathsTests.cs @@ -18,10 +18,12 @@ public class UmbracoRequestPathsTests { _hostEnvironment = Mock.Of(); _globalSettings = new GlobalSettings(); + _umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); } private IWebHostEnvironment _hostEnvironment; private GlobalSettings _globalSettings; + private UmbracoRequestPathsOptions _umbracoRequestPathsOptions; private IHostingEnvironment CreateHostingEnvironment(string virtualPath = "") { @@ -49,7 +51,7 @@ public class UmbracoRequestPathsTests public void Is_Client_Side_Request(string url, bool assert) { var hostingEnvironment = CreateHostingEnvironment(); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); var uri = new Uri("http://test.com" + url); var result = umbracoRequestPaths.IsClientSideRequest(uri.AbsolutePath); @@ -60,7 +62,7 @@ public class UmbracoRequestPathsTests public void Is_Client_Side_Request_InvalidPath_ReturnFalse() { var hostingEnvironment = CreateHostingEnvironment(); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); // This URL is invalid. Default to false when the extension cannot be determined var uri = new Uri("http://test.com/installing-modules+foobar+\"yipee\""); @@ -85,11 +87,13 @@ public class UmbracoRequestPathsTests [TestCase("http://www.domain.com/myvdir/umbraco/api/blah", "myvdir", false)] [TestCase("http://www.domain.com/MyVdir/umbraco/api/blah", "/myvdir", false)] [TestCase("http://www.domain.com/MyVdir/Umbraco/", "myvdir", true)] + // NOTE: this test case is false for now - will be true once the IsBackOfficeRequest tweak from the new management API is put into UmbracoRequestPaths + [TestCase("http://www.domain.com/umbraco/management/api/v1.0/my/controller/action/", "", false)] public void Is_Back_Office_Request(string input, string virtualPath, bool expected) { var source = new Uri(input); var hostingEnvironment = CreateHostingEnvironment(virtualPath); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); Assert.AreEqual(expected, umbracoRequestPaths.IsBackOfficeRequest(source.AbsolutePath)); } @@ -106,7 +110,21 @@ public class UmbracoRequestPathsTests { var source = new Uri(input); var hostingEnvironment = CreateHostingEnvironment(); - var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment); + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(_umbracoRequestPathsOptions)); Assert.AreEqual(expected, umbracoRequestPaths.IsInstallerRequest(source.AbsolutePath)); } + + [TestCase("http://www.domain.com/some/path", false)] + [TestCase("http://www.domain.com/umbraco/surface/blah", false)] + [TestCase("http://www.domain.com/umbraco/api/blah", false)] + [TestCase("http://www.domain.com/umbraco/management/api/v1.0/my/controller/action/", true)] + public void Force_Back_Office_Request_With_Request_Paths_Options(string input, bool expected) + { + var source = new Uri(input); + var hostingEnvironment = CreateHostingEnvironment(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); + umbracoRequestPathsOptions.IsBackOfficeRequest = _ => true; + var umbracoRequestPaths = new UmbracoRequestPaths(Options.Create(_globalSettings), hostingEnvironment, Options.Create(umbracoRequestPathsOptions)); + Assert.AreEqual(expected, umbracoRequestPaths.IsBackOfficeRequest(source.AbsolutePath)); + } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs index b1881c132e..c59f9db68f 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeCookieManagerTests.cs @@ -24,12 +24,13 @@ public class BackOfficeCookieManagerTests public void ShouldAuthenticateRequest_When_Not_Configured() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); var mgr = new BackOfficeCookieManager( Mock.Of(), runtime, - new UmbracoRequestPaths(Options.Create(globalSettings), TestHelper.GetHostingEnvironment()), + new UmbracoRequestPaths(Options.Create(globalSettings), TestHelper.GetHostingEnvironment(), Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest("/umbraco"); @@ -41,6 +42,7 @@ public class BackOfficeCookieManagerTests public void ShouldAuthenticateRequest_When_Configured() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); var mgr = new BackOfficeCookieManager( @@ -49,7 +51,8 @@ public class BackOfficeCookieManagerTests new UmbracoRequestPaths( Options.Create(globalSettings), Mock.Of(x => - x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco")), + x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco"), + Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest("/umbraco"); @@ -61,6 +64,7 @@ public class BackOfficeCookieManagerTests public void ShouldAuthenticateRequest_Is_Back_Office() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); @@ -73,7 +77,8 @@ public class BackOfficeCookieManagerTests Options.Create(globalSettings), Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && - x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")), + x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"), + Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest(remainingTimeoutSecondsPath); @@ -87,6 +92,7 @@ public class BackOfficeCookieManagerTests public void ShouldAuthenticateRequest_Not_Back_Office() { var globalSettings = new GlobalSettings(); + var umbracoRequestPathsOptions = new UmbracoRequestPathsOptions(); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); @@ -97,7 +103,8 @@ public class BackOfficeCookieManagerTests Options.Create(globalSettings), Mock.Of(x => x.ApplicationVirtualPath == "/" && x.ToAbsolute(globalSettings.UmbracoPath) == "/umbraco" && - x.ToAbsolute(Constants.SystemDirectories.Install) == "/install")), + x.ToAbsolute(Constants.SystemDirectories.Install) == "/install"), + Options.Create(umbracoRequestPathsOptions)), Mock.Of()); var result = mgr.ShouldAuthenticateRequest("/notbackoffice"); From 73afd29b55b053220306e716ad20ee6058752bb8 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 9 Nov 2022 09:30:07 +0100 Subject: [PATCH 21/39] Updated to aspnetcore 7.0.0 (#13372) --- .../Search/IndexListExamineController.cs | 4 ++-- .../Umbraco.Cms.ManagementApi.csproj | 4 +--- .../Umbraco.Cms.Persistence.Sqlite.csproj | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 20 +++++++++---------- .../Umbraco.Infrastructure.csproj | 14 ++++++------- .../Umbraco.Web.Common.csproj | 4 ++-- .../Umbraco.Tests.Benchmarks.csproj | 2 +- .../Umbraco.Tests.Integration.csproj | 6 +++--- .../Umbraco.Tests.UnitTests.csproj | 6 +++--- 9 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Search/IndexListExamineController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Search/IndexListExamineController.cs index a7afea22d9..7fdad56404 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Search/IndexListExamineController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Search/IndexListExamineController.cs @@ -29,13 +29,13 @@ public class IndexListSearchController : SearchControllerBase [HttpGet("index")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task> Indexes(int skip, int take) + public Task> Indexes(int skip, int take) { IndexViewModel[] indexes = _examineManager.Indexes .Select(_indexViewModelFactory.Create) .OrderBy(indexModel => indexModel.Name.TrimEnd("Indexer")).ToArray(); var viewModel = new PagedViewModel { Items = indexes.Skip(skip).Take(take), Total = indexes.Length }; - return viewModel; + return Task.FromResult(viewModel); } } diff --git a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj index 37c95e98ee..586c2a2107 100644 --- a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj +++ b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj @@ -10,9 +10,7 @@ - - - + diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj b/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj index 7ccb38006b..b1a2aa91e5 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj +++ b/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index dc480b160f..908c0bf2e0 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -7,18 +7,18 @@ - - - - - - - - - + + + + + + + + + - + diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 6b922545bb..f2d8a23b86 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -17,11 +17,11 @@ - - - - - + + + + + @@ -38,8 +38,8 @@ - - + + diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 00feb6d8b6..bab408bde2 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 4ce8dbeb37..ce479ab2ce 100644 --- a/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 19afbfcc02..6a96686b52 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -10,10 +10,10 @@ - + - + @@ -21,7 +21,7 @@ - + diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index 8c2ae51c8d..1714bece2d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -9,9 +9,9 @@ - - - + + + From 84240d90b6fe80dd26f8e9a4486618769c33b58f Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 9 Nov 2022 09:30:07 +0100 Subject: [PATCH 22/39] Updated to aspnetcore 7.0.0 (#13372) --- .../Search/IndexListExamineController.cs | 4 ++-- .../Umbraco.Cms.ManagementApi.csproj | 4 +--- .../Umbraco.Cms.Persistence.Sqlite.csproj | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 20 +++++++++---------- .../Umbraco.Infrastructure.csproj | 14 ++++++------- .../Umbraco.Web.Common.csproj | 4 ++-- .../Umbraco.Tests.Benchmarks.csproj | 2 +- .../Umbraco.Tests.Integration.csproj | 6 +++--- .../Umbraco.Tests.UnitTests.csproj | 6 +++--- 9 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/Search/IndexListExamineController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/Search/IndexListExamineController.cs index a7afea22d9..7fdad56404 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/Search/IndexListExamineController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/Search/IndexListExamineController.cs @@ -29,13 +29,13 @@ public class IndexListSearchController : SearchControllerBase [HttpGet("index")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(PagedViewModel), StatusCodes.Status200OK)] - public async Task> Indexes(int skip, int take) + public Task> Indexes(int skip, int take) { IndexViewModel[] indexes = _examineManager.Indexes .Select(_indexViewModelFactory.Create) .OrderBy(indexModel => indexModel.Name.TrimEnd("Indexer")).ToArray(); var viewModel = new PagedViewModel { Items = indexes.Skip(skip).Take(take), Total = indexes.Length }; - return viewModel; + return Task.FromResult(viewModel); } } diff --git a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj index 37c95e98ee..586c2a2107 100644 --- a/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj +++ b/src/Umbraco.Cms.ManagementApi/Umbraco.Cms.ManagementApi.csproj @@ -10,9 +10,7 @@ - - - + diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj b/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj index 7ccb38006b..b1a2aa91e5 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj +++ b/src/Umbraco.Cms.Persistence.Sqlite/Umbraco.Cms.Persistence.Sqlite.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index dc480b160f..908c0bf2e0 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -7,18 +7,18 @@ - - - - - - - - - + + + + + + + + + - + diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 6b922545bb..f2d8a23b86 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -17,11 +17,11 @@ - - - - - + + + + + @@ -38,8 +38,8 @@ - - + + diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj index 00feb6d8b6..bab408bde2 100644 --- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj +++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 4ce8dbeb37..ce479ab2ce 100644 --- a/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 19afbfcc02..6a96686b52 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -10,10 +10,10 @@ - + - + @@ -21,7 +21,7 @@ - + diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index 8c2ae51c8d..1714bece2d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -9,9 +9,9 @@ - - - + + + From d11ed8acffafbff8618b622c00fac057921fb27d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 8 Nov 2022 13:34:12 +0100 Subject: [PATCH 23/39] Move block grid single area rendering to its own dedicated view (#13359) --- .../EmbeddedResources/BlockGrid/area.cshtml | 10 +++++++ .../EmbeddedResources/BlockGrid/areas.cshtml | 8 +---- .../Migrations/Upgrade/UmbracoPlan.cs | 8 +++-- .../V_10_4_0/AddBlockGridPartialViews.cs | 29 +++++++++++++++++++ .../Extensions/BlockGridTemplateExtensions.cs | 26 +++++++++++++++++ .../Views/Partials/blockgrid/area.cshtml | 10 +++++++ .../Views/Partials/blockgrid/areas.cshtml | 8 +---- 7 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 src/Umbraco.Core/EmbeddedResources/BlockGrid/area.cshtml create mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_4_0/AddBlockGridPartialViews.cs create mode 100644 src/Umbraco.Web.UI/Views/Partials/blockgrid/area.cshtml diff --git a/src/Umbraco.Core/EmbeddedResources/BlockGrid/area.cshtml b/src/Umbraco.Core/EmbeddedResources/BlockGrid/area.cshtml new file mode 100644 index 0000000000..361484757c --- /dev/null +++ b/src/Umbraco.Core/EmbeddedResources/BlockGrid/area.cshtml @@ -0,0 +1,10 @@ +@using Umbraco.Extensions +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage + +
+ @await Html.GetBlockGridItemsHtmlAsync(Model) +
diff --git a/src/Umbraco.Core/EmbeddedResources/BlockGrid/areas.cshtml b/src/Umbraco.Core/EmbeddedResources/BlockGrid/areas.cshtml index 1afd941a76..cf65b36a3a 100644 --- a/src/Umbraco.Core/EmbeddedResources/BlockGrid/areas.cshtml +++ b/src/Umbraco.Core/EmbeddedResources/BlockGrid/areas.cshtml @@ -8,12 +8,6 @@ style="--umb-block-grid--area-grid-columns: @(Model.AreaGridColumns?.ToString() ?? Model.GridColumns?.ToString() ?? "12");"> @foreach (var area in Model.Areas) { -
- @await Html.GetBlockGridItemsHtmlAsync(area) -
+ @await Html.GetBlockGridItemAreaHtmlAsync(area) } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index 58dfacb9ae..c80d70fa1f 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -5,7 +5,6 @@ using Umbraco.Cms.Core.Semver; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.Common; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_0_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_2_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_3_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_1; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; @@ -295,7 +294,10 @@ public class UmbracoPlan : MigrationPlan To("{D0B3D29D-F4D5-43E3-BA67-9D49256F3266}"); To("{79D8217B-5920-4C0E-8E9A-3CF8FA021882}"); - // To 11.0.0/10.4.0 - To("{56833770-3B7E-4FD5-A3B6-3416A26A7A3F}"); + // To 10.3.0 + To("{56833770-3B7E-4FD5-A3B6-3416A26A7A3F}"); + + // To 10.4.0 + To("{3F5D492A-A3DB-43F9-A73E-9FEE3B180E6C}"); } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_4_0/AddBlockGridPartialViews.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_4_0/AddBlockGridPartialViews.cs new file mode 100644 index 0000000000..fafd5b1a6f --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_4_0/AddBlockGridPartialViews.cs @@ -0,0 +1,29 @@ +using Umbraco.Cms.Infrastructure.Templates.PartialViews; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_4_0; + +public class AddBlockGridPartialViews : MigrationBase +{ + private readonly IPartialViewPopulator _partialViewPopulator; + private const string FolderPath = "/Views/Partials/blockgrid"; + private static readonly string[] _filesToAdd = + { + "area.cshtml", + }; + + public AddBlockGridPartialViews(IMigrationContext context, IPartialViewPopulator partialViewPopulator) : base(context) + => _partialViewPopulator = partialViewPopulator; + + protected override void Migrate() + { + var embeddedBasePath = _partialViewPopulator.CoreEmbeddedPath + ".BlockGrid"; + + foreach (var fileName in _filesToAdd) + { + _partialViewPopulator.CopyPartialViewIfNotExists( + _partialViewPopulator.GetCoreAssembly(), + $"{embeddedBasePath}.{fileName}", + $"{FolderPath}/{fileName}"); + } + } +} diff --git a/src/Umbraco.Web.Common/Extensions/BlockGridTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/BlockGridTemplateExtensions.cs index ee0375da4f..5951c6e009 100644 --- a/src/Umbraco.Web.Common/Extensions/BlockGridTemplateExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/BlockGridTemplateExtensions.cs @@ -14,6 +14,7 @@ public static class BlockGridTemplateExtensions public const string DefaultTemplate = "default"; public const string DefaultItemsTemplate = "items"; public const string DefaultItemAreasTemplate = "areas"; + public const string DefaultItemAreaTemplate = "area"; #region Async @@ -60,6 +61,20 @@ public static class BlockGridTemplateExtensions public static async Task GetBlockGridItemAreasHtmlAsync(this IHtmlHelper html, BlockGridItem item, string template = DefaultItemAreasTemplate) => await html.PartialAsync(DefaultFolderTemplate(template), item); + public static async Task GetBlockGridItemAreaHtmlAsync(this IHtmlHelper html, BlockGridArea area, string template = DefaultItemAreaTemplate) + => await html.PartialAsync(DefaultFolderTemplate(template), area); + + public static async Task GetBlockGridItemAreaHtmlAsync(this IHtmlHelper html, BlockGridItem item, string areaAlias, string template = DefaultItemAreaTemplate) + { + BlockGridArea? area = item.Areas.FirstOrDefault(a => a.Alias == areaAlias); + if (area == null) + { + return new HtmlString(string.Empty); + } + + return await GetBlockGridItemAreaHtmlAsync(html, area, template); + } + #endregion #region Sync @@ -95,6 +110,17 @@ public static class BlockGridTemplateExtensions public static IHtmlContent GetBlockGridItemAreasHtml(this IHtmlHelper html, BlockGridItem item, string template = DefaultItemAreasTemplate) => html.Partial(DefaultFolderTemplate(template), item); + public static IHtmlContent GetBlockGridItemAreaHtml(this IHtmlHelper html, BlockGridArea area, string template = DefaultItemAreaTemplate) + => html.Partial(DefaultFolderTemplate(template), area); + + public static IHtmlContent GetBlockGridItemAreaHtml(this IHtmlHelper html, BlockGridItem item, string areaAlias, string template = DefaultItemAreaTemplate) + { + BlockGridArea? area = item.Areas.FirstOrDefault(a => a.Alias == areaAlias); + return area != null + ? GetBlockGridItemAreaHtml(html, area, template) + : new HtmlString(string.Empty); + } + #endregion private static string DefaultFolderTemplate(string template) => $"{DefaultFolder}{template}"; diff --git a/src/Umbraco.Web.UI/Views/Partials/blockgrid/area.cshtml b/src/Umbraco.Web.UI/Views/Partials/blockgrid/area.cshtml new file mode 100644 index 0000000000..361484757c --- /dev/null +++ b/src/Umbraco.Web.UI/Views/Partials/blockgrid/area.cshtml @@ -0,0 +1,10 @@ +@using Umbraco.Extensions +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage + +
+ @await Html.GetBlockGridItemsHtmlAsync(Model) +
diff --git a/src/Umbraco.Web.UI/Views/Partials/blockgrid/areas.cshtml b/src/Umbraco.Web.UI/Views/Partials/blockgrid/areas.cshtml index 94eef55ad8..30f987ce1e 100644 --- a/src/Umbraco.Web.UI/Views/Partials/blockgrid/areas.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/blockgrid/areas.cshtml @@ -8,12 +8,6 @@ style="--umb-block-grid--area-grid-columns: @(Model.AreaGridColumns?.ToString() ?? Model.GridColumns?.ToString() ?? "12");"> @foreach (var area in Model.Areas) { -
- @await Html.GetBlockGridItemsHtmlAsync(area) -
+ @await Html.GetBlockGridItemAreaHtmlAsync(area) } From 69c6eae7a22edc1cb5c76ce5f0022a0b1142ef5a Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 8 Nov 2022 13:34:12 +0100 Subject: [PATCH 24/39] Move block grid single area rendering to its own dedicated view (#13359) --- .../EmbeddedResources/BlockGrid/area.cshtml | 10 +++++++ .../EmbeddedResources/BlockGrid/areas.cshtml | 8 +---- .../Migrations/Upgrade/UmbracoPlan.cs | 8 +++-- .../V_10_4_0/AddBlockGridPartialViews.cs | 29 +++++++++++++++++++ .../Extensions/BlockGridTemplateExtensions.cs | 26 +++++++++++++++++ .../Views/Partials/blockgrid/area.cshtml | 10 +++++++ .../Views/Partials/blockgrid/areas.cshtml | 8 +---- 7 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 src/Umbraco.Core/EmbeddedResources/BlockGrid/area.cshtml create mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_4_0/AddBlockGridPartialViews.cs create mode 100644 src/Umbraco.Web.UI/Views/Partials/blockgrid/area.cshtml diff --git a/src/Umbraco.Core/EmbeddedResources/BlockGrid/area.cshtml b/src/Umbraco.Core/EmbeddedResources/BlockGrid/area.cshtml new file mode 100644 index 0000000000..361484757c --- /dev/null +++ b/src/Umbraco.Core/EmbeddedResources/BlockGrid/area.cshtml @@ -0,0 +1,10 @@ +@using Umbraco.Extensions +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage + +
+ @await Html.GetBlockGridItemsHtmlAsync(Model) +
diff --git a/src/Umbraco.Core/EmbeddedResources/BlockGrid/areas.cshtml b/src/Umbraco.Core/EmbeddedResources/BlockGrid/areas.cshtml index 1afd941a76..cf65b36a3a 100644 --- a/src/Umbraco.Core/EmbeddedResources/BlockGrid/areas.cshtml +++ b/src/Umbraco.Core/EmbeddedResources/BlockGrid/areas.cshtml @@ -8,12 +8,6 @@ style="--umb-block-grid--area-grid-columns: @(Model.AreaGridColumns?.ToString() ?? Model.GridColumns?.ToString() ?? "12");"> @foreach (var area in Model.Areas) { -
- @await Html.GetBlockGridItemsHtmlAsync(area) -
+ @await Html.GetBlockGridItemAreaHtmlAsync(area) } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index 58dfacb9ae..c80d70fa1f 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -5,7 +5,6 @@ using Umbraco.Cms.Core.Semver; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.Common; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_0_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_2_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_3_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_1; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; @@ -295,7 +294,10 @@ public class UmbracoPlan : MigrationPlan To("{D0B3D29D-F4D5-43E3-BA67-9D49256F3266}"); To("{79D8217B-5920-4C0E-8E9A-3CF8FA021882}"); - // To 11.0.0/10.4.0 - To("{56833770-3B7E-4FD5-A3B6-3416A26A7A3F}"); + // To 10.3.0 + To("{56833770-3B7E-4FD5-A3B6-3416A26A7A3F}"); + + // To 10.4.0 + To("{3F5D492A-A3DB-43F9-A73E-9FEE3B180E6C}"); } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_4_0/AddBlockGridPartialViews.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_4_0/AddBlockGridPartialViews.cs new file mode 100644 index 0000000000..fafd5b1a6f --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_4_0/AddBlockGridPartialViews.cs @@ -0,0 +1,29 @@ +using Umbraco.Cms.Infrastructure.Templates.PartialViews; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_4_0; + +public class AddBlockGridPartialViews : MigrationBase +{ + private readonly IPartialViewPopulator _partialViewPopulator; + private const string FolderPath = "/Views/Partials/blockgrid"; + private static readonly string[] _filesToAdd = + { + "area.cshtml", + }; + + public AddBlockGridPartialViews(IMigrationContext context, IPartialViewPopulator partialViewPopulator) : base(context) + => _partialViewPopulator = partialViewPopulator; + + protected override void Migrate() + { + var embeddedBasePath = _partialViewPopulator.CoreEmbeddedPath + ".BlockGrid"; + + foreach (var fileName in _filesToAdd) + { + _partialViewPopulator.CopyPartialViewIfNotExists( + _partialViewPopulator.GetCoreAssembly(), + $"{embeddedBasePath}.{fileName}", + $"{FolderPath}/{fileName}"); + } + } +} diff --git a/src/Umbraco.Web.Common/Extensions/BlockGridTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/BlockGridTemplateExtensions.cs index ee0375da4f..5951c6e009 100644 --- a/src/Umbraco.Web.Common/Extensions/BlockGridTemplateExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/BlockGridTemplateExtensions.cs @@ -14,6 +14,7 @@ public static class BlockGridTemplateExtensions public const string DefaultTemplate = "default"; public const string DefaultItemsTemplate = "items"; public const string DefaultItemAreasTemplate = "areas"; + public const string DefaultItemAreaTemplate = "area"; #region Async @@ -60,6 +61,20 @@ public static class BlockGridTemplateExtensions public static async Task GetBlockGridItemAreasHtmlAsync(this IHtmlHelper html, BlockGridItem item, string template = DefaultItemAreasTemplate) => await html.PartialAsync(DefaultFolderTemplate(template), item); + public static async Task GetBlockGridItemAreaHtmlAsync(this IHtmlHelper html, BlockGridArea area, string template = DefaultItemAreaTemplate) + => await html.PartialAsync(DefaultFolderTemplate(template), area); + + public static async Task GetBlockGridItemAreaHtmlAsync(this IHtmlHelper html, BlockGridItem item, string areaAlias, string template = DefaultItemAreaTemplate) + { + BlockGridArea? area = item.Areas.FirstOrDefault(a => a.Alias == areaAlias); + if (area == null) + { + return new HtmlString(string.Empty); + } + + return await GetBlockGridItemAreaHtmlAsync(html, area, template); + } + #endregion #region Sync @@ -95,6 +110,17 @@ public static class BlockGridTemplateExtensions public static IHtmlContent GetBlockGridItemAreasHtml(this IHtmlHelper html, BlockGridItem item, string template = DefaultItemAreasTemplate) => html.Partial(DefaultFolderTemplate(template), item); + public static IHtmlContent GetBlockGridItemAreaHtml(this IHtmlHelper html, BlockGridArea area, string template = DefaultItemAreaTemplate) + => html.Partial(DefaultFolderTemplate(template), area); + + public static IHtmlContent GetBlockGridItemAreaHtml(this IHtmlHelper html, BlockGridItem item, string areaAlias, string template = DefaultItemAreaTemplate) + { + BlockGridArea? area = item.Areas.FirstOrDefault(a => a.Alias == areaAlias); + return area != null + ? GetBlockGridItemAreaHtml(html, area, template) + : new HtmlString(string.Empty); + } + #endregion private static string DefaultFolderTemplate(string template) => $"{DefaultFolder}{template}"; diff --git a/src/Umbraco.Web.UI/Views/Partials/blockgrid/area.cshtml b/src/Umbraco.Web.UI/Views/Partials/blockgrid/area.cshtml new file mode 100644 index 0000000000..361484757c --- /dev/null +++ b/src/Umbraco.Web.UI/Views/Partials/blockgrid/area.cshtml @@ -0,0 +1,10 @@ +@using Umbraco.Extensions +@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage + +
+ @await Html.GetBlockGridItemsHtmlAsync(Model) +
diff --git a/src/Umbraco.Web.UI/Views/Partials/blockgrid/areas.cshtml b/src/Umbraco.Web.UI/Views/Partials/blockgrid/areas.cshtml index 94eef55ad8..30f987ce1e 100644 --- a/src/Umbraco.Web.UI/Views/Partials/blockgrid/areas.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/blockgrid/areas.cshtml @@ -8,12 +8,6 @@ style="--umb-block-grid--area-grid-columns: @(Model.AreaGridColumns?.ToString() ?? Model.GridColumns?.ToString() ?? "12");"> @foreach (var area in Model.Areas) { -
- @await Html.GetBlockGridItemsHtmlAsync(area) -
+ @await Html.GetBlockGridItemAreaHtmlAsync(area) } From bee6adfe278cee2732175439b3148b7f5bd5d1f6 Mon Sep 17 00:00:00 2001 From: Andreas Zerbst <73799582+andr317c@users.noreply.github.com> Date: Mon, 14 Nov 2022 07:01:33 +0100 Subject: [PATCH 25/39] V11: Add new blocklist acceptance tests (#13299) * Created test for block list editor * Added a couple of test for the block list editor * Changed parts of builder * Split the test into two files instead of one * Updated and added test for blockListEditor * Updated version * Fixed so it now targets the correct selector * Removed unnecessary login in the afterEach * Fixed up the test so they are now using constants and the correct helpers * Bumped version * Updated the package-lock * Added local function to battle code reuse! * Updated the path of the image for the test 'can edit a block list editor' * Placed an expect before the button so it waits to click the button till it's visible * Yet another fix for the imagePath and a missing " --- .../package-lock.json | 32 +- .../Umbraco.Tests.AcceptanceTest/package.json | 4 +- .../blockListEditorContent.spec.ts | 520 ++++++++++++++++++ .../blockListEditorDataType.spec.ts | 288 ++++++++++ .../blockListEditorDocument.spec.ts | 149 +++++ .../DefaultConfig/Content/routing.spec.ts | 1 - 6 files changed, 975 insertions(+), 19 deletions(-) create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorContent.spec.ts create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDataType.spec.ts create mode 100644 tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDocument.spec.ts diff --git a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json index 6a2a2a53b5..92b5121640 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package-lock.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package-lock.json @@ -7,8 +7,8 @@ "name": "acceptancetest", "hasInstallScript": true, "dependencies": { - "@umbraco/json-models-builders": "^1.0.0", - "@umbraco/playwright-testhelpers": "^1.0.11", + "@umbraco/json-models-builders": "^1.0.2", + "@umbraco/playwright-testhelpers": "^1.0.12", "camelize": "^1.0.0", "dotenv": "^16.0.2", "faker": "^4.1.0", @@ -129,20 +129,20 @@ "dev": true }, "node_modules/@umbraco/json-models-builders": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.0.tgz", - "integrity": "sha512-UuJmA2S0xFuW2IT004K8U5Ek2sK9DJ0VZysPoeqdCN/aqk0Xi+EIBILFgk5xuSSSQDPUrWS7rjgnv6fawkFceg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.2.tgz", + "integrity": "sha512-IxKvdTPHe4O5YB+gCmi9G31ytcUaWyQx5/xWxJOeaD+4/p/xyTOFLBdq1rXme645i2ef3QtWxlCnGK/j15w2dQ==", "dependencies": { "camelize": "^1.0.0", "faker": "^4.1.0" } }, "node_modules/@umbraco/playwright-testhelpers": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.11.tgz", - "integrity": "sha512-subAc4t3903+uRZayNnoVxv9yXDnz32th2emTAuwTBi2yc+JRRGOB5Yv4WSYYRWUnMgBXaiDdsJFNjoMQSFWTQ==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.12.tgz", + "integrity": "sha512-O6P307ocTaBzaNE0QjFS100w94DalTx9nXUGczpwku3oAZZyoNaG//A7xh5zmlmgAGHqyKnAMQBoH/RX3t8C6g==", "dependencies": { - "@umbraco/json-models-builders": "^1.0.0", + "@umbraco/json-models-builders": "^1.0.2", "camelize": "^1.0.0", "faker": "^4.1.0", "form-data": "^4.0.0", @@ -1055,20 +1055,20 @@ "dev": true }, "@umbraco/json-models-builders": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.0.tgz", - "integrity": "sha512-UuJmA2S0xFuW2IT004K8U5Ek2sK9DJ0VZysPoeqdCN/aqk0Xi+EIBILFgk5xuSSSQDPUrWS7rjgnv6fawkFceg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@umbraco/json-models-builders/-/json-models-builders-1.0.2.tgz", + "integrity": "sha512-IxKvdTPHe4O5YB+gCmi9G31ytcUaWyQx5/xWxJOeaD+4/p/xyTOFLBdq1rXme645i2ef3QtWxlCnGK/j15w2dQ==", "requires": { "camelize": "^1.0.0", "faker": "^4.1.0" } }, "@umbraco/playwright-testhelpers": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.11.tgz", - "integrity": "sha512-subAc4t3903+uRZayNnoVxv9yXDnz32th2emTAuwTBi2yc+JRRGOB5Yv4WSYYRWUnMgBXaiDdsJFNjoMQSFWTQ==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@umbraco/playwright-testhelpers/-/playwright-testhelpers-1.0.12.tgz", + "integrity": "sha512-O6P307ocTaBzaNE0QjFS100w94DalTx9nXUGczpwku3oAZZyoNaG//A7xh5zmlmgAGHqyKnAMQBoH/RX3t8C6g==", "requires": { - "@umbraco/json-models-builders": "^1.0.0", + "@umbraco/json-models-builders": "^1.0.2", "camelize": "^1.0.0", "faker": "^4.1.0", "form-data": "^4.0.0", diff --git a/tests/Umbraco.Tests.AcceptanceTest/package.json b/tests/Umbraco.Tests.AcceptanceTest/package.json index d569e0ef7c..997514c470 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/package.json +++ b/tests/Umbraco.Tests.AcceptanceTest/package.json @@ -19,8 +19,8 @@ "wait-on": "^6.0.1" }, "dependencies": { - "@umbraco/json-models-builders": "^1.0.0", - "@umbraco/playwright-testhelpers": "^1.0.11", + "@umbraco/json-models-builders": "^1.0.2", + "@umbraco/playwright-testhelpers": "^1.0.12", "camelize": "^1.0.0", "faker": "^4.1.0", "form-data": "^4.0.0", diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorContent.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorContent.spec.ts new file mode 100644 index 0000000000..ffc14e1a18 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorContent.spec.ts @@ -0,0 +1,520 @@ +import {AliasHelper, ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import {ContentBuilder, DocumentTypeBuilder, PartialViewBuilder} from "@umbraco/json-models-builders"; +import {BlockListDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes"; + +test.describe('BlockListEditorContent', () => { + + const documentName = 'DocumentTestName'; + const blockListName = 'BlockListTest'; + const elementName = 'TestElement'; + + const documentAlias = AliasHelper.toAlias(documentName); + const blockListAlias = AliasHelper.toAlias(blockListName); + // Won't work if I use the to alias for the elementAlias + const elementAlias = 'testElement'; + + test.beforeEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.login(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.dataTypes.ensureNameNotExists(blockListName); + }); + + test.afterEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.dataTypes.ensureNameNotExists(blockListName); + }); + + async function createDefaultBlockList(umbracoApi, blockListName, element){ + const dataTypeBlockList = new BlockListDataTypeBuilder() + .withName(blockListName) + .addBlock() + .withContentElementTypeKey(element['key']) + .withSettingsElementTypeKey(element['key']) + .done() + .build(); + return await umbracoApi.dataTypes.save(dataTypeBlockList); + } + + async function createDocumentWithOneBlockListEditor(umbracoApi, element, dataType){ + + if(element == null) { + element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + } + + if(dataType == null) { + dataType = await createDefaultBlockList(umbracoApi, blockListName, element); + } + + const docType = new DocumentTypeBuilder() + .withName(documentName) + .withAlias(documentAlias) + .withAllowAsRoot(true) + .addGroup() + .withName('BlockListGroup') + .addCustomProperty(dataType['id']) + .withAlias(blockListAlias) + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(docType); + + return element; + } + + async function createContentWithOneBlockListEditor(umbracoApi, element) { + + if(element == null) { + element = await createDocumentWithOneBlockListEditor(umbracoApi, null, null); + } + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockListName) + .withSave(true) + .addProperty() + .withAlias(blockListAlias) + .addBlockListValue() + .addBlockListEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, "aliasTest") + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + return element; + } + + test('can create content with a block list editor', async ({page, umbracoApi, umbracoUi}) => { + await createDocumentWithOneBlockListEditor(umbracoApi, null, null); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockListName) + .withSave(true) + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.refreshContentTree(); + + // Opens the content with the block list editor + await umbracoUi.clickDataElementByElementName('tree-item-' + blockListName); + + // Adds TestElement + await page.locator('[key="blockEditor_addThis"]', {hasText: elementName}).click(); + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill('Testing...'); + await page.locator('[label="Create"]').click(); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Checks if the content was created + await expect(page.locator('.umb-block-list__block--view')).toHaveCount(1); + await expect(page.locator('.umb-block-list__block--view').nth(0)).toHaveText(elementName); + }); + + test('can update content with a block list editor', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.deleteAllContent(); + + await createContentWithOneBlockListEditor(umbracoApi, null); + + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.refreshContentTree(); + + // Opens the content with the block list editor + await umbracoUi.clickDataElementByElementName('tree-item-' + blockListName); + + // Updates the block list editor inside of the content + await page.locator('[ui-sortable="vm.sortableOptions"]').nth(0).click(); + // Updates content + await page.locator('[id="sub-view-0"]').locator('[id="title"]').fill('ContentTest'); + await umbracoUi.clickDataElementByElementName('sub-view-settings'); + // Adds text to the setting element + await page.locator('[id="sub-view-1"]').locator('[id="title"]').fill('SettingTest'); + await page.locator('[label="Submit"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Clean + await umbracoApi.content.deleteAllContent(); + }); + + test('can delete a block list editor in content', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.deleteAllContent(); + + await createContentWithOneBlockListEditor(umbracoApi, null); + + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.refreshContentTree(); + + // Opens the content with the block list editor + await umbracoUi.clickDataElementByElementName('tree-item-' + blockListName); + + // Deletes the block list editor inside of the content + await page.locator('[title="Delete"]').click(); + + // Can't use our constant helper because the action for delete does not contain an s. The correct way is 'action-delete' + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('actions_delete')); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.isSuccessNotificationVisible(); + + // Checks if the content is actually deleted + await expect(page.locator('[ui-sortable="vm.sortableOptions"]').nth(0)).not.toBeVisible(); + + // Clean + await umbracoApi.content.deleteAllContent(); + }); + + test('can copy block list content and paste it', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.deleteAllContent(); + + await createContentWithOneBlockListEditor(umbracoApi, null); + + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.refreshContentTree(); + + // Opens the content with the block list editor + await umbracoUi.clickDataElementByElementName('tree-item-' + blockListName); + + // Checks to make sure that there is only one item + await expect(page.locator('.umb-block-list__block--view')).toHaveCount(1); + + // Copies block list content + await page.locator('[title="Copy"]').click(); + await expect(page.locator('.alert-success', {hasText: 'Copied to clipboard'})).toBeVisible(); + // Pastes block list content + await page.locator('[title="Clipboard"]').click(); + await page.locator('umb-block-card', {hasText: elementName}).click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await expect(page.locator('.umb-block-list__block--view')).toHaveCount(2); + await page.locator('.umb-block-list__block--view').nth(1).click(); + await expect(page.locator('[id="sub-view-0"] >> [name="textbox"]')).toHaveValue('aliasTest'); + + // Clean + await umbracoApi.content.deleteAllContent(); + }); + + test('can copy block list content and paste it into another group with the same block list editor', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.deleteAllContent(); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataType = await createDefaultBlockList(umbracoApi, blockListName, element); + + const docType = new DocumentTypeBuilder() + .withName(documentName) + .withAlias(documentAlias) + .withAllowAsRoot(true) + .addGroup() + .withName('BlockListGroup') + .addCustomProperty(dataType['id']) + .withAlias(blockListAlias) + .done() + .done() + .addGroup() + .withName('TheBlockListGroupTheSecond') + .addCustomProperty(dataType['id']) + .withAlias('theBlockListAliasTheSecond') + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(docType); + + await createContentWithOneBlockListEditor(umbracoApi, element); + + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.refreshContentTree(); + + // Opens the content with the block list editor + await umbracoUi.clickDataElementByElementName('tree-item-' + blockListName); + + // Checks to make sure that there is only one item in the first group + await expect(page.locator('[data-element="group-aBlockListGroup"] >> .umb-block-list__block--view')).toHaveCount(1); + // Checks to make sure that there is no items in the second group + await expect(page.locator('[data-element="group-aTheBlockListGroupTheSecond"] >> .umb-block-list__block--view')).toHaveCount(0); + + // Copies block list content from the first group + await page.locator('[title="Copy"]').click(); + await expect(page.locator('.alert-success', {hasText: 'Copied to clipboard'})).toBeVisible(); + // Pastes into the second group + await page.locator('[title="Clipboard"]').nth(1).click(); + await page.locator('umb-block-card', {hasText: elementName}).click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + await expect(page.locator('.alert-success', {hasText: 'Content Published'})).toBeVisible(); + + // Assert + await expect(page.locator('[data-element="group-aBlockListGroup"] >> .umb-block-list__block--view')).toHaveCount(1); + await expect(page.locator('[data-element="group-aTheBlockListGroupTheSecond"] >> .umb-block-list__block--view')).toHaveCount(1); + await page.locator('[data-element="group-aTheBlockListGroupTheSecond"] >> .umb-block-list__block--view').click(); + await expect(page.locator('[id="sub-view-0"] >> [name="textbox"]')).toHaveValue('aliasTest'); + + // Clean + await umbracoApi.content.deleteAllContent(); + }); + + test('can set a minimum of required blocks in content with a block list editor', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.deleteAllContent(); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockList = new BlockListDataTypeBuilder() + .withName(blockListName) + .withMin(2) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockList); + + await createDocumentWithOneBlockListEditor(umbracoApi, element, dataType); + + await createContentWithOneBlockListEditor(umbracoApi, element); + + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.refreshContentTree(); + + // Opens the content with the block list editor + await umbracoUi.clickDataElementByElementName('tree-item-' + blockListName); + // Checks if there is validation for needing 2 entries or more + await expect(page.locator('[key="validation_entriesShort"]')).toContainText('Minimum 2 entries'); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + // Checks if a validation error is thrown when trying to save when there is not enough blocks + await expect(page.locator('.alert-error')).toBeVisible(); + + // Adds another block + await page.locator('[id="' + blockListAlias + '"]').click(); + await page.locator('[label="Create"]').click(); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + + // Assert + await umbracoUi.getSuccessNotification(); + + // Clean + await umbracoApi.content.deleteAllContent(); + }); + + test('can set a maximum of required blocks in content with a block list editor', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.deleteAllContent(); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockList = new BlockListDataTypeBuilder() + .withName(blockListName) + .withMax(2) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockList); + + await createDocumentWithOneBlockListEditor(umbracoApi, element, dataType); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.save) + .addVariant() + .withName(blockListName) + .withSave(true) + .addProperty() + .withAlias(blockListAlias) + .addBlockListValue() + .addBlockListEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, "aliasTest") + .done() + .addBlockListEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, "aliasTests") + .done() + .addBlockListEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, "aliasTester") + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.refreshContentTree(); + + // Opens the content with the block list editor + await umbracoUi.clickDataElementByElementName('tree-item-' + blockListName); + + // Checks if there is validation + await expect(page.locator('[key="validation_entriesExceed"]')).toContainText('Maximum 2 entries'); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish)); + // Checks if a validation error is thrown when trying to save when there is too many blocks + await expect(page.locator('.alert-error')).toBeVisible(); + + // Deletes a block + await page.locator('[title="Delete"]').nth(2).click(); + + // Can't use our constant helper because the action for delete does not contain an s. The correct way is 'action-delete' + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('actions_delete')); + + // Assert + await umbracoUi.getSuccessNotification(); + + // Clean + await umbracoApi.content.deleteAllContent(); + }); + + test('can use inline editing mode in content with a block list editor', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.content.deleteAllContent(); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const dataTypeBlockList = new BlockListDataTypeBuilder() + .withName(blockListName) + .withUseInlineEditingAsDefault(true) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .build(); + const dataType = await umbracoApi.dataTypes.save(dataTypeBlockList); + + await createDocumentWithOneBlockListEditor(umbracoApi, element, dataType); + + await createContentWithOneBlockListEditor(umbracoApi, element); + + await umbracoUi.goToSection(ConstantHelper.sections.content); + await umbracoUi.refreshContentTree(); + + // Opens the content with the block list editor + await umbracoUi.clickDataElementByElementName('tree-item-' + blockListName); + + // Opens the block in content + await page.locator('[ui-sortable="vm.sortableOptions"]').nth(0).click(); + + // Assert + await expect(page.locator('[ui-sortable="vm.sortableOptions"]').nth(0).locator('[data-element="property-title"]')).toBeVisible(); + + // Clean + await umbracoApi.content.deleteAllContent(); + }); + + test('can see rendered content with a block list editor', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.templates.ensureNameNotExists(documentName); + await umbracoApi.partialViews.ensureNameNotExists(elementName + '.cshtml'); + await umbracoApi.content.deleteAllContent(); + + const element = new DocumentTypeBuilder() + .withName(elementName) + .withAlias(elementAlias) + .AsElementType() + .addGroup() + .withName("TestString") + .withAlias('testString') + .addTextBoxProperty() + .withLabel("Title") + .withAlias("title") + .done() + .addRichTextProperty() + .withLabel('Body') + .withAlias('body') + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(element); + + const dataType = await createDefaultBlockList(umbracoApi, blockListName, element); + + const docType = new DocumentTypeBuilder() + .withName(documentName) + .withAlias('documentTestName') + .withDefaultTemplate('documentTestName') + .withAllowAsRoot(true) + .addGroup() + .withName('BlockListGroup') + .addCustomProperty(dataType['id']) + .withAlias(elementAlias) + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(docType); + + await umbracoApi.templates.edit(documentName, '@using Umbraco.Cms.Web.Common.PublishedModels;\n' + + '@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage\n' + + '@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;' + + '\n@{' + + '\n Layout = null;' + + '\n}' + + '\n' + + '@Html.GetBlockListHtml(Model.' + elementName + ')'); + + const partialView = new PartialViewBuilder() + .withName(elementAlias) + .withContent("@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage;\n" + + "@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;\n" + + "@{\n" + + "var content = (ContentModels." + elementName + ")Model.Content;\n" + + "var settings = (ContentModels." + elementName + ")Model.Settings;\n" + + "}\n" + + "\n" + + "

@content.Title

" + + "

@content.Body

" + + "\n" + + "

@settings.Title

" + + "

@settings.Body

") + .build(); + partialView.virtualPath = "/Views/Partials/blocklist/Components/"; + await umbracoApi.partialViews.save(partialView); + + const rootContentNode = new ContentBuilder() + .withContentTypeAlias(documentAlias) + .withAction(ConstantHelper.actions.publish) + .addVariant() + .withName('BlockListContent') + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias(elementAlias) + .addBlockListValue() + .addBlockListEntry() + .withContentTypeKey(element['key']) + .appendContentProperties(element.groups[0].properties[0].alias, "ContentTest") + .appendContentProperties(element.groups[0].properties[1].alias, "RTEContent") + .withSettingsTypeKey(element['key']) + .appendSettingsProperties(element.groups[0].properties[0].alias, "SettingTest") + .appendSettingsProperties(element.groups[0].properties[1].alias, "RTESetting") + .done() + .done() + .done() + .done() + .build(); + await umbracoApi.content.save(rootContentNode); + + // Assert + // Ensure that the view gets rendered correctly + const expected = `

ContentTest

RTEContent

SettingTest

RTESetting

`; + await expect(await umbracoApi.content.verifyRenderedContent('/', expected, true)).toBeTruthy(); + + // Clean + await umbracoApi.templates.ensureNameNotExists(documentName); + await umbracoApi.partialViews.ensureNameNotExists(elementAlias + '.cshtml'); + await umbracoApi.content.deleteAllContent(); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDataType.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDataType.spec.ts new file mode 100644 index 0000000000..70c4112c25 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDataType.spec.ts @@ -0,0 +1,288 @@ +import {AliasHelper, ConstantHelper, test} from '@umbraco/playwright-testhelpers'; +import {expect} from "@playwright/test"; +import {MediaBuilder, MediaFileBuilder, StylesheetBuilder} from "@umbraco/json-models-builders"; +import {BlockListDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes"; + +test.describe('BlockListEditorDataType', () => { + const documentName = 'DocumentName' + const blockListName = 'BlockListTest'; + const elementName = 'TestElement'; + + const elementAlias = AliasHelper.toAlias(elementName); + + test.beforeEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.login(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.dataTypes.ensureNameNotExists(blockListName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + }); + + test.afterEach(async({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.dataTypes.ensureNameNotExists(blockListName); + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + }) + + test('can create an empty block list datatype', async ({page, umbracoApi, umbracoUi}) => { + await umbracoUi.goToSection(ConstantHelper.sections.settings); + + // Creates a new datatype + await umbracoUi.clickDataElementByElementName('tree-item-dataTypes', {button: 'right'}); + await umbracoUi.clickDataElementByElementName(ConstantHelper.actions.create); + await umbracoUi.clickDataElementByElementName(ConstantHelper.actions.dataType); + + await umbracoUi.setEditorHeaderName(blockListName); + + // Adds BlockList as property editor + await umbracoUi.clickDataElementByElementName('property-editor-add'); + await umbracoUi.clickDataElementByElementName('propertyeditor-', {hasText: 'Block List'}); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await expect(page.locator('.umb-notifications__notifications > .alert-success', {hasText: "Datatype saved"})).toBeVisible(); + }); + + test('can create a block list datatype with an element', async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const blockListType = new BlockListDataTypeBuilder() + .withName(blockListName) + .build(); + await umbracoApi.dataTypes.save(blockListType); + + await umbracoUi.navigateToDataType(blockListName); + + // Adds an element to the block list + await umbracoUi.clickElement(umbracoUi.getButtonByKey(ConstantHelper.buttons.add)); + await page.locator('[data-element="editor-container"]').locator('[data-element="tree-item-' + elementName + '"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.submitChanges)); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await expect(page.locator('.umb-notifications__notifications > .alert-success', {hasText: "Datatype saved"})).toBeVisible(); + // Checks if the element is added + await expect(page.locator('.umb-block-card-grid', {hasText: elementName})).toBeVisible(); + }); + + test('can create block list datatype with two elements', async ({page, umbracoApi, umbracoUi}) => { + const elementNameTwo = 'SecondElement'; + const elementTwoAlias = AliasHelper.toAlias(elementNameTwo); + + await umbracoApi.documentTypes.ensureNameNotExists(elementNameTwo); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + await umbracoApi.documentTypes.createDefaultElementType(elementNameTwo, elementTwoAlias); + + const blockListType = new BlockListDataTypeBuilder() + .withName(blockListName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .build(); + await umbracoApi.dataTypes.save(blockListType); + + await umbracoUi.navigateToDataType(blockListName); + + // Adds an element to the block list + await umbracoUi.clickElement(umbracoUi.getButtonByKey(ConstantHelper.buttons.add)); + await page.locator('[data-element="editor-container"]').locator('[data-element="tree-item-' + elementNameTwo + '"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.submitChanges)); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await expect(page.locator('.umb-notifications__notifications > .alert-success', {hasText: "Datatype saved"})).toBeVisible(); + // Checks if the elements are added + await expect(page.locator('umb-block-card', {hasText: elementName})).toBeVisible(); + await expect(page.locator('umb-block-card', {hasText: elementNameTwo})).toBeVisible(); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementNameTwo); + }); + + test('can remove an element in a block list datatype', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const blockListType = new BlockListDataTypeBuilder() + .withName(blockListName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .build(); + await umbracoApi.dataTypes.save(blockListType); + + await umbracoUi.navigateToDataType(blockListName); + + // Deletes the element in the block list + await umbracoUi.clickElement(umbracoUi.getButtonByKey(ConstantHelper.buttons.delete)); + + // Can't use our constant helper because the action for delete does not contain an s. The correct way is 'action-delete' + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey('actions_delete')); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await expect(page.locator('.umb-notifications__notifications > .alert-success', {hasText: "Datatype saved"})).toBeVisible(); + // Checks if the element is deleted + await expect(page.locator('.umb-block-card-grid', {hasText: elementName})).not.toBeVisible(); + }); + + test('cant put the same element in a block list editor', async ({page, umbracoApi, umbracoUi}) => { + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + + const blockListType = new BlockListDataTypeBuilder() + .withName(blockListName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .build(); + await umbracoApi.dataTypes.save(blockListType); + + await umbracoUi.navigateToDataType(blockListName); + + // Tries adding the same element to the block list editor + await umbracoUi.clickElement(umbracoUi.getButtonByKey(ConstantHelper.buttons.add)); + await page.locator('[data-element="editor-container"]').locator('[data-element="tree-item-' + elementName + '"]').click(); + + // Assert + await expect(page.locator('.not-allowed', {hasText: elementName})).toBeVisible(); + // Checks if the button create New Element Type is still visible. If visible the element was not clickable. + await expect(page.locator('[label-key="blockEditor_labelcreateNewElementType"]')).toBeVisible(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.close)); + await expect(page.locator('[block-config-model="block"]')).toHaveCount(1); + }); + + test('can edit a block list editor', async ({page, umbracoApi, umbracoUi}) => { + const elementNameTwo = 'SecondElement'; + const elementTwoAlias = AliasHelper.toAlias(elementNameTwo); + const stylesheetName = 'TestStyleSheet'; + + const imageName = "Umbraco"; + const umbracoFileValue = {"src": "Umbraco.png"}; + const fileName = "Umbraco.png"; + const path = fileName; + const mimeType = "image/png"; + + await umbracoApi.documentTypes.ensureNameNotExists(elementNameTwo); + await umbracoApi.stylesheets.ensureNameNotExists(stylesheetName + '.css'); + await umbracoApi.media.ensureNameNotExists(imageName); + + const mediaItem = new MediaBuilder() + .withName(imageName) + .withContentTypeAlias('Image') + .addProperty() + .withAlias('umbracoFile') + .withValue(umbracoFileValue) + .done() + .build(); + const mediaFile = new MediaFileBuilder() + .withName(fileName) + .withPath(path) + .withMimeType(mimeType) + const testImage = await umbracoApi.media.saveFile(mediaItem, mediaFile) + // Finds the image path so we are able to locate where the image is located in the wwwroot + const imagePath = testImage.mediaLink.replace('/media/', '').replace('/umbraco.png', ''); + const stylesheet = new StylesheetBuilder() + .withVirtualPath("/css/") + .withFileType("stylesheets") + .withName(stylesheetName) + .build(); + await umbracoApi.stylesheets.save(stylesheet); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + await umbracoApi.documentTypes.createDefaultElementType(elementNameTwo, elementTwoAlias); + + const blockListType = new BlockListDataTypeBuilder() + .withName(blockListName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .build(); + await umbracoApi.dataTypes.save(blockListType); + + await umbracoUi.navigateToDataType(blockListName); + + // Updates properties in a block + await page.locator('[block-config-model="block"] >> nth=0').click(); + // Changes label + await page.locator('[name="label"]').fill('test'); + // Adds a stylesheet + await page.locator('[key="blockEditor_addCustomStylesheet"]').click(); + await page.locator('[data-element="tree-item-wwwroot"]').locator('[data-element="tree-item-expand"]').click(); + await page.locator('[data-element="tree-item-css"]').locator('[data-element="tree-item-expand"]').click(); + await page.locator('.umb-tree-item__label', {hasText: stylesheetName + '.css'}).click(); + // Changes editor size + await page.locator('[id="blockEditorSize"]').selectOption({value: "large"}); + // Adds element type as settings model + await page.locator('[key="blockEditor_addSettingsElementType"]').click(); + await umbracoUi.clickDataElementByElementName('tree-item-' + elementNameTwo); + // Changes background color + await page.locator('.umb-el-wrap', {hasText: 'Background color'}).locator('.sp-replacer').click(); + await page.locator('[title="#f44336"] >> nth=0').click(); + await page.locator('.sp-choose >> nth=0').click(); + // Changes icon color + await page.locator('.umb-el-wrap', {hasText: 'Icon color'}).locator('.sp-replacer').click(); + await page.locator('[title="#c90076"] >> nth=1').click(); + await page.locator('.sp-choose >> nth=1').click(); + // Adds a thumbnail + await page.locator('[key="blockEditor_addThumbnail"]').click(); + await page.locator('[data-element="tree-item-wwwroot"]').locator('[data-element="tree-item-expand"]').click(); + await page.locator('[data-element="tree-item-media"]').locator('[data-element="tree-item-expand"]').click(); + await page.locator('[data-element="tree-item-' + imagePath + '"]').locator('[data-element="tree-item-expand"]').click(); + await page.locator('.umb-tree-item__label', {hasText: fileName}).click(); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.submitChanges)); + + // Updates properties in the block list editor + await page.locator('[name="numberFieldMin"]').fill('1'); + await page.locator('[name="numberFieldMax"]').fill('10'); + await page.locator('[id="useLiveEditing"]').click(); + await page.locator('[id="useInlineEditingAsDefault"]').click(); + await page.locator('[id="maxPropertyWidth"]').fill('100px'); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await expect(page.locator('.alert-success', {hasText: 'Datatype saved'})).toBeVisible(); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementNameTwo); + await umbracoApi.stylesheets.ensureNameNotExists(stylesheetName + '.css'); + await umbracoApi.media.ensureNameNotExists(imageName); + }); + + test('can delete a block list editor', async ({page, umbracoApi, umbracoUi}) => { + const elementNameTwo = 'SecondElement'; + const elementTwoAlias = AliasHelper.toAlias(elementNameTwo); + + await umbracoApi.documentTypes.ensureNameNotExists(elementNameTwo); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName, elementAlias); + const elementTwo = await umbracoApi.documentTypes.createDefaultElementType(elementNameTwo, elementTwoAlias); + + const blockListType = new BlockListDataTypeBuilder() + .withName(blockListName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .addBlock() + .withContentElementTypeKey(elementTwo['key']) + .done() + .build(); + await umbracoApi.dataTypes.save(blockListType); + + // Navigates to the block list editor + await umbracoUi.goToSection(ConstantHelper.sections.settings); + await page.locator('[data-element="tree-item-dataTypes"]').locator('[data-element="tree-item-expand"]').click(); + await umbracoUi.clickDataElementByElementName('tree-item-' + blockListName, {button: "right"}); + + // Deletes the block list editor + await umbracoUi.clickDataElementByElementName(ConstantHelper.actions.delete); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.delete)); + + // Assert + await expect(await umbracoUi.getDataElementByElementName('tree-item-' + blockListName)).not.toBeVisible(); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementNameTwo); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDocument.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDocument.spec.ts new file mode 100644 index 0000000000..e38236f3dd --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/BlockListEditor/blockListEditorDocument.spec.ts @@ -0,0 +1,149 @@ +import {AliasHelper, ConstantHelper, test} from "@umbraco/playwright-testhelpers"; +import {DocumentTypeBuilder} from "@umbraco/json-models-builders"; +import {expect} from "@playwright/test"; +import {BlockListDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes"; + +test.describe('BlockListEditorDocument', () => { + const documentName = 'DocumentName'; + const elementName = 'TestElement'; + const blockListName = 'BlockListTest'; + + const blockListAlias = AliasHelper.toAlias(blockListName); + const elementAlias = AliasHelper.toAlias(elementName); + + test.beforeEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.login(); + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.dataTypes.ensureNameNotExists(blockListName); + }); + + test.afterEach(async ({page, umbracoApi, umbracoUi}) => { + await umbracoApi.documentTypes.ensureNameNotExists(documentName); + await umbracoApi.dataTypes.ensureNameNotExists(blockListName); + }); + + test('can create empty block list in a document', async ({page, umbracoApi, umbracoUi}) => { + const groupName = 'blockListGroup'; + + const rootDocType = new DocumentTypeBuilder() + .withName(documentName) + .withAllowAsRoot(true) + .build(); + await umbracoApi.documentTypes.save(rootDocType); + + await umbracoUi.navigateToDocumentType(documentName); + + // Adds a group with a BlockList editor + await umbracoUi.goToAddEditor(groupName, blockListName); + // Waits until the selector is visible + await expect(page.locator('[data-element="datatype-Block List"]')).toBeVisible(); + await umbracoUi.clickDataElementByElementName('datatype-Block List'); + + // Creates new BlockList editor + await page.locator('[title="Create a new configuration of Block List"]').click(); + await page.locator('[id="dataTypeName"]').fill(blockListName); + await page.locator('[data-element="editor-data-type-settings"]').locator('[label-key=' + ConstantHelper.buttons.submit + ']').click(); + // Checks to be sure that the clicked button is not visible + await expect(page.locator('[data-element="editor-data-type-settings"]').locator('[label-key=' + ConstantHelper.buttons.submit + ']')).not.toBeVisible(); + // Checks to ensure that the button is visible + await expect(page.locator('[name="propertySettingsForm"]').locator('[label-key=' + ConstantHelper.buttons.submit + ']')).toBeVisible(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.submit)); + + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await expect(page.locator('.umb-notifications__notifications > .alert-success', {hasText: "Datatype saved"})).toBeVisible(); + await expect(page.locator('.umb-notifications__notifications > .alert-success', {hasText: "Document Type saved"})).toBeVisible(); + }); + + test('can create multiple block list editors in a document', async ({page, umbracoApi, umbracoUi}) => { + const elementNameSecond = 'TestElementTwo'; + const blockListNameSecond = 'BlockListTestNumbaTwo'; + + const blockListSecondAlias = AliasHelper.toAlias(blockListNameSecond); + + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.documentTypes.ensureNameNotExists(elementNameSecond); + await umbracoApi.dataTypes.ensureNameNotExists(blockListNameSecond); + + const element = await umbracoApi.documentTypes.createDefaultElementType(elementName,elementAlias); + + const elementAliasSecond = AliasHelper.toAlias(elementNameSecond); + const elementTypeSecond = new DocumentTypeBuilder() + .withName(elementNameSecond) + .withAlias(elementAliasSecond) + .AsElementType() + .addGroup() + .withName('TestString') + .withAlias('testString') + .addTextBoxProperty() + .withLabel('Title') + .withAlias('title') + .done() + .addTextBoxProperty() + .withLabel('Body') + .withAlias('body') + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(elementTypeSecond); + + const blockListType = new BlockListDataTypeBuilder() + .withName(blockListName) + .addBlock() + .withContentElementTypeKey(element['key']) + .done() + .build(); + const blockList = await umbracoApi.dataTypes.save(blockListType); + + const blockListTypeTwo = new BlockListDataTypeBuilder() + .withName(blockListNameSecond) + .addBlock() + .withContentElementTypeKey(elementTypeSecond['key']) + .withLabel('Howdy') + .withBackgroundColor('#e06666') + .withIconColor('#93c47d') + .done() + .build(); + const blockListTwo = await umbracoApi.dataTypes.save(blockListTypeTwo); + + const rootDocType = new DocumentTypeBuilder() + .withName(documentName) + .withAllowAsRoot(true) + .addGroup() + .withName('TestName') + .addCustomProperty(blockList['id']) + .withAlias(blockListAlias) + .done() + .done() + .addGroup() + .withName('Uno Mas') + .addCustomProperty(blockListTwo['id']) + .withAlias(blockListSecondAlias) + .done() + .done() + .build(); + await umbracoApi.documentTypes.save(rootDocType); + + await umbracoUi.navigateToDocumentType(documentName); + + // Adds another block list editor to the first group + await page.locator('[data-element="group-TestName"]').locator('[data-element="property-add"]').click(); + await page.locator('[data-element="property-name"]').fill('TheBlock'); + await umbracoUi.clickDataElementByElementName('editor-add'); + await umbracoUi.clickDataElementByElementName('datatype-Block List'); + await page.locator('[title="Select ' + blockListNameSecond + '"]').click(); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.submit)); + await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save)); + + // Assert + await expect(page.locator('.umb-notifications__notifications > .alert-success', {hasText: "Document Type saved"})).toBeVisible(); + // Checks if the new block list is in the group + await expect(page.locator('[data-element="group-TestName"] >> [data-element="property-theBlock"]')).toBeVisible(); + + // Clean + await umbracoApi.documentTypes.ensureNameNotExists(elementName); + await umbracoApi.documentTypes.ensureNameNotExists(elementNameSecond); + await umbracoApi.dataTypes.ensureNameNotExists(blockListNameSecond); + }); +}); \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/routing.spec.ts b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/routing.spec.ts index 14cfd99185..0838b5acde 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/routing.spec.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/tests/DefaultConfig/Content/routing.spec.ts @@ -20,7 +20,6 @@ test.describe('Routing', () => { }); test.afterEach(async ({page, umbracoApi}) => { - await umbracoApi.login(); await umbracoApi.content.deleteAllContent(); await umbracoApi.documentTypes.ensureNameNotExists(rootDocTypeName); await umbracoApi.languages.ensureCultureNotExists(danishCulture); From a9e605d5322131ebe2065ef0958e44da32cbd7f8 Mon Sep 17 00:00:00 2001 From: Mole Date: Mon, 14 Nov 2022 11:37:55 +0100 Subject: [PATCH 26/39] V11: InMemory - only add models assembly as runtime view reference if it exists (#13390) * Only add models assembly as runtime view reference if it exists * Add in memory metadata reference properly compilations are readonly --- .../CollectibleRuntimeViewCompiler.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs index 742e7ffc29..d266b51dd1 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs @@ -396,26 +396,30 @@ internal class CollectibleRuntimeViewCompiler : IViewCompiler private CSharpCompilation CreateCompilation(string compilationContent, string assemblyName) { IReadOnlyList refs = _referenceManager.CompilationReferences; - // We'll add the reference to the InMemory assembly directly, this means we don't have to hack around with assembly parts. - if (_loadContextManager.ModelsAssemblyLocation is null) - { - throw new InvalidOperationException("No InMemory assembly available, cannot compile views"); - } - - PortableExecutableReference inMemoryAutoReference = MetadataReference.CreateFromFile(_loadContextManager.ModelsAssemblyLocation); - var sourceText = SourceText.From(compilationContent, Encoding.UTF8); SyntaxTree syntaxTree = SyntaxFactory .ParseSyntaxTree(sourceText, _compilationOptionsProvider.ParseOptions) .WithFilePath(assemblyName); - return CSharpCompilation + CSharpCompilation compilation = CSharpCompilation .Create(assemblyName) .AddSyntaxTrees(syntaxTree) .AddReferences(refs) - .AddReferences(inMemoryAutoReference) .WithOptions(_compilationOptionsProvider.CSharpCompilationOptions); + + // We'll add the reference to the InMemory assembly directly, this means we don't have to hack around with assembly parts. + // We might be asked to compile views before the InMemory models assembly is created tho (if you replace the no-nodes for instance) + // In this case we'll just skip the InMemory models assembly reference + if (_loadContextManager.ModelsAssemblyLocation is null) + { + _logger.LogInformation("No InMemory models assembly available, skipping reference"); + return compilation; + } + + PortableExecutableReference inMemoryAutoReference = MetadataReference.CreateFromFile(_loadContextManager.ModelsAssemblyLocation); + compilation = compilation.AddReferences(inMemoryAutoReference); + return compilation; } private string GetNormalizedPath(string relativePath) From 1bd03374dc8bbf0f4856ff40d66ae34fd6089ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 11 Nov 2022 22:15:07 +0100 Subject: [PATCH 27/39] Block Grid Editor Improvements (#13282) --- .../umbBlockGridDemoHeadlineBlock.html | 10 +- .../umbBlockGridDemoImageBlock.html | 30 +-- .../umbBlockGridDemoRichTextBlock.html | 11 +- .../umbBlockGridDemoTwoColumnLayoutBlock.html | 49 +---- .../EmbeddedResources/Lang/da.xml | 12 +- .../EmbeddedResources/Lang/en.xml | 14 +- .../EmbeddedResources/Lang/en_us.xml | 12 +- .../PropertyEditors/BlockGridConfiguration.cs | 6 + .../property/umbproperty.directive.js | 3 +- .../property/umbpropertyactions.component.js | 45 ++-- .../filters/mediaItemResolver.filter.js | 65 ++++++ .../subheader/umb-editor-sub-header.less | 6 + src/Umbraco.Web.UI.Client/src/less/rte.less | 2 +- .../components/property/umb-property.html | 4 +- .../gridblock/gridblock.editor.html | 19 +- .../gridinlineblock.editor.controller.js | 64 +++++- .../gridinlineblock.editor.html | 74 ++++--- .../gridinlineblock.editor.less | 173 --------------- .../gridsortblock/gridsortblock.editor.html | 92 ++++++++ .../heroblock/heroblock.editor.controller.js | 45 ---- .../mediablock.editor.controller.js | 47 ---- .../blockgrid/blockgridui.less | 207 ++++++------------ ...ckconfiguration.area.overlay.controller.js | 12 + ...kgrid.blockconfiguration.area.overlay.html | 9 +- .../blockgrid.blockconfiguration.html | 14 +- .../blockgrid.blockconfiguration.less | 27 +-- .../blockgrid.blockconfiguration.overlay.html | 15 +- .../blockgrid.groupconfiguration.html | 6 +- .../umb-block-grid-area-allowance-editor.html | 14 +- .../umb-block-grid-area-allowance-editor.less | 7 +- .../umbBlockGridAreaEditor.component.js | 38 +++- .../blockgrid/umb-block-grid-entries.html | 4 +- .../blockgrid/umb-block-grid-entry.html | 31 +-- .../umb-block-grid-property-editor.html | 19 +- .../umb-block-grid-property-editor.less | 4 + .../umb-block-grid-render-area-slots.html | 3 + .../umbBlockGridPropertyEditor.component.js | 148 +++++++++++-- .../blockgrid/umbblockgridblock.component.js | 2 +- .../umbblockgridentries.component.js | 37 +++- .../blockgrid/umbblockgridentry.component.js | 207 ++++++++++++++---- .../umbblockgridrenderareaslots.directive.js | 20 ++ .../blockgrid/umbblockgridroot.component.js | 4 +- .../blockgrid/umbraco-blockgridlayout.css | 9 +- .../propertyeditors/rte/rte.controller.js | 20 +- .../propertyeditors/rte-controller.spec.js | 6 +- 45 files changed, 934 insertions(+), 712 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/filters/mediaItemResolver.filter.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridsortblock/gridsortblock.editor.html delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/heroblock/heroblock.editor.controller.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/mediablock/mediablock.editor.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridrenderareaslots.directive.js diff --git a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoHeadlineBlock.html b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoHeadlineBlock.html index 523b0fe7d3..495c704e28 100644 --- a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoHeadlineBlock.html +++ b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoHeadlineBlock.html @@ -1,11 +1,4 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html index dbe157cf84..b3362fcda9 100644 --- a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html +++ b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html @@ -1,13 +1,5 @@
-
- \ No newline at end of file + \ No newline at end of file diff --git a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoTwoColumnLayoutBlock.html b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoTwoColumnLayoutBlock.html index ccc3af22e8..bc290fe8fb 100644 --- a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoTwoColumnLayoutBlock.html +++ b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoTwoColumnLayoutBlock.html @@ -1,50 +1,3 @@ -
- - +
\ No newline at end of file diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml index 387f1ef652..049b617f93 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml @@ -2201,6 +2201,8 @@ Mange hilsner fra Umbraco robotten Avanceret Skjul indholdseditoren Skjul indholds redigerings knappen samt indholdseditoren i Blok Redigerings vinduet + Direkte redigering + Tilføjer direkte redigering a det første felt. Yderligere felter optræder kun i redigerings vinduet. Du har lavet ændringer til dette indhold. Er du sikker på at du vil kassere dem? Annuller oprettelse? @@ -2218,6 +2220,7 @@ Mange hilsner fra Umbraco robotten Tillad kun specifikke blok-typer Tilladte blok-typer Vælg de blok-typer, der er tilladt i dette område, og evt. også hvor mange af hver type, redaktørerne skal tilføje til området. + Når denne er tom er alle block-typer tilladt for områder tilladt. Er du sikker på, at du vil slette dette område? Alle blokke, der er oprettet i dette område, vil blive slettet. Layout-opsætning @@ -2255,9 +2258,9 @@ Mange hilsner fra Umbraco robotten Tilføj katalog udseende Tilføj Blok Tilføj gruppe - Tilføj gruppe eller block - Sæt minimum krav for denne tilladelse - Set maksimum krav for denne tilladelse + Tilføj gruppe eller Blok + Sæt minimum krav + Sæt maksimum krav Blok Blok Indstillinger @@ -2267,6 +2270,9 @@ Mange hilsner fra Umbraco robotten Installer demo konfiguration Dette indeholder Blokke for Overskrift, Beriget-Tekst, Billede og To-Koloners-Layout.]]> Installer + Sortings tilstand + Afslut sortings tilstand + Dette område alias skal være unikt sammenlignet med andre områder af denne Blok. Hvad er Indholdsskabeloner? diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index 1e41048126..3d3116afc7 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -2749,7 +2749,9 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Settings Advanced Hide content editor - Hide the content edit button and the content editor from the Block Editor overlay + Hide the content edit button and the content editor from the Block Editor overlay. + Inline editing + Enables inline editing for the first Property. Additional properties can be edited in the overlay. You have made changes to this content. Are you sure you want to discard them? Discard creation? @@ -2781,6 +2783,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Make this block available in the root of the layout. Allow in areas Make this block available within other Blocks. + When empty all Blocks allowed for Areas can be created. Areas Grid Columns for Areas Define how many columns that will be available for areas. If not defined, the number of columns defined for the entire layout will be used. @@ -2807,17 +2810,20 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Add Block Add group Pick group or Block - Set minimum requirement for this allowance - Set maximum requirement for this allowance + Set a minimum requirement + Set a maximum requirement Block Block Settings Areas Advanced - Allowance + Permissions Install Sample Configuration Install + Sort mode + End sort mode + This Areas Alias must be unique compared to the other Areas of this Block. What are Content Templates? diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index ed6bd37157..29817fc8ed 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -2853,6 +2853,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Advanced Hide content editor Hide the content edit button and the content editor from the Block Editor overlay + Inline editing + Enables inline editing for the first Property. Additional properties can be edited in the overlay. You have made changes to this content. Are you sure you want to discard them? Discard creation? @@ -2884,6 +2886,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Make this block available in the root of the layout. Allow in areas Make this block available within other Blocks. + When empty all Blocks allowed for Areas can be created. Areas Grid Columns for Areas Define how many columns that will be available for areas. If not defined, the number of columns defined for the entire layout will be used. @@ -2910,17 +2913,20 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Add Block Add group Pick group or Block - Set minimum requirement for this allowance - Set maximum requirement for this allowance + Set a minimum requirement + Set a maximum requirement Block Block Settings Areas Advanced - Allowance + permissions Install Sample Configuration Install + Sort mode + End sort mode + This Areas Alias must be unique compared to the other Areas of this Block. What are Content Templates? diff --git a/src/Umbraco.Core/PropertyEditors/BlockGridConfiguration.cs b/src/Umbraco.Core/PropertyEditors/BlockGridConfiguration.cs index 0a5bacad14..f635eb6324 100644 --- a/src/Umbraco.Core/PropertyEditors/BlockGridConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/BlockGridConfiguration.cs @@ -40,6 +40,9 @@ public class BlockGridConfiguration [DataMember(Name ="columnSpanOptions")] public BlockGridColumnSpanOption[] ColumnSpanOptions { get; set; } = Array.Empty(); + [DataMember(Name ="rowMinSpan")] + public int? RowMinSpan { get; set; } + [DataMember(Name ="rowMaxSpan")] public int? RowMaxSpan { get; set; } @@ -82,6 +85,9 @@ public class BlockGridConfiguration [DataMember(Name ="editorSize")] public string? EditorSize { get; set; } + [DataMember(Name ="inlineEditing")] + public bool InlineEditing { get; set; } + [DataMember(Name ="forceHideContentEditorInOverlay")] public bool ForceHideContentEditorInOverlay { get; set; } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js index 702cd5aeda..5a30f81d4b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js @@ -24,7 +24,8 @@ // optional, if set this will be used for the property alias validation path (hack required because NC changes the actual property.alias :/) propertyAlias: "@", showInherit: "<", - inheritsFrom: "<" + inheritsFrom: "<", + hideLabel: " { - - if (action.labelKey) { - localizationService.localize(action.labelKey, (action.labelTokens || []), action.label).then(data => { - action.label = data; - }); - - action.useLegacyIcon = action.useLegacyIcon === false ? false : true; - action.icon = (action.useLegacyIcon ? 'icon-' : '') + action.icon; - } - }); + updateActions(); } } + + function updateActions() { + + Utilities.forEach(vm.actions || [], action => { + + if (action.labelKey) { + localizationService.localize(action.labelKey, (action.labelTokens || []), action.label).then(data => { + action.label = data; + }); + + action.useLegacyIcon = action.useLegacyIcon === false ? false : true; + action.icon = (action.useLegacyIcon && action.icon.indexOf('icon-') !== 0 ? 'icon-' : '') + action.icon; + } + }); + } } var umbPropertyActionsComponent = { diff --git a/src/Umbraco.Web.UI.Client/src/common/filters/mediaItemResolver.filter.js b/src/Umbraco.Web.UI.Client/src/common/filters/mediaItemResolver.filter.js new file mode 100644 index 0000000000..be0338093c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/filters/mediaItemResolver.filter.js @@ -0,0 +1,65 @@ +(function () { + "use strict"; + + function mediaItemResolverFilterService(mediaResource, eventsService) { + + var mediaKeysRequest = []; + var mediaItemCache = []; + + var service = { + + getByKey: function (key) { + // Is it cached, then get that: + const cachedMediaItem = mediaItemCache.find(cache => key === cache.key); + if(cachedMediaItem) { + return cachedMediaItem; + } + + // check its not already being loaded, and then start loading: + if(mediaKeysRequest.indexOf(key) === -1) { + mediaKeysRequest.push(key); + mediaResource.getById(key).then(function (mediaItem) { + if(mediaItem) { + mediaItemCache.push(mediaItem); + } + }); + } + + return null; + } + }; + + eventsService.on("editors.media.saved", function (name, args) { + const index = mediaItemCache.findIndex(cache => cache.key === args.media.key); + if(index !== -1) { + mediaItemCache[index] = args.media; + } + }); + + return service; + + } + + angular.module("umbraco.filters").factory("mediaItemResolverFilterService", mediaItemResolverFilterService); + + + // Filter loads Media Item Model from a Media Key. + // Usage: {{ myMediaProperty[0].mediaKey | mediaItemResolver }} + angular.module("umbraco.filters").filter("mediaItemResolver", function (mediaItemResolverFilterService) { + + mediaItemResolverFilter.$stateful = true; + function mediaItemResolverFilter(input) { + + // Check we have a value at all + if (typeof input === 'string' && input.length > 0) { + return mediaItemResolverFilterService.getByKey(input); + } + + return null; + } + + return mediaItemResolverFilter; + + }); + +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less index 0dd7bfc7f4..4eaf00724c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less @@ -20,6 +20,12 @@ background-color: @white; border-color: @white; } +.umb-editor-sub-header--blue { + background-color: @ui-selected-border; + border-color: @ui-selected-border; + color: @white; + border-radius: 3px; +} .umb-editor-sub-header.--state-selection { padding-left: 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index 8ff4d128ed..ed25ce4e90 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -20,7 +20,7 @@ } .umb-rte.--initialized .umb-rte-editor-con { height:auto; - min-height: 100px; + min-height: 95px; visibility: visible; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html index f00ba725b2..cc41896a0f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html @@ -1,13 +1,13 @@
+ ng-class="{'hidelabel':vm.hideLabel || vm.property.hideLabel, '--label-on-top':vm.property.labelOnTop, 'umb-control-group__listview': vm.property.alias === '_umb_containerView'}">
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridblock/gridblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridblock/gridblock.editor.html index a4efb41717..710dd15e53 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridblock/gridblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridblock/gridblock.editor.html @@ -53,6 +53,23 @@ padding-top:2px; padding-bottom:2px; } + + :host { + --inherited--column-gap: var(--umb-block-grid--column-gap, 10px); + --inherited--row-gap: var(--umb-block-grid--row-gap, 10px); + --inherited--areas-column-gap: var(--umb-block-grid--areas-column-gap, 10px); + --inherited--areas-row-gap: var(--umb-block-grid--areas-row-gap, 10px); + } + + [part='area-container'] { + box-sizing: border-box; + padding: 10px; + --umb-block-grid--column-gap: var(--inherited--column-gap, 10px); + --umb-block-grid--row-gap: var(--inherited--row-gap, 10px); + --umb-block-grid--areas-column-gap: var(--inherited--areas-column-gap, 10px); + --umb-block-grid--areas-row-gap: var(--inherited--areas-row-gap, 10px); + } +
@@ -64,6 +81,6 @@ {{block.label}} - +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.controller.js index 2a77e81b5c..3d55ea1836 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.controller.js @@ -1,27 +1,71 @@ (function () { 'use strict'; - function GridInlineBlockEditor($scope, $element) { + function GridInlineBlockEditor($scope, $compile, $element) { const vm = this; + var propertyEditorElement; + vm.$onInit = function() { - const host = $element[0].getRootNode(); + + vm.property = $scope.block.content.variants[0].tabs[0]?.properties[0]; - console.log(document.styleSheets) + if (vm.property) { + vm.propertySlotName = "umbBlockGridProxy_" + vm.property.alias + "_" + String.CreateGuid(); + + propertyEditorElement = $('
'); + propertyEditorElement.html( + ` + - for (const stylesheet of document.styleSheets) { + + - console.log(stylesheet); - const styleEl = document.createElement('link'); - styleEl.setAttribute('rel', 'stylesheet'); - styleEl.setAttribute('type', stylesheet.type); - styleEl.setAttribute('href', stylesheet.href); + + ` + ); + + $element[0].addEventListener('umb-rte-focus', onRteFocus); + $element[0].addEventListener('umb-rte-blur', onRteBlur); - host.appendChild(styleEl); + const connectedCallback = () => { + + $compile(propertyEditorElement)($scope) + }; + + const event = new CustomEvent("UmbBlockGrid_AppendProperty", {composed: true, bubbles: true, detail: {'property': propertyEditorElement[0], 'contentUdi': $scope.block.layout.contentUdi, 'slotName': vm.propertySlotName, 'connectedCallback':connectedCallback}}); + + $element[0].dispatchEvent(event); + } } + function onRteFocus() { + $element[0].classList.add('umb-block-grid--force-focus'); + } + function onRteBlur() { + $element[0].classList.remove('umb-block-grid--force-focus'); + } + + vm.$onDestroy = function() { + if (vm.property) { + const event = new CustomEvent("UmbBlockGrid_RemoveProperty", {composed: true, bubbles: true, detail: {'slotName': vm.propertySlotName}}); + $element[0].dispatchEvent(event); + } + + $element[0].removeEventListener('umb-rte-focus', onRteFocus); + $element[0].removeEventListener('umb-rte-blur', onRteBlur); + propertyEditorElement = null; + } + } angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.GridInlineBlockEditor", GridInlineBlockEditor); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html index ca12c9dd98..3a67a1be88 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html @@ -1,24 +1,6 @@ -
- -
- -
-
- - - - -
- +
+ + {{block.label}} +
- + - +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.less deleted file mode 100644 index b8ffcff3ec..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.less +++ /dev/null @@ -1,173 +0,0 @@ -.blockelement-inlineblock-editor { - display: block; - margin-bottom: 4px; - margin-top: 4px; - border: 1px solid @gray-9; - border-radius: @baseBorderRadius; - transition: border-color 120ms, background-color 120ms; - - .umb-block-list__block:not(.--active) &:hover { - border-color: @gray-8; - } - - .umb-editor-tab-bar { - margin: 0; - position: static; - padding: 0; - } - - > button { - width: 100%; - min-height: 48px; - cursor: pointer; - color: @ui-action-discreet-type; - text-align: left; - padding-left: 10px; - padding-bottom: 2px; - user-select: none; - background-color: white; - - .caret { - vertical-align: middle; - transform: rotate(-90deg); - transition: transform 80ms ease-out; - } - - .icon { - font-size: 1.1rem; - display: inline-block; - vertical-align: middle; - } - - span.name { - position: relative; - display: inline-block; - vertical-align: middle; - } - - &:hover { - color: @ui-action-discreet-type-hover; - border-color: @gray-8; - } - } - - ng-form.ng-invalid-val-server-match-content > .umb-block-list__block > .umb-block-list__block--content > div > & { - > button { - color: @formErrorText; - - .show-validation-type-warning & { - color: @formWarningText; - } - - span.caret { - border-top-color: @formErrorText; - - .show-validation-type-warning & { - border-top-color: @formWarningText; - } - } - } - } - - ng-form.ng-invalid-val-server-match-content > .umb-block-list__block:not(.--active) > .umb-block-list__block--content > div > & { - > button { - span.name { - &::after { - content: "!"; - text-align: center; - position: absolute; - top: -6px; - right: -15px; - min-width: 10px; - color: @white; - background-color: @ui-active-type; - border: 2px solid @white; - border-radius: 50%; - font-size: 10px; - font-weight: bold; - padding: 2px; - line-height: 10px; - background-color: @formErrorText; - - .show-validation-type-warning & { - background-color: @formWarningText; - } - - font-weight: 900; - animation-duration: 1.4s; - animation-iteration-count: infinite; - animation-name: blockelement-inlineblock-editor--badge-bounce; - animation-timing-function: ease; - - @keyframes blockelement-inlineblock-editor--badge-bounce { - 0% { - transform: translateY(0); - } - - 20% { - transform: translateY(-4px); - } - - 40% { - transform: translateY(0); - } - - 55% { - transform: translateY(-2px); - } - - 70% { - transform: translateY(0); - } - - 100% { - transform: translateY(0); - } - } - } - } - } - } -} - -.umb-block-list__block.--active { - border-color: @gray-8; - box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.05); - - > .umb-block-list__block--content { - > .umb-block-list__block--view { - > .blockelement-inlineblock-editor { - > button { - > .caret { - transform: rotate(0deg); - } - } - } - } - } -} - -.blockelement-inlineblock-editor__inner { - border-top: 1px solid @gray-8; - background-color: @gray-12; - - > * > * > * > .umb-group-panel { - background-color: transparent; - box-shadow: none; - margin-top: 10px; - margin-bottom: 0; - > .umb-group-panel__content .umb-property { - margin-bottom: 20px; - } - } - .umb-group-panel + .umb-group-panel { - margin-top: 20px; - } - &.--singleGroup > * > * > * > .umb-group-panel { - margin-top: 0; - > .umb-group-panel__header { - display: none; - } - } - -} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridsortblock/gridsortblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridsortblock/gridsortblock.editor.html new file mode 100644 index 0000000000..bd6eebfac6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridsortblock/gridsortblock.editor.html @@ -0,0 +1,92 @@ + + +
+ + + + +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/heroblock/heroblock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/heroblock/heroblock.editor.controller.js deleted file mode 100644 index 2dc717d8ef..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/heroblock/heroblock.editor.controller.js +++ /dev/null @@ -1,45 +0,0 @@ -(function () { - 'use strict'; - - function HeroBlockEditor($scope, mediaResource, mediaHelper) { - - var unsubscribe = []; - - const bc = this; - - $scope.$watch("block.data.image", function(newValue, oldValue) { - if (newValue !== oldValue) { - bc.retrieveMedia(); - } - }, true); - - bc.retrieveMedia = function() { - - if($scope.block.data.image && $scope.block.data.image.length > 0) { - mediaResource.getById($scope.block.data.image[0].mediaKey).then(function (mediaEntity) { - - var mediaPath = mediaEntity.mediaLink; - - //set a property on the 'scope' for the returned media object. - bc.mediaName = mediaEntity.name; - bc.isImage = mediaHelper.detectIfImageByExtension(mediaPath); - bc.imageSource = mediaPath; - }); - } - } - - bc.retrieveMedia(); - - - - $scope.$on("$destroy", function () { - for (const subscription of unsubscribe) { - subscription(); - } - }); - - } - - angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.HeroBlockEditor", HeroBlockEditor); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/mediablock/mediablock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/mediablock/mediablock.editor.controller.js deleted file mode 100644 index 2f79f3b9f1..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/mediablock/mediablock.editor.controller.js +++ /dev/null @@ -1,47 +0,0 @@ -(function () { - 'use strict'; - - function MediaBlockEditor($scope, mediaResource, mediaHelper) { - - var unsubscribe = []; - - const bc = this; - - $scope.$watch("block.data.image", function(newValue, oldValue) { - if (newValue !== oldValue) { - bc.retrieveMedia(); - } - }, true); - - bc.retrieveMedia = function() { - - if($scope.block.data.image && $scope.block.data.image.length > 0) { - mediaResource.getById($scope.block.data.image[0].mediaKey).then(function (mediaEntity) { - - var mediaPath = mediaEntity.mediaLink; - - //set a property on the 'scope' for the returned media object - bc.icon = mediaEntity.contentType.icon; - bc.mediaName = mediaEntity.name; - bc.fileExtension = mediaHelper.getFileExtension(mediaPath); - bc.isImage = mediaHelper.detectIfImageByExtension(mediaPath); - bc.imageSource = mediaHelper.getThumbnailFromPath(mediaPath); - }); - } - } - - bc.retrieveMedia(); - - - - $scope.$on("$destroy", function () { - for (const subscription of unsubscribe) { - subscription(); - } - }); - - } - - angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.MediaBlockEditor", MediaBlockEditor); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less index 6cfdb05482..38ff8086c6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less @@ -21,13 +21,18 @@ .umb-block-grid__layout-item { position: relative; &:hover { - z-index: 3; -/* - > .umb-block-grid__force-left, - > .umb-block-grid__force-right { + + > ng-form > .umb-block-grid__block--context { z-index: 4; } - */ + + > ng-form > .umb-block-grid__block--inline-create-button, + > ng-form > .umb-block-grid__block--validation-border, + > ng-form > .umb-block-grid__block--actions, + > ng-form > .umb-block-grid__force-left, + > ng-form > .umb-block-grid__force-right { + z-index: 3; + } } } @@ -49,52 +54,12 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti pointer-events: none; } -/*.umb-block-grid__block--validation-badge { - display:none; -} -ng-form.ng-invalid-val-server-match-settings > .umb-block-grid__block:not(.--active) > .umb-block-grid__block--validation-badge, -ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--active) > .umb-block-grid__block--validation-badge { - display:block; - text-align: center; - position: absolute; - top: -9px; - right: -9px; - min-width: 10px; - color: @white; - border: 2px solid @white; - border-radius: 50%; - font-size: 10px; - font-weight: bold; - padding: 2px; - line-height: 10px; - background-color: @formErrorText; - .show-validation-type-warning & { - background-color: @formWarningText; - } - font-weight: 900; - pointer-events: none; - - animation-duration: 1.4s; - animation-iteration-count: infinite; - animation-name: blockelement-inlineblock-editor--badge-bounce; - animation-timing-function: ease; - @keyframes blockelement-inlineblock-editor--badge-bounce { - 0% { transform: translateY(0); } - 20% { transform: translateY(-4px); } - 40% { transform: translateY(0); } - 55% { transform: translateY(-2px); } - 70% { transform: translateY(0); } - 100% { transform: translateY(0); } - } -} -*/ - .umb-block-grid__block { position: relative; width: 100%; height: 100%; - --umb-block-grid__block--show-ui: 0;// Publicly available. + --umb-block-grid--block-ui-opacity: 0; --umb-block-grid--hint-area-ui: 0; &::after { @@ -136,7 +101,6 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti --umb-block-grid--hint-area-ui: 1; &::after { - /*border-color: @blueDark;*/ display: var(--umb-block-grid--block-ui-display, block); animation: umb-block-grid__block__border-pulse 400ms ease-in-out alternate infinite; @keyframes umb-block-grid__block__border-pulse { @@ -211,18 +175,21 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti } } &.--block-ui-visible { + > .umb-block-grid__block--context { /* take full width to prevent interaction with elements behind.*/ left: 0; } .umb-block-grid__area-container, .umb-block-grid__block--view::part(area-container) { --umb-block-grid--block-ui-display: none; - .umb-block-grid__layout-item { - pointer-events: none; - } - .umb-block-grid__block { - pointer-events: none; - } + pointer-events: none; + } + .umb-block-grid__layout-item { + pointer-events: none; + } + .umb-block-grid__block { + pointer-events: none; + --umb-block-grid--block-ui-opacity: 0; } } @@ -236,48 +203,34 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti &.--active { /** Avoid displaying hover when dragging-mode */ - --umb-block-grid--block_ui-opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0)); + --umb-block-grid--block-ui-opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0)); > .umb-block-grid__block--context { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } &:not(.--scale-mode) { > .umb-block-grid__block--actions { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } > umb-block-grid-block > umb-block-grid-entries > .umb-block-grid__layout-container > .umb-block-grid__area-actions { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } } > .umb-block-grid__scale-handler { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } > .umb-block-grid__force-left, > .umb-block-grid__force-right { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } } - - /* - &.--show-validation { - ng-form.ng-invalid-val-server-match-content > & { - border: 2px solid @formErrorText; - border-radius: @baseBorderRadius; - } - } - */ } ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__block--actions { opacity: 1; } -/* -ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__block--context { - opacity: 1; -} -*/ .umb-block-grid__block--view { height: 100%; @@ -291,10 +244,20 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl top: -20px; right: 0; font-size: 12px; - z-index: 2; + z-index: 4; display: var(--umb-block-grid--block-ui-display, flex); justify-content: end; + /** prevent interaction with inline-create button just beneath the context-bar: */ + ::after { + content: ''; + position: absolute; + top: 100%; + left: 0; + right: 0; + height: 12px; + } + .__context-bar { padding: 0 9px; padding-top: 1px; @@ -509,27 +472,6 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl } } -/* -umb-block-grid-block { - - > div { - position: relative; - width: 100%; - min-height: @umb-block-grid__item_minimum_height; - background-color: @white; - border-radius: @baseBorderRadius; - box-sizing: border-box; - } - -} -*/ - -/* -.blockelement__draggable-element { - cursor: grab; -} -*/ - .umb-block-grid__scale-handler { cursor: nwse-resize; @@ -586,7 +528,7 @@ umb-block-grid-block { .umb-block-grid__block--inline-create-button { top: 0px; position: absolute; - z-index: 1; + z-index: 1; /** overwritten for the first one of an area. */ /** Avoid showing inline-create in dragging-mode */ opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0)); @@ -594,6 +536,12 @@ umb-block-grid-block { .umb-block-grid__block--inline-create-button.--above { left: 0; width: 100%; + + top: calc(var(--umb-block-grid--row-gap, 0px) * -0.5); +} +.umb-block-grid__layout-item:first-of-type .umb-block-grid__block--inline-create-button.--above { + /* Do not use row-gap if the first one. */ + top: 0; } .umb-block-grid__block--inline-create-button.--above.--at-root { /* If at root, and full-width then become 40px wider: */ @@ -601,14 +549,9 @@ umb-block-grid-block { left: calc(-20px * var(--calc)); width: calc(100% + 40px * var(--calc)); } + .umb-block-grid__block--inline-create-button.--after { - right: 1px; -} -.umb-block-grid__block--inline-create-button.--after.--detector { - width: 10px; - margin-right: -10px; - height: 100%; - z-index: 0; + right: calc(1px - (var(--umb-block-grid--column-gap, 0px) * 0.5)); } .umb-block-grid__block--inline-create-button.--after.--at-root { /* If at root, and full-width then move a little out to the right: */ @@ -624,8 +567,8 @@ umb-block-grid-block { pointer-events: none; } -.umb-block-grid__block--after-inline-create-button { - z-index:2; +.umb-block-grid__block--last-inline-create-button { + z-index:4; width: 100%; /* Move inline create button slightly up, to avoid collision with others*/ margin-bottom: -7px; @@ -746,13 +689,11 @@ umb-block-grid-block { align-items: center; justify-content: center; - /* TODO: dont use --umb-text-color, its temporary to inherit UI */ - color: var(--umb-text-color, @ui-action-discreet-type); + color: var(--umb-block-grid--text-color, @ui-action-discreet-type); font-weight: bold; padding: 5px 15px; - /* TODO: dont use --umb-text-color, its temporary to inherit UI */ - border: 1px dashed var(--umb-text-color, @ui-action-discreet-border); + border: 1px dashed var(--umb-block-grid--text-color, @ui-action-discreet-border); border-radius: @baseBorderRadius; box-sizing: border-box; @@ -760,24 +701,14 @@ umb-block-grid-block { height: 100%; &:hover { - color: var(--umb-text-color, @ui-action-discreet-type-hover); - border-color: var(--umb-text-color, @ui-action-discreet-border-hover); + color: var(--umb-block-grid--text-color-hover, @ui-action-discreet-type-hover); + border-color: var(--umb-block-grid--text-color-hover, @ui-action-discreet-border-hover); text-decoration: none; z-index: 1; } } } - -/** make sure block with areas stay on top, so they don't look like they are 'not-allowed'*/ -/* -.umb-block-grid__layout-container.--droppable-indication { - .umb-block-grid__area-actions { - display: none; - } -} -*/ - .umb-block-grid__layout-item-placeholder { background: transparent; border-radius: 3px; @@ -859,16 +790,19 @@ umb-block-grid-block { content: ''; position: absolute; inset: 0; - /* Moved slightly in to align with the inline-create button, which is moved slightly in to avoid collision with other create buttons. */ - top:2px; - bottom: 2px; + top:0; + bottom: 0; border-radius: 3px; border: 1px solid rgba(@gray-5, 0.3); pointer-events: none; opacity: var(--umb-block-grid--show-area-ui, 0); transition: opacity 240ms; + z-index:3; } .umb-block-grid__area.--highlight::after { + /* Moved slightly in to align with the inline-create button, which is moved slightly in to avoid collision with other create buttons. */ + top:2px; + bottom: 2px; /** Avoid displaying highlight when in dragging-mode */ opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0)); border-color: @blueDark; @@ -899,31 +833,10 @@ umb-block-grid-block { z-index: 1; cursor: nwse-resize; } -/* -.umb-block-grid__scalebox { - position: absolute; - top:0; - left:0; - z-index: 10; - cursor: nwse-resize; - - transition: background-color 240ms ease-in; - animation: umb-block-grid__scalebox__pulse 400ms ease-in-out alternate infinite; - @keyframes umb-block-grid__scalebox__pulse { - 0% { background-color: rgba(@blueMidLight, 0.33); } - 100% { background-color: rgba(@blueMidLight, 0.22); } - } -} -*/ -/* -.umb-block-grid__layout-container { - -} -*/ /** make sure block with areas stay on top, so they don't look like they are 'not-allowed'*/ @@ -934,8 +847,10 @@ umb-block-grid-block { } .umb-block-grid__layout-container .umb-block-grid__layout-item:not([depth='0']):first-of-type .umb-block-grid__block--inline-create-button.--above { - /* Move first above inline create button slightly up, to avoid collision with others*/ + /* Move the above inline create button slightly down, to avoid collision with others*/ margin-top: -7px; + + z-index:4; } .umb-block-grid__not-allowed-box { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js index 6a466cf052..f20f14f227 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js @@ -25,13 +25,25 @@ value: {min:vm.area.minAllowed, max:vm.area.maxAllowed} } + unsubscribe.push($scope.$watch('vm.area.alias', (newVal, oldVal) => { + $scope.model.updateTitle(); + if($scope.blockGridBlockConfigurationAreaForm.alias) { + $scope.blockGridBlockConfigurationAreaForm.alias.$setValidity("alias", $scope.model.otherAreaAliases.indexOf(newVal) === -1); + } + })); + vm.submit = function() { + if($scope.blockGridBlockConfigurationAreaForm.$valid === false) { + $scope.submitButtonState = "error"; + return; + } if ($scope.model && $scope.model.submit) { // Transfer minMaxModel to area: vm.area.minAllowed = vm.minMaxModel.value.min; vm.area.maxAllowed = vm.minMaxModel.value.max; + $scope.submitButtonState = "success"; $scope.model.submit($scope.model); } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.html index cccceb7afb..90918d71e1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.html @@ -27,11 +27,18 @@
+ * The alias will be printed by GetBlockGridHTML(), use the alias to target the Element representing this area. Ex. .umb-block-grid__area[data-area-alias="MyAreaAlias"] { ... }
- + +
+
+
+ This Areas Alias must be unique compared to the other Areas of this Block. +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.html index bfb9fdc90c..e78d94d486 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.html @@ -6,6 +6,7 @@
+
@@ -30,9 +31,14 @@
- + + + + Install demo Blocks + +
@@ -82,9 +88,9 @@
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.less index aa09b5238e..43151fcabb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.less @@ -2,29 +2,10 @@ margin-bottom: 20px; - .__add-button { - position: relative; - display: inline-flex; - width: 100%; - height: 100%; - margin-right: 20px; - margin-bottom: 20px; - - color: @ui-action-discreet-type; - border: 1px dashed @ui-action-discreet-border; - border-radius: @doubleBorderRadius; - - align-items: center; - justify-content: center; - - padding: 5px 15px; - box-sizing: border-box; - font-weight: bold; - } - - .__add-button:hover { - color: @ui-action-discreet-type-hover; - border-color: @ui-action-discreet-border-hover; + uui-button { + font-weight: 700; + --uui-button-border-radius: 6px; + min-height: 80px; } .__get-sample-box { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.html index 4b05e4ad43..27c1a2f006 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.html @@ -355,12 +355,25 @@ + +
+
+ + + Hide the content edit button and the content editor from the Block Editor overlay. + +
+ +
+
+
+
- Define the range of layout rows this block is allowed to span across. + Hide the content edit button and the content editor from the Block Editor overlay.
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.groupconfiguration.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.groupconfiguration.html index 86c975639c..25a7282ca2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.groupconfiguration.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.groupconfiguration.html @@ -1,5 +1,5 @@ -
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.html index c288afc3ff..55ceb941bb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.html @@ -2,13 +2,11 @@ -
- +
+ ng-click="vm.deleteAllowance(allowance);"> Delete @@ -56,12 +51,15 @@ +
+ When empty all Blocks allowed for Areas can be created. +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.less index 9389906fac..2ce5a2ef16 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.less @@ -5,8 +5,11 @@ width: 100%; } -.umb-block-grid-area-allowance-editor .__list.--disabled { - +.umb-block-grid-area-allowance-editor .__empty-label { + font-size: 12px; + color: @gray-6; + line-height: 1.5em; + padding-top: 5px; } .umb-block-grid-area-allowance-editor__entry { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js index e8ae592d8f..9126a6cc54 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js @@ -56,15 +56,32 @@ function initializeSortable() { - const gridLayoutContainerEl = $element[0].querySelector('.umb-block-grid-area-editor__grid-wrapper'); + function _sync(evt) { - const sortable = Sortable.create(gridLayoutContainerEl, { + const oldIndex = evt.oldIndex, + newIndex = evt.newIndex; + + vm.model.splice(newIndex, 0, vm.model.splice(oldIndex, 1)[0]); + + } + + const gridContainerEl = $element[0].querySelector('.umb-block-grid-area-editor__grid-wrapper'); + + const sortable = Sortable.create(gridContainerEl, { sort: true, // sorting inside list animation: 150, // ms, animation speed moving items when sorting, `0` — without animation easing: "cubic-bezier(1, 0, 0, 1)", // Easing for animation. Defaults to null. See https://easings.net/ for examples. cancel: '', draggable: ".umb-block-grid-area-editor__area", // Specifies which items inside the element should be draggable - ghostClass: "umb-block-grid-area-editor__area-placeholder" + ghostClass: "umb-block-grid-area-editor__area-placeholder", + onAdd: function (evt) { + _sync(evt); + $scope.$evalAsync(); + }, + onUpdate: function (evt) { + _sync(evt); + $scope.$evalAsync(); + } }); // TODO: setDirty if sort has happend. @@ -130,14 +147,23 @@ vm.openAreaOverlay = function (area) { // TODO: use the right localization key: - localizationService.localize("blockEditor_blockConfigurationOverlayTitle", [area.alias]).then(function (localized) { + localizationService.localize("blockEditor_blockConfigurationOverlayTitle").then(function (localized) { var clonedAreaData = Utilities.copy(area); vm.openArea = area; + function updateTitle() { + overlayModel.title = localizationService.tokenReplace(localized, [clonedAreaData.alias]); + } + + const areaIndex = vm.model.indexOf(area); + const otherAreas = [...vm.model]; + otherAreas.splice(areaIndex, 1); + var overlayModel = { + otherAreaAliases: otherAreas.map(x => x.alias), area: clonedAreaData, - title: localized, + updateTitle: updateTitle, allBlockTypes: vm.allBlockTypes, allBlockGroups: vm.allBlockGroups, loadedElementTypes: vm.loadedElementTypes, @@ -154,6 +180,8 @@ } }; + updateTitle(); + // open property settings editor editorService.open(overlayModel); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html index b4ef7b9266..301208c6f2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html @@ -60,7 +60,7 @@ @@ -119,7 +119,7 @@ key="{{(invalidBlockType.amount < invalidBlockType.minRequirement) ? 'blockEditor_areaValidationEntriesShort' : 'blockEditor_areaValidationEntriesExceed'}}" tokens="[invalidBlockType.name, invalidBlockType.amount, invalidBlockType.minRequirement, invalidBlockType.maxRequirement]" watch-tokens="true" - >%0% must be present between %2% – %3% times. + >%0% must be present between %2%–%3% times.
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html index 9c8860eaa5..e3f439a891 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html @@ -1,10 +1,11 @@ + ng-click="vm.clickInlineCreateAbove()" + ng-mouseover="vm.mouseOverInlineCreate()" + ng-mouseleave="vm.mouseOutInlineCreate()"> @@ -29,9 +30,15 @@ parent-form="vm.blockForm" style="--umb-block-grid--area-grid-columns: {{vm.areaGridColumns}}" > + +
-
@@ -159,7 +165,7 @@ -
{{vm.layoutEntry.columnSpan}} x {{vm.layoutEntry.rowSpan}}
@@ -180,10 +186,7 @@ class="umb-block-grid__block--inline-create-button --after" ng-class="{'--at-root': vm.depth === '0'}" ng-click="vm.clickInlineCreateAfter($event)" - ng-mouseover="vm.mouseOverInlineCreateAfter()" - ng-mouseleave="vm.blockEditorApi.internal.hideAreaHighlight(vm.parentBlock, vm.areaKey)" + ng-mouseover="vm.mouseOverInlineCreate()" + ng-mouseleave="vm.mouseOutInlineCreate()" vertical> - -
-
+ \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.html index 767be6d559..834f55e685 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.html @@ -4,7 +4,19 @@
-
+ + + + + + + +
+ entries="vm.layout" + loading="vm.loading" + >
- diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.less index be3d1cc9ec..d06b6377c3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.less @@ -1,4 +1,8 @@ .umb-block-grid__wrapper { position: relative; max-width: 1200px; +} + +.umb-block-grid__wrapper .umb-rte { + max-width: 100%; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html new file mode 100644 index 0000000000..e8d53c7457 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js index 57ddea9757..829fc918b6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js @@ -13,6 +13,31 @@ return null; } + function closestColumnSpanOption(target, map, max) { + if(map.length > 0) { + const result = map.reduce((a, b) => { + if (a.columnSpan > max) { + return b; + } + let aDiff = Math.abs(a.columnSpan - target); + let bDiff = Math.abs(b.columnSpan - target); + + if (aDiff === bDiff) { + return a.columnSpan < b.columnSpan ? a : b; + } else { + return bDiff < aDiff ? b : a; + } + }); + if(result) { + return result; + } + } + return null; + } + + + const DefaultViewFolderPath = "views/propertyeditors/blockgrid/blockgridentryeditors/"; + /** * @ngdoc directive @@ -44,14 +69,20 @@ var unsubscribe = []; var modelObject; + var gridRootEl; // Property actions: + var propertyActions = null; + var enterSortModeAction = null; + var exitSortModeAction = null; var copyAllBlocksAction = null; var deleteAllBlocksAction = null; var liveEditing = true; var shadowRoot; + var firstLayoutContainer; + var vm = this; @@ -107,6 +138,8 @@ vm.options = { createFlow: false }; + vm.sortMode = false; + vm.sortModeView = DefaultViewFolderPath + "gridsortblock/gridsortblock.editor.html";; localizationService.localizeMany(["grid_addElement", "content_createEmpty", "blockEditor_addThis"]).then(function (data) { vm.labels.grid_addElement = data[0]; @@ -114,8 +147,23 @@ vm.labels.blockEditor_addThis = data[2] }); + vm.onAppendProxyProperty = (event) => { + event.stopPropagation(); + gridRootEl.appendChild(event.detail.property); + event.detail.connectedCallback(); + }; + vm.onRemoveProxyProperty = (event) => { + event.stopPropagation(); + const el = gridRootEl.querySelector(`:scope > [slot='${event.detail.slotName}']`); + gridRootEl.removeChild(el); + }; + vm.$onInit = function() { + gridRootEl = $element[0].querySelector('umb-block-grid-root'); + + $element[0].addEventListener("UmbBlockGrid_AppendProperty", vm.onAppendProxyProperty); + $element[0].addEventListener("UmbBlockGrid_RemoveProperty", vm.onRemoveProxyProperty); //listen for form validation changes vm.valFormManager.onValidationStatusChanged(function (evt, args) { @@ -177,6 +225,19 @@ scopeOfExistence = vm.umbElementEditorContent.getScope(); } + enterSortModeAction = { + labelKey: 'blockEditor_actionEnterSortMode', + icon: 'navigation-vertical', + method: enableSortMode, + isDisabled: false + }; + exitSortModeAction = { + labelKey: 'blockEditor_actionExitSortMode', + icon: 'navigation-vertical', + method: exitSortMode, + isDisabled: false + }; + copyAllBlocksAction = { labelKey: "clipboard_labelForCopyAllEntries", labelTokens: [vm.model.label], @@ -187,13 +248,13 @@ deleteAllBlocksAction = { labelKey: 'clipboard_labelForRemoveAllEntries', - labelTokens: [], icon: 'trash', method: requestDeleteAllBlocks, isDisabled: true }; - var propertyActions = [ + propertyActions = [ + enterSortModeAction, copyAllBlocksAction, deleteAllBlocksAction ]; @@ -223,7 +284,6 @@ } - function onLoaded() { // Store a reference to the layout model, because we need to maintain this model. @@ -241,6 +301,7 @@ window.requestAnimationFrame(() => { shadowRoot = $element[0].querySelector('umb-block-grid-root').shadowRoot; + firstLayoutContainer = shadowRoot.querySelector('.umb-block-grid__layout-container'); }) } @@ -314,21 +375,31 @@ } } - // if no columnSpan, then we set one: - if (!layoutEntry.columnSpan) { + // Ensure Areas are ordered like the area configuration is: + layoutEntry.areas.sort((left, right) => { + return block.config.areas?.findIndex(config => config.key === left.key) < block.config.areas?.findIndex(config => config.key === right.key) ? -1 : 1; + }); - const contextColumns = getContextColumns(parentBlock, areaKey) - if (block.config.columnSpanOptions.length > 0) { - // set columnSpan to minimum allowed span for this BlockType: - const minimumColumnSpan = block.config.columnSpanOptions.reduce((prev, option) => Math.min(prev, option.columnSpan), vm.gridColumns); + const contextColumns = getContextColumns(parentBlock, areaKey); + const relevantColumnSpanOptions = block.config.columnSpanOptions.filter(option => option.columnSpan <= contextColumns); - // If minimumColumnSpan is larger than contextColumns, then we will make it fit within context anyway: - layoutEntry.columnSpan = Math.min(minimumColumnSpan, contextColumns) + // if no columnSpan or no columnSpanOptions configured, then we set(or rewrite) one: + if (!layoutEntry.columnSpan || layoutEntry.columnSpan > contextColumns || relevantColumnSpanOptions.length === 0) { + if (relevantColumnSpanOptions.length > 0) { + // Find greatest columnSpanOption within contextColumns, or fallback to contextColumns. + layoutEntry.columnSpan = relevantColumnSpanOptions.reduce((prev, option) => Math.max(prev, option.columnSpan), 0) || contextColumns; } else { layoutEntry.columnSpan = contextColumns; } + } else { + // Check that columnSpanOption still is available or equal contextColumns, or find closest option fitting: + if (relevantColumnSpanOptions.find(option => option.columnSpan === layoutEntry.columnSpan) === undefined || layoutEntry.columnSpan !== contextColumns) { + console.log(layoutEntry.columnSpan, closestColumnSpanOption(layoutEntry.columnSpan, relevantColumnSpanOptions, contextColumns)?.columnSpan || contextColumns); + layoutEntry.columnSpan = closestColumnSpanOption(layoutEntry.columnSpan, relevantColumnSpanOptions, contextColumns)?.columnSpan || contextColumns; + } } + // if no rowSpan, then we set one: if (!layoutEntry.rowSpan) { layoutEntry.rowSpan = 1; @@ -375,12 +446,12 @@ function applyDefaultViewForBlock(block) { - var defaultViewFolderPath = "views/propertyeditors/blockgrid/blockgridentryeditors/"; - if (block.config.unsupported === true) { - block.view = defaultViewFolderPath + "unsupportedblock/unsupportedblock.editor.html"; + block.view = DefaultViewFolderPath + "unsupportedblock/unsupportedblock.editor.html"; + } else if (block.config.inlineEditing) { + block.view = DefaultViewFolderPath + "gridinlineblock/gridinlineblock.editor.html"; } else { - block.view = defaultViewFolderPath + "gridblock/gridblock.editor.html"; + block.view = DefaultViewFolderPath + "gridblock/gridblock.editor.html"; } } @@ -430,9 +501,11 @@ block.showCopy = vm.supportCopy && block.config.contentElementTypeKey != null; block.blockUiVisibility = false; - block.showBlockUI = function () { + block.showBlockUI = () => { delete block.__timeout; - shadowRoot.querySelector('*[data-element-udi="'+block.layout.contentUdi+'"] .umb-block-grid__block > .umb-block-grid__block--context').scrollIntoView({block: "nearest", inline: "nearest", behavior: "smooth"}); + $timeout(() => { + shadowRoot.querySelector('*[data-element-udi="'+block.layout.contentUdi+'"] > ng-form > .umb-block-grid__block > .umb-block-grid__block--context').scrollIntoView({block: "nearest", inline: "nearest", behavior: "smooth"}); + }, 100); block.blockUiVisibility = true; }; block.onMouseLeave = function () { @@ -778,6 +851,8 @@ vm.requestShowCreate = requestShowCreate; function requestShowCreate(parentBlock, areaKey, createIndex, mouseEvent, options) { + vm.hideAreaHighlight(parentBlock, areaKey); + if (vm.blockTypePickerIsOpen === true) { return; } @@ -1254,6 +1329,38 @@ } } + function enableSortMode() { + vm.sortMode = true; + propertyActions.splice(propertyActions.indexOf(enterSortModeAction), 1, exitSortModeAction); + if (vm.umbProperty) { + vm.umbProperty.setPropertyActions(propertyActions); + } + } + + vm.exitSortMode = exitSortMode; + function exitSortMode() { + vm.sortMode = false; + propertyActions.splice(propertyActions.indexOf(exitSortModeAction), 1, enterSortModeAction); + if (vm.umbProperty) { + vm.umbProperty.setPropertyActions(propertyActions); + } + } + + vm.startDraggingMode = startDraggingMode; + function startDraggingMode() { + + document.documentElement.style.setProperty("--umb-block-grid--dragging-mode", 1); + firstLayoutContainer.style.minHeight = firstLayoutContainer.getBoundingClientRect().height + "px"; + + } + vm.exitDraggingMode = exitDraggingMode; + function exitDraggingMode() { + + document.documentElement.style.setProperty("--umb-block-grid--dragging-mode", 0); + firstLayoutContainer.style.minHeight = ""; + + } + function onAmountOfBlocksChanged() { // enable/disable property actions @@ -1278,9 +1385,16 @@ unsubscribe.push($scope.$watch(() => vm.layout.length, onAmountOfBlocksChanged)); $scope.$on("$destroy", function () { + + $element[0].removeEventListener("UmbBlockGrid_AppendProperty", vm.onAppendProxyProperty); + $element[0].removeEventListener("UmbBlockGrid_RemoveProperty", vm.onRemoveProxyProperty); + for (const subscription of unsubscribe) { subscription(); } + + firstLayoutContainer = null; + gridRootEl = null; }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridblock.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridblock.component.js index 2c4a4bb262..1a918638ca 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridblock.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridblock.component.js @@ -58,7 +58,7 @@
+ ng-include="api.internal.sortMode ? api.internal.sortModeView : '${model.view}'">
`; $compile(shadowRoot)($scope); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentries.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentries.component.js index c1e2c43619..3f597292e8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentries.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentries.component.js @@ -76,6 +76,7 @@ vm.movingLayoutEntry = null; vm.layoutColumnsInt = 0; + vm.containedPropertyEditorProxies = []; vm.$onInit = function () { initializeSortable(); @@ -93,7 +94,6 @@ vm.layoutColumnsInt = parseInt(vm.layoutColumns, 10); })); - function onLocalAmountOfBlocksChanged() { if (vm.entriesForm && vm.areaConfig) { @@ -153,6 +153,11 @@ } } + + vm.notifyVisualUpdate = function () { + $scope.$broadcast("blockGridEditorVisualUpdate", {areaKey: vm.areaKey}); + } + vm.acceptBlock = function(contentTypeKey) { return vm.blockEditorApi.internal.isElementTypeKeyAllowedAt(vm.parentBlock, vm.areaKey, contentTypeKey); } @@ -210,6 +215,11 @@ var nextSibling; + function _removePropertyProxy(eventTarget, slotName) { + const event = new CustomEvent("UmbBlockGrid_RemoveProperty", {composed: true, bubbles: true, detail: {'slotName': slotName}}); + eventTarget.dispatchEvent(event); + } + // Borrowed concept from, its not identical as more has been implemented: https://github.com/SortableJS/angular-legacy-sortablejs/blob/master/angular-legacy-sortable.js function _sync(evt) { @@ -222,8 +232,15 @@ const prevEntries = fromCtrl.entries; const syncEntry = prevEntries[oldIndex]; - // Perform the transfer: + // Make sure Property Editor Proxies are destroyed, as we need to establish new when moving context: + + // unregister all property editor proxies via events: + fromCtrl.containedPropertyEditorProxies.forEach(slotName => { + _removePropertyProxy(evt.from, slotName); + }); + + // Perform the transfer: if (Sortable.active && Sortable.active.lastPullMode === 'clone') { syncEntry = Utilities.copy(syncEntry); prevEntries.splice(Sortable.utils.index(evt.clone, sortable.options.draggable), 0, prevEntries.splice(oldIndex, 1)[0]); @@ -231,7 +248,6 @@ else { prevEntries.splice(oldIndex, 1); } - vm.entries.splice(newIndex, 0, syncEntry); const contextColumns = vm.blockEditorApi.internal.getContextColumns(vm.parentBlock, vm.areaKey); @@ -261,6 +277,7 @@ function _indication(contextVM, movingEl) { + // Remove old indication: if(_lastIndicationContainerVM !== contextVM && _lastIndicationContainerVM !== null) { _lastIndicationContainerVM.hideNotAllowed(); _lastIndicationContainerVM.revertIndicateDroppable(); @@ -269,7 +286,7 @@ if(contextVM.acceptBlock(movingEl.dataset.contentElementTypeKey) === true) { _lastIndicationContainerVM.hideNotAllowed(); - _lastIndicationContainerVM.indicateDroppable();// This block is accepted to we will indicate a good drop. + _lastIndicationContainerVM.indicateDroppable();// This block is accepted so we will indicate a good drop. return true; } @@ -557,6 +574,10 @@ forceAutoScrollFallback: true, onStart: function (evt) { + + // TODO: This does not work correctly jet with SortableJS. With the replacement we should be able to call this before DOM is changed. + vm.blockEditorApi.internal.startDraggingMode(); + nextSibling = evt.from === evt.item.parentNode ? evt.item.nextSibling : evt.clone.nextSibling; var contextVM = vm; @@ -577,6 +598,7 @@ vm.movingLayoutEntry.$block.__scope.$evalAsync();// needed for the block to be updated ghostEl = evt.item; + vm.containedPropertyEditorProxies = Array.from(ghostEl.querySelectorAll('slot[data-is-property-editor-proxy]')).map(x => x.getAttribute('name')); targetRect = evt.to.getBoundingClientRect(); ghostRect = ghostEl.getBoundingClientRect(); @@ -587,8 +609,6 @@ window.addEventListener('drag', _onDragMove); window.addEventListener('dragover', _onDragMove); - document.documentElement.style.setProperty("--umb-block-grid--dragging-mode", 1); - $scope.$evalAsync(); }, // Called by any change to the list (add / update / remove) @@ -619,7 +639,7 @@ } window.removeEventListener('drag', _onDragMove); window.removeEventListener('dragover', _onDragMove); - document.documentElement.style.setProperty("--umb-block-grid--dragging-mode", 0); + vm.blockEditorApi.internal.exitDraggingMode(); if(ghostElIndicateForceLeft) { ghostEl.removeChild(ghostElIndicateForceLeft); @@ -643,6 +663,9 @@ ghostRect = null; ghostEl = null; relatedEl = null; + vm.containedPropertyEditorProxies = []; + + vm.notifyVisualUpdate(); } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js index a7c45ecdd8..d5b3ce5474 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js @@ -44,23 +44,25 @@ } function closestColumnSpanOption(target, map, max) { - const result = map.reduce((a, b) => { - if (a.columnSpan > max) { - return b; + if(map.length > 0) { + const result = map.reduce((a, b) => { + if (a.columnSpan > max) { + return b; + } + let aDiff = Math.abs(a.columnSpan - target); + let bDiff = Math.abs(b.columnSpan - target); + + if (aDiff === bDiff) { + return a.columnSpan < b.columnSpan ? a : b; + } else { + return bDiff < aDiff ? b : a; + } + }); + if(result) { + return result; } - let aDiff = Math.abs(a.columnSpan - target); - let bDiff = Math.abs(b.columnSpan - target); - - if (aDiff === bDiff) { - return a.columnSpan < b.columnSpan ? a : b; - } else { - return bDiff < aDiff ? b : a; - } - }); - if(result) { - return result; } - return max; + return null; } @@ -86,11 +88,17 @@ areaKey: "<", propertyEditorForm: " { + // Only insert a proxy slot for the direct Block of this entry (as all the blocks share the same ShadowDom though they are slotted into each other when nested through areas.) + if (event.detail.contentUdi === vm.layoutEntry.contentUdi) { + vm.proxyProperties.push({ + slotName: event.detail.slotName + }); + $scope.$evalAsync(); + } + }; + vm.onRemoveProxyProperty = (event) => { + // Only react to proxies from the direct Block of this entry: + if (event.detail.contentUdi === vm.layoutEntry.contentUdi) { + const index = vm.proxyProperties.findIndex(x => x.slotName === event.detail.slotName); + if(index !== -1) { + vm.proxyProperties.splice(index, 1); + } + $scope.$evalAsync(); + } + }; vm.$onInit = function() { + $element[0].addEventListener("UmbBlockGrid_AppendProperty", vm.onAppendProxyProperty); + $element[0].addEventListener("UmbBlockGrid_RemoveProperty", vm.onRemoveProxyProperty); + vm.childDepth = parseInt(vm.depth) + 1; if(vm.layoutEntry.$block.config.areaGridColumns) { @@ -110,13 +145,29 @@ vm.areaGridColumns = vm.blockEditorApi.internal.gridColumns.toString(); } - vm.layoutColumnsInt = parseInt(vm.layoutColumns, 10) + vm.layoutColumnsInt = parseInt(vm.layoutColumns, 10); + + vm.relevantColumnSpanOptions = vm.layoutEntry.$block.config.columnSpanOptions.filter(x => x.columnSpan <= vm.layoutColumnsInt).sort((a,b) => (a.columnSpan > b.columnSpan) ? 1 : ((b.columnSpan > a.columnSpan) ? -1 : 0)); + const hasRelevantColumnSpanOptions = vm.relevantColumnSpanOptions.length > 1; + const hasRowSpanOptions = vm.layoutEntry.$block.config.rowMinSpan && vm.layoutEntry.$block.config.rowMaxSpan && vm.layoutEntry.$block.config.rowMaxSpan !== vm.layoutEntry.$block.config.rowMinSpan; + vm.canScale = (hasRelevantColumnSpanOptions || hasRowSpanOptions); + + unsubscribe.push(vm.layoutEntry.$block.__scope.$watch(() => vm.layoutEntry.$block.index, visualUpdateCallback)); + unsubscribe.push($scope.$on("blockGridEditorVisualUpdate", (evt, data) => {if(data.areaKey === vm.areaKey) { visualUpdateCallback()}})); + + updateInlineCreateTimeout = $timeout(updateInlineCreate, 500); $scope.$evalAsync(); } unsubscribe.push($scope.$watch("depth", (newVal, oldVal) => { vm.childDepth = parseInt(vm.depth) + 1; })); + + function visualUpdateCallback() { + cancelAnimationFrame(updateInlineCreateRaf); + updateInlineCreateRaf = requestAnimationFrame(updateInlineCreate); + } + /** * We want to only show the validation errors on the specific Block, not the parent blocks. * So we need to avoid having a Block as the parent to the Block Form. @@ -157,9 +208,12 @@ // Block sizing functionality: let layoutContainer = null; let gridColumns = null; + let columnGap = 0; + let rowGap = 0; let gridRows = null; + let lockedGridRows = 0; let scaleBoxBackdropEl = null; - + let raf = null; function getNewSpans(startX, startY, endX, endY) { @@ -171,7 +225,8 @@ let newColumnSpan = Math.max(blockEndCol-blockStartCol, 1); // Find nearest allowed Column: - newColumnSpan = closestColumnSpanOption(newColumnSpan , vm.layoutEntry.$block.config.columnSpanOptions, gridColumns.length - blockStartCol).columnSpan; + const bestColumnSpanOption = closestColumnSpanOption(newColumnSpan , vm.relevantColumnSpanOptions, vm.layoutColumnsInt - blockStartCol) + newColumnSpan = bestColumnSpanOption ? bestColumnSpanOption.columnSpan : vm.layoutColumnsInt; let newRowSpan = Math.round(Math.max(blockEndRow-blockStartRow, vm.layoutEntry.$block.config.rowMinSpan || 1)); if(vm.layoutEntry.$block.config.rowMaxSpan != null) { @@ -181,10 +236,14 @@ return {'columnSpan': newColumnSpan, 'rowSpan': newRowSpan, 'startCol': blockStartCol, 'startRow': blockStartRow}; } - function updateGridLayoutData(layoutContainerRect, layoutItemRect) { + function updateGridLayoutData(layoutContainerRect, layoutItemRect, updateRowTemplate) { const computedStyles = window.getComputedStyle(layoutContainer); + + columnGap = Number(computedStyles.columnGap.split("px")[0]) || 0; + rowGap = Number(computedStyles.rowGap.split("px")[0]) || 0; + gridColumns = computedStyles.gridTemplateColumns.trim().split("px").map(x => Number(x)); gridRows = computedStyles.gridTemplateRows.trim().split("px").map(x => Number(x)); @@ -192,6 +251,18 @@ gridColumns = gridColumns.filter(n => n > 0); gridRows = gridRows.filter(n => n > 0); + // We use this code to lock the templateRows, while scaling. otherwise scaling Rows is too crazy. + if(updateRowTemplate || gridRows.length > lockedGridRows) { + lockedGridRows = gridRows.length; + layoutContainer.style.gridTemplateRows = computedStyles.gridTemplateRows; + } + + // add gaps: + const gridColumnsLen = gridColumns.length; + gridColumns = gridColumns.map((n, i) => gridColumnsLen === i ? n : n + columnGap); + const gridRowsLen = gridRows.length; + gridRows = gridRows.map((n, i) => gridRowsLen === i ? n : n + rowGap); + // ensure all columns are there. // This will also ensure handling non-css-grid mode, // use container width divided by amount of columns( or the item width divided by its amount of columnSpan) @@ -226,25 +297,28 @@ gridRows.push(50); gridRows.push(50); gridRows.push(50); + gridRows.push(50); + gridRows.push(50); } vm.scaleHandlerMouseDown = function($event) { $event.originalEvent.preventDefault(); + + layoutContainer = $element[0].closest('.umb-block-grid__layout-container'); + if(!layoutContainer) { + console.error($element[0], 'could not find parent layout-container'); + return; + } + vm.isScaleMode = true; window.addEventListener('mousemove', vm.onMouseMove); window.addEventListener('mouseup', vm.onMouseUp); window.addEventListener('mouseleave', vm.onMouseUp); - - layoutContainer = $element[0].closest('.umb-block-grid__layout-container'); - if(!layoutContainer) { - console.error($element[0], 'could not find parent layout-container'); - } - const layoutContainerRect = layoutContainer.getBoundingClientRect(); const layoutItemRect = $element[0].getBoundingClientRect(); - updateGridLayoutData(layoutContainerRect, layoutItemRect); + updateGridLayoutData(layoutContainerRect, layoutItemRect, true); scaleBoxBackdropEl = document.createElement('div'); @@ -256,7 +330,6 @@ const layoutContainerRect = layoutContainer.getBoundingClientRect(); const layoutItemRect = $element[0].getBoundingClientRect(); - updateGridLayoutData(layoutContainerRect, layoutItemRect); const startX = layoutItemRect.left - layoutContainerRect.left; @@ -266,6 +339,18 @@ const newSpans = getNewSpans(startX, startY, endX, endY); + const updateRowTemplate = vm.layoutEntry.columnSpan !== newSpans.columnSpan; + + if(updateRowTemplate) { + // If we like to update we need to first remove the lock, make the browser render onces and then update. + layoutContainer.style.gridTemplateRows = ""; + } + cancelAnimationFrame(raf); + raf = requestAnimationFrame(() => { + // As mentioned above we need to wait until the browser has rendered DOM without the lock of gridTemplateRows. + updateGridLayoutData(layoutContainerRect, layoutItemRect, updateRowTemplate); + }) + // update as we go: vm.layoutEntry.columnSpan = newSpans.columnSpan; vm.layoutEntry.rowSpan = newSpans.rowSpan; @@ -275,7 +360,13 @@ vm.onMouseUp = function(e) { - vm.isScaleMode = false; + cancelAnimationFrame(raf); + + // Remove listeners: + window.removeEventListener('mousemove', vm.onMouseMove); + window.removeEventListener('mouseup', vm.onMouseUp); + window.removeEventListener('mouseleave', vm.onMouseUp); + const layoutContainerRect = layoutContainer.getBoundingClientRect(); const layoutItemRect = $element[0].getBoundingClientRect(); @@ -287,22 +378,24 @@ const newSpans = getNewSpans(startX, startY, endX, endY); - // Remove listeners: - window.removeEventListener('mousemove', vm.onMouseMove); - window.removeEventListener('mouseup', vm.onMouseUp); - window.removeEventListener('mouseleave', vm.onMouseUp); + // release the lock of gridTemplateRows: layoutContainer.removeChild(scaleBoxBackdropEl); + layoutContainer.style.gridTemplateRows = ""; + vm.isScaleMode = false; // Clean up variables: layoutContainer = null; gridColumns = null; gridRows = null; + lockedGridRows = 0; scaleBoxBackdropEl = null; // Update block size: vm.layoutEntry.columnSpan = newSpans.columnSpan; vm.layoutEntry.rowSpan = newSpans.rowSpan; + + vm.umbBlockGridEntries.notifyVisualUpdate(); vm.blockEditorApi.internal.setDirty(); $scope.$evalAsync(); } @@ -331,8 +424,8 @@ } if(addColIndex !== 0) { - if (vm.layoutEntry.$block.config.columnSpanOptions.length > 0) { - const sortOptions = vm.layoutEntry.$block.config.columnSpanOptions.sort((a,b) => (a.columnSpan > b.columnSpan) ? 1 : ((b.columnSpan > a.columnSpan) ? -1 : 0)); + if (vm.relevantColumnSpanOptions.length > 0) { + const sortOptions = vm.relevantColumnSpanOptions; const currentColIndex = sortOptions.findIndex(x => x.columnSpan === vm.layoutEntry.columnSpan); const newColIndex = Math.min(Math.max(currentColIndex + addColIndex, 0), sortOptions.length-1); vm.layoutEntry.columnSpan = sortOptions[newColIndex].columnSpan; @@ -346,18 +439,30 @@ } vm.layoutEntry.rowSpan = newRowSpan; + vm.umbBlockGridEntries.notifyVisualUpdate(); vm.blockEditorApi.internal.setDirty(); $event.originalEvent.stopPropagation(); } + vm.clickInlineCreateAbove = function($event) { + if(vm.hideInlineCreateAbove === false) { + vm.blockEditorApi.requestShowCreate(vm.parentBlock, vm.areaKey, vm.index, $event); + } + } vm.clickInlineCreateAfter = function($event) { if(vm.hideInlineCreateAfter === false) { vm.blockEditorApi.requestShowCreate(vm.parentBlock, vm.areaKey, vm.index+1, $event, {'fitInRow': true}); } } - vm.mouseOverInlineCreateAfter = function() { - + vm.mouseOverInlineCreate = function() { + vm.blockEditorApi.internal.showAreaHighlight(vm.parentBlock, vm.areaKey); + } + vm.mouseOutInlineCreate = function() { + vm.blockEditorApi.internal.hideAreaHighlight(vm.parentBlock, vm.areaKey); + } + + function updateInlineCreate() { layoutContainer = $element[0].closest('.umb-block-grid__layout-container'); if(!layoutContainer) { return; @@ -366,17 +471,39 @@ const layoutContainerRect = layoutContainer.getBoundingClientRect(); const layoutItemRect = $element[0].getBoundingClientRect(); - if(layoutItemRect.right > layoutContainerRect.right - 5) { + if(layoutContainerRect.width === 0) { + $timeout.cancel(updateInlineCreateTimeout); + vm.hideInlineCreateAbove = true; vm.hideInlineCreateAfter = true; + vm.inlineCreateAboveWidth = ""; + $scope.$evalAsync(); + updateInlineCreateTimeout = $timeout(updateInlineCreate, 500); return; } - vm.hideInlineCreateAfter = false; - vm.blockEditorApi.internal.showAreaHighlight(vm.parentBlock, vm.areaKey); + if(layoutItemRect.right > layoutContainerRect.right - 5) { + vm.hideInlineCreateAfter = true; + } else { + vm.hideInlineCreateAfter = false; + } + if(layoutItemRect.left > layoutContainerRect.left + 5) { + vm.hideInlineCreateAbove = true; + vm.inlineCreateAboveWidth = ""; + } else { + vm.inlineCreateAboveWidth = getComputedStyle(layoutContainer).width; + vm.hideInlineCreateAbove = false; + } + $scope.$evalAsync(); } $scope.$on("$destroy", function () { + + $timeout.cancel(updateInlineCreateTimeout); + + $element[0].removeEventListener("UmbBlockGrid_AppendProperty", vm.onAppendProxyProperty); + $element[0].removeEventListener("UmbBlockGrid_RemoveProperty", vm.onRemoveProxyProperty); + for (const subscription of unsubscribe) { subscription(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridrenderareaslots.directive.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridrenderareaslots.directive.js new file mode 100644 index 0000000000..43837e9c5c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridrenderareaslots.directive.js @@ -0,0 +1,20 @@ +(function () { + 'use strict'; + + function UmbBlockGridRenderAreaSlots() { + + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html', + scope: false + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbBlockGridRenderAreaSlots', UmbBlockGridRenderAreaSlots); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridroot.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridroot.component.js index 130150a3ae..fbda5cf92e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridroot.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridroot.component.js @@ -20,7 +20,8 @@ stylesheet: "@", blockEditorApi: "<", propertyEditorForm: "
hello

"}; + element = $("
"); })); @@ -31,7 +32,8 @@ describe('RTE controller tests', function () { it('should define the default properties on construction', function () { controllerFactory('Umbraco.PropertyEditors.RTEController', { $scope: scope, - $routeParams: routeParams + $routeParams: routeParams, + $element: element }); }); From 0e1095061e80b527cad4265f3e0e9bbe7af9a830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 14 Nov 2022 12:14:25 +0100 Subject: [PATCH 28/39] remove console log --- .../blockgrid/umbBlockGridPropertyEditor.component.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js index 829fc918b6..ebd4625c3b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js @@ -395,7 +395,6 @@ } else { // Check that columnSpanOption still is available or equal contextColumns, or find closest option fitting: if (relevantColumnSpanOptions.find(option => option.columnSpan === layoutEntry.columnSpan) === undefined || layoutEntry.columnSpan !== contextColumns) { - console.log(layoutEntry.columnSpan, closestColumnSpanOption(layoutEntry.columnSpan, relevantColumnSpanOptions, contextColumns)?.columnSpan || contextColumns); layoutEntry.columnSpan = closestColumnSpanOption(layoutEntry.columnSpan, relevantColumnSpanOptions, contextColumns)?.columnSpan || contextColumns; } } From 27ec62016d9d3769c8b278837f0f391e7fa2f371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 11 Nov 2022 22:15:07 +0100 Subject: [PATCH 29/39] Block Grid Editor Improvements (#13282) --- .../umbBlockGridDemoHeadlineBlock.html | 10 +- .../umbBlockGridDemoImageBlock.html | 30 +-- .../umbBlockGridDemoRichTextBlock.html | 11 +- .../umbBlockGridDemoTwoColumnLayoutBlock.html | 49 +---- .../EmbeddedResources/Lang/da.xml | 12 +- .../EmbeddedResources/Lang/en.xml | 14 +- .../EmbeddedResources/Lang/en_us.xml | 12 +- .../PropertyEditors/BlockGridConfiguration.cs | 6 + .../property/umbproperty.directive.js | 3 +- .../property/umbpropertyactions.component.js | 45 ++-- .../filters/mediaItemResolver.filter.js | 65 ++++++ .../subheader/umb-editor-sub-header.less | 6 + src/Umbraco.Web.UI.Client/src/less/rte.less | 2 +- .../components/property/umb-property.html | 4 +- .../gridblock/gridblock.editor.html | 19 +- .../gridinlineblock.editor.controller.js | 64 +++++- .../gridinlineblock.editor.html | 74 ++++--- .../gridinlineblock.editor.less | 173 --------------- .../gridsortblock/gridsortblock.editor.html | 92 ++++++++ .../heroblock/heroblock.editor.controller.js | 45 ---- .../mediablock.editor.controller.js | 47 ---- .../blockgrid/blockgridui.less | 207 ++++++------------ ...ckconfiguration.area.overlay.controller.js | 12 + ...kgrid.blockconfiguration.area.overlay.html | 9 +- .../blockgrid.blockconfiguration.html | 14 +- .../blockgrid.blockconfiguration.less | 27 +-- .../blockgrid.blockconfiguration.overlay.html | 15 +- .../blockgrid.groupconfiguration.html | 6 +- .../umb-block-grid-area-allowance-editor.html | 14 +- .../umb-block-grid-area-allowance-editor.less | 7 +- .../umbBlockGridAreaEditor.component.js | 38 +++- .../blockgrid/umb-block-grid-entries.html | 4 +- .../blockgrid/umb-block-grid-entry.html | 31 +-- .../umb-block-grid-property-editor.html | 19 +- .../umb-block-grid-property-editor.less | 4 + .../umb-block-grid-render-area-slots.html | 3 + .../umbBlockGridPropertyEditor.component.js | 148 +++++++++++-- .../blockgrid/umbblockgridblock.component.js | 2 +- .../umbblockgridentries.component.js | 37 +++- .../blockgrid/umbblockgridentry.component.js | 207 ++++++++++++++---- .../umbblockgridrenderareaslots.directive.js | 20 ++ .../blockgrid/umbblockgridroot.component.js | 4 +- .../blockgrid/umbraco-blockgridlayout.css | 9 +- .../propertyeditors/rte/rte.controller.js | 20 +- .../propertyeditors/rte-controller.spec.js | 6 +- 45 files changed, 934 insertions(+), 712 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/filters/mediaItemResolver.filter.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridsortblock/gridsortblock.editor.html delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/heroblock/heroblock.editor.controller.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/mediablock/mediablock.editor.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridrenderareaslots.directive.js diff --git a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoHeadlineBlock.html b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoHeadlineBlock.html index 523b0fe7d3..495c704e28 100644 --- a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoHeadlineBlock.html +++ b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoHeadlineBlock.html @@ -1,11 +1,4 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html index dbe157cf84..b3362fcda9 100644 --- a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html +++ b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html @@ -1,13 +1,5 @@
-
- \ No newline at end of file +
\ No newline at end of file diff --git a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoTwoColumnLayoutBlock.html b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoTwoColumnLayoutBlock.html index ccc3af22e8..bc290fe8fb 100644 --- a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoTwoColumnLayoutBlock.html +++ b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoTwoColumnLayoutBlock.html @@ -1,50 +1,3 @@ -
- - +
\ No newline at end of file diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml index 387f1ef652..049b617f93 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml @@ -2201,6 +2201,8 @@ Mange hilsner fra Umbraco robotten Avanceret Skjul indholdseditoren Skjul indholds redigerings knappen samt indholdseditoren i Blok Redigerings vinduet + Direkte redigering + Tilføjer direkte redigering a det første felt. Yderligere felter optræder kun i redigerings vinduet. Du har lavet ændringer til dette indhold. Er du sikker på at du vil kassere dem? Annuller oprettelse? @@ -2218,6 +2220,7 @@ Mange hilsner fra Umbraco robotten Tillad kun specifikke blok-typer Tilladte blok-typer Vælg de blok-typer, der er tilladt i dette område, og evt. også hvor mange af hver type, redaktørerne skal tilføje til området. + Når denne er tom er alle block-typer tilladt for områder tilladt. Er du sikker på, at du vil slette dette område? Alle blokke, der er oprettet i dette område, vil blive slettet. Layout-opsætning @@ -2255,9 +2258,9 @@ Mange hilsner fra Umbraco robotten Tilføj katalog udseende Tilføj Blok Tilføj gruppe - Tilføj gruppe eller block - Sæt minimum krav for denne tilladelse - Set maksimum krav for denne tilladelse + Tilføj gruppe eller Blok + Sæt minimum krav + Sæt maksimum krav Blok Blok Indstillinger @@ -2267,6 +2270,9 @@ Mange hilsner fra Umbraco robotten Installer demo konfiguration Dette indeholder Blokke for Overskrift, Beriget-Tekst, Billede og To-Koloners-Layout.]]> Installer + Sortings tilstand + Afslut sortings tilstand + Dette område alias skal være unikt sammenlignet med andre områder af denne Blok. Hvad er Indholdsskabeloner? diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index 82311e4311..99428a8837 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -2747,7 +2747,9 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Settings Advanced Hide content editor - Hide the content edit button and the content editor from the Block Editor overlay + Hide the content edit button and the content editor from the Block Editor overlay. + Inline editing + Enables inline editing for the first Property. Additional properties can be edited in the overlay. You have made changes to this content. Are you sure you want to discard them? Discard creation? @@ -2779,6 +2781,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Make this block available in the root of the layout. Allow in areas Make this block available within other Blocks. + When empty all Blocks allowed for Areas can be created. Areas Grid Columns for Areas Define how many columns that will be available for areas. If not defined, the number of columns defined for the entire layout will be used. @@ -2805,17 +2808,20 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Add Block Add group Pick group or Block - Set minimum requirement for this allowance - Set maximum requirement for this allowance + Set a minimum requirement + Set a maximum requirement Block Block Settings Areas Advanced - Allowance + Permissions Install Sample Configuration Install + Sort mode + End sort mode + This Areas Alias must be unique compared to the other Areas of this Block. What are Content Templates? diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index c0bc8763f7..483219eeb8 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -2851,6 +2851,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Advanced Hide content editor Hide the content edit button and the content editor from the Block Editor overlay + Inline editing + Enables inline editing for the first Property. Additional properties can be edited in the overlay. You have made changes to this content. Are you sure you want to discard them? Discard creation? @@ -2882,6 +2884,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Make this block available in the root of the layout. Allow in areas Make this block available within other Blocks. + When empty all Blocks allowed for Areas can be created. Areas Grid Columns for Areas Define how many columns that will be available for areas. If not defined, the number of columns defined for the entire layout will be used. @@ -2908,17 +2911,20 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Add Block Add group Pick group or Block - Set minimum requirement for this allowance - Set maximum requirement for this allowance + Set a minimum requirement + Set a maximum requirement Block Block Settings Areas Advanced - Allowance + permissions Install Sample Configuration Install + Sort mode + End sort mode + This Areas Alias must be unique compared to the other Areas of this Block. What are Content Templates? diff --git a/src/Umbraco.Core/PropertyEditors/BlockGridConfiguration.cs b/src/Umbraco.Core/PropertyEditors/BlockGridConfiguration.cs index 0a5bacad14..f635eb6324 100644 --- a/src/Umbraco.Core/PropertyEditors/BlockGridConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/BlockGridConfiguration.cs @@ -40,6 +40,9 @@ public class BlockGridConfiguration [DataMember(Name ="columnSpanOptions")] public BlockGridColumnSpanOption[] ColumnSpanOptions { get; set; } = Array.Empty(); + [DataMember(Name ="rowMinSpan")] + public int? RowMinSpan { get; set; } + [DataMember(Name ="rowMaxSpan")] public int? RowMaxSpan { get; set; } @@ -82,6 +85,9 @@ public class BlockGridConfiguration [DataMember(Name ="editorSize")] public string? EditorSize { get; set; } + [DataMember(Name ="inlineEditing")] + public bool InlineEditing { get; set; } + [DataMember(Name ="forceHideContentEditorInOverlay")] public bool ForceHideContentEditorInOverlay { get; set; } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js index 702cd5aeda..5a30f81d4b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/property/umbproperty.directive.js @@ -24,7 +24,8 @@ // optional, if set this will be used for the property alias validation path (hack required because NC changes the actual property.alias :/) propertyAlias: "@", showInherit: "<", - inheritsFrom: "<" + inheritsFrom: "<", + hideLabel: " { - - if (action.labelKey) { - localizationService.localize(action.labelKey, (action.labelTokens || []), action.label).then(data => { - action.label = data; - }); - - action.useLegacyIcon = action.useLegacyIcon === false ? false : true; - action.icon = (action.useLegacyIcon ? 'icon-' : '') + action.icon; - } - }); + updateActions(); } } + + function updateActions() { + + Utilities.forEach(vm.actions || [], action => { + + if (action.labelKey) { + localizationService.localize(action.labelKey, (action.labelTokens || []), action.label).then(data => { + action.label = data; + }); + + action.useLegacyIcon = action.useLegacyIcon === false ? false : true; + action.icon = (action.useLegacyIcon && action.icon.indexOf('icon-') !== 0 ? 'icon-' : '') + action.icon; + } + }); + } } var umbPropertyActionsComponent = { diff --git a/src/Umbraco.Web.UI.Client/src/common/filters/mediaItemResolver.filter.js b/src/Umbraco.Web.UI.Client/src/common/filters/mediaItemResolver.filter.js new file mode 100644 index 0000000000..be0338093c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/filters/mediaItemResolver.filter.js @@ -0,0 +1,65 @@ +(function () { + "use strict"; + + function mediaItemResolverFilterService(mediaResource, eventsService) { + + var mediaKeysRequest = []; + var mediaItemCache = []; + + var service = { + + getByKey: function (key) { + // Is it cached, then get that: + const cachedMediaItem = mediaItemCache.find(cache => key === cache.key); + if(cachedMediaItem) { + return cachedMediaItem; + } + + // check its not already being loaded, and then start loading: + if(mediaKeysRequest.indexOf(key) === -1) { + mediaKeysRequest.push(key); + mediaResource.getById(key).then(function (mediaItem) { + if(mediaItem) { + mediaItemCache.push(mediaItem); + } + }); + } + + return null; + } + }; + + eventsService.on("editors.media.saved", function (name, args) { + const index = mediaItemCache.findIndex(cache => cache.key === args.media.key); + if(index !== -1) { + mediaItemCache[index] = args.media; + } + }); + + return service; + + } + + angular.module("umbraco.filters").factory("mediaItemResolverFilterService", mediaItemResolverFilterService); + + + // Filter loads Media Item Model from a Media Key. + // Usage: {{ myMediaProperty[0].mediaKey | mediaItemResolver }} + angular.module("umbraco.filters").filter("mediaItemResolver", function (mediaItemResolverFilterService) { + + mediaItemResolverFilter.$stateful = true; + function mediaItemResolverFilter(input) { + + // Check we have a value at all + if (typeof input === 'string' && input.length > 0) { + return mediaItemResolverFilterService.getByKey(input); + } + + return null; + } + + return mediaItemResolverFilter; + + }); + +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less index 0dd7bfc7f4..4eaf00724c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less @@ -20,6 +20,12 @@ background-color: @white; border-color: @white; } +.umb-editor-sub-header--blue { + background-color: @ui-selected-border; + border-color: @ui-selected-border; + color: @white; + border-radius: 3px; +} .umb-editor-sub-header.--state-selection { padding-left: 10px; diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index 8ff4d128ed..ed25ce4e90 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -20,7 +20,7 @@ } .umb-rte.--initialized .umb-rte-editor-con { height:auto; - min-height: 100px; + min-height: 95px; visibility: visible; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html index f00ba725b2..cc41896a0f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/property/umb-property.html @@ -1,13 +1,13 @@
+ ng-class="{'hidelabel':vm.hideLabel || vm.property.hideLabel, '--label-on-top':vm.property.labelOnTop, 'umb-control-group__listview': vm.property.alias === '_umb_containerView'}">
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridblock/gridblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridblock/gridblock.editor.html index a4efb41717..710dd15e53 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridblock/gridblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridblock/gridblock.editor.html @@ -53,6 +53,23 @@ padding-top:2px; padding-bottom:2px; } + + :host { + --inherited--column-gap: var(--umb-block-grid--column-gap, 10px); + --inherited--row-gap: var(--umb-block-grid--row-gap, 10px); + --inherited--areas-column-gap: var(--umb-block-grid--areas-column-gap, 10px); + --inherited--areas-row-gap: var(--umb-block-grid--areas-row-gap, 10px); + } + + [part='area-container'] { + box-sizing: border-box; + padding: 10px; + --umb-block-grid--column-gap: var(--inherited--column-gap, 10px); + --umb-block-grid--row-gap: var(--inherited--row-gap, 10px); + --umb-block-grid--areas-column-gap: var(--inherited--areas-column-gap, 10px); + --umb-block-grid--areas-row-gap: var(--inherited--areas-row-gap, 10px); + } +
@@ -64,6 +81,6 @@ {{block.label}} - +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.controller.js index 2a77e81b5c..3d55ea1836 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.controller.js @@ -1,27 +1,71 @@ (function () { 'use strict'; - function GridInlineBlockEditor($scope, $element) { + function GridInlineBlockEditor($scope, $compile, $element) { const vm = this; + var propertyEditorElement; + vm.$onInit = function() { - const host = $element[0].getRootNode(); + + vm.property = $scope.block.content.variants[0].tabs[0]?.properties[0]; - console.log(document.styleSheets) + if (vm.property) { + vm.propertySlotName = "umbBlockGridProxy_" + vm.property.alias + "_" + String.CreateGuid(); + + propertyEditorElement = $('
'); + propertyEditorElement.html( + ` + - for (const stylesheet of document.styleSheets) { + + - console.log(stylesheet); - const styleEl = document.createElement('link'); - styleEl.setAttribute('rel', 'stylesheet'); - styleEl.setAttribute('type', stylesheet.type); - styleEl.setAttribute('href', stylesheet.href); + + ` + ); + + $element[0].addEventListener('umb-rte-focus', onRteFocus); + $element[0].addEventListener('umb-rte-blur', onRteBlur); - host.appendChild(styleEl); + const connectedCallback = () => { + + $compile(propertyEditorElement)($scope) + }; + + const event = new CustomEvent("UmbBlockGrid_AppendProperty", {composed: true, bubbles: true, detail: {'property': propertyEditorElement[0], 'contentUdi': $scope.block.layout.contentUdi, 'slotName': vm.propertySlotName, 'connectedCallback':connectedCallback}}); + + $element[0].dispatchEvent(event); + } } + function onRteFocus() { + $element[0].classList.add('umb-block-grid--force-focus'); + } + function onRteBlur() { + $element[0].classList.remove('umb-block-grid--force-focus'); + } + + vm.$onDestroy = function() { + if (vm.property) { + const event = new CustomEvent("UmbBlockGrid_RemoveProperty", {composed: true, bubbles: true, detail: {'slotName': vm.propertySlotName}}); + $element[0].dispatchEvent(event); + } + + $element[0].removeEventListener('umb-rte-focus', onRteFocus); + $element[0].removeEventListener('umb-rte-blur', onRteBlur); + propertyEditorElement = null; + } + } angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.GridInlineBlockEditor", GridInlineBlockEditor); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html index ca12c9dd98..3a67a1be88 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.html @@ -1,24 +1,6 @@ -
- -
- -
-
- - - - -
- +
+ + {{block.label}} +
- + - +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.less deleted file mode 100644 index b8ffcff3ec..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridinlineblock/gridinlineblock.editor.less +++ /dev/null @@ -1,173 +0,0 @@ -.blockelement-inlineblock-editor { - display: block; - margin-bottom: 4px; - margin-top: 4px; - border: 1px solid @gray-9; - border-radius: @baseBorderRadius; - transition: border-color 120ms, background-color 120ms; - - .umb-block-list__block:not(.--active) &:hover { - border-color: @gray-8; - } - - .umb-editor-tab-bar { - margin: 0; - position: static; - padding: 0; - } - - > button { - width: 100%; - min-height: 48px; - cursor: pointer; - color: @ui-action-discreet-type; - text-align: left; - padding-left: 10px; - padding-bottom: 2px; - user-select: none; - background-color: white; - - .caret { - vertical-align: middle; - transform: rotate(-90deg); - transition: transform 80ms ease-out; - } - - .icon { - font-size: 1.1rem; - display: inline-block; - vertical-align: middle; - } - - span.name { - position: relative; - display: inline-block; - vertical-align: middle; - } - - &:hover { - color: @ui-action-discreet-type-hover; - border-color: @gray-8; - } - } - - ng-form.ng-invalid-val-server-match-content > .umb-block-list__block > .umb-block-list__block--content > div > & { - > button { - color: @formErrorText; - - .show-validation-type-warning & { - color: @formWarningText; - } - - span.caret { - border-top-color: @formErrorText; - - .show-validation-type-warning & { - border-top-color: @formWarningText; - } - } - } - } - - ng-form.ng-invalid-val-server-match-content > .umb-block-list__block:not(.--active) > .umb-block-list__block--content > div > & { - > button { - span.name { - &::after { - content: "!"; - text-align: center; - position: absolute; - top: -6px; - right: -15px; - min-width: 10px; - color: @white; - background-color: @ui-active-type; - border: 2px solid @white; - border-radius: 50%; - font-size: 10px; - font-weight: bold; - padding: 2px; - line-height: 10px; - background-color: @formErrorText; - - .show-validation-type-warning & { - background-color: @formWarningText; - } - - font-weight: 900; - animation-duration: 1.4s; - animation-iteration-count: infinite; - animation-name: blockelement-inlineblock-editor--badge-bounce; - animation-timing-function: ease; - - @keyframes blockelement-inlineblock-editor--badge-bounce { - 0% { - transform: translateY(0); - } - - 20% { - transform: translateY(-4px); - } - - 40% { - transform: translateY(0); - } - - 55% { - transform: translateY(-2px); - } - - 70% { - transform: translateY(0); - } - - 100% { - transform: translateY(0); - } - } - } - } - } - } -} - -.umb-block-list__block.--active { - border-color: @gray-8; - box-shadow: 0 0 2px 0px rgba(0, 0, 0, 0.05); - - > .umb-block-list__block--content { - > .umb-block-list__block--view { - > .blockelement-inlineblock-editor { - > button { - > .caret { - transform: rotate(0deg); - } - } - } - } - } -} - -.blockelement-inlineblock-editor__inner { - border-top: 1px solid @gray-8; - background-color: @gray-12; - - > * > * > * > .umb-group-panel { - background-color: transparent; - box-shadow: none; - margin-top: 10px; - margin-bottom: 0; - > .umb-group-panel__content .umb-property { - margin-bottom: 20px; - } - } - .umb-group-panel + .umb-group-panel { - margin-top: 20px; - } - &.--singleGroup > * > * > * > .umb-group-panel { - margin-top: 0; - > .umb-group-panel__header { - display: none; - } - } - -} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridsortblock/gridsortblock.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridsortblock/gridsortblock.editor.html new file mode 100644 index 0000000000..bd6eebfac6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/gridsortblock/gridsortblock.editor.html @@ -0,0 +1,92 @@ + + +
+ + + + +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/heroblock/heroblock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/heroblock/heroblock.editor.controller.js deleted file mode 100644 index 2dc717d8ef..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/heroblock/heroblock.editor.controller.js +++ /dev/null @@ -1,45 +0,0 @@ -(function () { - 'use strict'; - - function HeroBlockEditor($scope, mediaResource, mediaHelper) { - - var unsubscribe = []; - - const bc = this; - - $scope.$watch("block.data.image", function(newValue, oldValue) { - if (newValue !== oldValue) { - bc.retrieveMedia(); - } - }, true); - - bc.retrieveMedia = function() { - - if($scope.block.data.image && $scope.block.data.image.length > 0) { - mediaResource.getById($scope.block.data.image[0].mediaKey).then(function (mediaEntity) { - - var mediaPath = mediaEntity.mediaLink; - - //set a property on the 'scope' for the returned media object. - bc.mediaName = mediaEntity.name; - bc.isImage = mediaHelper.detectIfImageByExtension(mediaPath); - bc.imageSource = mediaPath; - }); - } - } - - bc.retrieveMedia(); - - - - $scope.$on("$destroy", function () { - for (const subscription of unsubscribe) { - subscription(); - } - }); - - } - - angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.HeroBlockEditor", HeroBlockEditor); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/mediablock/mediablock.editor.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/mediablock/mediablock.editor.controller.js deleted file mode 100644 index 2f79f3b9f1..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridentryeditors/mediablock/mediablock.editor.controller.js +++ /dev/null @@ -1,47 +0,0 @@ -(function () { - 'use strict'; - - function MediaBlockEditor($scope, mediaResource, mediaHelper) { - - var unsubscribe = []; - - const bc = this; - - $scope.$watch("block.data.image", function(newValue, oldValue) { - if (newValue !== oldValue) { - bc.retrieveMedia(); - } - }, true); - - bc.retrieveMedia = function() { - - if($scope.block.data.image && $scope.block.data.image.length > 0) { - mediaResource.getById($scope.block.data.image[0].mediaKey).then(function (mediaEntity) { - - var mediaPath = mediaEntity.mediaLink; - - //set a property on the 'scope' for the returned media object - bc.icon = mediaEntity.contentType.icon; - bc.mediaName = mediaEntity.name; - bc.fileExtension = mediaHelper.getFileExtension(mediaPath); - bc.isImage = mediaHelper.detectIfImageByExtension(mediaPath); - bc.imageSource = mediaHelper.getThumbnailFromPath(mediaPath); - }); - } - } - - bc.retrieveMedia(); - - - - $scope.$on("$destroy", function () { - for (const subscription of unsubscribe) { - subscription(); - } - }); - - } - - angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.MediaBlockEditor", MediaBlockEditor); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less index 6cfdb05482..38ff8086c6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less @@ -21,13 +21,18 @@ .umb-block-grid__layout-item { position: relative; &:hover { - z-index: 3; -/* - > .umb-block-grid__force-left, - > .umb-block-grid__force-right { + + > ng-form > .umb-block-grid__block--context { z-index: 4; } - */ + + > ng-form > .umb-block-grid__block--inline-create-button, + > ng-form > .umb-block-grid__block--validation-border, + > ng-form > .umb-block-grid__block--actions, + > ng-form > .umb-block-grid__force-left, + > ng-form > .umb-block-grid__force-right { + z-index: 3; + } } } @@ -49,52 +54,12 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti pointer-events: none; } -/*.umb-block-grid__block--validation-badge { - display:none; -} -ng-form.ng-invalid-val-server-match-settings > .umb-block-grid__block:not(.--active) > .umb-block-grid__block--validation-badge, -ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--active) > .umb-block-grid__block--validation-badge { - display:block; - text-align: center; - position: absolute; - top: -9px; - right: -9px; - min-width: 10px; - color: @white; - border: 2px solid @white; - border-radius: 50%; - font-size: 10px; - font-weight: bold; - padding: 2px; - line-height: 10px; - background-color: @formErrorText; - .show-validation-type-warning & { - background-color: @formWarningText; - } - font-weight: 900; - pointer-events: none; - - animation-duration: 1.4s; - animation-iteration-count: infinite; - animation-name: blockelement-inlineblock-editor--badge-bounce; - animation-timing-function: ease; - @keyframes blockelement-inlineblock-editor--badge-bounce { - 0% { transform: translateY(0); } - 20% { transform: translateY(-4px); } - 40% { transform: translateY(0); } - 55% { transform: translateY(-2px); } - 70% { transform: translateY(0); } - 100% { transform: translateY(0); } - } -} -*/ - .umb-block-grid__block { position: relative; width: 100%; height: 100%; - --umb-block-grid__block--show-ui: 0;// Publicly available. + --umb-block-grid--block-ui-opacity: 0; --umb-block-grid--hint-area-ui: 0; &::after { @@ -136,7 +101,6 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti --umb-block-grid--hint-area-ui: 1; &::after { - /*border-color: @blueDark;*/ display: var(--umb-block-grid--block-ui-display, block); animation: umb-block-grid__block__border-pulse 400ms ease-in-out alternate infinite; @keyframes umb-block-grid__block__border-pulse { @@ -211,18 +175,21 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti } } &.--block-ui-visible { + > .umb-block-grid__block--context { /* take full width to prevent interaction with elements behind.*/ left: 0; } .umb-block-grid__area-container, .umb-block-grid__block--view::part(area-container) { --umb-block-grid--block-ui-display: none; - .umb-block-grid__layout-item { - pointer-events: none; - } - .umb-block-grid__block { - pointer-events: none; - } + pointer-events: none; + } + .umb-block-grid__layout-item { + pointer-events: none; + } + .umb-block-grid__block { + pointer-events: none; + --umb-block-grid--block-ui-opacity: 0; } } @@ -236,48 +203,34 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti &.--active { /** Avoid displaying hover when dragging-mode */ - --umb-block-grid--block_ui-opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0)); + --umb-block-grid--block-ui-opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0)); > .umb-block-grid__block--context { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } &:not(.--scale-mode) { > .umb-block-grid__block--actions { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } > umb-block-grid-block > umb-block-grid-entries > .umb-block-grid__layout-container > .umb-block-grid__area-actions { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } } > .umb-block-grid__scale-handler { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } > .umb-block-grid__force-left, > .umb-block-grid__force-right { - opacity: var(--umb-block-grid--block_ui-opacity); + opacity: var(--umb-block-grid--block-ui-opacity); } } - - /* - &.--show-validation { - ng-form.ng-invalid-val-server-match-content > & { - border: 2px solid @formErrorText; - border-radius: @baseBorderRadius; - } - } - */ } ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__block--actions { opacity: 1; } -/* -ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__block--context { - opacity: 1; -} -*/ .umb-block-grid__block--view { height: 100%; @@ -291,10 +244,20 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl top: -20px; right: 0; font-size: 12px; - z-index: 2; + z-index: 4; display: var(--umb-block-grid--block-ui-display, flex); justify-content: end; + /** prevent interaction with inline-create button just beneath the context-bar: */ + ::after { + content: ''; + position: absolute; + top: 100%; + left: 0; + right: 0; + height: 12px; + } + .__context-bar { padding: 0 9px; padding-top: 1px; @@ -509,27 +472,6 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl } } -/* -umb-block-grid-block { - - > div { - position: relative; - width: 100%; - min-height: @umb-block-grid__item_minimum_height; - background-color: @white; - border-radius: @baseBorderRadius; - box-sizing: border-box; - } - -} -*/ - -/* -.blockelement__draggable-element { - cursor: grab; -} -*/ - .umb-block-grid__scale-handler { cursor: nwse-resize; @@ -586,7 +528,7 @@ umb-block-grid-block { .umb-block-grid__block--inline-create-button { top: 0px; position: absolute; - z-index: 1; + z-index: 1; /** overwritten for the first one of an area. */ /** Avoid showing inline-create in dragging-mode */ opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0)); @@ -594,6 +536,12 @@ umb-block-grid-block { .umb-block-grid__block--inline-create-button.--above { left: 0; width: 100%; + + top: calc(var(--umb-block-grid--row-gap, 0px) * -0.5); +} +.umb-block-grid__layout-item:first-of-type .umb-block-grid__block--inline-create-button.--above { + /* Do not use row-gap if the first one. */ + top: 0; } .umb-block-grid__block--inline-create-button.--above.--at-root { /* If at root, and full-width then become 40px wider: */ @@ -601,14 +549,9 @@ umb-block-grid-block { left: calc(-20px * var(--calc)); width: calc(100% + 40px * var(--calc)); } + .umb-block-grid__block--inline-create-button.--after { - right: 1px; -} -.umb-block-grid__block--inline-create-button.--after.--detector { - width: 10px; - margin-right: -10px; - height: 100%; - z-index: 0; + right: calc(1px - (var(--umb-block-grid--column-gap, 0px) * 0.5)); } .umb-block-grid__block--inline-create-button.--after.--at-root { /* If at root, and full-width then move a little out to the right: */ @@ -624,8 +567,8 @@ umb-block-grid-block { pointer-events: none; } -.umb-block-grid__block--after-inline-create-button { - z-index:2; +.umb-block-grid__block--last-inline-create-button { + z-index:4; width: 100%; /* Move inline create button slightly up, to avoid collision with others*/ margin-bottom: -7px; @@ -746,13 +689,11 @@ umb-block-grid-block { align-items: center; justify-content: center; - /* TODO: dont use --umb-text-color, its temporary to inherit UI */ - color: var(--umb-text-color, @ui-action-discreet-type); + color: var(--umb-block-grid--text-color, @ui-action-discreet-type); font-weight: bold; padding: 5px 15px; - /* TODO: dont use --umb-text-color, its temporary to inherit UI */ - border: 1px dashed var(--umb-text-color, @ui-action-discreet-border); + border: 1px dashed var(--umb-block-grid--text-color, @ui-action-discreet-border); border-radius: @baseBorderRadius; box-sizing: border-box; @@ -760,24 +701,14 @@ umb-block-grid-block { height: 100%; &:hover { - color: var(--umb-text-color, @ui-action-discreet-type-hover); - border-color: var(--umb-text-color, @ui-action-discreet-border-hover); + color: var(--umb-block-grid--text-color-hover, @ui-action-discreet-type-hover); + border-color: var(--umb-block-grid--text-color-hover, @ui-action-discreet-border-hover); text-decoration: none; z-index: 1; } } } - -/** make sure block with areas stay on top, so they don't look like they are 'not-allowed'*/ -/* -.umb-block-grid__layout-container.--droppable-indication { - .umb-block-grid__area-actions { - display: none; - } -} -*/ - .umb-block-grid__layout-item-placeholder { background: transparent; border-radius: 3px; @@ -859,16 +790,19 @@ umb-block-grid-block { content: ''; position: absolute; inset: 0; - /* Moved slightly in to align with the inline-create button, which is moved slightly in to avoid collision with other create buttons. */ - top:2px; - bottom: 2px; + top:0; + bottom: 0; border-radius: 3px; border: 1px solid rgba(@gray-5, 0.3); pointer-events: none; opacity: var(--umb-block-grid--show-area-ui, 0); transition: opacity 240ms; + z-index:3; } .umb-block-grid__area.--highlight::after { + /* Moved slightly in to align with the inline-create button, which is moved slightly in to avoid collision with other create buttons. */ + top:2px; + bottom: 2px; /** Avoid displaying highlight when in dragging-mode */ opacity: calc(1 - var(--umb-block-grid--dragging-mode, 0)); border-color: @blueDark; @@ -899,31 +833,10 @@ umb-block-grid-block { z-index: 1; cursor: nwse-resize; } -/* -.umb-block-grid__scalebox { - position: absolute; - top:0; - left:0; - z-index: 10; - cursor: nwse-resize; - - transition: background-color 240ms ease-in; - animation: umb-block-grid__scalebox__pulse 400ms ease-in-out alternate infinite; - @keyframes umb-block-grid__scalebox__pulse { - 0% { background-color: rgba(@blueMidLight, 0.33); } - 100% { background-color: rgba(@blueMidLight, 0.22); } - } -} -*/ -/* -.umb-block-grid__layout-container { - -} -*/ /** make sure block with areas stay on top, so they don't look like they are 'not-allowed'*/ @@ -934,8 +847,10 @@ umb-block-grid-block { } .umb-block-grid__layout-container .umb-block-grid__layout-item:not([depth='0']):first-of-type .umb-block-grid__block--inline-create-button.--above { - /* Move first above inline create button slightly up, to avoid collision with others*/ + /* Move the above inline create button slightly down, to avoid collision with others*/ margin-top: -7px; + + z-index:4; } .umb-block-grid__not-allowed-box { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js index 6a466cf052..f20f14f227 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.controller.js @@ -25,13 +25,25 @@ value: {min:vm.area.minAllowed, max:vm.area.maxAllowed} } + unsubscribe.push($scope.$watch('vm.area.alias', (newVal, oldVal) => { + $scope.model.updateTitle(); + if($scope.blockGridBlockConfigurationAreaForm.alias) { + $scope.blockGridBlockConfigurationAreaForm.alias.$setValidity("alias", $scope.model.otherAreaAliases.indexOf(newVal) === -1); + } + })); + vm.submit = function() { + if($scope.blockGridBlockConfigurationAreaForm.$valid === false) { + $scope.submitButtonState = "error"; + return; + } if ($scope.model && $scope.model.submit) { // Transfer minMaxModel to area: vm.area.minAllowed = vm.minMaxModel.value.min; vm.area.maxAllowed = vm.minMaxModel.value.max; + $scope.submitButtonState = "success"; $scope.model.submit($scope.model); } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.html index cccceb7afb..90918d71e1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.area.overlay.html @@ -27,11 +27,18 @@
+ * The alias will be printed by GetBlockGridHTML(), use the alias to target the Element representing this area. Ex. .umb-block-grid__area[data-area-alias="MyAreaAlias"] { ... }
- + +
+
+
+ This Areas Alias must be unique compared to the other Areas of this Block. +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.html index bfb9fdc90c..e78d94d486 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.html @@ -6,6 +6,7 @@
+
@@ -30,9 +31,14 @@
- + + + + Install demo Blocks + +
@@ -82,9 +88,9 @@
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.less index aa09b5238e..43151fcabb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.less @@ -2,29 +2,10 @@ margin-bottom: 20px; - .__add-button { - position: relative; - display: inline-flex; - width: 100%; - height: 100%; - margin-right: 20px; - margin-bottom: 20px; - - color: @ui-action-discreet-type; - border: 1px dashed @ui-action-discreet-border; - border-radius: @doubleBorderRadius; - - align-items: center; - justify-content: center; - - padding: 5px 15px; - box-sizing: border-box; - font-weight: bold; - } - - .__add-button:hover { - color: @ui-action-discreet-type-hover; - border-color: @ui-action-discreet-border-hover; + uui-button { + font-weight: 700; + --uui-button-border-radius: 6px; + min-height: 80px; } .__get-sample-box { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.html index 4b05e4ad43..27c1a2f006 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.blockconfiguration.overlay.html @@ -355,12 +355,25 @@
+ +
+
+ + + Hide the content edit button and the content editor from the Block Editor overlay. + +
+ +
+
+
+
- Define the range of layout rows this block is allowed to span across. + Hide the content edit button and the content editor from the Block Editor overlay.
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.groupconfiguration.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.groupconfiguration.html index 86c975639c..25a7282ca2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.groupconfiguration.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/blockgrid.groupconfiguration.html @@ -1,5 +1,5 @@ -
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.html index c288afc3ff..55ceb941bb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.html @@ -2,13 +2,11 @@ -
- +
+ ng-click="vm.deleteAllowance(allowance);"> Delete @@ -56,12 +51,15 @@ +
+ When empty all Blocks allowed for Areas can be created. +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.less index 9389906fac..2ce5a2ef16 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umb-block-grid-area-allowance-editor.less @@ -5,8 +5,11 @@ width: 100%; } -.umb-block-grid-area-allowance-editor .__list.--disabled { - +.umb-block-grid-area-allowance-editor .__empty-label { + font-size: 12px; + color: @gray-6; + line-height: 1.5em; + padding-top: 5px; } .umb-block-grid-area-allowance-editor__entry { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js index e8ae592d8f..9126a6cc54 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/prevalue/umbBlockGridAreaEditor.component.js @@ -56,15 +56,32 @@ function initializeSortable() { - const gridLayoutContainerEl = $element[0].querySelector('.umb-block-grid-area-editor__grid-wrapper'); + function _sync(evt) { - const sortable = Sortable.create(gridLayoutContainerEl, { + const oldIndex = evt.oldIndex, + newIndex = evt.newIndex; + + vm.model.splice(newIndex, 0, vm.model.splice(oldIndex, 1)[0]); + + } + + const gridContainerEl = $element[0].querySelector('.umb-block-grid-area-editor__grid-wrapper'); + + const sortable = Sortable.create(gridContainerEl, { sort: true, // sorting inside list animation: 150, // ms, animation speed moving items when sorting, `0` — without animation easing: "cubic-bezier(1, 0, 0, 1)", // Easing for animation. Defaults to null. See https://easings.net/ for examples. cancel: '', draggable: ".umb-block-grid-area-editor__area", // Specifies which items inside the element should be draggable - ghostClass: "umb-block-grid-area-editor__area-placeholder" + ghostClass: "umb-block-grid-area-editor__area-placeholder", + onAdd: function (evt) { + _sync(evt); + $scope.$evalAsync(); + }, + onUpdate: function (evt) { + _sync(evt); + $scope.$evalAsync(); + } }); // TODO: setDirty if sort has happend. @@ -130,14 +147,23 @@ vm.openAreaOverlay = function (area) { // TODO: use the right localization key: - localizationService.localize("blockEditor_blockConfigurationOverlayTitle", [area.alias]).then(function (localized) { + localizationService.localize("blockEditor_blockConfigurationOverlayTitle").then(function (localized) { var clonedAreaData = Utilities.copy(area); vm.openArea = area; + function updateTitle() { + overlayModel.title = localizationService.tokenReplace(localized, [clonedAreaData.alias]); + } + + const areaIndex = vm.model.indexOf(area); + const otherAreas = [...vm.model]; + otherAreas.splice(areaIndex, 1); + var overlayModel = { + otherAreaAliases: otherAreas.map(x => x.alias), area: clonedAreaData, - title: localized, + updateTitle: updateTitle, allBlockTypes: vm.allBlockTypes, allBlockGroups: vm.allBlockGroups, loadedElementTypes: vm.loadedElementTypes, @@ -154,6 +180,8 @@ } }; + updateTitle(); + // open property settings editor editorService.open(overlayModel); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html index b4ef7b9266..301208c6f2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html @@ -60,7 +60,7 @@ @@ -119,7 +119,7 @@ key="{{(invalidBlockType.amount < invalidBlockType.minRequirement) ? 'blockEditor_areaValidationEntriesShort' : 'blockEditor_areaValidationEntriesExceed'}}" tokens="[invalidBlockType.name, invalidBlockType.amount, invalidBlockType.minRequirement, invalidBlockType.maxRequirement]" watch-tokens="true" - >%0% must be present between %2% – %3% times. + >%0% must be present between %2%–%3% times.
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html index 9c8860eaa5..e3f439a891 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html @@ -1,10 +1,11 @@ + ng-click="vm.clickInlineCreateAbove()" + ng-mouseover="vm.mouseOverInlineCreate()" + ng-mouseleave="vm.mouseOutInlineCreate()"> @@ -29,9 +30,15 @@ parent-form="vm.blockForm" style="--umb-block-grid--area-grid-columns: {{vm.areaGridColumns}}" > + +
-
@@ -159,7 +165,7 @@ -
{{vm.layoutEntry.columnSpan}} x {{vm.layoutEntry.rowSpan}}
@@ -180,10 +186,7 @@ class="umb-block-grid__block--inline-create-button --after" ng-class="{'--at-root': vm.depth === '0'}" ng-click="vm.clickInlineCreateAfter($event)" - ng-mouseover="vm.mouseOverInlineCreateAfter()" - ng-mouseleave="vm.blockEditorApi.internal.hideAreaHighlight(vm.parentBlock, vm.areaKey)" + ng-mouseover="vm.mouseOverInlineCreate()" + ng-mouseleave="vm.mouseOutInlineCreate()" vertical> - -
-
+ \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.html index 767be6d559..834f55e685 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.html @@ -4,7 +4,19 @@
-
+ + + + + + + +
+ entries="vm.layout" + loading="vm.loading" + >
- diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.less index be3d1cc9ec..d06b6377c3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-property-editor.less @@ -1,4 +1,8 @@ .umb-block-grid__wrapper { position: relative; max-width: 1200px; +} + +.umb-block-grid__wrapper .umb-rte { + max-width: 100%; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html new file mode 100644 index 0000000000..e8d53c7457 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js index 57ddea9757..829fc918b6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js @@ -13,6 +13,31 @@ return null; } + function closestColumnSpanOption(target, map, max) { + if(map.length > 0) { + const result = map.reduce((a, b) => { + if (a.columnSpan > max) { + return b; + } + let aDiff = Math.abs(a.columnSpan - target); + let bDiff = Math.abs(b.columnSpan - target); + + if (aDiff === bDiff) { + return a.columnSpan < b.columnSpan ? a : b; + } else { + return bDiff < aDiff ? b : a; + } + }); + if(result) { + return result; + } + } + return null; + } + + + const DefaultViewFolderPath = "views/propertyeditors/blockgrid/blockgridentryeditors/"; + /** * @ngdoc directive @@ -44,14 +69,20 @@ var unsubscribe = []; var modelObject; + var gridRootEl; // Property actions: + var propertyActions = null; + var enterSortModeAction = null; + var exitSortModeAction = null; var copyAllBlocksAction = null; var deleteAllBlocksAction = null; var liveEditing = true; var shadowRoot; + var firstLayoutContainer; + var vm = this; @@ -107,6 +138,8 @@ vm.options = { createFlow: false }; + vm.sortMode = false; + vm.sortModeView = DefaultViewFolderPath + "gridsortblock/gridsortblock.editor.html";; localizationService.localizeMany(["grid_addElement", "content_createEmpty", "blockEditor_addThis"]).then(function (data) { vm.labels.grid_addElement = data[0]; @@ -114,8 +147,23 @@ vm.labels.blockEditor_addThis = data[2] }); + vm.onAppendProxyProperty = (event) => { + event.stopPropagation(); + gridRootEl.appendChild(event.detail.property); + event.detail.connectedCallback(); + }; + vm.onRemoveProxyProperty = (event) => { + event.stopPropagation(); + const el = gridRootEl.querySelector(`:scope > [slot='${event.detail.slotName}']`); + gridRootEl.removeChild(el); + }; + vm.$onInit = function() { + gridRootEl = $element[0].querySelector('umb-block-grid-root'); + + $element[0].addEventListener("UmbBlockGrid_AppendProperty", vm.onAppendProxyProperty); + $element[0].addEventListener("UmbBlockGrid_RemoveProperty", vm.onRemoveProxyProperty); //listen for form validation changes vm.valFormManager.onValidationStatusChanged(function (evt, args) { @@ -177,6 +225,19 @@ scopeOfExistence = vm.umbElementEditorContent.getScope(); } + enterSortModeAction = { + labelKey: 'blockEditor_actionEnterSortMode', + icon: 'navigation-vertical', + method: enableSortMode, + isDisabled: false + }; + exitSortModeAction = { + labelKey: 'blockEditor_actionExitSortMode', + icon: 'navigation-vertical', + method: exitSortMode, + isDisabled: false + }; + copyAllBlocksAction = { labelKey: "clipboard_labelForCopyAllEntries", labelTokens: [vm.model.label], @@ -187,13 +248,13 @@ deleteAllBlocksAction = { labelKey: 'clipboard_labelForRemoveAllEntries', - labelTokens: [], icon: 'trash', method: requestDeleteAllBlocks, isDisabled: true }; - var propertyActions = [ + propertyActions = [ + enterSortModeAction, copyAllBlocksAction, deleteAllBlocksAction ]; @@ -223,7 +284,6 @@ } - function onLoaded() { // Store a reference to the layout model, because we need to maintain this model. @@ -241,6 +301,7 @@ window.requestAnimationFrame(() => { shadowRoot = $element[0].querySelector('umb-block-grid-root').shadowRoot; + firstLayoutContainer = shadowRoot.querySelector('.umb-block-grid__layout-container'); }) } @@ -314,21 +375,31 @@ } } - // if no columnSpan, then we set one: - if (!layoutEntry.columnSpan) { + // Ensure Areas are ordered like the area configuration is: + layoutEntry.areas.sort((left, right) => { + return block.config.areas?.findIndex(config => config.key === left.key) < block.config.areas?.findIndex(config => config.key === right.key) ? -1 : 1; + }); - const contextColumns = getContextColumns(parentBlock, areaKey) - if (block.config.columnSpanOptions.length > 0) { - // set columnSpan to minimum allowed span for this BlockType: - const minimumColumnSpan = block.config.columnSpanOptions.reduce((prev, option) => Math.min(prev, option.columnSpan), vm.gridColumns); + const contextColumns = getContextColumns(parentBlock, areaKey); + const relevantColumnSpanOptions = block.config.columnSpanOptions.filter(option => option.columnSpan <= contextColumns); - // If minimumColumnSpan is larger than contextColumns, then we will make it fit within context anyway: - layoutEntry.columnSpan = Math.min(minimumColumnSpan, contextColumns) + // if no columnSpan or no columnSpanOptions configured, then we set(or rewrite) one: + if (!layoutEntry.columnSpan || layoutEntry.columnSpan > contextColumns || relevantColumnSpanOptions.length === 0) { + if (relevantColumnSpanOptions.length > 0) { + // Find greatest columnSpanOption within contextColumns, or fallback to contextColumns. + layoutEntry.columnSpan = relevantColumnSpanOptions.reduce((prev, option) => Math.max(prev, option.columnSpan), 0) || contextColumns; } else { layoutEntry.columnSpan = contextColumns; } + } else { + // Check that columnSpanOption still is available or equal contextColumns, or find closest option fitting: + if (relevantColumnSpanOptions.find(option => option.columnSpan === layoutEntry.columnSpan) === undefined || layoutEntry.columnSpan !== contextColumns) { + console.log(layoutEntry.columnSpan, closestColumnSpanOption(layoutEntry.columnSpan, relevantColumnSpanOptions, contextColumns)?.columnSpan || contextColumns); + layoutEntry.columnSpan = closestColumnSpanOption(layoutEntry.columnSpan, relevantColumnSpanOptions, contextColumns)?.columnSpan || contextColumns; + } } + // if no rowSpan, then we set one: if (!layoutEntry.rowSpan) { layoutEntry.rowSpan = 1; @@ -375,12 +446,12 @@ function applyDefaultViewForBlock(block) { - var defaultViewFolderPath = "views/propertyeditors/blockgrid/blockgridentryeditors/"; - if (block.config.unsupported === true) { - block.view = defaultViewFolderPath + "unsupportedblock/unsupportedblock.editor.html"; + block.view = DefaultViewFolderPath + "unsupportedblock/unsupportedblock.editor.html"; + } else if (block.config.inlineEditing) { + block.view = DefaultViewFolderPath + "gridinlineblock/gridinlineblock.editor.html"; } else { - block.view = defaultViewFolderPath + "gridblock/gridblock.editor.html"; + block.view = DefaultViewFolderPath + "gridblock/gridblock.editor.html"; } } @@ -430,9 +501,11 @@ block.showCopy = vm.supportCopy && block.config.contentElementTypeKey != null; block.blockUiVisibility = false; - block.showBlockUI = function () { + block.showBlockUI = () => { delete block.__timeout; - shadowRoot.querySelector('*[data-element-udi="'+block.layout.contentUdi+'"] .umb-block-grid__block > .umb-block-grid__block--context').scrollIntoView({block: "nearest", inline: "nearest", behavior: "smooth"}); + $timeout(() => { + shadowRoot.querySelector('*[data-element-udi="'+block.layout.contentUdi+'"] > ng-form > .umb-block-grid__block > .umb-block-grid__block--context').scrollIntoView({block: "nearest", inline: "nearest", behavior: "smooth"}); + }, 100); block.blockUiVisibility = true; }; block.onMouseLeave = function () { @@ -778,6 +851,8 @@ vm.requestShowCreate = requestShowCreate; function requestShowCreate(parentBlock, areaKey, createIndex, mouseEvent, options) { + vm.hideAreaHighlight(parentBlock, areaKey); + if (vm.blockTypePickerIsOpen === true) { return; } @@ -1254,6 +1329,38 @@ } } + function enableSortMode() { + vm.sortMode = true; + propertyActions.splice(propertyActions.indexOf(enterSortModeAction), 1, exitSortModeAction); + if (vm.umbProperty) { + vm.umbProperty.setPropertyActions(propertyActions); + } + } + + vm.exitSortMode = exitSortMode; + function exitSortMode() { + vm.sortMode = false; + propertyActions.splice(propertyActions.indexOf(exitSortModeAction), 1, enterSortModeAction); + if (vm.umbProperty) { + vm.umbProperty.setPropertyActions(propertyActions); + } + } + + vm.startDraggingMode = startDraggingMode; + function startDraggingMode() { + + document.documentElement.style.setProperty("--umb-block-grid--dragging-mode", 1); + firstLayoutContainer.style.minHeight = firstLayoutContainer.getBoundingClientRect().height + "px"; + + } + vm.exitDraggingMode = exitDraggingMode; + function exitDraggingMode() { + + document.documentElement.style.setProperty("--umb-block-grid--dragging-mode", 0); + firstLayoutContainer.style.minHeight = ""; + + } + function onAmountOfBlocksChanged() { // enable/disable property actions @@ -1278,9 +1385,16 @@ unsubscribe.push($scope.$watch(() => vm.layout.length, onAmountOfBlocksChanged)); $scope.$on("$destroy", function () { + + $element[0].removeEventListener("UmbBlockGrid_AppendProperty", vm.onAppendProxyProperty); + $element[0].removeEventListener("UmbBlockGrid_RemoveProperty", vm.onRemoveProxyProperty); + for (const subscription of unsubscribe) { subscription(); } + + firstLayoutContainer = null; + gridRootEl = null; }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridblock.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridblock.component.js index 2c4a4bb262..1a918638ca 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridblock.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridblock.component.js @@ -58,7 +58,7 @@
+ ng-include="api.internal.sortMode ? api.internal.sortModeView : '${model.view}'">
`; $compile(shadowRoot)($scope); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentries.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentries.component.js index c1e2c43619..3f597292e8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentries.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentries.component.js @@ -76,6 +76,7 @@ vm.movingLayoutEntry = null; vm.layoutColumnsInt = 0; + vm.containedPropertyEditorProxies = []; vm.$onInit = function () { initializeSortable(); @@ -93,7 +94,6 @@ vm.layoutColumnsInt = parseInt(vm.layoutColumns, 10); })); - function onLocalAmountOfBlocksChanged() { if (vm.entriesForm && vm.areaConfig) { @@ -153,6 +153,11 @@ } } + + vm.notifyVisualUpdate = function () { + $scope.$broadcast("blockGridEditorVisualUpdate", {areaKey: vm.areaKey}); + } + vm.acceptBlock = function(contentTypeKey) { return vm.blockEditorApi.internal.isElementTypeKeyAllowedAt(vm.parentBlock, vm.areaKey, contentTypeKey); } @@ -210,6 +215,11 @@ var nextSibling; + function _removePropertyProxy(eventTarget, slotName) { + const event = new CustomEvent("UmbBlockGrid_RemoveProperty", {composed: true, bubbles: true, detail: {'slotName': slotName}}); + eventTarget.dispatchEvent(event); + } + // Borrowed concept from, its not identical as more has been implemented: https://github.com/SortableJS/angular-legacy-sortablejs/blob/master/angular-legacy-sortable.js function _sync(evt) { @@ -222,8 +232,15 @@ const prevEntries = fromCtrl.entries; const syncEntry = prevEntries[oldIndex]; - // Perform the transfer: + // Make sure Property Editor Proxies are destroyed, as we need to establish new when moving context: + + // unregister all property editor proxies via events: + fromCtrl.containedPropertyEditorProxies.forEach(slotName => { + _removePropertyProxy(evt.from, slotName); + }); + + // Perform the transfer: if (Sortable.active && Sortable.active.lastPullMode === 'clone') { syncEntry = Utilities.copy(syncEntry); prevEntries.splice(Sortable.utils.index(evt.clone, sortable.options.draggable), 0, prevEntries.splice(oldIndex, 1)[0]); @@ -231,7 +248,6 @@ else { prevEntries.splice(oldIndex, 1); } - vm.entries.splice(newIndex, 0, syncEntry); const contextColumns = vm.blockEditorApi.internal.getContextColumns(vm.parentBlock, vm.areaKey); @@ -261,6 +277,7 @@ function _indication(contextVM, movingEl) { + // Remove old indication: if(_lastIndicationContainerVM !== contextVM && _lastIndicationContainerVM !== null) { _lastIndicationContainerVM.hideNotAllowed(); _lastIndicationContainerVM.revertIndicateDroppable(); @@ -269,7 +286,7 @@ if(contextVM.acceptBlock(movingEl.dataset.contentElementTypeKey) === true) { _lastIndicationContainerVM.hideNotAllowed(); - _lastIndicationContainerVM.indicateDroppable();// This block is accepted to we will indicate a good drop. + _lastIndicationContainerVM.indicateDroppable();// This block is accepted so we will indicate a good drop. return true; } @@ -557,6 +574,10 @@ forceAutoScrollFallback: true, onStart: function (evt) { + + // TODO: This does not work correctly jet with SortableJS. With the replacement we should be able to call this before DOM is changed. + vm.blockEditorApi.internal.startDraggingMode(); + nextSibling = evt.from === evt.item.parentNode ? evt.item.nextSibling : evt.clone.nextSibling; var contextVM = vm; @@ -577,6 +598,7 @@ vm.movingLayoutEntry.$block.__scope.$evalAsync();// needed for the block to be updated ghostEl = evt.item; + vm.containedPropertyEditorProxies = Array.from(ghostEl.querySelectorAll('slot[data-is-property-editor-proxy]')).map(x => x.getAttribute('name')); targetRect = evt.to.getBoundingClientRect(); ghostRect = ghostEl.getBoundingClientRect(); @@ -587,8 +609,6 @@ window.addEventListener('drag', _onDragMove); window.addEventListener('dragover', _onDragMove); - document.documentElement.style.setProperty("--umb-block-grid--dragging-mode", 1); - $scope.$evalAsync(); }, // Called by any change to the list (add / update / remove) @@ -619,7 +639,7 @@ } window.removeEventListener('drag', _onDragMove); window.removeEventListener('dragover', _onDragMove); - document.documentElement.style.setProperty("--umb-block-grid--dragging-mode", 0); + vm.blockEditorApi.internal.exitDraggingMode(); if(ghostElIndicateForceLeft) { ghostEl.removeChild(ghostElIndicateForceLeft); @@ -643,6 +663,9 @@ ghostRect = null; ghostEl = null; relatedEl = null; + vm.containedPropertyEditorProxies = []; + + vm.notifyVisualUpdate(); } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js index a7c45ecdd8..d5b3ce5474 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridentry.component.js @@ -44,23 +44,25 @@ } function closestColumnSpanOption(target, map, max) { - const result = map.reduce((a, b) => { - if (a.columnSpan > max) { - return b; + if(map.length > 0) { + const result = map.reduce((a, b) => { + if (a.columnSpan > max) { + return b; + } + let aDiff = Math.abs(a.columnSpan - target); + let bDiff = Math.abs(b.columnSpan - target); + + if (aDiff === bDiff) { + return a.columnSpan < b.columnSpan ? a : b; + } else { + return bDiff < aDiff ? b : a; + } + }); + if(result) { + return result; } - let aDiff = Math.abs(a.columnSpan - target); - let bDiff = Math.abs(b.columnSpan - target); - - if (aDiff === bDiff) { - return a.columnSpan < b.columnSpan ? a : b; - } else { - return bDiff < aDiff ? b : a; - } - }); - if(result) { - return result; } - return max; + return null; } @@ -86,11 +88,17 @@ areaKey: "<", propertyEditorForm: " { + // Only insert a proxy slot for the direct Block of this entry (as all the blocks share the same ShadowDom though they are slotted into each other when nested through areas.) + if (event.detail.contentUdi === vm.layoutEntry.contentUdi) { + vm.proxyProperties.push({ + slotName: event.detail.slotName + }); + $scope.$evalAsync(); + } + }; + vm.onRemoveProxyProperty = (event) => { + // Only react to proxies from the direct Block of this entry: + if (event.detail.contentUdi === vm.layoutEntry.contentUdi) { + const index = vm.proxyProperties.findIndex(x => x.slotName === event.detail.slotName); + if(index !== -1) { + vm.proxyProperties.splice(index, 1); + } + $scope.$evalAsync(); + } + }; vm.$onInit = function() { + $element[0].addEventListener("UmbBlockGrid_AppendProperty", vm.onAppendProxyProperty); + $element[0].addEventListener("UmbBlockGrid_RemoveProperty", vm.onRemoveProxyProperty); + vm.childDepth = parseInt(vm.depth) + 1; if(vm.layoutEntry.$block.config.areaGridColumns) { @@ -110,13 +145,29 @@ vm.areaGridColumns = vm.blockEditorApi.internal.gridColumns.toString(); } - vm.layoutColumnsInt = parseInt(vm.layoutColumns, 10) + vm.layoutColumnsInt = parseInt(vm.layoutColumns, 10); + + vm.relevantColumnSpanOptions = vm.layoutEntry.$block.config.columnSpanOptions.filter(x => x.columnSpan <= vm.layoutColumnsInt).sort((a,b) => (a.columnSpan > b.columnSpan) ? 1 : ((b.columnSpan > a.columnSpan) ? -1 : 0)); + const hasRelevantColumnSpanOptions = vm.relevantColumnSpanOptions.length > 1; + const hasRowSpanOptions = vm.layoutEntry.$block.config.rowMinSpan && vm.layoutEntry.$block.config.rowMaxSpan && vm.layoutEntry.$block.config.rowMaxSpan !== vm.layoutEntry.$block.config.rowMinSpan; + vm.canScale = (hasRelevantColumnSpanOptions || hasRowSpanOptions); + + unsubscribe.push(vm.layoutEntry.$block.__scope.$watch(() => vm.layoutEntry.$block.index, visualUpdateCallback)); + unsubscribe.push($scope.$on("blockGridEditorVisualUpdate", (evt, data) => {if(data.areaKey === vm.areaKey) { visualUpdateCallback()}})); + + updateInlineCreateTimeout = $timeout(updateInlineCreate, 500); $scope.$evalAsync(); } unsubscribe.push($scope.$watch("depth", (newVal, oldVal) => { vm.childDepth = parseInt(vm.depth) + 1; })); + + function visualUpdateCallback() { + cancelAnimationFrame(updateInlineCreateRaf); + updateInlineCreateRaf = requestAnimationFrame(updateInlineCreate); + } + /** * We want to only show the validation errors on the specific Block, not the parent blocks. * So we need to avoid having a Block as the parent to the Block Form. @@ -157,9 +208,12 @@ // Block sizing functionality: let layoutContainer = null; let gridColumns = null; + let columnGap = 0; + let rowGap = 0; let gridRows = null; + let lockedGridRows = 0; let scaleBoxBackdropEl = null; - + let raf = null; function getNewSpans(startX, startY, endX, endY) { @@ -171,7 +225,8 @@ let newColumnSpan = Math.max(blockEndCol-blockStartCol, 1); // Find nearest allowed Column: - newColumnSpan = closestColumnSpanOption(newColumnSpan , vm.layoutEntry.$block.config.columnSpanOptions, gridColumns.length - blockStartCol).columnSpan; + const bestColumnSpanOption = closestColumnSpanOption(newColumnSpan , vm.relevantColumnSpanOptions, vm.layoutColumnsInt - blockStartCol) + newColumnSpan = bestColumnSpanOption ? bestColumnSpanOption.columnSpan : vm.layoutColumnsInt; let newRowSpan = Math.round(Math.max(blockEndRow-blockStartRow, vm.layoutEntry.$block.config.rowMinSpan || 1)); if(vm.layoutEntry.$block.config.rowMaxSpan != null) { @@ -181,10 +236,14 @@ return {'columnSpan': newColumnSpan, 'rowSpan': newRowSpan, 'startCol': blockStartCol, 'startRow': blockStartRow}; } - function updateGridLayoutData(layoutContainerRect, layoutItemRect) { + function updateGridLayoutData(layoutContainerRect, layoutItemRect, updateRowTemplate) { const computedStyles = window.getComputedStyle(layoutContainer); + + columnGap = Number(computedStyles.columnGap.split("px")[0]) || 0; + rowGap = Number(computedStyles.rowGap.split("px")[0]) || 0; + gridColumns = computedStyles.gridTemplateColumns.trim().split("px").map(x => Number(x)); gridRows = computedStyles.gridTemplateRows.trim().split("px").map(x => Number(x)); @@ -192,6 +251,18 @@ gridColumns = gridColumns.filter(n => n > 0); gridRows = gridRows.filter(n => n > 0); + // We use this code to lock the templateRows, while scaling. otherwise scaling Rows is too crazy. + if(updateRowTemplate || gridRows.length > lockedGridRows) { + lockedGridRows = gridRows.length; + layoutContainer.style.gridTemplateRows = computedStyles.gridTemplateRows; + } + + // add gaps: + const gridColumnsLen = gridColumns.length; + gridColumns = gridColumns.map((n, i) => gridColumnsLen === i ? n : n + columnGap); + const gridRowsLen = gridRows.length; + gridRows = gridRows.map((n, i) => gridRowsLen === i ? n : n + rowGap); + // ensure all columns are there. // This will also ensure handling non-css-grid mode, // use container width divided by amount of columns( or the item width divided by its amount of columnSpan) @@ -226,25 +297,28 @@ gridRows.push(50); gridRows.push(50); gridRows.push(50); + gridRows.push(50); + gridRows.push(50); } vm.scaleHandlerMouseDown = function($event) { $event.originalEvent.preventDefault(); + + layoutContainer = $element[0].closest('.umb-block-grid__layout-container'); + if(!layoutContainer) { + console.error($element[0], 'could not find parent layout-container'); + return; + } + vm.isScaleMode = true; window.addEventListener('mousemove', vm.onMouseMove); window.addEventListener('mouseup', vm.onMouseUp); window.addEventListener('mouseleave', vm.onMouseUp); - - layoutContainer = $element[0].closest('.umb-block-grid__layout-container'); - if(!layoutContainer) { - console.error($element[0], 'could not find parent layout-container'); - } - const layoutContainerRect = layoutContainer.getBoundingClientRect(); const layoutItemRect = $element[0].getBoundingClientRect(); - updateGridLayoutData(layoutContainerRect, layoutItemRect); + updateGridLayoutData(layoutContainerRect, layoutItemRect, true); scaleBoxBackdropEl = document.createElement('div'); @@ -256,7 +330,6 @@ const layoutContainerRect = layoutContainer.getBoundingClientRect(); const layoutItemRect = $element[0].getBoundingClientRect(); - updateGridLayoutData(layoutContainerRect, layoutItemRect); const startX = layoutItemRect.left - layoutContainerRect.left; @@ -266,6 +339,18 @@ const newSpans = getNewSpans(startX, startY, endX, endY); + const updateRowTemplate = vm.layoutEntry.columnSpan !== newSpans.columnSpan; + + if(updateRowTemplate) { + // If we like to update we need to first remove the lock, make the browser render onces and then update. + layoutContainer.style.gridTemplateRows = ""; + } + cancelAnimationFrame(raf); + raf = requestAnimationFrame(() => { + // As mentioned above we need to wait until the browser has rendered DOM without the lock of gridTemplateRows. + updateGridLayoutData(layoutContainerRect, layoutItemRect, updateRowTemplate); + }) + // update as we go: vm.layoutEntry.columnSpan = newSpans.columnSpan; vm.layoutEntry.rowSpan = newSpans.rowSpan; @@ -275,7 +360,13 @@ vm.onMouseUp = function(e) { - vm.isScaleMode = false; + cancelAnimationFrame(raf); + + // Remove listeners: + window.removeEventListener('mousemove', vm.onMouseMove); + window.removeEventListener('mouseup', vm.onMouseUp); + window.removeEventListener('mouseleave', vm.onMouseUp); + const layoutContainerRect = layoutContainer.getBoundingClientRect(); const layoutItemRect = $element[0].getBoundingClientRect(); @@ -287,22 +378,24 @@ const newSpans = getNewSpans(startX, startY, endX, endY); - // Remove listeners: - window.removeEventListener('mousemove', vm.onMouseMove); - window.removeEventListener('mouseup', vm.onMouseUp); - window.removeEventListener('mouseleave', vm.onMouseUp); + // release the lock of gridTemplateRows: layoutContainer.removeChild(scaleBoxBackdropEl); + layoutContainer.style.gridTemplateRows = ""; + vm.isScaleMode = false; // Clean up variables: layoutContainer = null; gridColumns = null; gridRows = null; + lockedGridRows = 0; scaleBoxBackdropEl = null; // Update block size: vm.layoutEntry.columnSpan = newSpans.columnSpan; vm.layoutEntry.rowSpan = newSpans.rowSpan; + + vm.umbBlockGridEntries.notifyVisualUpdate(); vm.blockEditorApi.internal.setDirty(); $scope.$evalAsync(); } @@ -331,8 +424,8 @@ } if(addColIndex !== 0) { - if (vm.layoutEntry.$block.config.columnSpanOptions.length > 0) { - const sortOptions = vm.layoutEntry.$block.config.columnSpanOptions.sort((a,b) => (a.columnSpan > b.columnSpan) ? 1 : ((b.columnSpan > a.columnSpan) ? -1 : 0)); + if (vm.relevantColumnSpanOptions.length > 0) { + const sortOptions = vm.relevantColumnSpanOptions; const currentColIndex = sortOptions.findIndex(x => x.columnSpan === vm.layoutEntry.columnSpan); const newColIndex = Math.min(Math.max(currentColIndex + addColIndex, 0), sortOptions.length-1); vm.layoutEntry.columnSpan = sortOptions[newColIndex].columnSpan; @@ -346,18 +439,30 @@ } vm.layoutEntry.rowSpan = newRowSpan; + vm.umbBlockGridEntries.notifyVisualUpdate(); vm.blockEditorApi.internal.setDirty(); $event.originalEvent.stopPropagation(); } + vm.clickInlineCreateAbove = function($event) { + if(vm.hideInlineCreateAbove === false) { + vm.blockEditorApi.requestShowCreate(vm.parentBlock, vm.areaKey, vm.index, $event); + } + } vm.clickInlineCreateAfter = function($event) { if(vm.hideInlineCreateAfter === false) { vm.blockEditorApi.requestShowCreate(vm.parentBlock, vm.areaKey, vm.index+1, $event, {'fitInRow': true}); } } - vm.mouseOverInlineCreateAfter = function() { - + vm.mouseOverInlineCreate = function() { + vm.blockEditorApi.internal.showAreaHighlight(vm.parentBlock, vm.areaKey); + } + vm.mouseOutInlineCreate = function() { + vm.blockEditorApi.internal.hideAreaHighlight(vm.parentBlock, vm.areaKey); + } + + function updateInlineCreate() { layoutContainer = $element[0].closest('.umb-block-grid__layout-container'); if(!layoutContainer) { return; @@ -366,17 +471,39 @@ const layoutContainerRect = layoutContainer.getBoundingClientRect(); const layoutItemRect = $element[0].getBoundingClientRect(); - if(layoutItemRect.right > layoutContainerRect.right - 5) { + if(layoutContainerRect.width === 0) { + $timeout.cancel(updateInlineCreateTimeout); + vm.hideInlineCreateAbove = true; vm.hideInlineCreateAfter = true; + vm.inlineCreateAboveWidth = ""; + $scope.$evalAsync(); + updateInlineCreateTimeout = $timeout(updateInlineCreate, 500); return; } - vm.hideInlineCreateAfter = false; - vm.blockEditorApi.internal.showAreaHighlight(vm.parentBlock, vm.areaKey); + if(layoutItemRect.right > layoutContainerRect.right - 5) { + vm.hideInlineCreateAfter = true; + } else { + vm.hideInlineCreateAfter = false; + } + if(layoutItemRect.left > layoutContainerRect.left + 5) { + vm.hideInlineCreateAbove = true; + vm.inlineCreateAboveWidth = ""; + } else { + vm.inlineCreateAboveWidth = getComputedStyle(layoutContainer).width; + vm.hideInlineCreateAbove = false; + } + $scope.$evalAsync(); } $scope.$on("$destroy", function () { + + $timeout.cancel(updateInlineCreateTimeout); + + $element[0].removeEventListener("UmbBlockGrid_AppendProperty", vm.onAppendProxyProperty); + $element[0].removeEventListener("UmbBlockGrid_RemoveProperty", vm.onRemoveProxyProperty); + for (const subscription of unsubscribe) { subscription(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridrenderareaslots.directive.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridrenderareaslots.directive.js new file mode 100644 index 0000000000..43837e9c5c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridrenderareaslots.directive.js @@ -0,0 +1,20 @@ +(function () { + 'use strict'; + + function UmbBlockGridRenderAreaSlots() { + + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/propertyeditors/blockgrid/umb-block-grid-render-area-slots.html', + scope: false + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbBlockGridRenderAreaSlots', UmbBlockGridRenderAreaSlots); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridroot.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridroot.component.js index 130150a3ae..fbda5cf92e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridroot.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbblockgridroot.component.js @@ -20,7 +20,8 @@ stylesheet: "@", blockEditorApi: "<", propertyEditorForm: "
hello

"}; + element = $("
"); })); @@ -31,7 +32,8 @@ describe('RTE controller tests', function () { it('should define the default properties on construction', function () { controllerFactory('Umbraco.PropertyEditors.RTEController', { $scope: scope, - $routeParams: routeParams + $routeParams: routeParams, + $element: element }); }); From e321982f44f1c5c73d4b292dd2bc381d8a41dfb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 14 Nov 2022 12:14:25 +0100 Subject: [PATCH 30/39] remove console log --- .../blockgrid/umbBlockGridPropertyEditor.component.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js index 829fc918b6..ebd4625c3b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umbBlockGridPropertyEditor.component.js @@ -395,7 +395,6 @@ } else { // Check that columnSpanOption still is available or equal contextColumns, or find closest option fitting: if (relevantColumnSpanOptions.find(option => option.columnSpan === layoutEntry.columnSpan) === undefined || layoutEntry.columnSpan !== contextColumns) { - console.log(layoutEntry.columnSpan, closestColumnSpanOption(layoutEntry.columnSpan, relevantColumnSpanOptions, contextColumns)?.columnSpan || contextColumns); layoutEntry.columnSpan = closestColumnSpanOption(layoutEntry.columnSpan, relevantColumnSpanOptions, contextColumns)?.columnSpan || contextColumns; } } From 233c9df1d6485ebe93aa27df63c0077a27362ef8 Mon Sep 17 00:00:00 2001 From: Mole Date: Mon, 14 Nov 2022 11:37:55 +0100 Subject: [PATCH 31/39] V11: InMemory - only add models assembly as runtime view reference if it exists (#13390) * Only add models assembly as runtime view reference if it exists * Add in memory metadata reference properly compilations are readonly --- .../CollectibleRuntimeViewCompiler.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs index 742e7ffc29..d266b51dd1 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/InMemoryAuto/CollectibleRuntimeViewCompiler.cs @@ -396,26 +396,30 @@ internal class CollectibleRuntimeViewCompiler : IViewCompiler private CSharpCompilation CreateCompilation(string compilationContent, string assemblyName) { IReadOnlyList refs = _referenceManager.CompilationReferences; - // We'll add the reference to the InMemory assembly directly, this means we don't have to hack around with assembly parts. - if (_loadContextManager.ModelsAssemblyLocation is null) - { - throw new InvalidOperationException("No InMemory assembly available, cannot compile views"); - } - - PortableExecutableReference inMemoryAutoReference = MetadataReference.CreateFromFile(_loadContextManager.ModelsAssemblyLocation); - var sourceText = SourceText.From(compilationContent, Encoding.UTF8); SyntaxTree syntaxTree = SyntaxFactory .ParseSyntaxTree(sourceText, _compilationOptionsProvider.ParseOptions) .WithFilePath(assemblyName); - return CSharpCompilation + CSharpCompilation compilation = CSharpCompilation .Create(assemblyName) .AddSyntaxTrees(syntaxTree) .AddReferences(refs) - .AddReferences(inMemoryAutoReference) .WithOptions(_compilationOptionsProvider.CSharpCompilationOptions); + + // We'll add the reference to the InMemory assembly directly, this means we don't have to hack around with assembly parts. + // We might be asked to compile views before the InMemory models assembly is created tho (if you replace the no-nodes for instance) + // In this case we'll just skip the InMemory models assembly reference + if (_loadContextManager.ModelsAssemblyLocation is null) + { + _logger.LogInformation("No InMemory models assembly available, skipping reference"); + return compilation; + } + + PortableExecutableReference inMemoryAutoReference = MetadataReference.CreateFromFile(_loadContextManager.ModelsAssemblyLocation); + compilation = compilation.AddReferences(inMemoryAutoReference); + return compilation; } private string GetNormalizedPath(string relativePath) From 616d0925778befab94f946eee8e929559fca9470 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 14 Nov 2022 12:40:06 +0100 Subject: [PATCH 32/39] Obsolete old migrations and exif code (#13382) * Obsoleted and deleted internal migrations + Enabled breaking changes checks agains v11.0.0-rc1 * Obsoleted and deleted internal exif stuff * Added CompatibilitySuppressions.xml * Change GenerateCompatibilitySuppressionFile --- .gitignore | 1 + Directory.Build.props | 4 +- .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 42 +- src/Umbraco.Core/Media/Exif/BitConverterEx.cs | 346 ----- .../Media/Exif/ExifBitConverter.cs | 392 ----- src/Umbraco.Core/Media/Exif/ExifEnums.cs | 297 ---- src/Umbraco.Core/Media/Exif/ExifExceptions.cs | 1 + .../Media/Exif/ExifExtendedProperty.cs | 487 ------ .../Media/Exif/ExifFileTypeDescriptor.cs | 108 -- .../Media/Exif/ExifInterOperability.cs | 55 - src/Umbraco.Core/Media/Exif/ExifProperty.cs | 672 --------- .../Media/Exif/ExifPropertyCollection.cs | 493 ------ .../Media/Exif/ExifPropertyFactory.cs | 598 -------- src/Umbraco.Core/Media/Exif/ExifTag.cs | 310 ---- src/Umbraco.Core/Media/Exif/ExifTagFactory.cs | 63 - src/Umbraco.Core/Media/Exif/IFD.cs | 17 - src/Umbraco.Core/Media/Exif/ImageFile.cs | 144 -- .../Media/Exif/ImageFileDirectory.cs | 100 -- .../Media/Exif/ImageFileDirectoryEntry.cs | 144 -- .../Media/Exif/ImageFileFormat.cs | 27 - src/Umbraco.Core/Media/Exif/JFIFEnums.cs | 44 - .../Media/Exif/JFIFExtendedProperty.cs | 76 - src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs | 62 - src/Umbraco.Core/Media/Exif/JPEGExceptions.cs | 1 + src/Umbraco.Core/Media/Exif/JPEGFile.cs | 1110 -------------- src/Umbraco.Core/Media/Exif/JPEGMarker.cs | 95 -- src/Umbraco.Core/Media/Exif/JPEGSection.cs | 66 - src/Umbraco.Core/Media/Exif/MathEx.cs | 1329 ----------------- src/Umbraco.Core/Media/Exif/SvgFile.cs | 31 - src/Umbraco.Core/Media/Exif/TIFFFile.cs | 186 --- src/Umbraco.Core/Media/Exif/TIFFHeader.cs | 98 -- src/Umbraco.Core/Media/Exif/TIFFStrip.cs | 24 - src/Umbraco.Core/Media/Exif/Utility.cs | 29 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 14 +- .../UmbracoBuilder.CoreServices.cs | 13 +- .../Migrations/Upgrade/UmbracoPlan.cs | 251 +--- .../Upgrade/V_8_0_0/AddContentNuTable.cs | 23 - .../V_8_0_0/AddContentTypeIsElementColumn.cs | 1 + .../Upgrade/V_8_0_0/AddLockObjects.cs | 1 + .../Upgrade/V_8_0_0/AddLogTableColumns.cs | 1 + .../V_8_0_0/AddPackagesSectionAccess.cs | 1 + .../Upgrade/V_8_0_0/AddTypedLabels.cs | 1 + .../Upgrade/V_8_0_0/AddVariationTables1A.cs | 1 + .../Upgrade/V_8_0_0/AddVariationTables2.cs | 1 + .../V_8_0_0/ContentVariationMigration.cs | 46 +- .../ConvertRelatedLinksToMultiUrlPicker.cs | 148 +- .../Upgrade/V_8_0_0/DataTypeMigration.cs | 95 +- .../ContentPickerPreValueMigrator.cs | 23 - .../DataTypes/DecimalPreValueMigrator.cs | 22 - .../DataTypes/DefaultPreValueMigrator.cs | 44 - .../DropDownFlexiblePreValueMigrator.cs | 30 - .../V_8_0_0/DataTypes/IPreValueMigrator.cs | 1 + .../DataTypes/ListViewPreValueMigrator.cs | 26 - .../MarkdownEditorPreValueMigrator.cs | 19 - .../DataTypes/MediaPickerPreValueMigrator.cs | 37 - .../NestedContentPreValueMigrator.cs | 39 - .../Upgrade/V_8_0_0/DataTypes/PreValueDto.cs | 1 + .../V_8_0_0/DataTypes/PreValueMigratorBase.cs | 1 + .../DataTypes/PreValueMigratorCollection.cs | 1 + .../PreValueMigratorCollectionBuilder.cs | 1 + .../DataTypes/RenamingPreValueMigrator.cs | 23 - .../DataTypes/RichTextPreValueMigrator.cs | 24 - .../UmbracoSliderPreValueMigrator.cs | 21 - .../DataTypes/ValueListPreValueMigrator.cs | 29 - .../DropDownPropertyEditorsMigration.cs | 1 + .../Upgrade/V_8_0_0/DropMigrationsTable.cs | 1 + .../Upgrade/V_8_0_0/DropPreValueTable.cs | 1 + .../Upgrade/V_8_0_0/DropTaskTables.cs | 1 + .../V_8_0_0/DropTemplateDesignColumn.cs | 1 + .../Upgrade/V_8_0_0/DropXmlTables.cs | 1 + .../Upgrade/V_8_0_0/FallbackLanguage.cs | 1 + .../V_8_0_0/FixLanguageIsoCodeLength.cs | 1 + .../Upgrade/V_8_0_0/LanguageColumns.cs | 1 + .../Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs | 1 + .../Upgrade/V_8_0_0/MakeTagsVariant.cs | 1 + .../MergeDateAndDateTimePropertyEditor.cs | 1 + .../V_8_0_0/Models/ContentTypeDto80.cs | 63 - .../V_8_0_0/Models/PropertyDataDto80.cs | 142 -- .../V_8_0_0/Models/PropertyTypeDto80.cs | 76 - .../V_8_0_0/PropertyEditorsMigration.cs | 1 + .../V_8_0_0/PropertyEditorsMigrationBase.cs | 1 + ...adioAndCheckboxPropertyEditorsMigration.cs | 1 + .../Upgrade/V_8_0_0/RefactorMacroColumns.cs | 1 + .../Upgrade/V_8_0_0/RefactorVariantsModel.cs | 1 + ...meLabelAndRichTextPropertyEditorAliases.cs | 1 + .../V_8_0_0/RenameMediaVersionTable.cs | 1 + .../V_8_0_0/RenameUmbracoDomainsTable.cs | 1 + .../Migrations/Upgrade/V_8_0_0/SuperZero.cs | 1 + .../V_8_0_0/TablesForScheduledPublishing.cs | 1 + .../Upgrade/V_8_0_0/TagsMigration.cs | 1 + .../Upgrade/V_8_0_0/TagsMigrationFix.cs | 1 + .../V_8_0_0/UpdateDefaultMandatoryLanguage.cs | 1 + .../V_8_0_0/UpdatePickerIntegerValuesToUdi.cs | 1 + .../Upgrade/V_8_0_0/UserForeignKeys.cs | 1 + .../Upgrade/V_8_0_0/VariantsMigration.cs | 1 + .../V_8_0_1/ChangeNuCacheJsonFormat.cs | 1 + .../AddPropertyTypeLabelOnTopColumn.cs | 1 + .../V_8_15_0/AddCmsContentNuByteColumn.cs | 1 + .../V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs | 1 + .../V_8_15_0/UpgradedIncludeIndexes.cs | 1 + .../V_8_17_0/AddPropertyTypeGroupColumns.cs | 1 + ...nvertTinyMceAndGridMediaUrlsToLocalLink.cs | 117 +- .../Upgrade/V_8_1_0/FixContentNuCascade.cs | 1 + .../V_8_1_0/RenameUserLoginDtoDateIndex.cs | 1 + .../Upgrade/V_8_6_0/AddMainDomLock.cs | 1 + .../Upgrade/V_8_6_0/AddNewRelationTypes.cs | 1 + ...AddPropertyTypeValidationMessageColumns.cs | 1 + .../V_8_6_0/MissingContentVersionsIndexes.cs | 1 + .../V_8_6_0/UpdateRelationTypeTable.cs | 1 + .../Upgrade/V_8_7_0/MissingDictionaryIndex.cs | 1 + .../V_8_9_0/ExternalLoginTableUserData.cs | 1 + .../V_9_0_0/AddPasswordConfigToMemberTable.cs | 1 + .../V_9_0_0/DictionaryTablesIndexes.cs | 1 + .../V_9_0_0/ExternalLoginTableIndexes.cs | 1 + .../V_9_0_0/ExternalLoginTableIndexesFixup.cs | 1 + .../V_9_0_0/ExternalLoginTokenTable.cs | 1 + .../Upgrade/V_9_0_0/MemberTableColumns.cs | 1 + .../MigrateLogViewerQueriesFromFileToDb.cs | 1 + .../Upgrade/V_9_0_0/UmbracoServerColumn.cs | 1 + .../AddContentVersionCleanupFeature.cs | 29 - .../AddDefaultForNotificationsToggle.cs | 1 + .../Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs | 31 - .../Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs | 1 + .../Upgrade/V_9_3_0/MovePackageXMLToDb.cs | 1 + .../UpdateExternalLoginToUseKeyInsteadOfId.cs | 1 + .../V_9_4_0/AddScheduledPublishingLock.cs | 15 - ...UpdateRelationTypesToHandleDependencies.cs | 31 - .../PropertyEditors/GridPropertyEditor.cs | 3 +- .../NestedContentPropertyEditor.cs | 5 +- .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - 139 files changed, 151 insertions(+), 8877 deletions(-) delete mode 100644 src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Core/Media/Exif/BitConverterEx.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifBitConverter.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifEnums.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifExtendedProperty.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifInterOperability.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifProperty.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifPropertyFactory.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifTag.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifTagFactory.cs delete mode 100644 src/Umbraco.Core/Media/Exif/IFD.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ImageFile.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ImageFileDirectory.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ImageFileDirectoryEntry.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ImageFileFormat.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JFIFEnums.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JFIFExtendedProperty.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JPEGFile.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JPEGMarker.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JPEGSection.cs delete mode 100644 src/Umbraco.Core/Media/Exif/MathEx.cs delete mode 100644 src/Umbraco.Core/Media/Exif/SvgFile.cs delete mode 100644 src/Umbraco.Core/Media/Exif/TIFFFile.cs delete mode 100644 src/Umbraco.Core/Media/Exif/TIFFHeader.cs delete mode 100644 src/Umbraco.Core/Media/Exif/TIFFStrip.cs delete mode 100644 src/Umbraco.Core/Media/Exif/Utility.cs delete mode 100644 src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentNuTable.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ContentPickerPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DecimalPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DropDownFlexiblePreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ListViewPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MarkdownEditorPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MediaPickerPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/NestedContentPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RenamingPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RichTextPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/UmbracoSliderPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ValueListPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/AddScheduledPublishingLock.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/UpdateRelationTypesToHandleDependencies.cs delete mode 100644 src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Web.Common/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Web.Website/CompatibilitySuppressions.xml delete mode 100644 tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml delete mode 100644 tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml diff --git a/.gitignore b/.gitignore index 210ba2ec87..81c60b6019 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,4 @@ preserve.belle /src/Umbraco.Web.UI/appsettings-schema.*.json /tests/Umbraco.Tests.Integration/appsettings-schema.json /tests/Umbraco.Tests.Integration/appsettings-schema.*.json +/src/Umbraco.Cms/appsettings-schema.json diff --git a/Directory.Build.props b/Directory.Build.props index 7b4f5c7794..dd6f8126b1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -29,9 +29,9 @@ + false true - - 10.2.1 + 11.0.0-rc1 true true diff --git a/src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml b/src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Core/CompatibilitySuppressions.xml b/src/Umbraco.Core/CompatibilitySuppressions.xml index 7baad05aa0..e34b117b9a 100644 --- a/src/Umbraco.Core/CompatibilitySuppressions.xml +++ b/src/Umbraco.Core/CompatibilitySuppressions.xml @@ -1,7 +1,45 @@  - PKV006 - net6.0 + CP0006 + M:Umbraco.Cms.Core.Deploy.IDataTypeConfigurationConnector.FromArtifact(Umbraco.Cms.Core.Models.IDataType,System.String,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IDataTypeConfigurationConnector.ToArtifact(Umbraco.Cms.Core.Models.IDataType,System.Collections.Generic.ICollection{Umbraco.Cms.Core.Deploy.ArtifactDependency},Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IServiceConnector.GetArtifact(System.Object,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IServiceConnector.GetArtifact(Umbraco.Cms.Core.Udi,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IValueConnector.FromArtifact(System.String,Umbraco.Cms.Core.Models.IPropertyType,System.Object,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IValueConnector.ToArtifact(System.Object,Umbraco.Cms.Core.Models.IPropertyType,System.Collections.Generic.ICollection{Umbraco.Cms.Core.Deploy.ArtifactDependency},Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true \ No newline at end of file diff --git a/src/Umbraco.Core/Media/Exif/BitConverterEx.cs b/src/Umbraco.Core/Media/Exif/BitConverterEx.cs deleted file mode 100644 index f6cc50f801..0000000000 --- a/src/Umbraco.Core/Media/Exif/BitConverterEx.cs +++ /dev/null @@ -1,346 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// An endian-aware converter for converting between base data types -/// and an array of bytes. -/// -internal class BitConverterEx -{ - #region Public Enums - - /// - /// Represents the byte order. - /// - public enum ByteOrder - { - LittleEndian = 1, - BigEndian = 2, - } - - #endregion - - #region Member Variables - - private readonly ByteOrder mFrom; - private readonly ByteOrder mTo; - - #endregion - - #region Constructors - - public BitConverterEx(ByteOrder from, ByteOrder to) - { - mFrom = from; - mTo = to; - } - - #endregion - - #region Properties - - /// - /// Indicates the byte order in which data is stored in this platform. - /// - public static ByteOrder SystemByteOrder => - BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian; - - #endregion - - #region Predefined Values - - /// - /// Returns a bit converter that converts between little-endian and system byte-order. - /// - public static BitConverterEx LittleEndian => new BitConverterEx(ByteOrder.LittleEndian, SystemByteOrder); - - /// - /// Returns a bit converter that converts between big-endian and system byte-order. - /// - public static BitConverterEx BigEndian => new BitConverterEx(ByteOrder.BigEndian, SystemByteOrder); - - /// - /// Returns a bit converter that does not do any byte-order conversion. - /// - public static BitConverterEx SystemEndian => new BitConverterEx(SystemByteOrder, SystemByteOrder); - - #endregion - - #region Static Methods - - /// - /// Converts the given array of bytes to a Unicode character. - /// - public static char ToChar(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 2, from, to); - return BitConverter.ToChar(data, 0); - } - - /// - /// Converts the given array of bytes to a 16-bit unsigned integer. - /// - public static ushort ToUInt16(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 2, from, to); - return BitConverter.ToUInt16(data, 0); - } - - /// - /// Converts the given array of bytes to a 32-bit unsigned integer. - /// - public static uint ToUInt32(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 4, from, to); - return BitConverter.ToUInt32(data, 0); - } - - /// - /// Converts the given array of bytes to a 64-bit unsigned integer. - /// - public static ulong ToUInt64(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 8, from, to); - return BitConverter.ToUInt64(data, 0); - } - - /// - /// Converts the given array of bytes to a 16-bit signed integer. - /// - public static short ToInt16(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 2, from, to); - return BitConverter.ToInt16(data, 0); - } - - /// - /// Converts the given array of bytes to a 32-bit signed integer. - /// - public static int ToInt32(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 4, from, to); - return BitConverter.ToInt32(data, 0); - } - - /// - /// Converts the given array of bytes to a 64-bit signed integer. - /// - public static long ToInt64(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 8, from, to); - return BitConverter.ToInt64(data, 0); - } - - /// - /// Converts the given array of bytes to a single precision floating number. - /// - public static float ToSingle(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 4, from, to); - return BitConverter.ToSingle(data, 0); - } - - /// - /// Converts the given array of bytes to a double precision floating number. - /// - public static double ToDouble(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 8, from, to); - return BitConverter.ToDouble(data, 0); - } - - /// - /// Converts the given 16-bit unsigned integer to an array of bytes. - /// - public static byte[] GetBytes(ushort value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 32-bit unsigned integer to an array of bytes. - /// - public static byte[] GetBytes(uint value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 64-bit unsigned integer to an array of bytes. - /// - public static byte[] GetBytes(ulong value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 16-bit signed integer to an array of bytes. - /// - public static byte[] GetBytes(short value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 32-bit signed integer to an array of bytes. - /// - public static byte[] GetBytes(int value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 64-bit signed integer to an array of bytes. - /// - public static byte[] GetBytes(long value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given single precision floating-point number to an array of bytes. - /// - public static byte[] GetBytes(float value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given double precision floating-point number to an array of bytes. - /// - public static byte[] GetBytes(double value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - #endregion - - #region Instance Methods - - /// - /// Converts the given array of bytes to a 16-bit unsigned integer. - /// - public char ToChar(byte[] value, long startIndex) => ToChar(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 16-bit unsigned integer. - /// - public ushort ToUInt16(byte[] value, long startIndex) => ToUInt16(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 32-bit unsigned integer. - /// - public uint ToUInt32(byte[] value, long startIndex) => ToUInt32(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 64-bit unsigned integer. - /// - public ulong ToUInt64(byte[] value, long startIndex) => ToUInt64(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 16-bit signed integer. - /// - public short ToInt16(byte[] value, long startIndex) => ToInt16(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 32-bit signed integer. - /// - public int ToInt32(byte[] value, long startIndex) => ToInt32(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 64-bit signed integer. - /// - public long ToInt64(byte[] value, long startIndex) => ToInt64(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a single precision floating number. - /// - public float ToSingle(byte[] value, long startIndex) => ToSingle(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a double precision floating number. - /// - public double ToDouble(byte[] value, long startIndex) => ToDouble(value, startIndex, mFrom, mTo); - - /// - /// Converts the given 16-bit unsigned integer to an array of bytes. - /// - public byte[] GetBytes(ushort value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 32-bit unsigned integer to an array of bytes. - /// - public byte[] GetBytes(uint value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 64-bit unsigned integer to an array of bytes. - /// - public byte[] GetBytes(ulong value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 16-bit signed integer to an array of bytes. - /// - public byte[] GetBytes(short value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 32-bit signed integer to an array of bytes. - /// - public byte[] GetBytes(int value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 64-bit signed integer to an array of bytes. - /// - public byte[] GetBytes(long value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given single precision floating-point number to an array of bytes. - /// - public byte[] GetBytes(float value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given double precision floating-point number to an array of bytes. - /// - public byte[] GetBytes(double value) => GetBytes(value, mFrom, mTo); - - #endregion - - #region Private Helpers - - /// - /// Reverse the array of bytes as needed. - /// - private static byte[] CheckData(byte[] value, long startIndex, long length, ByteOrder from, ByteOrder to) - { - var data = new byte[length]; - Array.Copy(value, startIndex, data, 0, length); - if (from != to) - { - Array.Reverse(data); - } - - return data; - } - - /// - /// Reverse the array of bytes as needed. - /// - private static byte[] CheckData(byte[] value, ByteOrder from, ByteOrder to) => - CheckData(value, 0, value.Length, from, to); - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ExifBitConverter.cs b/src/Umbraco.Core/Media/Exif/ExifBitConverter.cs deleted file mode 100644 index e8dc7c4eb9..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifBitConverter.cs +++ /dev/null @@ -1,392 +0,0 @@ -using System.Globalization; -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Converts between exif data types and array of bytes. -/// -internal class ExifBitConverter : BitConverterEx -{ - #region Constructors - - public ExifBitConverter(ByteOrder from, ByteOrder to) - : base(from, to) - { - } - - #endregion - - #region Static Methods - - /// - /// Returns an ASCII string converted from the given byte array. - /// - public static string ToAscii(byte[] data, bool endatfirstnull, Encoding encoding) - { - var len = data.Length; - if (endatfirstnull) - { - len = Array.IndexOf(data, (byte)0); - if (len == -1) - { - len = data.Length; - } - } - - return encoding.GetString(data, 0, len); - } - - /// - /// Returns an ASCII string converted from the given byte array. - /// - public static string ToAscii(byte[] data, Encoding encoding) => ToAscii(data, true, encoding); - - /// - /// Returns a string converted from the given byte array. - /// from the numeric value of each byte. - /// - public static string ToString(byte[] data) - { - var sb = new StringBuilder(); - foreach (var b in data) - { - sb.Append(b); - } - - return sb.ToString(); - } - - /// - /// Returns a DateTime object converted from the given byte array. - /// - public static DateTime ToDateTime(byte[] data, bool hastime) - { - var str = ToAscii(data, Encoding.ASCII); - var parts = str.Split(':', ' '); - try - { - if (hastime && parts.Length == 6) - { - // yyyy:MM:dd HH:mm:ss - // This is the expected format though some cameras - // can use single digits. See Issue 21. - return new DateTime( - int.Parse(parts[0], CultureInfo.InvariantCulture), - int.Parse(parts[1], CultureInfo.InvariantCulture), - int.Parse(parts[2], CultureInfo.InvariantCulture), - int.Parse(parts[3], CultureInfo.InvariantCulture), - int.Parse(parts[4], CultureInfo.InvariantCulture), - int.Parse(parts[5], CultureInfo.InvariantCulture)); - } - - if (!hastime && parts.Length == 3) - { - // yyyy:MM:dd - return new DateTime( - int.Parse(parts[0], CultureInfo.InvariantCulture), - int.Parse(parts[1], CultureInfo.InvariantCulture), - int.Parse(parts[2], CultureInfo.InvariantCulture)); - } - - return DateTime.MinValue; - } - catch (ArgumentOutOfRangeException) - { - return DateTime.MinValue; - } - catch (ArgumentException) - { - return DateTime.MinValue; - } - } - - /// - /// Returns a DateTime object converted from the given byte array. - /// - public static DateTime ToDateTime(byte[] data) => ToDateTime(data, true); - - /// - /// Returns an unsigned rational number converted from the first - /// eight bytes of the given byte array. The first four bytes are - /// assumed to be the numerator and the next four bytes are the - /// denominator. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static MathEx.UFraction32 ToURational(byte[] data, ByteOrder frombyteorder) - { - var num = new byte[4]; - var den = new byte[4]; - Array.Copy(data, 0, num, 0, 4); - Array.Copy(data, 4, den, 0, 4); - return new MathEx.UFraction32( - ToUInt32(num, 0, frombyteorder, SystemByteOrder), - ToUInt32(den, 0, frombyteorder, SystemByteOrder)); - } - - /// - /// Returns a signed rational number converted from the first - /// eight bytes of the given byte array. The first four bytes are - /// assumed to be the numerator and the next four bytes are the - /// denominator. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static MathEx.Fraction32 ToSRational(byte[] data, ByteOrder frombyteorder) - { - var num = new byte[4]; - var den = new byte[4]; - Array.Copy(data, 0, num, 0, 4); - Array.Copy(data, 4, den, 0, 4); - return new MathEx.Fraction32( - ToInt32(num, 0, frombyteorder, SystemByteOrder), - ToInt32(den, 0, frombyteorder, SystemByteOrder)); - } - - /// - /// Returns an array of 16-bit unsigned integers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static ushort[] ToUShortArray(byte[] data, int count, ByteOrder frombyteorder) - { - var numbers = new ushort[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[2]; - Array.Copy(data, i * 2, num, 0, 2); - numbers[i] = ToUInt16(num, 0, frombyteorder, SystemByteOrder); - } - - return numbers; - } - - /// - /// Returns an array of 32-bit unsigned integers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static uint[] ToUIntArray(byte[] data, int count, ByteOrder frombyteorder) - { - var numbers = new uint[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[4]; - Array.Copy(data, i * 4, num, 0, 4); - numbers[i] = ToUInt32(num, 0, frombyteorder, SystemByteOrder); - } - - return numbers; - } - - /// - /// Returns an array of 32-bit signed integers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static int[] ToSIntArray(byte[] data, int count, ByteOrder byteorder) - { - var numbers = new int[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[4]; - Array.Copy(data, i * 4, num, 0, 4); - numbers[i] = ToInt32(num, 0, byteorder, SystemByteOrder); - } - - return numbers; - } - - /// - /// Returns an array of unsigned rational numbers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static MathEx.UFraction32[] ToURationalArray(byte[] data, int count, ByteOrder frombyteorder) - { - var numbers = new MathEx.UFraction32[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[4]; - var den = new byte[4]; - Array.Copy(data, i * 8, num, 0, 4); - Array.Copy(data, (i * 8) + 4, den, 0, 4); - numbers[i].Set( - ToUInt32(num, 0, frombyteorder, SystemByteOrder), - ToUInt32(den, 0, frombyteorder, SystemByteOrder)); - } - - return numbers; - } - - /// - /// Returns an array of signed rational numbers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static MathEx.Fraction32[] ToSRationalArray(byte[] data, int count, ByteOrder frombyteorder) - { - var numbers = new MathEx.Fraction32[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[4]; - var den = new byte[4]; - Array.Copy(data, i * 8, num, 0, 4); - Array.Copy(data, (i * 8) + 4, den, 0, 4); - numbers[i].Set( - ToInt32(num, 0, frombyteorder, SystemByteOrder), - ToInt32(den, 0, frombyteorder, SystemByteOrder)); - } - - return numbers; - } - - /// - /// Converts the given ascii string to an array of bytes optionally adding a null terminator. - /// - public static byte[] GetBytes(string value, bool addnull, Encoding encoding) - { - if (addnull) - { - value += '\0'; - } - - return encoding.GetBytes(value); - } - - /// - /// Converts the given ascii string to an array of bytes without adding a null terminator. - /// - public static byte[] GetBytes(string value, Encoding encoding) => GetBytes(value, false, encoding); - - /// - /// Converts the given datetime to an array of bytes with a null terminator. - /// - public static byte[] GetBytes(DateTime value, bool hastime) - { - var str = string.Empty; - if (hastime) - { - str = value.ToString("yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture); - } - else - { - str = value.ToString("yyyy:MM:dd", CultureInfo.InvariantCulture); - } - - return GetBytes(str, true, Encoding.ASCII); - } - - /// - /// Converts the given unsigned rational number to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(MathEx.UFraction32 value, ByteOrder tobyteorder) - { - var num = GetBytes(value.Numerator, SystemByteOrder, tobyteorder); - var den = GetBytes(value.Denominator, SystemByteOrder, tobyteorder); - var data = new byte[8]; - Array.Copy(num, 0, data, 0, 4); - Array.Copy(den, 0, data, 4, 4); - return data; - } - - /// - /// Converts the given signed rational number to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(MathEx.Fraction32 value, ByteOrder tobyteorder) - { - var num = GetBytes(value.Numerator, SystemByteOrder, tobyteorder); - var den = GetBytes(value.Denominator, SystemByteOrder, tobyteorder); - var data = new byte[8]; - Array.Copy(num, 0, data, 0, 4); - Array.Copy(den, 0, data, 4, 4); - return data; - } - - /// - /// Converts the given array of 16-bit unsigned integers to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(ushort[] value, ByteOrder tobyteorder) - { - var data = new byte[2 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i], SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 2, 2); - } - - return data; - } - - /// - /// Converts the given array of 32-bit unsigned integers to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(uint[] value, ByteOrder tobyteorder) - { - var data = new byte[4 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i], SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 4, 4); - } - - return data; - } - - /// - /// Converts the given array of 32-bit signed integers to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(int[] value, ByteOrder tobyteorder) - { - var data = new byte[4 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i], SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 4, 4); - } - - return data; - } - - /// - /// Converts the given array of unsigned rationals to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(MathEx.UFraction32[] value, ByteOrder tobyteorder) - { - var data = new byte[8 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i].Numerator, SystemByteOrder, tobyteorder); - var den = GetBytes(value[i].Denominator, SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 8, 4); - Array.Copy(den, 0, data, (i * 8) + 4, 4); - } - - return data; - } - - /// - /// Converts the given array of signed rationals to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(MathEx.Fraction32[] value, ByteOrder tobyteorder) - { - var data = new byte[8 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i].Numerator, SystemByteOrder, tobyteorder); - var den = GetBytes(value[i].Denominator, SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 8, 4); - Array.Copy(den, 0, data, (i * 8) + 4, 4); - } - - return data; - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ExifEnums.cs b/src/Umbraco.Core/Media/Exif/ExifEnums.cs deleted file mode 100644 index 5e27b8dd24..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifEnums.cs +++ /dev/null @@ -1,297 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -internal enum Compression : ushort -{ - Uncompressed = 1, - CCITT1D = 2, - Group3Fax = 3, - Group4Fax = 4, - LZW = 5, - JPEG = 6, - PackBits = 32773, -} - -internal enum PhotometricInterpretation : ushort -{ - WhiteIsZero = 0, - BlackIsZero = 1, - RGB = 2, - RGBPalette = 3, - TransparencyMask = 4, - CMYK = 5, - YCbCr = 6, - CIELab = 8, -} - -internal enum Orientation : ushort -{ - Normal = 1, - MirroredVertically = 2, - Rotated180 = 3, - MirroredHorizontally = 4, - RotatedLeftAndMirroredVertically = 5, - RotatedRight = 6, - RotatedLeft = 7, - RotatedRightAndMirroredVertically = 8, -} - -internal enum PlanarConfiguration : ushort -{ - ChunkyFormat = 1, - PlanarFormat = 2, -} - -internal enum YCbCrPositioning : ushort -{ - Centered = 1, - CoSited = 2, -} - -internal enum ResolutionUnit : ushort -{ - Inches = 2, - Centimeters = 3, -} - -internal enum ColorSpace : ushort -{ - SRGB = 1, - Uncalibrated = 0xfff, -} - -internal enum ExposureProgram : ushort -{ - NotDefined = 0, - Manual = 1, - Normal = 2, - AperturePriority = 3, - ShutterPriority = 4, - - /// - /// Biased toward depth of field. - /// - Creative = 5, - - /// - /// Biased toward fast shutter speed. - /// - Action = 6, - - /// - /// For closeup photos with the background out of focus. - /// - Portrait = 7, - - /// - /// For landscape photos with the background in focus. - /// - Landscape = 8, -} - -internal enum MeteringMode : ushort -{ - Unknown = 0, - Average = 1, - CenterWeightedAverage = 2, - Spot = 3, - MultiSpot = 4, - Pattern = 5, - Partial = 6, - Other = 255, -} - -internal enum LightSource : ushort -{ - Unknown = 0, - Daylight = 1, - Fluorescent = 2, - Tungsten = 3, - Flash = 4, - FineWeather = 9, - CloudyWeather = 10, - Shade = 11, - - /// - /// D 5700 – 7100K - /// - DaylightFluorescent = 12, - - /// - /// N 4600 – 5400K - /// - DayWhiteFluorescent = 13, - - /// - /// W 3900 – 4500K - /// - CoolWhiteFluorescent = 14, - - /// - /// WW 3200 – 3700K - /// - WhiteFluorescent = 15, - StandardLightA = 17, - StandardLightB = 18, - StandardLightC = 19, - D55 = 20, - D65 = 21, - D75 = 22, - D50 = 23, - ISOStudioTungsten = 24, - OtherLightSource = 255, -} - -[Flags] -internal enum Flash : ushort -{ - FlashDidNotFire = 0, - StrobeReturnLightNotDetected = 4, - StrobeReturnLightDetected = 2, - FlashFired = 1, - CompulsoryFlashMode = 8, - AutoMode = 16, - NoFlashFunction = 32, - RedEyeReductionMode = 64, -} - -internal enum SensingMethod : ushort -{ - NotDefined = 1, - OneChipColorAreaSensor = 2, - TwoChipColorAreaSensor = 3, - ThreeChipColorAreaSensor = 4, - ColorSequentialAreaSensor = 5, - TriLinearSensor = 7, - ColorSequentialLinearSensor = 8, -} - -internal enum FileSource : byte // UNDEFINED -{ - DSC = 3, -} - -internal enum SceneType : byte // UNDEFINED -{ - DirectlyPhotographedImage = 1, -} - -internal enum CustomRendered : ushort -{ - NormalProcess = 0, - CustomProcess = 1, -} - -internal enum ExposureMode : ushort -{ - Auto = 0, - Manual = 1, - AutoBracket = 2, -} - -internal enum WhiteBalance : ushort -{ - Auto = 0, - Manual = 1, -} - -internal enum SceneCaptureType : ushort -{ - Standard = 0, - Landscape = 1, - Portrait = 2, - NightScene = 3, -} - -internal enum GainControl : ushort -{ - None = 0, - LowGainUp = 1, - HighGainUp = 2, - LowGainDown = 3, - HighGainDown = 4, -} - -internal enum Contrast : ushort -{ - Normal = 0, - Soft = 1, - Hard = 2, -} - -internal enum Saturation : ushort -{ - Normal = 0, - Low = 1, - High = 2, -} - -internal enum Sharpness : ushort -{ - Normal = 0, - Soft = 1, - Hard = 2, -} - -internal enum SubjectDistanceRange : ushort -{ - Unknown = 0, - Macro = 1, - CloseView = 2, - DistantView = 3, -} - -internal enum GPSLatitudeRef : byte // ASCII -{ - North = 78, // 'N' - South = 83, // 'S' -} - -internal enum GPSLongitudeRef : byte // ASCII -{ - West = 87, // 'W' - East = 69, // 'E' -} - -internal enum GPSAltitudeRef : byte -{ - AboveSeaLevel = 0, - BelowSeaLevel = 1, -} - -internal enum GPSStatus : byte // ASCII -{ - MeasurementInProgress = 65, // 'A' - MeasurementInteroperability = 86, // 'V' -} - -internal enum GPSMeasureMode : byte // ASCII -{ - TwoDimensional = 50, // '2' - ThreeDimensional = 51, // '3' -} - -internal enum GPSSpeedRef : byte // ASCII -{ - KilometersPerHour = 75, // 'K' - MilesPerHour = 77, // 'M' - Knots = 78, // 'N' -} - -internal enum GPSDirectionRef : byte // ASCII -{ - TrueDirection = 84, // 'T' - MagneticDirection = 77, // 'M' -} - -internal enum GPSDistanceRef : byte // ASCII -{ - Kilometers = 75, // 'K' - Miles = 77, // 'M' - Knots = 78, // 'N' -} - -internal enum GPSDifferential : ushort -{ - MeasurementWithoutDifferentialCorrection = 0, - DifferentialCorrectionApplied = 1, -} diff --git a/src/Umbraco.Core/Media/Exif/ExifExceptions.cs b/src/Umbraco.Core/Media/Exif/ExifExceptions.cs index bd4426e15a..9f78bd9e15 100644 --- a/src/Umbraco.Core/Media/Exif/ExifExceptions.cs +++ b/src/Umbraco.Core/Media/Exif/ExifExceptions.cs @@ -7,6 +7,7 @@ namespace Umbraco.Cms.Core.Media.Exif; ///
/// [Serializable] +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class NotValidExifFileException : Exception { /// diff --git a/src/Umbraco.Core/Media/Exif/ExifExtendedProperty.cs b/src/Umbraco.Core/Media/Exif/ExifExtendedProperty.cs deleted file mode 100644 index ffa31f0cc1..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifExtendedProperty.cs +++ /dev/null @@ -1,487 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents an enumerated value. -/// -internal class ExifEnumProperty : ExifProperty - where T : notnull -{ - protected bool mIsBitField; - protected T mValue; - - public ExifEnumProperty(ExifTag tag, T value, bool isbitfield) - : base(tag) - { - mValue = value; - mIsBitField = isbitfield; - } - - public ExifEnumProperty(ExifTag tag, T value) - : this(tag, value, false) - { - } - - public new T Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (T)value; - } - - public bool IsBitField => mIsBitField; - - public override ExifInterOperability Interoperability - { - get - { - var tagid = ExifTagFactory.GetTagID(mTag); - - Type type = typeof(T); - Type basetype = Enum.GetUnderlyingType(type); - - if (type == typeof(FileSource) || type == typeof(SceneType)) - { - // UNDEFINED - return new ExifInterOperability(tagid, 7, 1, new[] { (byte)(object)mValue }); - } - - if (type == typeof(GPSLatitudeRef) || type == typeof(GPSLongitudeRef) || - type == typeof(GPSStatus) || type == typeof(GPSMeasureMode) || - type == typeof(GPSSpeedRef) || type == typeof(GPSDirectionRef) || - type == typeof(GPSDistanceRef)) - { - // ASCII - return new ExifInterOperability(tagid, 2, 2, new byte[] { (byte)(object)mValue, 0 }); - } - - if (basetype == typeof(byte)) - { - // BYTE - return new ExifInterOperability(tagid, 1, 1, new[] { (byte)(object)mValue }); - } - - if (basetype == typeof(ushort)) - { - // SHORT - return new ExifInterOperability( - tagid, - 3, - 1, - BitConverterEx.GetBytes((ushort)(object)mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder)); - } - - throw new InvalidOperationException( - $"An invalid enum type ({basetype.FullName}) was provided for type {type.FullName}"); - } - } - - public static implicit operator T(ExifEnumProperty obj) => obj.mValue; - - public override string? ToString() => mValue.ToString(); -} - -/// -/// Represents an ASCII string. (EXIF Specification: UNDEFINED) Used for the UserComment field. -/// -internal class ExifEncodedString : ExifProperty -{ - protected string mValue; - - public ExifEncodedString(ExifTag tag, string value, Encoding encoding) - : base(tag) - { - mValue = value; - Encoding = encoding; - } - - public new string Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (string)value; - } - - public Encoding Encoding { get; set; } - - public override ExifInterOperability Interoperability - { - get - { - var enc = string.Empty; - if (Encoding == null) - { - enc = "\0\0\0\0\0\0\0\0"; - } - else if (Encoding.EncodingName == "US-ASCII") - { - enc = "ASCII\0\0\0"; - } - else if (Encoding.EncodingName == "Japanese (JIS 0208-1990 and 0212-1990)") - { - enc = "JIS\0\0\0\0\0"; - } - else if (Encoding.EncodingName == "Unicode") - { - enc = "Unicode\0"; - } - else - { - enc = "\0\0\0\0\0\0\0\0"; - } - - var benc = Encoding.ASCII.GetBytes(enc); - var bstr = Encoding == null ? Encoding.ASCII.GetBytes(mValue) : Encoding.GetBytes(mValue); - var data = new byte[benc.Length + bstr.Length]; - Array.Copy(benc, 0, data, 0, benc.Length); - Array.Copy(bstr, 0, data, benc.Length, bstr.Length); - - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, (uint)data.Length, data); - } - } - - public static implicit operator string(ExifEncodedString obj) => obj.mValue; - - public override string ToString() => mValue; -} - -/// -/// Represents an ASCII string formatted as DateTime. (EXIF Specification: ASCII) Used for the date time fields. -/// -internal class ExifDateTime : ExifProperty -{ - protected DateTime mValue; - - public ExifDateTime(ExifTag tag, DateTime value) - : base(tag) => - mValue = value; - - public new DateTime Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (DateTime)value; - } - - public override ExifInterOperability Interoperability => - new(ExifTagFactory.GetTagID(mTag), 2, 20, ExifBitConverter.GetBytes(mValue, true)); - - public static implicit operator DateTime(ExifDateTime obj) => obj.mValue; - - public override string ToString() => mValue.ToString("yyyy.MM.dd HH:mm:ss"); -} - -/// -/// Represents the exif version as a 4 byte ASCII string. (EXIF Specification: UNDEFINED) -/// Used for the ExifVersion, FlashpixVersion, InteroperabilityVersion and GPSVersionID fields. -/// -internal class ExifVersion : ExifProperty -{ - protected string mValue; - - public ExifVersion(ExifTag tag, string value) - : base(tag) - { - if (value.Length > 4) - { - mValue = value[..4]; - } - else if (value.Length < 4) - { - mValue = value + new string(' ', 4 - value.Length); - } - else - { - mValue = value; - } - } - - public new string Value - { - get => mValue; - set => mValue = value[..4]; - } - - protected override object _Value - { - get => Value; - set => Value = (string)value; - } - - public override ExifInterOperability Interoperability - { - get - { - if (mTag == ExifTag.ExifVersion || mTag == ExifTag.FlashpixVersion || - mTag == ExifTag.InteroperabilityVersion) - { - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, 4, Encoding.ASCII.GetBytes(mValue)); - } - - var data = new byte[4]; - for (var i = 0; i < 4; i++) - { - data[i] = byte.Parse(mValue[0].ToString()); - } - - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, 4, data); - } - } - - public override string ToString() => mValue; -} - -/// -/// Represents the location and area of the subject (EXIF Specification: 2xSHORT) -/// The coordinate values, width, and height are expressed in relation to the -/// upper left as origin, prior to rotation processing as per the Rotation tag. -/// -internal class ExifPointSubjectArea : ExifUShortArray -{ - public ExifPointSubjectArea(ExifTag tag, ushort[] value) - : base(tag, value) - { - } - - public ExifPointSubjectArea(ExifTag tag, ushort x, ushort y) - : base(tag, new[] { x, y }) - { - } - - public ushort X - { - get => mValue[0]; - set => mValue[0] = value; - } - - protected new ushort[] Value - { - get => mValue; - set => mValue = value; - } - - public ushort Y - { - get => mValue[1]; - set => mValue[1] = value; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.AppendFormat("({0:d}, {1:d})", mValue[0], mValue[1]); - return sb.ToString(); - } -} - -/// -/// Represents the location and area of the subject (EXIF Specification: 3xSHORT) -/// The coordinate values, width, and height are expressed in relation to the -/// upper left as origin, prior to rotation processing as per the Rotation tag. -/// -internal class ExifCircularSubjectArea : ExifPointSubjectArea -{ - public ExifCircularSubjectArea(ExifTag tag, ushort[] value) - : base(tag, value) - { - } - - public ExifCircularSubjectArea(ExifTag tag, ushort x, ushort y, ushort d) - : base(tag, new[] { x, y, d }) - { - } - - public ushort Diamater - { - get => mValue[2]; - set => mValue[2] = value; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.AppendFormat("({0:d}, {1:d}) {2:d}", mValue[0], mValue[1], mValue[2]); - return sb.ToString(); - } -} - -/// -/// Represents the location and area of the subject (EXIF Specification: 4xSHORT) -/// The coordinate values, width, and height are expressed in relation to the -/// upper left as origin, prior to rotation processing as per the Rotation tag. -/// -internal class ExifRectangularSubjectArea : ExifPointSubjectArea -{ - public ExifRectangularSubjectArea(ExifTag tag, ushort[] value) - : base(tag, value) - { - } - - public ExifRectangularSubjectArea(ExifTag tag, ushort x, ushort y, ushort w, ushort h) - : base(tag, new[] { x, y, w, h }) - { - } - - public ushort Width - { - get => mValue[2]; - set => mValue[2] = value; - } - - public ushort Height - { - get => mValue[3]; - set => mValue[3] = value; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.AppendFormat("({0:d}, {1:d}) ({2:d} x {3:d})", mValue[0], mValue[1], mValue[2], mValue[3]); - return sb.ToString(); - } -} - -/// -/// Represents GPS latitudes and longitudes (EXIF Specification: 3xRATIONAL) -/// -internal class GPSLatitudeLongitude : ExifURationalArray -{ - public GPSLatitudeLongitude(ExifTag tag, MathEx.UFraction32[] value) - : base(tag, value) - { - } - - public GPSLatitudeLongitude(ExifTag tag, float d, float m, float s) - : base(tag, new[] { new(d), new MathEx.UFraction32(m), new MathEx.UFraction32(s) }) - { - } - - public MathEx.UFraction32 Degrees - { - get => mValue[0]; - set => mValue[0] = value; - } - - protected new MathEx.UFraction32[] Value - { - get => mValue; - set => mValue = value; - } - - public MathEx.UFraction32 Minutes - { - get => mValue[1]; - set => mValue[1] = value; - } - - public MathEx.UFraction32 Seconds - { - get => mValue[2]; - set => mValue[2] = value; - } - - public static explicit operator float(GPSLatitudeLongitude obj) => obj.ToFloat(); - - public float ToFloat() => (float)Degrees + ((float)Minutes / 60.0f) + ((float)Seconds / 3600.0f); - - public override string ToString() => - string.Format("{0:F2}°{1:F2}'{2:F2}\"", (float)Degrees, (float)Minutes, (float)Seconds); -} - -/// -/// Represents a GPS time stamp as UTC (EXIF Specification: 3xRATIONAL) -/// -internal class GPSTimeStamp : ExifURationalArray -{ - public GPSTimeStamp(ExifTag tag, MathEx.UFraction32[] value) - : base(tag, value) - { - } - - public GPSTimeStamp(ExifTag tag, float h, float m, float s) - : base(tag, new[] { new(h), new MathEx.UFraction32(m), new MathEx.UFraction32(s) }) - { - } - - public MathEx.UFraction32 Hour - { - get => mValue[0]; - set => mValue[0] = value; - } - - protected new MathEx.UFraction32[] Value - { - get => mValue; - set => mValue = value; - } - - public MathEx.UFraction32 Minute - { - get => mValue[1]; - set => mValue[1] = value; - } - - public MathEx.UFraction32 Second - { - get => mValue[2]; - set => mValue[2] = value; - } - - public override string ToString() => - string.Format("{0:F2}:{1:F2}:{2:F2}\"", (float)Hour, (float)Minute, (float)Second); -} - -/// -/// Represents an ASCII string. (EXIF Specification: BYTE) -/// Used by Windows XP. -/// -internal class WindowsByteString : ExifProperty -{ - protected string mValue; - - public WindowsByteString(ExifTag tag, string value) - : base(tag) => - mValue = value; - - public new string Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (string)value; - } - - public override ExifInterOperability Interoperability - { - get - { - var data = Encoding.Unicode.GetBytes(mValue); - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)data.Length, data); - } - } - - public static implicit operator string(WindowsByteString obj) => obj.mValue; - - public override string ToString() => mValue; -} diff --git a/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs b/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs deleted file mode 100644 index a07ade9963..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.ComponentModel; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Provides a custom type descriptor for an ExifFile instance. -/// -internal sealed class ExifFileTypeDescriptionProvider : TypeDescriptionProvider -{ - public ExifFileTypeDescriptionProvider() - : this(TypeDescriptor.GetProvider(typeof(ImageFile))) - { - } - - public ExifFileTypeDescriptionProvider(TypeDescriptionProvider parent) - : base(parent) - { - } - - /// - /// Gets a custom type descriptor for the given type and object. - /// - /// The type of object for which to retrieve the type descriptor. - /// - /// An instance of the type. Can be null if no instance was passed to the - /// . - /// - /// - /// An that can provide metadata for the type. - /// - public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object? instance) => - new ExifFileTypeDescriptor(base.GetTypeDescriptor(objectType, instance), instance); -} - -/// -/// Expands ExifProperty objects contained in an ExifFile as separate properties. -/// -internal sealed class ExifFileTypeDescriptor : CustomTypeDescriptor -{ - private readonly ImageFile? owner; - - public ExifFileTypeDescriptor(ICustomTypeDescriptor? parent, object? instance) - : base(parent) => - owner = (ImageFile?)instance; - - public override PropertyDescriptorCollection GetProperties(Attribute[]? attributes) => GetProperties(); - - /// - /// Returns a collection of property descriptors for the object represented by this type descriptor. - /// - /// - /// A containing the property descriptions for the - /// object represented by this type descriptor. The default is - /// . - /// - public override PropertyDescriptorCollection GetProperties() - { - // Enumerate the original set of properties and create our new set with it - var properties = new List(); - - if (owner is not null) - { - foreach (ExifProperty prop in owner.Properties) - { - var pd = new ExifPropertyDescriptor(prop); - properties.Add(pd); - } - } - - // Finally return the list - return new PropertyDescriptorCollection(properties.ToArray(), true); - } -} - -internal sealed class ExifPropertyDescriptor : PropertyDescriptor -{ - private readonly ExifProperty linkedProperty; - private readonly object originalValue; - - public ExifPropertyDescriptor(ExifProperty property) - : base(property.Name, new Attribute[] { new BrowsableAttribute(true) }) - { - linkedProperty = property; - originalValue = property.Value; - } - - public override Type ComponentType => typeof(JPEGFile); - - public override bool IsReadOnly => false; - - public override Type PropertyType => linkedProperty.Value.GetType(); - - public override bool CanResetValue(object component) => true; - - public override object GetValue(object? component) => linkedProperty.Value; - - public override void ResetValue(object component) => linkedProperty.Value = originalValue; - - public override void SetValue(object? component, object? value) - { - if (value is not null) - { - linkedProperty.Value = value; - } - } - - public override bool ShouldSerializeValue(object component) => false; -} diff --git a/src/Umbraco.Core/Media/Exif/ExifInterOperability.cs b/src/Umbraco.Core/Media/Exif/ExifInterOperability.cs deleted file mode 100644 index d2d9f8be6a..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifInterOperability.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents interoperability data for an exif tag in the platform byte order. -/// -internal struct ExifInterOperability -{ - public ExifInterOperability(ushort tagid, ushort typeid, uint count, byte[] data) - { - TagID = tagid; - TypeID = typeid; - Count = count; - Data = data; - } - - /// - /// Gets the tag ID defined in the Exif standard. - /// - public ushort TagID { get; } - - /// - /// Gets the type code defined in the Exif standard. - /// - /// 1 = BYTE (byte) - /// 2 = ASCII (byte array) - /// 3 = SHORT (ushort) - /// 4 = LONG (uint) - /// 5 = RATIONAL (2 x uint: numerator, denominator) - /// 6 = BYTE (sbyte) - /// 7 = UNDEFINED (byte array) - /// 8 = SSHORT (short) - /// 9 = SLONG (int) - /// 10 = SRATIONAL (2 x int: numerator, denominator) - /// 11 = FLOAT (float) - /// 12 = DOUBLE (double) - /// - /// - public ushort TypeID { get; } - - /// - /// Gets the byte count or number of components. - /// - public uint Count { get; } - - /// - /// Gets the field value as an array of bytes. - /// - public byte[] Data { get; } - - /// - /// Returns the string representation of this instance. - /// - /// - public override string ToString() => string.Format("Tag: {0}, Type: {1}, Count: {2}, Data Length: {3}", TagID, TypeID, Count, Data.Length); -} diff --git a/src/Umbraco.Core/Media/Exif/ExifProperty.cs b/src/Umbraco.Core/Media/Exif/ExifProperty.cs deleted file mode 100644 index 6d742bb3ba..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifProperty.cs +++ /dev/null @@ -1,672 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the abstract base class for an Exif property. -/// -internal abstract class ExifProperty -{ - protected IFD mIFD; - protected string? mName; - protected ExifTag mTag; - - public ExifProperty(ExifTag tag) - { - mTag = tag; - mIFD = ExifTagFactory.GetTagIFD(tag); - } - - /// - /// Gets the Exif tag associated with this property. - /// - public ExifTag Tag => mTag; - - /// - /// Gets the IFD section containing this property. - /// - public IFD IFD => mIFD; - - /// - /// Gets or sets the name of this property. - /// - public string Name - { - get - { - if (string.IsNullOrEmpty(mName)) - { - return ExifTagFactory.GetTagName(mTag); - } - - return mName; - } - set => mName = value; - } - - /// - /// Gets or sets the value of this property. - /// - public object Value - { - get => _Value; - set => _Value = value; - } - - protected abstract object _Value { get; set; } - - /// - /// Gets interoperability data for this property. - /// - public abstract ExifInterOperability Interoperability { get; } -} - -/// -/// Represents an 8-bit unsigned integer. (EXIF Specification: BYTE) -/// -internal class ExifByte : ExifProperty -{ - protected byte mValue; - - public ExifByte(ExifTag tag, byte value) - : base(tag) => - mValue = value; - - public new byte Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = Convert.ToByte(value); - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, 1, new[] { mValue }); - - public static implicit operator byte(ExifByte obj) => obj.mValue; - - public override string ToString() => mValue.ToString(); -} - -/// -/// Represents an array of 8-bit unsigned integers. (EXIF Specification: BYTE with count > 1) -/// -internal class ExifByteArray : ExifProperty -{ - protected byte[] mValue; - - public ExifByteArray(ExifTag tag, byte[] value) - : base(tag) => - mValue = value; - - public new byte[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (byte[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.Length, mValue); - - public static implicit operator byte[](ExifByteArray obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents an ASCII string. (EXIF Specification: ASCII) -/// -internal class ExifAscii : ExifProperty -{ - protected string mValue; - - public ExifAscii(ExifTag tag, string value, Encoding encoding) - : base(tag) - { - mValue = value; - Encoding = encoding; - } - - public new string Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (string)value; - } - - public Encoding Encoding { get; } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 2, - (uint)mValue.Length + 1, - ExifBitConverter.GetBytes(mValue, true, Encoding)); - - public static implicit operator string(ExifAscii obj) => obj.mValue; - - public override string ToString() => mValue; -} - -/// -/// Represents a 16-bit unsigned integer. (EXIF Specification: SHORT) -/// -internal class ExifUShort : ExifProperty -{ - protected ushort mValue; - - public ExifUShort(ExifTag tag, ushort value) - : base(tag) => - mValue = value; - - public new ushort Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = Convert.ToUInt16(value); - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 3, - 1, - BitConverterEx.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder)); - - public static implicit operator ushort(ExifUShort obj) => obj.mValue; - - public override string ToString() => mValue.ToString(); -} - -/// -/// Represents an array of 16-bit unsigned integers. -/// (EXIF Specification: SHORT with count > 1) -/// -internal class ExifUShortArray : ExifProperty -{ - protected ushort[] mValue; - - public ExifUShortArray(ExifTag tag, ushort[] value) - : base(tag) => - mValue = value; - - public new ushort[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (ushort[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 3, - (uint)mValue.Length, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static implicit operator ushort[](ExifUShortArray obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a 32-bit unsigned integer. (EXIF Specification: LONG) -/// -internal class ExifUInt : ExifProperty -{ - protected uint mValue; - - public ExifUInt(ExifTag tag, uint value) - : base(tag) => - mValue = value; - - public new uint Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = Convert.ToUInt32(value); - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 4, - 1, - BitConverterEx.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder)); - - public static implicit operator uint(ExifUInt obj) => obj.mValue; - - public override string ToString() => mValue.ToString(); -} - -/// -/// Represents an array of 16-bit unsigned integers. -/// (EXIF Specification: LONG with count > 1) -/// -internal class ExifUIntArray : ExifProperty -{ - protected uint[] mValue; - - public ExifUIntArray(ExifTag tag, uint[] value) - : base(tag) => - mValue = value; - - public new uint[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (uint[])value; - } - - public override ExifInterOperability Interoperability => new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 3, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static implicit operator uint[](ExifUIntArray obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a rational number defined with a 32-bit unsigned numerator -/// and denominator. (EXIF Specification: RATIONAL) -/// -internal class ExifURational : ExifProperty -{ - protected MathEx.UFraction32 mValue; - - public ExifURational(ExifTag tag, uint numerator, uint denominator) - : base(tag) => - mValue = new MathEx.UFraction32(numerator, denominator); - - public ExifURational(ExifTag tag, MathEx.UFraction32 value) - : base(tag) => - mValue = value; - - public new MathEx.UFraction32 Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (MathEx.UFraction32)value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 5, - 1, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static explicit operator float(ExifURational obj) => (float)obj.mValue; - - public override string ToString() => mValue.ToString(); - - public float ToFloat() => (float)mValue; - - public uint[] ToArray() => new[] { mValue.Numerator, mValue.Denominator }; -} - -/// -/// Represents an array of unsigned rational numbers. -/// (EXIF Specification: RATIONAL with count > 1) -/// -internal class ExifURationalArray : ExifProperty -{ - protected MathEx.UFraction32[] mValue; - - public ExifURationalArray(ExifTag tag, MathEx.UFraction32[] value) - : base(tag) => - mValue = value; - - public new MathEx.UFraction32[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (MathEx.UFraction32[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 5, - (uint)mValue.Length, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static explicit operator float[](ExifURationalArray obj) - { - var result = new float[obj.mValue.Length]; - for (var i = 0; i < obj.mValue.Length; i++) - { - result[i] = (float)obj.mValue[i]; - } - - return result; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (MathEx.UFraction32 b in mValue) - { - sb.Append(b.ToString()); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a byte array that can take any value. (EXIF Specification: UNDEFINED) -/// -internal class ExifUndefined : ExifProperty -{ - protected byte[] mValue; - - public ExifUndefined(ExifTag tag, byte[] value) - : base(tag) => - mValue = value; - - public new byte[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (byte[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, (uint)mValue.Length, mValue); - - public static implicit operator byte[](ExifUndefined obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a 32-bit signed integer. (EXIF Specification: SLONG) -/// -internal class ExifSInt : ExifProperty -{ - protected int mValue; - - public ExifSInt(ExifTag tag, int value) - : base(tag) => - mValue = value; - - public new int Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = Convert.ToInt32(value); - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 9, - 1, - BitConverterEx.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder)); - - public static implicit operator int(ExifSInt obj) => obj.mValue; - - public override string ToString() => mValue.ToString(); -} - -/// -/// Represents an array of 32-bit signed integers. -/// (EXIF Specification: SLONG with count > 1) -/// -internal class ExifSIntArray : ExifProperty -{ - protected int[] mValue; - - public ExifSIntArray(ExifTag tag, int[] value) - : base(tag) => - mValue = value; - - public new int[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (int[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 9, - (uint)mValue.Length, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static implicit operator int[](ExifSIntArray obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a rational number defined with a 32-bit signed numerator -/// and denominator. (EXIF Specification: SRATIONAL) -/// -internal class ExifSRational : ExifProperty -{ - protected MathEx.Fraction32 mValue; - - public ExifSRational(ExifTag tag, int numerator, int denominator) - : base(tag) => - mValue = new MathEx.Fraction32(numerator, denominator); - - public ExifSRational(ExifTag tag, MathEx.Fraction32 value) - : base(tag) => - mValue = value; - - public new MathEx.Fraction32 Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (MathEx.Fraction32)value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 10, - 1, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static explicit operator float(ExifSRational obj) => (float)obj.mValue; - - public override string ToString() => mValue.ToString(); - - public float ToFloat() => (float)mValue; - - public int[] ToArray() => new[] { mValue.Numerator, mValue.Denominator }; -} - -/// -/// Represents an array of signed rational numbers. -/// (EXIF Specification: SRATIONAL with count > 1) -/// -internal class ExifSRationalArray : ExifProperty -{ - protected MathEx.Fraction32[] mValue; - - public ExifSRationalArray(ExifTag tag, MathEx.Fraction32[] value) - : base(tag) => - mValue = value; - - public new MathEx.Fraction32[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (MathEx.Fraction32[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 10, - (uint)mValue.Length, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static explicit operator float[](ExifSRationalArray obj) - { - var result = new float[obj.mValue.Length]; - for (var i = 0; i < obj.mValue.Length; i++) - { - result[i] = (float)obj.mValue[i]; - } - - return result; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (MathEx.Fraction32 b in mValue) - { - sb.Append(b.ToString()); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} diff --git a/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs b/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs deleted file mode 100644 index f77f0c89cd..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs +++ /dev/null @@ -1,493 +0,0 @@ -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a collection of objects. -/// -internal class ExifPropertyCollection : IDictionary -{ - #region Constructor - - internal ExifPropertyCollection(ImageFile parentFile) - { - parent = parentFile; - items = new Dictionary(); - } - - #endregion - - #region Member Variables - - private readonly ImageFile parent; - private readonly Dictionary items; - - #endregion - - #region Properties - - /// - /// Gets the number of elements contained in the collection. - /// - public int Count => items.Count; - - /// - /// Gets a collection containing the keys in this collection. - /// - public ICollection Keys => items.Keys; - - /// - /// Gets a collection containing the values in this collection. - /// - public ICollection Values => items.Values; - - /// - /// Gets or sets the with the specified key. - /// - public ExifProperty this[ExifTag key] - { - get => items[key]; - set - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, value); - } - } - - #endregion - - #region ExifProperty Collection Setters - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, byte value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifByte(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, string value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || - key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) - { - items.Add(key, new WindowsByteString(key, value)); - } - else - { - items.Add(key, new ExifAscii(key, value, parent.Encoding)); - } - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, ushort value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifUShort(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, int value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifSInt(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, uint value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifUInt(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, float value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifURational(key, new MathEx.UFraction32(value))); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, double value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifURational(key, new MathEx.UFraction32(value))); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, object value) - { - Type type = value.GetType(); - if (type.IsEnum) - { - Type etype = typeof(ExifEnumProperty<>).MakeGenericType(type); - var prop = Activator.CreateInstance(etype, key, value); - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - if (prop is ExifProperty exifProperty) - { - items.Add(key, exifProperty); - } - } - else - { - throw new ArgumentException("No exif property exists for this tag.", "value"); - } - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - /// String encoding. - public void Set(ExifTag key, string value, Encoding encoding) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifEncodedString(key, value, encoding)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, DateTime value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifDateTime(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// Angular degrees (or clock hours for a timestamp). - /// Angular minutes (or clock minutes for a timestamp). - /// Angular seconds (or clock seconds for a timestamp). - public void Set(ExifTag key, float d, float m, float s) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifURationalArray(key, new[] { new(d), new MathEx.UFraction32(m), new MathEx.UFraction32(s) })); - } - - #endregion - - #region Instance Methods - - /// - /// Adds the specified item to the collection. - /// - /// The to add to the collection. - public void Add(ExifProperty item) - { - ExifProperty? oldItem = null; - if (items.TryGetValue(item.Tag, out oldItem)) - { - items[item.Tag] = item; - } - else - { - items.Add(item.Tag, item); - } - } - - /// - /// Removes all items from the collection. - /// - public void Clear() => items.Clear(); - - /// - /// Determines whether the collection contains an element with the specified key. - /// - /// The key to locate in the collection. - /// - /// true if the collection contains an element with the key; otherwise, false. - /// - /// - /// is null. - /// - public bool ContainsKey(ExifTag key) => items.ContainsKey(key); - - /// - /// Removes the element with the specified key from the collection. - /// - /// The key of the element to remove. - /// - /// true if the element is successfully removed; otherwise, false. This method also returns false if - /// was not found in the original collection. - /// - /// - /// is null. - /// - public bool Remove(ExifTag key) => items.Remove(key); - - /// - /// Removes all items with the given IFD from the collection. - /// - /// The IFD section to remove. - public void Remove(IFD ifd) - { - var toRemove = new List(); - foreach (KeyValuePair item in items) - { - if (item.Value.IFD == ifd) - { - toRemove.Add(item.Key); - } - } - - foreach (ExifTag tag in toRemove) - { - items.Remove(tag); - } - } - - /// - /// Gets the value associated with the specified key. - /// - /// The key whose value to get. - /// - /// When this method returns, the value associated with the specified key, if the key is found; - /// otherwise, the default value for the type of the parameter. This parameter is passed - /// uninitialized. - /// - /// - /// true if the collection contains an element with the specified key; otherwise, false. - /// - /// - /// is null. - /// - public bool TryGetValue(ExifTag key, [MaybeNullWhen(false)] out ExifProperty value) => - items.TryGetValue(key, out value); - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() => Values.GetEnumerator(); - - #endregion - - #region Hidden Interface - - /// - /// Adds an element with the provided key and value to the . - /// - /// The object to use as the key of the element to add. - /// The object to use as the value of the element to add. - /// - /// is null. - /// - /// - /// An element with the same key already exists in the - /// . - /// - /// - /// The is - /// read-only. - /// - void IDictionary.Add(ExifTag key, ExifProperty value) => Add(value); - - /// - /// Adds an item to the . - /// - /// The object to add to the . - /// - /// The is - /// read-only. - /// - void ICollection>.Add(KeyValuePair item) => - Add(item.Value); - - bool ICollection>.Contains(KeyValuePair item) => - throw new NotSupportedException(); - - /// - /// Copies the elements of the to an - /// , starting at a particular index. - /// - /// - /// The one-dimensional that is the destination of the elements copied - /// from . The must have - /// zero-based indexing. - /// - /// The zero-based index in at which copying begins. - /// - /// is null. - /// - /// - /// is less than 0. - /// - /// - /// is multidimensional.-or- is equal to or greater than the - /// length of .-or-The number of elements in the source - /// is greater than the available space from - /// to the end of the destination .-or-Type - /// cannot be cast automatically to the type of the destination . - /// - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (array == null) - { - throw new ArgumentNullException("array"); - } - - if (arrayIndex < 0) - { - throw new ArgumentOutOfRangeException("arrayIndex"); - } - - if (array.Rank > 1) - { - throw new ArgumentException("Destination array is multidimensional.", "array"); - } - - if (arrayIndex >= array.Length) - { - throw new ArgumentException("arrayIndex is equal to or greater than the length of destination array", "array"); - } - - if (arrayIndex + items.Count > array.Length) - { - throw new ArgumentException("There is not enough space in destination array.", "array"); - } - - var i = 0; - foreach (KeyValuePair item in items) - { - if (i >= arrayIndex) - { - array[i] = item; - } - - i++; - } - } - - /// - /// Gets a value indicating whether the is read-only. - /// - /// true if the is read-only; otherwise, false. - bool ICollection>.IsReadOnly => false; - - /// - /// Removes the first occurrence of a specific object from the - /// . - /// - /// The object to remove from the . - /// - /// true if was successfully removed from the - /// ; otherwise, false. This method also returns false if - /// is not found in the original . - /// - /// - /// The is - /// read-only. - /// - bool ICollection>.Remove(KeyValuePair item) => - throw new NotSupportedException(); - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// A that can be used to iterate through the collection. - /// - IEnumerator> IEnumerable>.GetEnumerator() => - items.GetEnumerator(); - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ExifPropertyFactory.cs b/src/Umbraco.Core/Media/Exif/ExifPropertyFactory.cs deleted file mode 100644 index 4290dcaf7c..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifPropertyFactory.cs +++ /dev/null @@ -1,598 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Creates exif properties from interoperability parameters. -/// -internal static class ExifPropertyFactory -{ - #region Static Methods - - /// - /// Creates an ExifProperty from the given interoperability parameters. - /// - /// The tag id of the exif property. - /// The type id of the exif property. - /// Byte or component count. - /// Field data as an array of bytes. - /// Byte order of value. - /// IFD section containing this property. - /// The encoding to be used for text metadata when the source encoding is unknown. - /// an ExifProperty initialized from the interoperability parameters. - public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd, Encoding encoding) - { - var conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder); - - // Find the exif tag corresponding to given tag id - ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag); - - if (ifd == IFD.Zeroth) - { - // Compression - if (tag == 0x103) - { - return new ExifEnumProperty(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0)); - } - - // PhotometricInterpretation - if (tag == 0x106) - { - return new ExifEnumProperty( - ExifTag.PhotometricInterpretation, - (PhotometricInterpretation)conv.ToUInt16(value, 0)); - } - - // Orientation - if (tag == 0x112) - { - return new ExifEnumProperty(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0)); - } - - // PlanarConfiguration - if (tag == 0x11c) - { - return new ExifEnumProperty( - ExifTag.PlanarConfiguration, - (PlanarConfiguration)conv.ToUInt16(value, 0)); - } - - // YCbCrPositioning - if (tag == 0x213) - { - return new ExifEnumProperty( - ExifTag.YCbCrPositioning, - (YCbCrPositioning)conv.ToUInt16(value, 0)); - } - - // ResolutionUnit - if (tag == 0x128) - { - return new ExifEnumProperty( - ExifTag.ResolutionUnit, - (ResolutionUnit)conv.ToUInt16(value, 0)); - } - - // DateTime - if (tag == 0x132) - { - return new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value)); - } - - if (tag == 0x9c9b || tag == 0x9c9c || // Windows tags - tag == 0x9c9d || tag == 0x9c9e || tag == 0x9c9f) - { - return new WindowsByteString( - etag, - Encoding.Unicode.GetString(value).TrimEnd(Constants.CharArrays.NullTerminator)); - } - } - else if (ifd == IFD.EXIF) - { - // ExifVersion - if (tag == 0x9000) - { - return new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)); - } - - // FlashpixVersion - if (tag == 0xa000) - { - return new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)); - } - - // ColorSpace - if (tag == 0xa001) - { - return new ExifEnumProperty(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0)); - } - - // UserComment - if (tag == 0x9286) - { - // Default to ASCII - Encoding enc = Encoding.ASCII; - bool hasenc; - if (value.Length < 8) - { - hasenc = false; - } - else - { - hasenc = true; - var encstr = enc.GetString(value, 0, 8); - if (string.Compare(encstr, "ASCII\0\0\0", StringComparison.OrdinalIgnoreCase) == 0) - { - enc = Encoding.ASCII; - } - else if (string.Compare(encstr, "JIS\0\0\0\0\0", StringComparison.OrdinalIgnoreCase) == 0) - { - enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)"); - } - else if (string.Compare(encstr, "Unicode\0", StringComparison.OrdinalIgnoreCase) == 0) - { - enc = Encoding.Unicode; - } - else - { - hasenc = false; - } - } - - var val = (hasenc ? enc.GetString(value, 8, value.Length - 8) : enc.GetString(value)).Trim( - Constants.CharArrays.NullTerminator); - - return new ExifEncodedString(ExifTag.UserComment, val, enc); - } - - // DateTimeOriginal - if (tag == 0x9003) - { - return new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value)); - } - - // DateTimeDigitized - if (tag == 0x9004) - { - return new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value)); - } - - // ExposureProgram - if (tag == 0x8822) - { - return new ExifEnumProperty( - ExifTag.ExposureProgram, - (ExposureProgram)conv.ToUInt16(value, 0)); - } - - // MeteringMode - if (tag == 0x9207) - { - return new ExifEnumProperty(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0)); - } - - // LightSource - if (tag == 0x9208) - { - return new ExifEnumProperty(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0)); - } - - // Flash - if (tag == 0x9209) - { - return new ExifEnumProperty(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true); - } - - // SubjectArea - if (tag == 0x9214) - { - if (count == 3) - { - return new ExifCircularSubjectArea( - ExifTag.SubjectArea, - ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - if (count == 4) - { - return new ExifRectangularSubjectArea( - ExifTag.SubjectArea, - ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - return new ExifPointSubjectArea( - ExifTag.SubjectArea, - ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - // FocalPlaneResolutionUnit - if (tag == 0xa210) - { - return new ExifEnumProperty( - ExifTag.FocalPlaneResolutionUnit, - (ResolutionUnit)conv.ToUInt16(value, 0), - true); - } - - // SubjectLocation - if (tag == 0xa214) - { - return new ExifPointSubjectArea( - ExifTag.SubjectLocation, - ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - // SensingMethod - if (tag == 0xa217) - { - return new ExifEnumProperty( - ExifTag.SensingMethod, - (SensingMethod)conv.ToUInt16(value, 0), - true); - } - - // FileSource - if (tag == 0xa300) - { - return new ExifEnumProperty(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true); - } - - // SceneType - if (tag == 0xa301) - { - return new ExifEnumProperty(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true); - } - - // CustomRendered - if (tag == 0xa401) - { - return new ExifEnumProperty( - ExifTag.CustomRendered, - (CustomRendered)conv.ToUInt16(value, 0), - true); - } - - // ExposureMode - if (tag == 0xa402) - { - return new ExifEnumProperty(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true); - } - - // WhiteBalance - if (tag == 0xa403) - { - return new ExifEnumProperty(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true); - } - - // SceneCaptureType - if (tag == 0xa406) - { - return new ExifEnumProperty( - ExifTag.SceneCaptureType, - (SceneCaptureType)conv.ToUInt16(value, 0), - true); - } - - // GainControl - if (tag == 0xa407) - { - return new ExifEnumProperty(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true); - } - - // Contrast - if (tag == 0xa408) - { - return new ExifEnumProperty(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true); - } - - // Saturation - if (tag == 0xa409) - { - return new ExifEnumProperty(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true); - } - - // Sharpness - if (tag == 0xa40a) - { - return new ExifEnumProperty(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true); - } - - // SubjectDistanceRange - if (tag == 0xa40c) - { - return new ExifEnumProperty( - ExifTag.SubjectDistanceRange, - (SubjectDistanceRange)conv.ToUInt16(value, 0), - true); - } - } - else if (ifd == IFD.GPS) - { - // GPSVersionID - if (tag == 0) - { - return new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value)); - } - - // GPSLatitudeRef - if (tag == 1) - { - return new ExifEnumProperty(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]); - } - - // GPSLatitude - if (tag == 2) - { - return new GPSLatitudeLongitude( - ExifTag.GPSLatitude, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSLongitudeRef - if (tag == 3) - { - return new ExifEnumProperty(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]); - } - - // GPSLongitude - if (tag == 4) - { - return new GPSLatitudeLongitude( - ExifTag.GPSLongitude, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSAltitudeRef - if (tag == 5) - { - return new ExifEnumProperty(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]); - } - - // GPSTimeStamp - if (tag == 7) - { - return new GPSTimeStamp( - ExifTag.GPSTimeStamp, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSStatus - if (tag == 9) - { - return new ExifEnumProperty(ExifTag.GPSStatus, (GPSStatus)value[0]); - } - - // GPSMeasureMode - if (tag == 10) - { - return new ExifEnumProperty(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]); - } - - // GPSSpeedRef - if (tag == 12) - { - return new ExifEnumProperty(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]); - } - - // GPSTrackRef - if (tag == 14) - { - return new ExifEnumProperty(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]); - } - - // GPSImgDirectionRef - if (tag == 16) - { - return new ExifEnumProperty(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]); - } - - // GPSDestLatitudeRef - if (tag == 19) - { - return new ExifEnumProperty(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]); - } - - // GPSDestLatitude - if (tag == 20) - { - return new GPSLatitudeLongitude( - ExifTag.GPSDestLatitude, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSDestLongitudeRef - if (tag == 21) - { - return new ExifEnumProperty(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]); - } - - // GPSDestLongitude - if (tag == 22) - { - return new GPSLatitudeLongitude( - ExifTag.GPSDestLongitude, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSDestBearingRef - if (tag == 23) - { - return new ExifEnumProperty(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]); - } - - // GPSDestDistanceRef - if (tag == 25) - { - return new ExifEnumProperty(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]); - } - - // GPSDate - if (tag == 29) - { - return new ExifDateTime(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false)); - } - - // GPSDifferential - if (tag == 30) - { - return new ExifEnumProperty( - ExifTag.GPSDifferential, - (GPSDifferential)conv.ToUInt16(value, 0)); - } - } - else if (ifd == IFD.Interop) - { - // InteroperabilityIndex - if (tag == 1) - { - return new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value, Encoding.ASCII), Encoding.ASCII); - } - - // InteroperabilityVersion - if (tag == 2) - { - return new ExifVersion( - ExifTag.InteroperabilityVersion, - ExifBitConverter.ToAscii(value, Encoding.ASCII)); - } - } - else if (ifd == IFD.First) - { - // Compression - if (tag == 0x103) - { - return new ExifEnumProperty( - ExifTag.ThumbnailCompression, - (Compression)conv.ToUInt16(value, 0)); - } - - // PhotometricInterpretation - if (tag == 0x106) - { - return new ExifEnumProperty( - ExifTag.ThumbnailPhotometricInterpretation, - (PhotometricInterpretation)conv.ToUInt16(value, 0)); - } - - // Orientation - if (tag == 0x112) - { - return new ExifEnumProperty( - ExifTag.ThumbnailOrientation, - (Orientation)conv.ToUInt16(value, 0)); - } - - // PlanarConfiguration - if (tag == 0x11c) - { - return new ExifEnumProperty( - ExifTag.ThumbnailPlanarConfiguration, - (PlanarConfiguration)conv.ToUInt16(value, 0)); - } - - // YCbCrPositioning - if (tag == 0x213) - { - return new ExifEnumProperty( - ExifTag.ThumbnailYCbCrPositioning, - (YCbCrPositioning)conv.ToUInt16(value, 0)); - } - - // ResolutionUnit - if (tag == 0x128) - { - return new ExifEnumProperty( - ExifTag.ThumbnailResolutionUnit, - (ResolutionUnit)conv.ToUInt16(value, 0)); - } - - // DateTime - if (tag == 0x132) - { - return new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value)); - } - } - - // 1 = BYTE An 8-bit unsigned integer. - if (type == 1) - { - if (count == 1) - { - return new ExifByte(etag, value[0]); - } - - return new ExifByteArray(etag, value); - } - - // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code. - if (type == 2) - { - return new ExifAscii(etag, ExifBitConverter.ToAscii(value, encoding), encoding); - } - - // 3 = SHORT A 16-bit (2-byte) unsigned integer. - if (type == 3) - { - if (count == 1) - { - return new ExifUShort(etag, conv.ToUInt16(value, 0)); - } - - return new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - // 4 = LONG A 32-bit (4-byte) unsigned integer. - if (type == 4) - { - if (count == 1) - { - return new ExifUInt(etag, conv.ToUInt32(value, 0)); - } - - return new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder)); - } - - // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator. - if (type == 5) - { - if (count == 1) - { - return new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder)); - } - - return new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition. - if (type == 7) - { - return new ExifUndefined(etag, value); - } - - // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation). - if (type == 9) - { - if (count == 1) - { - return new ExifSInt(etag, conv.ToInt32(value, 0)); - } - - return new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder)); - } - - // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator. - if (type == 10) - { - if (count == 1) - { - return new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder)); - } - - return new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder)); - } - - throw new ArgumentException("Unknown property type."); - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ExifTag.cs b/src/Umbraco.Core/Media/Exif/ExifTag.cs deleted file mode 100644 index 0ffd754836..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifTag.cs +++ /dev/null @@ -1,310 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the tags associated with exif fields. -/// -internal enum ExifTag -{ - // **************************** - // Zeroth IFD - // **************************** - NewSubfileType = IFD.Zeroth + 254, - SubfileType = IFD.Zeroth + 255, - ImageWidth = IFD.Zeroth + 256, - ImageLength = IFD.Zeroth + 257, - BitsPerSample = IFD.Zeroth + 258, - Compression = IFD.Zeroth + 259, - PhotometricInterpretation = IFD.Zeroth + 262, - Threshholding = IFD.Zeroth + 263, - CellWidth = IFD.Zeroth + 264, - CellLength = IFD.Zeroth + 265, - FillOrder = IFD.Zeroth + 266, - DocumentName = IFD.Zeroth + 269, - ImageDescription = IFD.Zeroth + 270, - Make = IFD.Zeroth + 271, - Model = IFD.Zeroth + 272, - StripOffsets = IFD.Zeroth + 273, - Orientation = IFD.Zeroth + 274, - SamplesPerPixel = IFD.Zeroth + 277, - RowsPerStrip = IFD.Zeroth + 278, - StripByteCounts = IFD.Zeroth + 279, - MinSampleValue = IFD.Zeroth + 280, - MaxSampleValue = IFD.Zeroth + 281, - XResolution = IFD.Zeroth + 282, - YResolution = IFD.Zeroth + 283, - PlanarConfiguration = IFD.Zeroth + 284, - PageName = IFD.Zeroth + 285, - XPosition = IFD.Zeroth + 286, - YPosition = IFD.Zeroth + 287, - FreeOffsets = IFD.Zeroth + 288, - FreeByteCounts = IFD.Zeroth + 289, - GrayResponseUnit = IFD.Zeroth + 290, - GrayResponseCurve = IFD.Zeroth + 291, - T4Options = IFD.Zeroth + 292, - T6Options = IFD.Zeroth + 293, - ResolutionUnit = IFD.Zeroth + 296, - PageNumber = IFD.Zeroth + 297, - TransferFunction = IFD.Zeroth + 301, - Software = IFD.Zeroth + 305, - DateTime = IFD.Zeroth + 306, - Artist = IFD.Zeroth + 315, - HostComputer = IFD.Zeroth + 316, - Predictor = IFD.Zeroth + 317, - WhitePoint = IFD.Zeroth + 318, - PrimaryChromaticities = IFD.Zeroth + 319, - ColorMap = IFD.Zeroth + 320, - HalftoneHints = IFD.Zeroth + 321, - TileWidth = IFD.Zeroth + 322, - TileLength = IFD.Zeroth + 323, - TileOffsets = IFD.Zeroth + 324, - TileByteCounts = IFD.Zeroth + 325, - InkSet = IFD.Zeroth + 332, - InkNames = IFD.Zeroth + 333, - NumberOfInks = IFD.Zeroth + 334, - DotRange = IFD.Zeroth + 336, - TargetPrinter = IFD.Zeroth + 337, - ExtraSamples = IFD.Zeroth + 338, - SampleFormat = IFD.Zeroth + 339, - SMinSampleValue = IFD.Zeroth + 340, - SMaxSampleValue = IFD.Zeroth + 341, - TransferRange = IFD.Zeroth + 342, - JPEGProc = IFD.Zeroth + 512, - JPEGInterchangeFormat = IFD.Zeroth + 513, - JPEGInterchangeFormatLength = IFD.Zeroth + 514, - JPEGRestartInterval = IFD.Zeroth + 515, - JPEGLosslessPredictors = IFD.Zeroth + 517, - JPEGPointTransforms = IFD.Zeroth + 518, - JPEGQTables = IFD.Zeroth + 519, - JPEGDCTables = IFD.Zeroth + 520, - JPEGACTables = IFD.Zeroth + 521, - YCbCrCoefficients = IFD.Zeroth + 529, - YCbCrSubSampling = IFD.Zeroth + 530, - YCbCrPositioning = IFD.Zeroth + 531, - ReferenceBlackWhite = IFD.Zeroth + 532, - Copyright = IFD.Zeroth + 33432, - - // Pointers to other IFDs - EXIFIFDPointer = IFD.Zeroth + 34665, - GPSIFDPointer = IFD.Zeroth + 34853, - - // Windows Tags - WindowsTitle = IFD.Zeroth + 0x9c9b, - WindowsComment = IFD.Zeroth + 0x9c9c, - WindowsAuthor = IFD.Zeroth + 0x9c9d, - WindowsKeywords = IFD.Zeroth + 0x9c9e, - WindowsSubject = IFD.Zeroth + 0x9c9f, - - // Rating - Rating = IFD.Zeroth + 0x4746, - RatingPercent = IFD.Zeroth + 0x4749, - - // Microsoft specifying padding and offset tags - ZerothIFDPadding = IFD.Zeroth + 0xea1c, - - // **************************** - // EXIF Tags - // **************************** - ExifVersion = IFD.EXIF + 36864, - FlashpixVersion = IFD.EXIF + 40960, - ColorSpace = IFD.EXIF + 40961, - ComponentsConfiguration = IFD.EXIF + 37121, - CompressedBitsPerPixel = IFD.EXIF + 37122, - PixelXDimension = IFD.EXIF + 40962, - PixelYDimension = IFD.EXIF + 40963, - MakerNote = IFD.EXIF + 37500, - UserComment = IFD.EXIF + 37510, - RelatedSoundFile = IFD.EXIF + 40964, - DateTimeOriginal = IFD.EXIF + 36867, - DateTimeDigitized = IFD.EXIF + 36868, - SubSecTime = IFD.EXIF + 37520, - SubSecTimeOriginal = IFD.EXIF + 37521, - SubSecTimeDigitized = IFD.EXIF + 37522, - ExposureTime = IFD.EXIF + 33434, - FNumber = IFD.EXIF + 33437, - ExposureProgram = IFD.EXIF + 34850, - SpectralSensitivity = IFD.EXIF + 34852, - ISOSpeedRatings = IFD.EXIF + 34855, - OECF = IFD.EXIF + 34856, - ShutterSpeedValue = IFD.EXIF + 37377, - ApertureValue = IFD.EXIF + 37378, - BrightnessValue = IFD.EXIF + 37379, - ExposureBiasValue = IFD.EXIF + 37380, - MaxApertureValue = IFD.EXIF + 37381, - SubjectDistance = IFD.EXIF + 37382, - MeteringMode = IFD.EXIF + 37383, - LightSource = IFD.EXIF + 37384, - Flash = IFD.EXIF + 37385, - FocalLength = IFD.EXIF + 37386, - SubjectArea = IFD.EXIF + 37396, - FlashEnergy = IFD.EXIF + 41483, - SpatialFrequencyResponse = IFD.EXIF + 41484, - FocalPlaneXResolution = IFD.EXIF + 41486, - FocalPlaneYResolution = IFD.EXIF + 41487, - FocalPlaneResolutionUnit = IFD.EXIF + 41488, - SubjectLocation = IFD.EXIF + 41492, - ExposureIndex = IFD.EXIF + 41493, - SensingMethod = IFD.EXIF + 41495, - FileSource = IFD.EXIF + 41728, - SceneType = IFD.EXIF + 41729, - CFAPattern = IFD.EXIF + 41730, - CustomRendered = IFD.EXIF + 41985, - ExposureMode = IFD.EXIF + 41986, - WhiteBalance = IFD.EXIF + 41987, - DigitalZoomRatio = IFD.EXIF + 41988, - FocalLengthIn35mmFilm = IFD.EXIF + 41989, - SceneCaptureType = IFD.EXIF + 41990, - GainControl = IFD.EXIF + 41991, - Contrast = IFD.EXIF + 41992, - Saturation = IFD.EXIF + 41993, - Sharpness = IFD.EXIF + 41994, - DeviceSettingDescription = IFD.EXIF + 41995, - SubjectDistanceRange = IFD.EXIF + 41996, - ImageUniqueID = IFD.EXIF + 42016, - InteroperabilityIFDPointer = IFD.EXIF + 40965, - - // Microsoft specifying padding and offset tags - ExifIFDPadding = IFD.EXIF + 0xea1c, - OffsetSchema = IFD.EXIF + 0xea1d, - - // **************************** - // GPS Tags - // **************************** - GPSVersionID = IFD.GPS + 0, - GPSLatitudeRef = IFD.GPS + 1, - GPSLatitude = IFD.GPS + 2, - GPSLongitudeRef = IFD.GPS + 3, - GPSLongitude = IFD.GPS + 4, - GPSAltitudeRef = IFD.GPS + 5, - GPSAltitude = IFD.GPS + 6, - GPSTimeStamp = IFD.GPS + 7, - GPSSatellites = IFD.GPS + 8, - GPSStatus = IFD.GPS + 9, - GPSMeasureMode = IFD.GPS + 10, - GPSDOP = IFD.GPS + 11, - GPSSpeedRef = IFD.GPS + 12, - GPSSpeed = IFD.GPS + 13, - GPSTrackRef = IFD.GPS + 14, - GPSTrack = IFD.GPS + 15, - GPSImgDirectionRef = IFD.GPS + 16, - GPSImgDirection = IFD.GPS + 17, - GPSMapDatum = IFD.GPS + 18, - GPSDestLatitudeRef = IFD.GPS + 19, - GPSDestLatitude = IFD.GPS + 20, - GPSDestLongitudeRef = IFD.GPS + 21, - GPSDestLongitude = IFD.GPS + 22, - GPSDestBearingRef = IFD.GPS + 23, - GPSDestBearing = IFD.GPS + 24, - GPSDestDistanceRef = IFD.GPS + 25, - GPSDestDistance = IFD.GPS + 26, - GPSProcessingMethod = IFD.GPS + 27, - GPSAreaInformation = IFD.GPS + 28, - GPSDateStamp = IFD.GPS + 29, - GPSDifferential = IFD.GPS + 30, - - // **************************** - // InterOp Tags - // **************************** - InteroperabilityIndex = IFD.Interop + 1, - InteroperabilityVersion = IFD.Interop + 2, - RelatedImageWidth = IFD.Interop + 0x1001, - RelatedImageHeight = IFD.Interop + 0x1002, - - // **************************** - // First IFD TIFF Tags - // **************************** - ThumbnailImageWidth = IFD.First + 256, - ThumbnailImageLength = IFD.First + 257, - ThumbnailBitsPerSample = IFD.First + 258, - ThumbnailCompression = IFD.First + 259, - ThumbnailPhotometricInterpretation = IFD.First + 262, - ThumbnailOrientation = IFD.First + 274, - ThumbnailSamplesPerPixel = IFD.First + 277, - ThumbnailPlanarConfiguration = IFD.First + 284, - ThumbnailYCbCrSubSampling = IFD.First + 530, - ThumbnailYCbCrPositioning = IFD.First + 531, - ThumbnailXResolution = IFD.First + 282, - ThumbnailYResolution = IFD.First + 283, - ThumbnailResolutionUnit = IFD.First + 296, - ThumbnailStripOffsets = IFD.First + 273, - ThumbnailRowsPerStrip = IFD.First + 278, - ThumbnailStripByteCounts = IFD.First + 279, - ThumbnailJPEGInterchangeFormat = IFD.First + 513, - ThumbnailJPEGInterchangeFormatLength = IFD.First + 514, - ThumbnailTransferFunction = IFD.First + 301, - ThumbnailWhitePoint = IFD.First + 318, - ThumbnailPrimaryChromaticities = IFD.First + 319, - ThumbnailYCbCrCoefficients = IFD.First + 529, - ThumbnailReferenceBlackWhite = IFD.First + 532, - ThumbnailDateTime = IFD.First + 306, - ThumbnailImageDescription = IFD.First + 270, - ThumbnailMake = IFD.First + 271, - ThumbnailModel = IFD.First + 272, - ThumbnailSoftware = IFD.First + 305, - ThumbnailArtist = IFD.First + 315, - ThumbnailCopyright = IFD.First + 33432, - - // **************************** - // JFIF Tags - // **************************** - - /// - /// Represents the JFIF version. - /// - JFIFVersion = IFD.JFIF + 1, - - /// - /// Represents units for X and Y densities. - /// - JFIFUnits = IFD.JFIF + 101, - - /// - /// Horizontal pixel density. - /// - XDensity = IFD.JFIF + 102, - - /// - /// Vertical pixel density - /// - YDensity = IFD.JFIF + 103, - - /// - /// Thumbnail horizontal pixel count. - /// - JFIFXThumbnail = IFD.JFIF + 201, - - /// - /// Thumbnail vertical pixel count. - /// - JFIFYThumbnail = IFD.JFIF + 202, - - /// - /// JFIF JPEG thumbnail. - /// - JFIFThumbnail = IFD.JFIF + 203, - - /// - /// Code which identifies the JFIF extension. - /// - JFXXExtensionCode = IFD.JFXX + 1, - - /// - /// Thumbnail horizontal pixel count. - /// - JFXXXThumbnail = IFD.JFXX + 101, - - /// - /// Thumbnail vertical pixel count. - /// - JFXXYThumbnail = IFD.JFXX + 102, - - /// - /// The 256-Color RGB palette. - /// - JFXXPalette = IFD.JFXX + 201, - - /// - /// JFIF thumbnail. The thumbnail will be either a JPEG, - /// a 256 color palette bitmap, or a 24-bit RGB bitmap. - /// - JFXXThumbnail = IFD.JFXX + 202, -} diff --git a/src/Umbraco.Core/Media/Exif/ExifTagFactory.cs b/src/Umbraco.Core/Media/Exif/ExifTagFactory.cs deleted file mode 100644 index 726da925aa..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifTagFactory.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -internal static class ExifTagFactory -{ - #region Static Methods - - /// - /// Returns the ExifTag corresponding to the given tag id. - /// - public static ExifTag GetExifTag(IFD ifd, ushort tagid) => (ExifTag)(ifd + tagid); - - /// - /// Returns the tag id corresponding to the given ExifTag. - /// - public static ushort GetTagID(ExifTag exiftag) - { - IFD ifd = GetTagIFD(exiftag); - return (ushort)((int)exiftag - (int)ifd); - } - - /// - /// Returns the IFD section containing the given tag. - /// - public static IFD GetTagIFD(ExifTag tag) => (IFD)((int)tag / 100000 * 100000); - - /// - /// Returns the string representation for the given exif tag. - /// - public static string GetTagName(ExifTag tag) - { - var name = Enum.GetName(typeof(ExifTag), tag); - if (name == null) - { - return "Unknown"; - } - - return name; - } - - /// - /// Returns the string representation for the given tag id. - /// - public static string GetTagName(IFD ifd, ushort tagid) => GetTagName(GetExifTag(ifd, tagid)); - - /// - /// Returns the string representation for the given exif tag including - /// IFD section and tag id. - /// - public static string GetTagLongName(ExifTag tag) - { - var ifdname = Enum.GetName(typeof(IFD), GetTagIFD(tag)); - var name = Enum.GetName(typeof(ExifTag), tag); - if (name == null) - { - name = "Unknown"; - } - - var tagidname = GetTagID(tag).ToString(); - return ifdname + ": " + name + " (" + tagidname + ")"; - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/IFD.cs b/src/Umbraco.Core/Media/Exif/IFD.cs deleted file mode 100644 index cda3cdcb69..0000000000 --- a/src/Umbraco.Core/Media/Exif/IFD.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the IFD section containing tags. -/// -internal enum IFD -{ - Unknown = 0, - Zeroth = 100000, - EXIF = 200000, - GPS = 300000, - Interop = 400000, - First = 500000, - MakerNote = 600000, - JFIF = 700000, - JFXX = 800000, -} diff --git a/src/Umbraco.Core/Media/Exif/ImageFile.cs b/src/Umbraco.Core/Media/Exif/ImageFile.cs deleted file mode 100644 index 23ea615be9..0000000000 --- a/src/Umbraco.Core/Media/Exif/ImageFile.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System.ComponentModel; -using System.Text; -using Umbraco.Cms.Core.Media.TypeDetector; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the base class for image files. -/// -[TypeDescriptionProvider(typeof(ExifFileTypeDescriptionProvider))] -internal abstract class ImageFile -{ - #region Constructor - - /// - /// Initializes a new instance of the class. - /// - protected ImageFile() - { - Format = ImageFileFormat.Unknown; - Properties = new ExifPropertyCollection(this); - Encoding = Encoding.Default; - } - - #endregion - - #region Properties - - /// - /// Returns the format of the . - /// - public ImageFileFormat Format { get; protected set; } - - /// - /// Gets the collection of Exif properties contained in the . - /// - public ExifPropertyCollection Properties { get; } - - /// - /// Gets or sets the embedded thumbnail image. - /// - public ImageFile? Thumbnail { get; set; } - - /// - /// Gets or sets the Exif property with the given key. - /// - /// The Exif tag associated with the Exif property. - public ExifProperty this[ExifTag key] - { - get => Properties[key]; - set => Properties[key] = value; - } - - /// - /// Gets the encoding used for text metadata when the source encoding is unknown. - /// - public Encoding Encoding { get; protected set; } - - #endregion - - #region Instance Methods - - /// - /// Saves the to the specified file. - /// - /// A string that contains the name of the file. - public virtual void Save(string filename) - { - using (var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None)) - { - Save(stream); - } - } - - /// - /// Saves the to the specified stream. - /// - /// A to save image data to. - public abstract void Save(Stream stream); - - #endregion - - #region Static Methods - - /// - /// Creates an from the specified file. - /// - /// A string that contains the name of the file. - /// The created from the file. - public static ImageFile? FromFile(string filename) => FromFile(filename, Encoding.Default); - - /// - /// Creates an from the specified file. - /// - /// A string that contains the name of the file. - /// The encoding to be used for text metadata when the source encoding is unknown. - /// The created from the file. - public static ImageFile? FromFile(string filename, Encoding encoding) - { - using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - return FromStream(stream, encoding); - } - } - - /// - /// Creates an from the specified data stream. - /// - /// A that contains image data. - /// The created from the file. - public static ImageFile? FromStream(Stream stream) => FromStream(stream, Encoding.Default); - - /// - /// Creates an from the specified data stream. - /// - /// A that contains image data. - /// The encoding to be used for text metadata when the source encoding is unknown. - /// The created from the file. - public static ImageFile? FromStream(Stream stream, Encoding encoding) - { - // JPEG - if (JpegDetector.IsOfType(stream)) - { - return new JPEGFile(stream, encoding); - } - - // TIFF - if (TIFFDetector.IsOfType(stream)) - { - return new TIFFFile(stream, encoding); - } - - // SVG - if (SvgDetector.IsOfType(stream)) - { - return new SvgFile(stream); - } - - // We don't know - return null; - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ImageFileDirectory.cs b/src/Umbraco.Core/Media/Exif/ImageFileDirectory.cs deleted file mode 100644 index 299e7619f9..0000000000 --- a/src/Umbraco.Core/Media/Exif/ImageFileDirectory.cs +++ /dev/null @@ -1,100 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents an image file directory. -/// -internal class ImageFileDirectory -{ - /// - /// Initializes a new instance of the class. - /// - public ImageFileDirectory() - { - Fields = new List(); - Strips = new List(); - } - - /// - /// The fields contained in this IFD. - /// - public List Fields { get; } - - /// - /// Offset to the next IFD. - /// - public uint NextIFDOffset { get; private set; } - - /// - /// Compressed image data. - /// - public List Strips { get; } - - /// - /// Returns a initialized from the given byte data. - /// - /// The data. - /// The offset into . - /// The byte order of . - /// A initialized from the given byte data. - public static ImageFileDirectory FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder) - { - var ifd = new ImageFileDirectory(); - var conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder); - - var stripOffsets = new List(); - var stripLengths = new List(); - - // Count - var fieldcount = conv.ToUInt16(data, offset); - - // Read fields - for (uint i = 0; i < fieldcount; i++) - { - var fieldoffset = offset + 2 + (12 * i); - var field = ImageFileDirectoryEntry.FromBytes(data, fieldoffset, byteOrder); - ifd.Fields.Add(field); - - // Read strip offsets - if (field.Tag == 273) - { - var baselen = field.Data.Length / (int)field.Count; - for (uint j = 0; j < field.Count; j++) - { - var val = new byte[baselen]; - Array.Copy(field.Data, j * baselen, val, 0, baselen); - var stripOffset = field.Type == 3 ? BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0); - stripOffsets.Add(stripOffset); - } - } - - // Read strip lengths - if (field.Tag == 279) - { - var baselen = field.Data.Length / (int)field.Count; - for (uint j = 0; j < field.Count; j++) - { - var val = new byte[baselen]; - Array.Copy(field.Data, j * baselen, val, 0, baselen); - var stripLength = field.Type == 3 ? BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0); - stripLengths.Add(stripLength); - } - } - } - - // Save strips - if (stripOffsets.Count != stripLengths.Count) - { - throw new NotValidTIFFileException(); - } - - for (var i = 0; i < stripOffsets.Count; i++) - { - ifd.Strips.Add(new TIFFStrip(data, stripOffsets[i], stripLengths[i])); - } - - // Offset to next ifd - ifd.NextIFDOffset = conv.ToUInt32(data, offset + 2 + (12 * fieldcount)); - - return ifd; - } -} diff --git a/src/Umbraco.Core/Media/Exif/ImageFileDirectoryEntry.cs b/src/Umbraco.Core/Media/Exif/ImageFileDirectoryEntry.cs deleted file mode 100644 index a3863b6a69..0000000000 --- a/src/Umbraco.Core/Media/Exif/ImageFileDirectoryEntry.cs +++ /dev/null @@ -1,144 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents an entry in the image file directory. -/// -internal struct ImageFileDirectoryEntry -{ - /// - /// The tag that identifies the field. - /// - public ushort Tag; - - /// - /// Field type identifier. - /// - public ushort Type; - - /// - /// Count of Type. - /// - public uint Count; - - /// - /// Field data. - /// - public byte[] Data; - - /// - /// Initializes a new instance of the struct. - /// - /// The tag that identifies the field. - /// Field type identifier. - /// Count of Type. - /// Field data. - public ImageFileDirectoryEntry(ushort tag, ushort type, uint count, byte[] data) - { - Tag = tag; - Type = type; - Count = count; - Data = data; - } - - /// - /// Returns a initialized from the given byte data. - /// - /// The data. - /// The offset into . - /// The byte order of . - /// A initialized from the given byte data. - public static ImageFileDirectoryEntry FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder) - { - // Tag ID - var tag = BitConverterEx.ToUInt16(data, offset, byteOrder, BitConverterEx.SystemByteOrder); - - // Tag Type - var type = BitConverterEx.ToUInt16(data, offset + 2, byteOrder, BitConverterEx.SystemByteOrder); - - // Count of Type - var count = BitConverterEx.ToUInt32(data, offset + 4, byteOrder, BitConverterEx.SystemByteOrder); - - // Field value or offset to field data - var value = new byte[4]; - Array.Copy(data, offset + 8, value, 0, 4); - - // Calculate the bytes we need to read - var baselength = GetBaseLength(type); - var totallength = count * baselength; - - // If field value does not fit in 4 bytes - // the value field is an offset to the actual - // field value - if (totallength > 4) - { - var dataoffset = BitConverterEx.ToUInt32(value, 0, byteOrder, BitConverterEx.SystemByteOrder); - value = new byte[totallength]; - Array.Copy(data, dataoffset, value, 0, totallength); - } - - // Reverse array order if byte orders are different - if (byteOrder != BitConverterEx.SystemByteOrder) - { - for (uint i = 0; i < count; i++) - { - var val = new byte[baselength]; - Array.Copy(value, i * baselength, val, 0, baselength); - Array.Reverse(val); - Array.Copy(val, 0, value, i * baselength, baselength); - } - } - - return new ImageFileDirectoryEntry(tag, type, count, value); - } - - /// - /// Gets the base byte length for the given type. - /// - /// Type identifier. - private static uint GetBaseLength(ushort type) - { - // BYTE and SBYTE - if (type == 1 || type == 6) - { - return 1; - } - - // ASCII and UNDEFINED - if (type == 2 || type == 7) - { - return 1; - } - - // SHORT and SSHORT - if (type == 3 || type == 8) - { - return 2; - } - - // LONG and SLONG - if (type == 4 || type == 9) - { - return 4; - } - - // RATIONAL (2xLONG) and SRATIONAL (2xSLONG) - if (type == 5 || type == 10) - { - return 8; - } - - // FLOAT - if (type == 11) - { - return 4; - } - - // DOUBLE - if (type == 12) - { - return 8; - } - - throw new ArgumentException("Unknown type identifier.", "type"); - } -} diff --git a/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs b/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs deleted file mode 100644 index fe30c713b2..0000000000 --- a/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the format of the . -/// -internal enum ImageFileFormat -{ - /// - /// The file is not recognized. - /// - Unknown, - - /// - /// The file is a JPEG/Exif or JPEG/JFIF file. - /// - JPEG, - - /// - /// The file is a TIFF File. - /// - TIFF, - - /// - /// The file is a SVG File. - /// - SVG, -} diff --git a/src/Umbraco.Core/Media/Exif/JFIFEnums.cs b/src/Umbraco.Core/Media/Exif/JFIFEnums.cs deleted file mode 100644 index 438d7bf3d4..0000000000 --- a/src/Umbraco.Core/Media/Exif/JFIFEnums.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the units for the X and Y densities -/// for a JFIF file. -/// -internal enum JFIFDensityUnit : byte -{ - /// - /// No units, XDensity and YDensity specify the pixel aspect ratio. - /// - None = 0, - - /// - /// XDensity and YDensity are dots per inch. - /// - DotsPerInch = 1, - - /// - /// XDensity and YDensity are dots per cm. - /// - DotsPerCm = 2, -} - -/// -/// Represents the JFIF extension. -/// -internal enum JFIFExtension : byte -{ - /// - /// Thumbnail coded using JPEG. - /// - ThumbnailJPEG = 0x10, - - /// - /// Thumbnail stored using a 256-Color RGB palette. - /// - ThumbnailPaletteRGB = 0x11, - - /// - /// Thumbnail stored using 3 bytes/pixel (24-bit) RGB values. - /// - Thumbnail24BitRGB = 0x13, -} diff --git a/src/Umbraco.Core/Media/Exif/JFIFExtendedProperty.cs b/src/Umbraco.Core/Media/Exif/JFIFExtendedProperty.cs deleted file mode 100644 index 71ea89228d..0000000000 --- a/src/Umbraco.Core/Media/Exif/JFIFExtendedProperty.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the JFIF version as a 16 bit unsigned integer. (EXIF Specification: SHORT) -/// -internal class JFIFVersion : ExifUShort -{ - public JFIFVersion(ExifTag tag, ushort value) - : base(tag, value) - { - } - - /// - /// Gets the major version. - /// - public byte Major => (byte)(mValue >> 8); - - /// - /// Gets the minor version. - /// - public byte Minor => (byte)(mValue - ((mValue >> 8) * 256)); - - public override string ToString() => string.Format("{0}.{1:00}", Major, Minor); -} - -/// -/// Represents a JFIF thumbnail. (EXIF Specification: BYTE) -/// -internal class JFIFThumbnailProperty : ExifProperty -{ - protected JFIFThumbnail mValue; - - public JFIFThumbnailProperty(ExifTag tag, JFIFThumbnail value) - : base(tag) => - mValue = value; - - public new JFIFThumbnail Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (JFIFThumbnail)value; - } - - public override ExifInterOperability Interoperability - { - get - { - if (mValue.Format == JFIFThumbnail.ImageFormat.BMP24Bit) - { - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.PixelData.Length, mValue.PixelData); - } - - if (mValue.Format == JFIFThumbnail.ImageFormat.BMPPalette) - { - var data = new byte[mValue.Palette.Length + mValue.PixelData.Length]; - Array.Copy(mValue.Palette, data, mValue.Palette.Length); - Array.Copy(mValue.PixelData, 0, data, mValue.Palette.Length, mValue.PixelData.Length); - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)data.Length, data); - } - - if (mValue.Format == JFIFThumbnail.ImageFormat.JPEG) - { - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.PixelData.Length, mValue.PixelData); - } - - throw new InvalidOperationException("Unknown thumbnail type."); - } - } - - public override string ToString() => mValue.Format.ToString(); -} diff --git a/src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs b/src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs deleted file mode 100644 index cafa804c3a..0000000000 --- a/src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs +++ /dev/null @@ -1,62 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a JFIF thumbnail. -/// -internal class JFIFThumbnail -{ - #region Public Enums - - public enum ImageFormat - { - JPEG, - BMPPalette, - BMP24Bit, - } - - #endregion - - #region Properties - - /// - /// Gets the 256 color RGB palette. - /// - public byte[] Palette { get; } - - /// - /// Gets raw image data. - /// - public byte[] PixelData { get; } - - /// - /// Gets the image format. - /// - public ImageFormat Format { get; } - - #endregion - - #region Constructors - - protected JFIFThumbnail() - { - Palette = new byte[0]; - PixelData = new byte[0]; - } - - public JFIFThumbnail(ImageFormat format, byte[] data) - : this() - { - Format = format; - PixelData = data; - } - - public JFIFThumbnail(byte[] palette, byte[] data) - : this() - { - Format = ImageFormat.BMPPalette; - Palette = palette; - PixelData = data; - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/JPEGExceptions.cs b/src/Umbraco.Core/Media/Exif/JPEGExceptions.cs index c44d6d1db0..4781415d7c 100644 --- a/src/Umbraco.Core/Media/Exif/JPEGExceptions.cs +++ b/src/Umbraco.Core/Media/Exif/JPEGExceptions.cs @@ -7,6 +7,7 @@ namespace Umbraco.Cms.Core.Media.Exif; /// /// [Serializable] +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class NotValidJPEGFileException : Exception { /// diff --git a/src/Umbraco.Core/Media/Exif/JPEGFile.cs b/src/Umbraco.Core/Media/Exif/JPEGFile.cs deleted file mode 100644 index bdf7208ea0..0000000000 --- a/src/Umbraco.Core/Media/Exif/JPEGFile.cs +++ /dev/null @@ -1,1110 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the binary view of a JPEG compressed file. -/// -internal class JPEGFile : ImageFile -{ - #region Constructor - - /// - /// Initializes a new instance of the class. - /// - /// A that contains image data. - /// The encoding to be used for text metadata when the source encoding is unknown. - protected internal JPEGFile(Stream stream, Encoding encoding) - { - Format = ImageFileFormat.JPEG; - Sections = new List(); - TrailingData = new byte[0]; - Encoding = encoding; - - stream.Seek(0, SeekOrigin.Begin); - - // Read the Start of Image (SOI) marker. SOI marker is represented - // with two bytes: 0xFF, 0xD8. - var markerbytes = new byte[2]; - if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] != 0xD8) - { - throw new NotValidJPEGFileException(); - } - - stream.Seek(0, SeekOrigin.Begin); - - // Search and read sections until we reach the end of file. - while (stream.Position != stream.Length) - { - // Read the next section marker. Section markers are two bytes - // with values 0xFF, 0x?? where ?? must not be 0x00 or 0xFF. - if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] == 0x00 || - markerbytes[1] == 0xFF) - { - throw new NotValidJPEGFileException(); - } - - var marker = (JPEGMarker)markerbytes[1]; - - var header = new byte[0]; - - // SOI, EOI and RST markers do not contain any header - if (marker != JPEGMarker.SOI && marker != JPEGMarker.EOI && - !(marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7)) - { - // Length of the header including the length bytes. - // This value is a 16-bit unsigned integer - // in big endian byte-order. - var lengthbytes = new byte[2]; - if (stream.Read(lengthbytes, 0, 2) != 2) - { - throw new NotValidJPEGFileException(); - } - - long length = BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0); - - // Read section header. - header = new byte[length - 2]; - var bytestoread = header.Length; - while (bytestoread > 0) - { - var count = Math.Min(bytestoread, 4 * 1024); - var bytesread = stream.Read(header, header.Length - bytestoread, count); - if (bytesread == 0) - { - throw new NotValidJPEGFileException(); - } - - bytestoread -= bytesread; - } - } - - // Start of Scan (SOS) sections and RST sections are immediately - // followed by entropy coded data. For that, we need to read until - // the next section marker once we reach a SOS or RST. - var entropydata = new byte[0]; - if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7)) - { - var position = stream.Position; - - // Search for the next section marker - while (true) - { - // Search for an 0xFF indicating start of a marker - var nextbyte = 0; - do - { - nextbyte = stream.ReadByte(); - if (nextbyte == -1) - { - throw new NotValidJPEGFileException(); - } - } - while ((byte)nextbyte != 0xFF); - - // Skip filler bytes (0xFF) - do - { - nextbyte = stream.ReadByte(); - if (nextbyte == -1) - { - throw new NotValidJPEGFileException(); - } - } - while ((byte)nextbyte == 0xFF); - - // Looks like a section marker. The next byte must not be 0x00. - if ((byte)nextbyte != 0x00) - { - // We reached a section marker. Calculate the - // length of the entropy coded data. - stream.Seek(-2, SeekOrigin.Current); - var edlength = stream.Position - position; - stream.Seek(-edlength, SeekOrigin.Current); - - // Read entropy coded data - entropydata = new byte[edlength]; - var bytestoread = entropydata.Length; - while (bytestoread > 0) - { - var count = Math.Min(bytestoread, 4 * 1024); - var bytesread = stream.Read(entropydata, entropydata.Length - bytestoread, count); - if (bytesread == 0) - { - throw new NotValidJPEGFileException(); - } - - bytestoread -= bytesread; - } - - break; - } - } - } - - // Store section. - var section = new JPEGSection(marker, header, entropydata); - Sections.Add(section); - - // Some propriety formats store data past the EOI marker - if (marker == JPEGMarker.EOI) - { - var bytestoread = (int)(stream.Length - stream.Position); - TrailingData = new byte[bytestoread]; - while (bytestoread > 0) - { - var count = Math.Min(bytestoread, 4 * 1024); - var bytesread = stream.Read(TrailingData, TrailingData.Length - bytestoread, count); - if (bytesread == 0) - { - throw new NotValidJPEGFileException(); - } - - bytestoread -= bytesread; - } - } - } - - // Read metadata sections - ReadJFIFAPP0(); - ReadJFXXAPP0(); - ReadExifAPP1(); - - // Process the maker note - _makerNoteProcessed = false; - } - - #endregion - - #region Member Variables - - private JPEGSection? _jfifApp0; - private JPEGSection? _jfxxApp0; - private JPEGSection? _exifApp1; - private uint _makerNoteOffset; - private long _exifIfdFieldOffset; - private long _gpsIfdFieldOffset; - private long _interopIfdFieldOffset; - private long _firstIfdFieldOffset; - private long _thumbOffsetLocation; - private long _thumbSizeLocation; - private uint _thumbOffsetValue; - private uint _thumbSizeValue; - private readonly bool _makerNoteProcessed; - - #endregion - - #region Properties - - /// - /// Gets or sets the byte-order of the Exif properties. - /// - public BitConverterEx.ByteOrder ByteOrder { get; set; } - - /// - /// Gets or sets the sections contained in the . - /// - public List Sections { get; } - - /// - /// Gets or sets non-standard trailing data following the End of Image (EOI) marker. - /// - public byte[] TrailingData { get; } - - #endregion - - #region Instance Methods - - /// - /// Saves the JPEG/Exif image to the given stream. - /// - /// The stream of the JPEG/Exif file. - /// - /// Determines whether the maker note offset of - /// the original file will be preserved. - /// - public void Save(Stream stream, bool preserveMakerNote) - { - WriteJFIFApp0(); - WriteJFXXApp0(); - WriteExifApp1(preserveMakerNote); - - // Write sections - foreach (JPEGSection section in Sections) - { - // Section header (including length bytes and section marker) - // must not exceed 64 kB. - if (section.Header.Length + 2 + 2 > 64 * 1024) - { - throw new SectionExceeds64KBException(); - } - - // APP sections must have a header. - // Otherwise skip the entire section. - if (section.Marker >= JPEGMarker.APP0 && section.Marker <= JPEGMarker.APP15 && section.Header.Length == 0) - { - continue; - } - - // Write section marker - stream.Write(new byte[] { 0xFF, (byte)section.Marker }, 0, 2); - - // SOI, EOI and RST markers do not contain any header - if (section.Marker != JPEGMarker.SOI && section.Marker != JPEGMarker.EOI && - !(section.Marker >= JPEGMarker.RST0 && section.Marker <= JPEGMarker.RST7)) - { - // Header length including the length field itself - stream.Write(BitConverterEx.BigEndian.GetBytes((ushort)(section.Header.Length + 2)), 0, 2); - - // Write section header - if (section.Header.Length != 0) - { - stream.Write(section.Header, 0, section.Header.Length); - } - } - - // Write entropy coded data - if (section.EntropyData.Length != 0) - { - stream.Write(section.EntropyData, 0, section.EntropyData.Length); - } - } - - // Write trailing data, if any - if (TrailingData.Length != 0) - { - stream.Write(TrailingData, 0, TrailingData.Length); - } - } - - /// - /// Saves the JPEG/Exif image with the given filename. - /// - /// The path to the JPEG/Exif file. - /// - /// Determines whether the maker note offset of - /// the original file will be preserved. - /// - public void Save(string filename, bool preserveMakerNote) - { - using (var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None)) - { - Save(stream, preserveMakerNote); - } - } - - /// - /// Saves the JPEG/Exif image with the given filename. - /// - /// The path to the JPEG/Exif file. - public override void Save(string filename) => Save(filename, true); - - /// - /// Saves the JPEG/Exif image to the given stream. - /// - /// The stream of the JPEG/Exif file. - public override void Save(Stream stream) => Save(stream, true); - - #endregion - - #region Private Helper Methods - - /// - /// Reads the APP0 section containing JFIF metadata. - /// - private void ReadJFIFAPP0() - { - // Find the APP0 section containing JFIF metadata - _jfifApp0 = Sections.Find(a => a.Marker == JPEGMarker.APP0 && - a.Header.Length >= 5 && - Encoding.ASCII.GetString(a.Header, 0, 5) == "JFIF\0"); - - // If there is no APP0 section, return. - if (_jfifApp0 == null) - { - return; - } - - var header = _jfifApp0.Header; - BitConverterEx jfifConv = BitConverterEx.BigEndian; - - // Version - var version = jfifConv.ToUInt16(header, 5); - Properties.Add(new JFIFVersion(ExifTag.JFIFVersion, version)); - - // Units - var unit = header[7]; - Properties.Add(new ExifEnumProperty(ExifTag.JFIFUnits, (JFIFDensityUnit)unit)); - - // X and Y densities - var xdensity = jfifConv.ToUInt16(header, 8); - Properties.Add(new ExifUShort(ExifTag.XDensity, xdensity)); - var ydensity = jfifConv.ToUInt16(header, 10); - Properties.Add(new ExifUShort(ExifTag.YDensity, ydensity)); - - // Thumbnails pixel count - var xthumbnail = header[12]; - Properties.Add(new ExifByte(ExifTag.JFIFXThumbnail, xthumbnail)); - var ythumbnail = header[13]; - Properties.Add(new ExifByte(ExifTag.JFIFYThumbnail, ythumbnail)); - - // Read JFIF thumbnail - var n = xthumbnail * ythumbnail; - var jfifThumbnail = new byte[n]; - Array.Copy(header, 14, jfifThumbnail, 0, n); - Properties.Add(new JFIFThumbnailProperty(ExifTag.JFIFThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, jfifThumbnail))); - } - - /// - /// Replaces the contents of the APP0 section with the JFIF properties. - /// - private bool WriteJFIFApp0() - { - // Which IFD sections do we have? - var ifdjfef = new List(); - foreach (ExifProperty prop in Properties) - { - if (prop.IFD == IFD.JFIF) - { - ifdjfef.Add(prop); - } - } - - if (ifdjfef.Count == 0) - { - // Nothing to write - return false; - } - - // Create a memory stream to write the APP0 section to - var ms = new MemoryStream(); - - // JFIF identifier - ms.Write(Encoding.ASCII.GetBytes("JFIF\0"), 0, 5); - - // Write tags - foreach (ExifProperty prop in ifdjfef) - { - ExifInterOperability interop = prop.Interoperability; - var data = interop.Data; - if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == 3) - { - Array.Reverse(data); - } - - ms.Write(data, 0, data.Length); - } - - ms.Close(); - - // Return APP0 header - if (_jfifApp0 is not null) - { - _jfifApp0.Header = ms.ToArray(); - return true; - } - - return false; - } - - /// - /// Reads the APP0 section containing JFIF extension metadata. - /// - private void ReadJFXXAPP0() - { - // Find the APP0 section containing JFIF metadata - _jfxxApp0 = Sections.Find(a => a.Marker == JPEGMarker.APP0 && - a.Header.Length >= 5 && - Encoding.ASCII.GetString(a.Header, 0, 5) == "JFXX\0"); - - // If there is no APP0 section, return. - if (_jfxxApp0 == null) - { - return; - } - - var header = _jfxxApp0.Header; - - // Version - var version = (JFIFExtension)header[5]; - Properties.Add(new ExifEnumProperty(ExifTag.JFXXExtensionCode, version)); - - // Read thumbnail - if (version == JFIFExtension.ThumbnailJPEG) - { - var data = new byte[header.Length - 6]; - Array.Copy(header, 6, data, 0, data.Length); - Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, data))); - } - else if (version == JFIFExtension.Thumbnail24BitRGB) - { - // Thumbnails pixel count - var xthumbnail = header[6]; - Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail)); - var ythumbnail = header[7]; - Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail)); - var data = new byte[3 * xthumbnail * ythumbnail]; - Array.Copy(header, 8, data, 0, data.Length); - Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.BMP24Bit, data))); - } - else if (version == JFIFExtension.ThumbnailPaletteRGB) - { - // Thumbnails pixel count - var xthumbnail = header[6]; - Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail)); - var ythumbnail = header[7]; - Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail)); - var palette = new byte[768]; - Array.Copy(header, 8, palette, 0, palette.Length); - var data = new byte[xthumbnail * ythumbnail]; - Array.Copy(header, 8 + 768, data, 0, data.Length); - Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(palette, data))); - } - } - - /// - /// Replaces the contents of the APP0 section with the JFIF extension properties. - /// - private bool WriteJFXXApp0() - { - // Which IFD sections do we have? - var ifdjfef = new List(); - foreach (ExifProperty prop in Properties) - { - if (prop.IFD == IFD.JFXX) - { - ifdjfef.Add(prop); - } - } - - if (ifdjfef.Count == 0) - { - // Nothing to write - return false; - } - - // Create a memory stream to write the APP0 section to - var ms = new MemoryStream(); - - // JFIF identifier - ms.Write(Encoding.ASCII.GetBytes("JFXX\0"), 0, 5); - - // Write tags - foreach (ExifProperty prop in ifdjfef) - { - ExifInterOperability interop = prop.Interoperability; - var data = interop.Data; - if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == 3) - { - Array.Reverse(data); - } - - ms.Write(data, 0, data.Length); - } - - ms.Close(); - - if (_jfxxApp0 is not null) - { - // Return APP0 header - _jfxxApp0.Header = ms.ToArray(); - return true; - } - - return false; - } - - /// - /// Reads the APP1 section containing Exif metadata. - /// - private void ReadExifAPP1() - { - // Find the APP1 section containing Exif metadata - _exifApp1 = Sections.Find(a => a.Marker == JPEGMarker.APP1 && - a.Header.Length >= 6 && - Encoding.ASCII.GetString(a.Header, 0, 6) == "Exif\0\0"); - - // If there is no APP1 section, add a new one after the last APP0 section (if any). - if (_exifApp1 == null) - { - var insertionIndex = Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0); - if (insertionIndex == -1) - { - insertionIndex = 0; - } - - insertionIndex++; - _exifApp1 = new JPEGSection(JPEGMarker.APP1); - Sections.Insert(insertionIndex, _exifApp1); - if (BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian) - { - ByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else - { - ByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - - return; - } - - var header = _exifApp1.Header; - var ifdqueue = new SortedList(); - _makerNoteOffset = 0; - - // TIFF header - var tiffoffset = 6; - if (header[tiffoffset] == 0x49 && header[tiffoffset + 1] == 0x49) - { - ByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else if (header[tiffoffset] == 0x4D && header[tiffoffset + 1] == 0x4D) - { - ByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - else - { - throw new NotValidExifFileException(); - } - - // TIFF header may have a different byte order - BitConverterEx.ByteOrder tiffByteOrder = ByteOrder; - if (BitConverterEx.LittleEndian.ToUInt16(header, tiffoffset + 2) == 42) - { - tiffByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else if (BitConverterEx.BigEndian.ToUInt16(header, tiffoffset + 2) == 42) - { - tiffByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - else - { - throw new NotValidExifFileException(); - } - - // Offset to 0th IFD - var ifd0offset = (int)BitConverterEx.ToUInt32(header, tiffoffset + 4, tiffByteOrder, BitConverterEx.SystemByteOrder); - ifdqueue.Add(ifd0offset, IFD.Zeroth); - - var conv = new BitConverterEx(ByteOrder, BitConverterEx.SystemByteOrder); - var thumboffset = -1; - var thumblength = 0; - var thumbtype = -1; - - // Read IFDs - while (ifdqueue.Count != 0) - { - var ifdoffset = tiffoffset + ifdqueue.Keys[0]; - IFD currentifd = ifdqueue.Values[0]; - ifdqueue.RemoveAt(0); - - // Field count - var fieldcount = conv.ToUInt16(header, ifdoffset); - for (short i = 0; i < fieldcount; i++) - { - // Read field info - var fieldoffset = ifdoffset + 2 + (12 * i); - var tag = conv.ToUInt16(header, fieldoffset); - var type = conv.ToUInt16(header, fieldoffset + 2); - var count = conv.ToUInt32(header, fieldoffset + 4); - var value = new byte[4]; - Array.Copy(header, fieldoffset + 8, value, 0, 4); - - // Fields containing offsets to other IFDs - if (currentifd == IFD.Zeroth && tag == 0x8769) - { - var exififdpointer = (int)conv.ToUInt32(value, 0); - ifdqueue.Add(exififdpointer, IFD.EXIF); - } - else if (currentifd == IFD.Zeroth && tag == 0x8825) - { - var gpsifdpointer = (int)conv.ToUInt32(value, 0); - ifdqueue.Add(gpsifdpointer, IFD.GPS); - } - else if (currentifd == IFD.EXIF && tag == 0xa005) - { - var interopifdpointer = (int)conv.ToUInt32(value, 0); - ifdqueue.Add(interopifdpointer, IFD.Interop); - } - - // Save the offset to maker note data - if (currentifd == IFD.EXIF && tag == 37500) - { - _makerNoteOffset = conv.ToUInt32(value, 0); - } - - // Calculate the bytes we need to read - uint baselength = 0; - if (type == 1 || type == 2 || type == 7) - { - baselength = 1; - } - else if (type == 3) - { - baselength = 2; - } - else if (type == 4 || type == 9) - { - baselength = 4; - } - else if (type == 5 || type == 10) - { - baselength = 8; - } - - var totallength = count * baselength; - - // If field value does not fit in 4 bytes - // the value field is an offset to the actual - // field value - var fieldposition = 0; - if (totallength > 4) - { - fieldposition = tiffoffset + (int)conv.ToUInt32(value, 0); - value = new byte[totallength]; - Array.Copy(header, fieldposition, value, 0, totallength); - } - - // Compressed thumbnail data - if (currentifd == IFD.First && tag == 0x201) - { - thumbtype = 0; - thumboffset = (int)conv.ToUInt32(value, 0); - } - else if (currentifd == IFD.First && tag == 0x202) - { - thumblength = (int)conv.ToUInt32(value, 0); - } - - // Uncompressed thumbnail data - if (currentifd == IFD.First && tag == 0x111) - { - thumbtype = 1; - - // Offset to first strip - if (type == 3) - { - thumboffset = conv.ToUInt16(value, 0); - } - else - { - thumboffset = (int)conv.ToUInt32(value, 0); - } - } - else if (currentifd == IFD.First && tag == 0x117) - { - thumblength = 0; - for (var j = 0; j < count; j++) - { - if (type == 3) - { - thumblength += conv.ToUInt16(value, 0); - } - else - { - thumblength += (int)conv.ToUInt32(value, 0); - } - } - } - - // Create the exif property from the interop data - ExifProperty prop = ExifPropertyFactory.Get(tag, type, count, value, ByteOrder, currentifd, Encoding); - Properties.Add(prop); - } - - // 1st IFD pointer - var firstifdpointer = (int)conv.ToUInt32(header, ifdoffset + 2 + (12 * fieldcount)); - if (firstifdpointer != 0) - { - ifdqueue.Add(firstifdpointer, IFD.First); - } - - // Read thumbnail - if (thumboffset != -1 && thumblength != 0 && Thumbnail == null) - { - if (thumbtype == 0) - { - using (var ts = new MemoryStream(header, tiffoffset + thumboffset, thumblength)) - { - Thumbnail = FromStream(ts); - } - } - } - } - } - - /// - /// Replaces the contents of the APP1 section with the Exif properties. - /// - private bool WriteExifApp1(bool preserveMakerNote) - { - // Zero out IFD field offsets. We will fill those as we write the IFD sections - _exifIfdFieldOffset = 0; - _gpsIfdFieldOffset = 0; - _interopIfdFieldOffset = 0; - _firstIfdFieldOffset = 0; - - // We also do not know the location of the embedded thumbnail yet - _thumbOffsetLocation = 0; - _thumbOffsetValue = 0; - _thumbSizeLocation = 0; - _thumbSizeValue = 0; - - // Write thumbnail tags if they are missing, remove otherwise - if (Thumbnail == null) - { - Properties.Remove(ExifTag.ThumbnailJPEGInterchangeFormat); - Properties.Remove(ExifTag.ThumbnailJPEGInterchangeFormatLength); - } - else - { - if (!Properties.ContainsKey(ExifTag.ThumbnailJPEGInterchangeFormat)) - { - Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormat, 0)); - } - - if (!Properties.ContainsKey(ExifTag.ThumbnailJPEGInterchangeFormatLength)) - { - Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormatLength, 0)); - } - } - - // Which IFD sections do we have? - var ifdzeroth = new Dictionary(); - var ifdexif = new Dictionary(); - var ifdgps = new Dictionary(); - var ifdinterop = new Dictionary(); - var ifdfirst = new Dictionary(); - - foreach (ExifProperty prop in Properties) - { - switch (prop.IFD) - { - case IFD.Zeroth: - ifdzeroth.Add(prop.Tag, prop); - break; - case IFD.EXIF: - ifdexif.Add(prop.Tag, prop); - break; - case IFD.GPS: - ifdgps.Add(prop.Tag, prop); - break; - case IFD.Interop: - ifdinterop.Add(prop.Tag, prop); - break; - case IFD.First: - ifdfirst.Add(prop.Tag, prop); - break; - } - } - - // Add IFD pointers if they are missing - // We will write the pointer values later on - if (ifdexif.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer)) - { - ifdzeroth.Add(ExifTag.EXIFIFDPointer, new ExifUInt(ExifTag.EXIFIFDPointer, 0)); - } - - if (ifdgps.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer)) - { - ifdzeroth.Add(ExifTag.GPSIFDPointer, new ExifUInt(ExifTag.GPSIFDPointer, 0)); - } - - if (ifdinterop.Count != 0 && !ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer)) - { - ifdexif.Add(ExifTag.InteroperabilityIFDPointer, new ExifUInt(ExifTag.InteroperabilityIFDPointer, 0)); - } - - // Remove IFD pointers if IFD sections are missing - if (ifdexif.Count == 0 && ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer)) - { - ifdzeroth.Remove(ExifTag.EXIFIFDPointer); - } - - if (ifdgps.Count == 0 && ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer)) - { - ifdzeroth.Remove(ExifTag.GPSIFDPointer); - } - - if (ifdinterop.Count == 0 && ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer)) - { - ifdexif.Remove(ExifTag.InteroperabilityIFDPointer); - } - - if (ifdzeroth.Count == 0 && ifdgps.Count == 0 && ifdinterop.Count == 0 && ifdfirst.Count == 0 && - Thumbnail == null) - { - // Nothing to write - return false; - } - - // We will need these BitConverters to write byte-ordered data - var bceExif = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder); - - // Create a memory stream to write the APP1 section to - var ms = new MemoryStream(); - - // Exif identifier - ms.Write(Encoding.ASCII.GetBytes("Exif\0\0"), 0, 6); - - // TIFF header - // Byte order - var tiffoffset = ms.Position; - ms.Write(ByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }, 0, 2); - - // TIFF ID - ms.Write(bceExif.GetBytes((ushort)42), 0, 2); - - // Offset to 0th IFD - ms.Write(bceExif.GetBytes((uint)8), 0, 4); - - // Write IFDs - WriteIFD(ms, ifdzeroth, IFD.Zeroth, tiffoffset, preserveMakerNote); - var exififdrelativeoffset = (uint)(ms.Position - tiffoffset); - WriteIFD(ms, ifdexif, IFD.EXIF, tiffoffset, preserveMakerNote); - var gpsifdrelativeoffset = (uint)(ms.Position - tiffoffset); - WriteIFD(ms, ifdgps, IFD.GPS, tiffoffset, preserveMakerNote); - var interopifdrelativeoffset = (uint)(ms.Position - tiffoffset); - WriteIFD(ms, ifdinterop, IFD.Interop, tiffoffset, preserveMakerNote); - var firstifdrelativeoffset = (uint)(ms.Position - tiffoffset); - WriteIFD(ms, ifdfirst, IFD.First, tiffoffset, preserveMakerNote); - - // Now that we now the location of IFDs we can go back and write IFD offsets - if (_exifIfdFieldOffset != 0) - { - ms.Seek(_exifIfdFieldOffset, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(exififdrelativeoffset), 0, 4); - } - - if (_gpsIfdFieldOffset != 0) - { - ms.Seek(_gpsIfdFieldOffset, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(gpsifdrelativeoffset), 0, 4); - } - - if (_interopIfdFieldOffset != 0) - { - ms.Seek(_interopIfdFieldOffset, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(interopifdrelativeoffset), 0, 4); - } - - if (_firstIfdFieldOffset != 0) - { - ms.Seek(_firstIfdFieldOffset, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(firstifdrelativeoffset), 0, 4); - } - - // We can write thumbnail location now - if (_thumbOffsetLocation != 0) - { - ms.Seek(_thumbOffsetLocation, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(_thumbOffsetValue), 0, 4); - } - - if (_thumbSizeLocation != 0) - { - ms.Seek(_thumbSizeLocation, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(_thumbSizeValue), 0, 4); - } - - ms.Close(); - - if (_exifApp1 is not null) - { - // Return APP1 header - _exifApp1.Header = ms.ToArray(); - return true; - } - - return false; - } - - private void WriteIFD(MemoryStream stream, Dictionary ifd, IFD ifdtype, long tiffoffset, bool preserveMakerNote) - { - var conv = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder); - - // Create a queue of fields to write - var fieldqueue = new Queue(); - foreach (ExifProperty prop in ifd.Values) - { - if (prop.Tag != ExifTag.MakerNote) - { - fieldqueue.Enqueue(prop); - } - } - - // Push the maker note data to the end - if (ifd.ContainsKey(ExifTag.MakerNote)) - { - fieldqueue.Enqueue(ifd[ExifTag.MakerNote]); - } - - // Offset to start of field data from start of TIFF header - var dataoffset = (uint)(2 + (ifd.Count * 12) + 4 + stream.Position - tiffoffset); - var currentdataoffset = dataoffset; - var absolutedataoffset = stream.Position + (2 + (ifd.Count * 12) + 4); - - var makernotewritten = false; - - // Field count - stream.Write(conv.GetBytes((ushort)ifd.Count), 0, 2); - - // Fields - while (fieldqueue.Count != 0) - { - ExifProperty field = fieldqueue.Dequeue(); - ExifInterOperability interop = field.Interoperability; - - uint fillerbytecount = 0; - - // Try to preserve the makernote data offset - if (!makernotewritten && - !_makerNoteProcessed && - _makerNoteOffset != 0 && - ifdtype == IFD.EXIF && - field.Tag != ExifTag.MakerNote && - interop.Data.Length > 4 && - currentdataoffset + interop.Data.Length > _makerNoteOffset && - ifd.ContainsKey(ExifTag.MakerNote)) - { - // Delay writing this field until we write the creator's note data - fieldqueue.Enqueue(field); - continue; - } - - if (field.Tag == ExifTag.MakerNote) - { - makernotewritten = true; - - // We may need to write filler bytes to preserve maker note offset - if (preserveMakerNote && !_makerNoteProcessed && _makerNoteOffset > currentdataoffset) - { - fillerbytecount = _makerNoteOffset - currentdataoffset; - } - else - { - fillerbytecount = 0; - } - } - - // Tag - stream.Write(conv.GetBytes(interop.TagID), 0, 2); - - // Type - stream.Write(conv.GetBytes(interop.TypeID), 0, 2); - - // Count - stream.Write(conv.GetBytes(interop.Count), 0, 4); - - // Field data - var data = interop.Data; - if (ByteOrder != BitConverterEx.SystemByteOrder && - (interop.TypeID == 3 || interop.TypeID == 4 || interop.TypeID == 9 || - interop.TypeID == 5 || interop.TypeID == 10)) - { - var vlen = 4; - if (interop.TypeID == 3) - { - vlen = 2; - } - - var n = data.Length / vlen; - - for (var i = 0; i < n; i++) - { - Array.Reverse(data, i * vlen, vlen); - } - } - - // Fields containing offsets to other IFDs - // Just store their offsets, we will write the values later on when we know the lengths of IFDs - if (ifdtype == IFD.Zeroth && interop.TagID == 0x8769) - { - _exifIfdFieldOffset = stream.Position; - } - else if (ifdtype == IFD.Zeroth && interop.TagID == 0x8825) - { - _gpsIfdFieldOffset = stream.Position; - } - else if (ifdtype == IFD.EXIF && interop.TagID == 0xa005) - { - _interopIfdFieldOffset = stream.Position; - } - else if (ifdtype == IFD.First && interop.TagID == 0x201) - { - _thumbOffsetLocation = stream.Position; - } - else if (ifdtype == IFD.First && interop.TagID == 0x202) - { - _thumbSizeLocation = stream.Position; - } - - // Write 4 byte field value or field data - if (data.Length <= 4) - { - stream.Write(data, 0, data.Length); - for (var i = data.Length; i < 4; i++) - { - stream.WriteByte(0); - } - } - else - { - // Pointer to data area relative to TIFF header - stream.Write(conv.GetBytes(currentdataoffset + fillerbytecount), 0, 4); - - // Actual data - var currentoffset = stream.Position; - stream.Seek(absolutedataoffset, SeekOrigin.Begin); - - // Write filler bytes - for (var i = 0; i < fillerbytecount; i++) - { - stream.WriteByte(0xFF); - } - - stream.Write(data, 0, data.Length); - stream.Seek(currentoffset, SeekOrigin.Begin); - - // Increment pointers - currentdataoffset += fillerbytecount + (uint)data.Length; - absolutedataoffset += fillerbytecount + data.Length; - } - } - - // Offset to 1st IFD - // We will write zeros for now. This will be filled after we write all IFDs - if (ifdtype == IFD.Zeroth) - { - _firstIfdFieldOffset = stream.Position; - } - - stream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4); - - // Seek to end of IFD - stream.Seek(absolutedataoffset, SeekOrigin.Begin); - - // Write thumbnail data - if (ifdtype == IFD.First) - { - if (Thumbnail != null) - { - var ts = new MemoryStream(); - Thumbnail.Save(ts); - ts.Close(); - var thumb = ts.ToArray(); - _thumbOffsetValue = (uint)(stream.Position - tiffoffset); - _thumbSizeValue = (uint)thumb.Length; - stream.Write(thumb, 0, thumb.Length); - ts.Dispose(); - } - else - { - _thumbOffsetValue = 0; - _thumbSizeValue = 0; - } - } - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/JPEGMarker.cs b/src/Umbraco.Core/Media/Exif/JPEGMarker.cs deleted file mode 100644 index 3912d87e82..0000000000 --- a/src/Umbraco.Core/Media/Exif/JPEGMarker.cs +++ /dev/null @@ -1,95 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a JPEG marker byte. -/// -internal enum JPEGMarker : byte -{ - // Start Of Frame markers, non-differential, Huffman coding - SOF0 = 0xc0, - SOF1 = 0xc1, - SOF2 = 0xc2, - SOF3 = 0xc3, - - // Start Of Frame markers, differential, Huffman coding - SOF5 = 0xc5, - SOF6 = 0xc6, - SOF7 = 0xc7, - - // Start Of Frame markers, non-differential, arithmetic coding - JPG = 0xc8, - SOF9 = 0xc9, - SOF10 = 0xca, - SOF11 = 0xcb, - - // Start Of Frame markers, differential, arithmetic coding - SOF13 = 0xcd, - SOF14 = 0xce, - SOF15 = 0xcf, - - // Huffman table specification - DHT = 0xc4, - - // Arithmetic coding conditioning specification - DAC = 0xcc, - - // Restart interval termination - RST0 = 0xd0, - RST1 = 0xd1, - RST2 = 0xd2, - RST3 = 0xd3, - RST4 = 0xd4, - RST5 = 0xd5, - RST6 = 0xd6, - RST7 = 0xd7, - - // Other markers - SOI = 0xd8, - EOI = 0xd9, - SOS = 0xda, - DQT = 0xdb, - DNL = 0xdc, - DRI = 0xdd, - DHP = 0xde, - EXP = 0xdf, - - // application segments - APP0 = 0xe0, - APP1 = 0xe1, - APP2 = 0xe2, - APP3 = 0xe3, - APP4 = 0xe4, - APP5 = 0xe5, - APP6 = 0xe6, - APP7 = 0xe7, - APP8 = 0xe8, - APP9 = 0xe9, - APP10 = 0xea, - APP11 = 0xeb, - APP12 = 0xec, - APP13 = 0xed, - APP14 = 0xee, - APP15 = 0xef, - - // JPEG extensions - JPG0 = 0xf0, - JPG1 = 0xf1, - JPG2 = 0xf2, - JPG3 = 0xf3, - JPG4 = 0xf4, - JPG5 = 0xf5, - JPG6 = 0xf6, - JPG7 = 0xf7, - JPG8 = 0xf8, - JPG9 = 0xf9, - JPG10 = 0xfa, - JPG11 = 0xfb, - JP1G2 = 0xfc, - JPG13 = 0xfd, - - // Comment - COM = 0xfe, - - // Temporary - TEM = 0x01, -} diff --git a/src/Umbraco.Core/Media/Exif/JPEGSection.cs b/src/Umbraco.Core/Media/Exif/JPEGSection.cs deleted file mode 100644 index 787b04b056..0000000000 --- a/src/Umbraco.Core/Media/Exif/JPEGSection.cs +++ /dev/null @@ -1,66 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the memory view of a JPEG section. -/// A JPEG section is the data between markers of the JPEG file. -/// -internal class JPEGSection -{ - #region Instance Methods - - /// - /// Returns a string representation of the current section. - /// - /// A System.String that represents the current section. - public override string ToString() => string.Format("{0} => Header: {1} bytes, Entropy Data: {2} bytes", Marker, Header.Length, EntropyData.Length); - - #endregion - - #region Properties - - /// - /// The marker byte representing the section. - /// - public JPEGMarker Marker { get; } - - /// - /// Section header as a byte array. This is different from the header - /// definition in JPEG specification in that it does not include the - /// two byte section length. - /// - public byte[] Header { get; set; } - - /// - /// For the SOS and RST markers, this contains the entropy coded data. - /// - public byte[] EntropyData { get; set; } - - #endregion - - #region Constructors - - /// - /// Constructs a JPEGSection represented by the marker byte and containing - /// the given data. - /// - /// The marker byte representing the section. - /// Section data. - /// Entropy coded data. - public JPEGSection(JPEGMarker marker, byte[] data, byte[] entropydata) - { - Marker = marker; - Header = data; - EntropyData = entropydata; - } - - /// - /// Constructs a JPEGSection represented by the marker byte. - /// - /// The marker byte representing the section. - public JPEGSection(JPEGMarker marker) - : this(marker, new byte[0], new byte[0]) - { - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/MathEx.cs b/src/Umbraco.Core/Media/Exif/MathEx.cs deleted file mode 100644 index fbf5f2dbde..0000000000 --- a/src/Umbraco.Core/Media/Exif/MathEx.cs +++ /dev/null @@ -1,1329 +0,0 @@ -using System.Globalization; -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Contains extended Math functions. -/// -internal static class MathEx -{ - /// - /// Returns the greatest common divisor of two numbers. - /// - /// First number. - /// Second number. - public static uint GCD(uint a, uint b) - { - while (b != 0) - { - var rem = a % b; - a = b; - b = rem; - } - - return a; - } - - /// - /// Returns the greatest common divisor of two numbers. - /// - /// First number. - /// Second number. - public static ulong GCD(ulong a, ulong b) - { - while (b != 0) - { - var rem = a % b; - a = b; - b = rem; - } - - return a; - } - - /// - /// Represents a generic rational number represented by 32-bit signed numerator and denominator. - /// - public struct Fraction32 : IComparable, IFormattable, IComparable, IEquatable - { - #region Constants - - private const uint MaximumIterations = 10000000; - - #endregion - - #region Member Variables - - private int mNumerator; - private int mDenominator; - - #endregion - - #region Properties - - /// - /// Gets or sets the numerator. - /// - public int Numerator - { - get => (IsNegative ? -1 : 1) * mNumerator; - set - { - if (value < 0) - { - IsNegative = true; - mNumerator = -1 * value; - } - else - { - IsNegative = false; - mNumerator = value; - } - - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Gets or sets the denominator. - /// - public int Denominator - { - get => mDenominator; - set - { - mDenominator = Math.Abs(value); - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Gets the error term. - /// - public double Error { get; } - - /// - /// Gets or sets a value determining id the fraction is a negative value. - /// - public bool IsNegative { get; set; } - - #endregion - - #region Predefined Values - - public static readonly Fraction32 NaN = new(0, 0); - public static readonly Fraction32 NegativeInfinity = new(-1, 0); - public static readonly Fraction32 PositiveInfinity = new(1, 0); - - #endregion - - #region Static Methods - - /// - /// Returns a value indicating whether the specified number evaluates to a value - /// that is not a number. - /// - /// A fraction. - /// true if f evaluates to Fraction.NaN; otherwise, false. - public static bool IsNan(Fraction32 f) => f.Numerator == 0 && f.Denominator == 0; - - /// - /// Returns a value indicating whether the specified number evaluates to negative - /// infinity. - /// - /// A fraction. - /// true if f evaluates to Fraction.NegativeInfinity; otherwise, false. - public static bool IsNegativeInfinity(Fraction32 f) => f.Numerator < 0 && f.Denominator == 0; - - /// - /// Returns a value indicating whether the specified number evaluates to positive - /// infinity. - /// - /// A fraction. - /// true if f evaluates to Fraction.PositiveInfinity; otherwise, false. - public static bool IsPositiveInfinity(Fraction32 f) => f.Numerator > 0 && f.Denominator == 0; - - /// - /// Returns a value indicating whether the specified number evaluates to negative - /// or positive infinity. - /// - /// A fraction. - /// true if f evaluates to Fraction.NegativeInfinity or Fraction.PositiveInfinity; otherwise, false. - public static bool IsInfinity(Fraction32 f) => f.Denominator == 0; - - /// - /// Returns the multiplicative inverse of a given value. - /// - /// A fraction. - /// Multiplicative inverse of f. - public static Fraction32 Inverse(Fraction32 f) => new(f.Denominator, f.Numerator); - - /// - /// Converts the string representation of a fraction to a fraction object. - /// - /// A string formatted as numerator/denominator - /// A fraction object converted from s. - /// s is null - /// s is not in the correct format - /// - /// s represents a number less than System.UInt32.MinValue or greater than - /// System.UInt32.MaxValue. - /// - public static Fraction32 Parse(string s) => FromString(s); - - /// - /// Converts the string representation of a fraction to a fraction object. - /// A return value indicates whether the conversion succeeded. - /// - /// A string formatted as numerator/denominator - /// true if s was converted successfully; otherwise, false. - public static bool TryParse(string s, out Fraction32 f) - { - try - { - f = Parse(s); - return true; - } - catch - { - f = new Fraction32(); - return false; - } - } - - #endregion - - #region Operators - - #region Arithmetic Operators - - // Multiplication - public static Fraction32 operator *(Fraction32 f, int n) => new(f.Numerator * n, f.Denominator * Math.Abs(n)); - - public static Fraction32 operator *(int n, Fraction32 f) => f * n; - - public static Fraction32 operator *(Fraction32 f, float n) => new((float)f * n); - - public static Fraction32 operator *(float n, Fraction32 f) => f * n; - - public static Fraction32 operator *(Fraction32 f, double n) => new((double)f * n); - - public static Fraction32 operator *(double n, Fraction32 f) => f * n; - - public static Fraction32 operator *(Fraction32 f1, Fraction32 f2) => - new(f1.Numerator * f2.Numerator, f1.Denominator * f2.Denominator); - - // Division - public static Fraction32 operator /(Fraction32 f, int n) => new(f.Numerator / n, f.Denominator / Math.Abs(n)); - - public static Fraction32 operator /(Fraction32 f, float n) => new((float)f / n); - - public static Fraction32 operator /(Fraction32 f, double n) => new((double)f / n); - - public static Fraction32 operator /(Fraction32 f1, Fraction32 f2) => f1 * Inverse(f2); - - // Addition - public static Fraction32 operator +(Fraction32 f, int n) => f + new Fraction32(n, 1); - - public static Fraction32 operator +(int n, Fraction32 f) => f + n; - - public static Fraction32 operator +(Fraction32 f, float n) => new((float)f + n); - - public static Fraction32 operator +(float n, Fraction32 f) => f + n; - - public static Fraction32 operator +(Fraction32 f, double n) => new((double)f + n); - - public static Fraction32 operator +(double n, Fraction32 f) => f + n; - - public static Fraction32 operator +(Fraction32 f1, Fraction32 f2) - { - int n1 = f1.Numerator, d1 = f1.Denominator; - int n2 = f2.Numerator, d2 = f2.Denominator; - - return new Fraction32((n1 * d2) + (n2 * d1), d1 * d2); - } - - // Subtraction - public static Fraction32 operator -(Fraction32 f, int n) => f - new Fraction32(n, 1); - - public static Fraction32 operator -(int n, Fraction32 f) => new Fraction32(n, 1) - f; - - public static Fraction32 operator -(Fraction32 f, float n) => new((float)f - n); - - public static Fraction32 operator -(float n, Fraction32 f) => new Fraction32(n) - f; - - public static Fraction32 operator -(Fraction32 f, double n) => new((double)f - n); - - public static Fraction32 operator -(double n, Fraction32 f) => new Fraction32(n) - f; - - public static Fraction32 operator -(Fraction32 f1, Fraction32 f2) - { - int n1 = f1.Numerator, d1 = f1.Denominator; - int n2 = f2.Numerator, d2 = f2.Denominator; - - return new Fraction32((n1 * d2) - (n2 * d1), d1 * d2); - } - - // Increment - public static Fraction32 operator ++(Fraction32 f) => f + new Fraction32(1, 1); - - // Decrement - public static Fraction32 operator --(Fraction32 f) => f - new Fraction32(1, 1); - - #endregion - - #region Casts To Integral Types - - public static explicit operator int(Fraction32 f) => f.Numerator / f.Denominator; - - public static explicit operator float(Fraction32 f) => f.Numerator / (float)f.Denominator; - - public static explicit operator double(Fraction32 f) => f.Numerator / (double)f.Denominator; - - #endregion - - #region Comparison Operators - - public static bool operator ==(Fraction32 f1, Fraction32 f2) => - f1.Numerator == f2.Numerator && f1.Denominator == f2.Denominator; - - public static bool operator !=(Fraction32 f1, Fraction32 f2) => - f1.Numerator != f2.Numerator || f1.Denominator != f2.Denominator; - - public static bool operator <(Fraction32 f1, Fraction32 f2) => - f1.Numerator * f2.Denominator < f2.Numerator * f1.Denominator; - - public static bool operator >(Fraction32 f1, Fraction32 f2) => - f1.Numerator * f2.Denominator > f2.Numerator * f1.Denominator; - - #endregion - - #endregion - - #region Constructors - - private Fraction32(int numerator, int denominator, double error) - { - IsNegative = false; - if (numerator < 0) - { - numerator = -numerator; - IsNegative = !IsNegative; - } - - if (denominator < 0) - { - denominator = -denominator; - IsNegative = !IsNegative; - } - - mNumerator = numerator; - mDenominator = denominator; - Error = error; - - if (mDenominator != 0) - { - Reduce(ref mNumerator, ref mDenominator); - } - } - - public Fraction32(int numerator, int denominator) - : this(numerator, denominator, 0) - { - } - - public Fraction32(int numerator) - : this(numerator, 1) - { - } - - public Fraction32(Fraction32 f) - : this(f.Numerator, f.Denominator, f.Error) - { - } - - public Fraction32(float value) - : this((double)value) - { - } - - public Fraction32(double value) - : this(FromDouble(value)) - { - } - - public Fraction32(string s) - : this(FromString(s)) - { - } - - #endregion - - #region Instance Methods - - /// - /// Sets the value of this instance to the fraction represented - /// by the given numerator and denominator. - /// - /// The new numerator. - /// The new denominator. - public void Set(int numerator, int denominator) - { - IsNegative = false; - if (numerator < 0) - { - IsNegative = !IsNegative; - numerator = -numerator; - } - - if (denominator < 0) - { - IsNegative = !IsNegative; - denominator = -denominator; - } - - mNumerator = numerator; - mDenominator = denominator; - - if (mDenominator != 0) - { - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Indicates whether this instance and a specified object are equal value-wise. - /// - /// Another object to compare to. - /// - /// true if obj and this instance are the same type and represent - /// the same value; otherwise, false. - /// - public override bool Equals(object? obj) - { - if (obj == null) - { - return false; - } - - if (obj is Fraction32) - { - return Equals((Fraction32)obj); - } - - return false; - } - - /// - /// Indicates whether this instance and a specified object are equal value-wise. - /// - /// Another fraction object to compare to. - /// - /// true if obj and this instance represent the same value; - /// otherwise, false. - /// - public bool Equals(Fraction32 obj) => IsNegative == obj.IsNegative && mNumerator == obj.Numerator && - mDenominator == obj.Denominator; - - /// - /// Returns the hash code for this instance. - /// - /// A 32-bit signed integer that is the hash code for this instance. - public override int GetHashCode() => mDenominator ^ ((IsNegative ? -1 : 1) * mNumerator); - - /// - /// Returns a string representation of the fraction. - /// - /// A numeric format string. - /// - /// An System.IFormatProvider that supplies culture-specific - /// formatting information. - /// - /// - /// The string representation of the value of this instance as - /// specified by format and provider. - /// - /// - /// format is invalid or not supported. - /// - public string ToString(string? format, IFormatProvider? formatProvider) - { - var sb = new StringBuilder(); - sb.Append(((IsNegative ? -1 : 1) * mNumerator).ToString(format, formatProvider)); - sb.Append('/'); - sb.Append(mDenominator.ToString(format, formatProvider)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// A numeric format string. - /// - /// The string representation of the value of this instance as - /// specified by format. - /// - /// - /// format is invalid or not supported. - /// - public string ToString(string format) - { - var sb = new StringBuilder(); - sb.Append(((IsNegative ? -1 : 1) * mNumerator).ToString(format)); - sb.Append('/'); - sb.Append(mDenominator.ToString(format)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// - /// An System.IFormatProvider that supplies culture-specific - /// formatting information. - /// - /// - /// The string representation of the value of this instance as - /// specified by provider. - /// - public string ToString(IFormatProvider formatProvider) - { - var sb = new StringBuilder(); - sb.Append(((IsNegative ? -1 : 1) * mNumerator).ToString(formatProvider)); - sb.Append('/'); - sb.Append(mDenominator.ToString(formatProvider)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// A string formatted as numerator/denominator. - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append(((IsNegative ? -1 : 1) * mNumerator).ToString()); - sb.Append('/'); - sb.Append(mDenominator.ToString()); - return sb.ToString(); - } - - /// - /// Compares this instance to a specified object and returns an indication of - /// their relative values. - /// - /// An object to compare, or null. - /// - /// A signed number indicating the relative values of this instance and value. - /// Less than zero: This instance is less than obj. - /// Zero: This instance is equal to obj. - /// Greater than zero: This instance is greater than obj or obj is null. - /// - /// obj is not a Fraction. - public int CompareTo(object? obj) - { - if (!(obj is Fraction32)) - { - throw new ArgumentException("obj must be of type Fraction", "obj"); - } - - return CompareTo((Fraction32)obj); - } - - /// - /// Compares this instance to a specified object and returns an indication of - /// their relative values. - /// - /// An fraction to compare with this instance. - /// - /// A signed number indicating the relative values of this instance and value. - /// Less than zero: This instance is less than obj. - /// Zero: This instance is equal to obj. - /// Greater than zero: This instance is greater than obj or obj is null. - /// - public int CompareTo(Fraction32 obj) - { - if (this < obj) - { - return -1; - } - - if (this > obj) - { - return 1; - } - - return 0; - } - - #endregion - - #region Private Helper Methods - - /// - /// Converts the given floating-point number to its rational representation. - /// - /// The floating-point number to be converted. - /// The rational representation of value. - private static Fraction32 FromDouble(double value) - { - if (double.IsNaN(value)) - { - return NaN; - } - - if (double.IsNegativeInfinity(value)) - { - return NegativeInfinity; - } - - if (double.IsPositiveInfinity(value)) - { - return PositiveInfinity; - } - - var isneg = value < 0; - if (isneg) - { - value = -value; - } - - var f = value; - var forg = f; - var lnum = 0; - var lden = 1; - var num = 1; - var den = 0; - var lasterr = 1.0; - var a = 0; - var currIteration = 0; - while (true) - { - if (++currIteration > MaximumIterations) - { - break; - } - - a = (int)Math.Floor(f); - f = f - a; - if (Math.Abs(f) < double.Epsilon) - { - break; - } - - f = 1.0 / f; - if (double.IsInfinity(f)) - { - break; - } - - var cnum = (num * a) + lnum; - var cden = (den * a) + lden; - if (Math.Abs((cnum / (double)cden) - forg) < double.Epsilon) - { - break; - } - - var err = ((cnum / (double)cden) - (num / (double)den)) / (num / (double)den); - - // Are we converging? - if (err >= lasterr) - { - break; - } - - lasterr = err; - lnum = num; - lden = den; - num = cnum; - den = cden; - } - - if (den > 0) - { - lasterr = value - (num / (double)den); - } - else - { - lasterr = double.PositiveInfinity; - } - - return new Fraction32((isneg ? -1 : 1) * num, den, lasterr); - } - - /// Converts the string representation of a fraction to a Fraction type. - /// The input string formatted as numerator/denominator. - /// s is null. - /// s is not formatted as numerator/denominator. - /// - /// s represents numbers less than System.Int32.MinValue or greater than - /// System.Int32.MaxValue. - /// - private static Fraction32 FromString(string s) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - var sa = s.Split(Constants.CharArrays.ForwardSlash); - var numerator = 1; - var denominator = 1; - - if (sa.Length == 1) - { - // Try to parse as int - if (int.TryParse(sa[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out numerator)) - { - denominator = 1; - } - else - { - // Parse as double - var dval = double.Parse(sa[0]); - return FromDouble(dval); - } - } - else if (sa.Length == 2) - { - numerator = int.Parse(sa[0], CultureInfo.InvariantCulture); - denominator = int.Parse(sa[1], CultureInfo.InvariantCulture); - } - else - { - throw new FormatException("The input string must be formatted as n/d where n and d are integers"); - } - - return new Fraction32(numerator, denominator); - } - - /// - /// Reduces the given numerator and denominator by dividing with their - /// greatest common divisor. - /// - /// numerator to be reduced. - /// denominator to be reduced. - private static void Reduce(ref int numerator, ref int denominator) - { - var gcd = GCD((uint)numerator, (uint)denominator); - if (gcd == 0) - { - gcd = 1; - } - - numerator = numerator / (int)gcd; - denominator = denominator / (int)gcd; - } - - #endregion - } - - /// - /// Represents a generic rational number represented by 32-bit unsigned numerator and denominator. - /// - public struct UFraction32 : IComparable, IFormattable, IComparable, IEquatable - { - #region Constants - - private const uint MaximumIterations = 10000000; - - #endregion - - #region Member Variables - - private uint mNumerator; - private uint mDenominator; - - #endregion - - #region Properties - - /// - /// Gets or sets the numerator. - /// - public uint Numerator - { - get => mNumerator; - set - { - mNumerator = value; - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Gets or sets the denominator. - /// - public uint Denominator - { - get => mDenominator; - set - { - mDenominator = value; - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Gets the error term. - /// - public double Error { get; } - - #endregion - - #region Predefined Values - - public static readonly UFraction32 NaN = new(0, 0); - public static readonly UFraction32 Infinity = new(1, 0); - - #endregion - - #region Static Methods - - /// - /// Returns a value indicating whether the specified number evaluates to a value - /// that is not a number. - /// - /// A fraction. - /// true if f evaluates to Fraction.NaN; otherwise, false. - public static bool IsNan(UFraction32 f) => f.Numerator == 0 && f.Denominator == 0; - - /// - /// Returns a value indicating whether the specified number evaluates to infinity. - /// - /// A fraction. - /// true if f evaluates to Fraction.Infinity; otherwise, false. - public static bool IsInfinity(UFraction32 f) => f.Denominator == 0; - - /// - /// Converts the string representation of a fraction to a fraction object. - /// - /// A string formatted as numerator/denominator - /// A fraction object converted from s. - /// s is null - /// s is not in the correct format - /// - /// s represents a number less than System.UInt32.MinValue or greater than - /// System.UInt32.MaxValue. - /// - public static UFraction32 Parse(string s) => FromString(s); - - /// - /// Converts the string representation of a fraction to a fraction object. - /// A return value indicates whether the conversion succeeded. - /// - /// A string formatted as numerator/denominator - /// true if s was converted successfully; otherwise, false. - public static bool TryParse(string s, out UFraction32 f) - { - try - { - f = Parse(s); - return true; - } - catch - { - f = new UFraction32(); - return false; - } - } - - #endregion - - #region Operators - - #region Arithmetic Operators - - // Multiplication - public static UFraction32 operator *(UFraction32 f, uint n) => new(f.Numerator * n, f.Denominator * n); - - public static UFraction32 operator *(uint n, UFraction32 f) => f * n; - - public static UFraction32 operator *(UFraction32 f, float n) => new((float)f * n); - - public static UFraction32 operator *(float n, UFraction32 f) => f * n; - - public static UFraction32 operator *(UFraction32 f, double n) => new((double)f * n); - - public static UFraction32 operator *(double n, UFraction32 f) => f * n; - - public static UFraction32 operator *(UFraction32 f1, UFraction32 f2) => - new(f1.Numerator * f2.Numerator, f1.Denominator * f2.Denominator); - - // Division - public static UFraction32 operator /(UFraction32 f, uint n) => new(f.Numerator / n, f.Denominator / n); - - public static UFraction32 operator /(UFraction32 f, float n) => new((float)f / n); - - public static UFraction32 operator /(UFraction32 f, double n) => new((double)f / n); - - public static UFraction32 operator /(UFraction32 f1, UFraction32 f2) => f1 * Inverse(f2); - - // Addition - public static UFraction32 operator +(UFraction32 f, uint n) => f + new UFraction32(n, 1); - - public static UFraction32 operator +(uint n, UFraction32 f) => f + n; - - public static UFraction32 operator +(UFraction32 f, float n) => new((float)f + n); - - public static UFraction32 operator +(float n, UFraction32 f) => f + n; - - public static UFraction32 operator +(UFraction32 f, double n) => new((double)f + n); - - public static UFraction32 operator +(double n, UFraction32 f) => f + n; - - public static UFraction32 operator +(UFraction32 f1, UFraction32 f2) - { - uint n1 = f1.Numerator, d1 = f1.Denominator; - uint n2 = f2.Numerator, d2 = f2.Denominator; - - return new UFraction32((n1 * d2) + (n2 * d1), d1 * d2); - } - - // Subtraction - public static UFraction32 operator -(UFraction32 f, uint n) => f - new UFraction32(n, 1); - - public static UFraction32 operator -(uint n, UFraction32 f) => new UFraction32(n, 1) - f; - - public static UFraction32 operator -(UFraction32 f, float n) => new((float)f - n); - - public static UFraction32 operator -(float n, UFraction32 f) => new UFraction32(n) - f; - - public static UFraction32 operator -(UFraction32 f, double n) => new((double)f - n); - - public static UFraction32 operator -(double n, UFraction32 f) => new UFraction32(n) - f; - - public static UFraction32 operator -(UFraction32 f1, UFraction32 f2) - { - uint n1 = f1.Numerator, d1 = f1.Denominator; - uint n2 = f2.Numerator, d2 = f2.Denominator; - - return new UFraction32((n1 * d2) - (n2 * d1), d1 * d2); - } - - // Increment - public static UFraction32 operator ++(UFraction32 f) => f + new UFraction32(1, 1); - - // Decrement - public static UFraction32 operator --(UFraction32 f) => f - new UFraction32(1, 1); - - #endregion - - #region Casts To Integral Types - - public static explicit operator uint(UFraction32 f) => f.Numerator / f.Denominator; - - public static explicit operator float(UFraction32 f) => f.Numerator / (float)f.Denominator; - public static explicit operator double(UFraction32 f) => f.Numerator / (double)f.Denominator; - - #endregion - - #region Comparison Operators - - public static bool operator ==(UFraction32 f1, UFraction32 f2) => - f1.Numerator == f2.Numerator && f1.Denominator == f2.Denominator; - - public static bool operator !=(UFraction32 f1, UFraction32 f2) => - f1.Numerator != f2.Numerator || f1.Denominator != f2.Denominator; - - public static bool operator <(UFraction32 f1, UFraction32 f2) => - f1.Numerator * f2.Denominator < f2.Numerator * f1.Denominator; - - public static bool operator >(UFraction32 f1, UFraction32 f2) => - f1.Numerator * f2.Denominator > f2.Numerator * f1.Denominator; - - #endregion - - #endregion - - #region Constructors - - public UFraction32(uint numerator, uint denominator, double error) - { - mNumerator = numerator; - mDenominator = denominator; - Error = error; - - if (mDenominator != 0) - { - Reduce(ref mNumerator, ref mDenominator); - } - } - - public UFraction32(uint numerator, uint denominator) - : this(numerator, denominator, 0) - { - } - - public UFraction32(uint numerator) - : this(numerator, 1) - { - } - - public UFraction32(UFraction32 f) - : this(f.Numerator, f.Denominator, f.Error) - { - } - - public UFraction32(float value) - : this((double)value) - { - } - - public UFraction32(double value) - : this(FromDouble(value)) - { - } - - public UFraction32(string s) - : this(FromString(s)) - { - } - - #endregion - - #region Instance Methods - - /// - /// Sets the value of this instance to the fraction represented - /// by the given numerator and denominator. - /// - /// The new numerator. - /// The new denominator. - public void Set(uint numerator, uint denominator) - { - mNumerator = numerator; - mDenominator = denominator; - - if (mDenominator != 0) - { - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Returns the multiplicative inverse of a given value. - /// - /// A fraction. - /// Multiplicative inverse of f. - public static UFraction32 Inverse(UFraction32 f) => new(f.Denominator, f.Numerator); - - /// - /// Indicates whether this instance and a specified object are equal value-wise. - /// - /// Another object to compare to. - /// - /// true if obj and this instance are the same type and represent - /// the same value; otherwise, false. - /// - public override bool Equals(object? obj) - { - if (obj == null) - { - return false; - } - - if (obj is UFraction32) - { - return Equals((UFraction32)obj); - } - - return false; - } - - /// - /// Indicates whether this instance and a specified object are equal value-wise. - /// - /// Another fraction object to compare to. - /// - /// true if obj and this instance represent the same value; - /// otherwise, false. - /// - public bool Equals(UFraction32 obj) => mNumerator == obj.Numerator && mDenominator == obj.Denominator; - - /// - /// Returns the hash code for this instance. - /// - /// A 32-bit signed integer that is the hash code for this instance. - public override int GetHashCode() => (int)mDenominator ^ (int)mNumerator; - - /// - /// Returns a string representation of the fraction. - /// - /// A numeric format string. - /// - /// An System.IFormatProvider that supplies culture-specific - /// formatting information. - /// - /// - /// The string representation of the value of this instance as - /// specified by format and provider. - /// - /// - /// format is invalid or not supported. - /// - public string ToString(string? format, IFormatProvider? formatProvider) - { - var sb = new StringBuilder(); - sb.Append(mNumerator.ToString(format, formatProvider)); - sb.Append('/'); - sb.Append(mDenominator.ToString(format, formatProvider)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// A numeric format string. - /// - /// The string representation of the value of this instance as - /// specified by format. - /// - /// - /// format is invalid or not supported. - /// - public string ToString(string format) - { - var sb = new StringBuilder(); - sb.Append(mNumerator.ToString(format)); - sb.Append('/'); - sb.Append(mDenominator.ToString(format)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// - /// An System.IFormatProvider that supplies culture-specific - /// formatting information. - /// - /// - /// The string representation of the value of this instance as - /// specified by provider. - /// - public string ToString(IFormatProvider formatProvider) - { - var sb = new StringBuilder(); - sb.Append(mNumerator.ToString(formatProvider)); - sb.Append('/'); - sb.Append(mDenominator.ToString(formatProvider)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// A string formatted as numerator/denominator. - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append(mNumerator.ToString()); - sb.Append('/'); - sb.Append(mDenominator.ToString()); - return sb.ToString(); - } - - /// - /// Compares this instance to a specified object and returns an indication of - /// their relative values. - /// - /// An object to compare, or null. - /// - /// A signed number indicating the relative values of this instance and value. - /// Less than zero: This instance is less than obj. - /// Zero: This instance is equal to obj. - /// Greater than zero: This instance is greater than obj or obj is null. - /// - /// obj is not a Fraction. - public int CompareTo(object? obj) - { - if (!(obj is UFraction32)) - { - throw new ArgumentException("obj must be of type UFraction32", "obj"); - } - - return CompareTo((UFraction32)obj); - } - - /// - /// Compares this instance to a specified object and returns an indication of - /// their relative values. - /// - /// An fraction to compare with this instance. - /// - /// A signed number indicating the relative values of this instance and value. - /// Less than zero: This instance is less than obj. - /// Zero: This instance is equal to obj. - /// Greater than zero: This instance is greater than obj or obj is null. - /// - public int CompareTo(UFraction32 obj) - { - if (this < obj) - { - return -1; - } - - if (this > obj) - { - return 1; - } - - return 0; - } - - #endregion - - #region Private Helper Methods - - /// - /// Converts the given floating-point number to its rational representation. - /// - /// The floating-point number to be converted. - /// The rational representation of value. - private static UFraction32 FromDouble(double value) - { - if (value < 0) - { - throw new ArgumentException("value cannot be negative.", "value"); - } - - if (double.IsNaN(value)) - { - return NaN; - } - - if (double.IsInfinity(value)) - { - return Infinity; - } - - var f = value; - var forg = f; - uint lnum = 0; - uint lden = 1; - uint num = 1; - uint den = 0; - var lasterr = 1.0; - uint a = 0; - var currIteration = 0; - while (true) - { - if (++currIteration > MaximumIterations) - { - break; - } - - a = (uint)Math.Floor(f); - f = f - a; - if (Math.Abs(f) < double.Epsilon) - { - break; - } - - f = 1.0 / f; - if (double.IsInfinity(f)) - { - break; - } - - var cnum = (num * a) + lnum; - var cden = (den * a) + lden; - if (Math.Abs((cnum / (double)cden) - forg) < double.Epsilon) - { - break; - } - - var err = ((cnum / (double)cden) - (num / (double)den)) / (num / (double)den); - - // Are we converging? - if (err >= lasterr) - { - break; - } - - lasterr = err; - lnum = num; - lden = den; - num = cnum; - den = cden; - } - - var fnum = (num * a) + lnum; - var fden = (den * a) + lden; - - if (fden > 0) - { - lasterr = value - (fnum / (double)fden); - } - else - { - lasterr = double.PositiveInfinity; - } - - return new UFraction32(fnum, fden, lasterr); - } - - /// Converts the string representation of a fraction to a Fraction type. - /// The input string formatted as numerator/denominator. - /// s is null. - /// s is not formatted as numerator/denominator. - /// - /// s represents numbers less than System.UInt32.MinValue or greater than - /// System.UInt32.MaxValue. - /// - private static UFraction32 FromString(string s) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - var sa = s.Split(Constants.CharArrays.ForwardSlash); - uint numerator = 1; - uint denominator = 1; - - if (sa.Length == 1) - { - // Try to parse as uint - if (uint.TryParse(sa[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out numerator)) - { - denominator = 1; - } - else - { - // Parse as double - var dval = double.Parse(sa[0]); - return FromDouble(dval); - } - } - else if (sa.Length == 2) - { - numerator = uint.Parse(sa[0], CultureInfo.InvariantCulture); - denominator = uint.Parse(sa[1], CultureInfo.InvariantCulture); - } - else - { - throw new FormatException("The input string must be formatted as n/d where n and d are integers"); - } - - return new UFraction32(numerator, denominator); - } - - /// - /// Reduces the given numerator and denominator by dividing with their - /// greatest common divisor. - /// - /// numerator to be reduced. - /// denominator to be reduced. - private static void Reduce(ref uint numerator, ref uint denominator) - { - var gcd = GCD(numerator, denominator); - numerator = numerator / gcd; - denominator = denominator / gcd; - } - - #endregion - } -} diff --git a/src/Umbraco.Core/Media/Exif/SvgFile.cs b/src/Umbraco.Core/Media/Exif/SvgFile.cs deleted file mode 100644 index 08326e634c..0000000000 --- a/src/Umbraco.Core/Media/Exif/SvgFile.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Globalization; -using System.Xml.Linq; - -namespace Umbraco.Cms.Core.Media.Exif; - -internal class SvgFile : ImageFile -{ - public SvgFile(Stream fileStream) - { - fileStream.Position = 0; - - var document = - XDocument.Load(fileStream); // if it throws an exception the ugly try catch in MediaFileSystem will catch it - - var width = document.Root?.Attributes().Where(x => x.Name == "width").Select(x => x.Value).FirstOrDefault(); - var height = document.Root?.Attributes().Where(x => x.Name == "height").Select(x => x.Value).FirstOrDefault(); - - Properties.Add(new ExifSInt( - ExifTag.PixelYDimension, - height == null ? Constants.Conventions.Media.DefaultSize : int.Parse(height, CultureInfo.InvariantCulture))); - Properties.Add(new ExifSInt( - ExifTag.PixelXDimension, - width == null ? Constants.Conventions.Media.DefaultSize : int.Parse(width, CultureInfo.InvariantCulture))); - - Format = ImageFileFormat.SVG; - } - - public override void Save(Stream stream) - { - } -} diff --git a/src/Umbraco.Core/Media/Exif/TIFFFile.cs b/src/Umbraco.Core/Media/Exif/TIFFFile.cs deleted file mode 100644 index 2ae27c46dc..0000000000 --- a/src/Umbraco.Core/Media/Exif/TIFFFile.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the binary view of a TIFF file. -/// -internal class TIFFFile : ImageFile -{ - #region Constructor - - /// - /// Initializes a new instance of the class from the - /// specified data stream. - /// - /// A that contains image data. - /// The encoding to be used for text metadata when the source encoding is unknown. - protected internal TIFFFile(Stream stream, Encoding encoding) - { - Format = ImageFileFormat.TIFF; - IFDs = new List(); - Encoding = encoding; - - // Read the entire stream - var data = Utility.GetStreamBytes(stream); - - // Read the TIFF header - TIFFHeader = TIFFHeader.FromBytes(data, 0); - var nextIFDOffset = TIFFHeader.IFDOffset; - if (nextIFDOffset == 0) - { - throw new NotValidTIFFileException("The first IFD offset is zero."); - } - - // Read IFDs in order - while (nextIFDOffset != 0) - { - var ifd = ImageFileDirectory.FromBytes(data, nextIFDOffset, TIFFHeader.ByteOrder); - nextIFDOffset = ifd.NextIFDOffset; - IFDs.Add(ifd); - } - - // Process IFDs - // TODO: Add support for multiple frames - foreach (ImageFileDirectoryEntry field in IFDs[0].Fields) - { - Properties.Add(ExifPropertyFactory.Get(field.Tag, field.Type, field.Count, field.Data, BitConverterEx.SystemByteOrder, IFD.Zeroth, Encoding)); - } - } - - #endregion - - #region Properties - - /// - /// Gets the TIFF header. - /// - public TIFFHeader TIFFHeader { get; } - - #endregion - - #region Instance Methods - - /// - /// Saves the to the given stream. - /// - /// The data stream used to save the image. - public override void Save(Stream stream) - { - BitConverterEx conv = BitConverterEx.SystemEndian; - - // Write TIFF header - uint ifdoffset = 8; - - // Byte order - stream.Write( - BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian - ? new byte[] { 0x49, 0x49 } - : new byte[] { 0x4D, 0x4D }, - 0, - 2); - - // TIFF ID - stream.Write(conv.GetBytes((ushort)42), 0, 2); - - // Offset to 0th IFD, will be corrected below - stream.Write(conv.GetBytes(ifdoffset), 0, 4); - - // Write IFD sections - for (var i = 0; i < IFDs.Count; i++) - { - ImageFileDirectory ifd = IFDs[i]; - - // Save the location of IFD offset - var ifdLocation = stream.Position - 4; - - // Write strips first - var stripOffsets = new byte[4 * ifd.Strips.Count]; - var stripLengths = new byte[4 * ifd.Strips.Count]; - var stripOffset = ifdoffset; - for (var j = 0; j < ifd.Strips.Count; j++) - { - var stripData = ifd.Strips[j].Data; - var oBytes = BitConverter.GetBytes(stripOffset); - var lBytes = BitConverter.GetBytes((uint)stripData.Length); - Array.Copy(oBytes, 0, stripOffsets, 4 * j, 4); - Array.Copy(lBytes, 0, stripLengths, 4 * j, 4); - stream.Write(stripData, 0, stripData.Length); - stripOffset += (uint)stripData.Length; - } - - // Remove old strip tags - for (var j = ifd.Fields.Count - 1; j > 0; j--) - { - var tag = ifd.Fields[j].Tag; - if (tag == 273 || tag == 279) - { - ifd.Fields.RemoveAt(j); - } - } - - // Write new strip tags - ifd.Fields.Add(new ImageFileDirectoryEntry(273, 4, (uint)ifd.Strips.Count, stripOffsets)); - ifd.Fields.Add(new ImageFileDirectoryEntry(279, 4, (uint)ifd.Strips.Count, stripLengths)); - - // Write fields after strips - ifdoffset = stripOffset; - - // Correct IFD offset - var currentLocation = stream.Position; - stream.Seek(ifdLocation, SeekOrigin.Begin); - stream.Write(conv.GetBytes(ifdoffset), 0, 4); - stream.Seek(currentLocation, SeekOrigin.Begin); - - // Offset to field data - var dataOffset = ifdoffset + 2 + ((uint)ifd.Fields.Count * 12) + 4; - - // Field count - stream.Write(conv.GetBytes((ushort)ifd.Fields.Count), 0, 2); - - // Fields - foreach (ImageFileDirectoryEntry field in ifd.Fields) - { - // Tag - stream.Write(conv.GetBytes(field.Tag), 0, 2); - - // Type - stream.Write(conv.GetBytes(field.Type), 0, 2); - - // Count - stream.Write(conv.GetBytes(field.Count), 0, 4); - - // Field data - var data = field.Data; - if (data.Length <= 4) - { - stream.Write(data, 0, data.Length); - for (var j = data.Length; j < 4; j++) - { - stream.WriteByte(0); - } - } - else - { - stream.Write(conv.GetBytes(dataOffset), 0, 4); - var currentOffset = stream.Position; - stream.Seek(dataOffset, SeekOrigin.Begin); - stream.Write(data, 0, data.Length); - dataOffset += (uint)data.Length; - stream.Seek(currentOffset, SeekOrigin.Begin); - } - } - - // Offset to next IFD - ifdoffset = dataOffset; - stream.Write(conv.GetBytes(i == IFDs.Count - 1 ? 0 : ifdoffset), 0, 4); - } - } - - /// - /// Gets the image file directories. - /// - public List IFDs { get; } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/TIFFHeader.cs b/src/Umbraco.Core/Media/Exif/TIFFHeader.cs deleted file mode 100644 index 54a79d90b4..0000000000 --- a/src/Umbraco.Core/Media/Exif/TIFFHeader.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a TIFF Header. -/// -internal struct TIFFHeader -{ - /// - /// The byte order of the image file. - /// - public BitConverterEx.ByteOrder ByteOrder; - - /// - /// TIFF ID. This value should always be 42. - /// - public byte ID; - - /// - /// The offset to the first IFD section from the - /// start of the TIFF header. - /// - public uint IFDOffset; - - /// - /// The byte order of the TIFF header itself. - /// - public BitConverterEx.ByteOrder TIFFHeaderByteOrder; - - /// - /// Initializes a new instance of the struct. - /// - /// The byte order. - /// The TIFF ID. This value should always be 42. - /// - /// The offset to the first IFD section from the - /// start of the TIFF header. - /// - /// The byte order of the TIFF header itself. - public TIFFHeader(BitConverterEx.ByteOrder byteOrder, byte id, uint ifdOffset, BitConverterEx.ByteOrder headerByteOrder) - { - if (id != 42) - { - throw new NotValidTIFFHeader(); - } - - ByteOrder = byteOrder; - ID = id; - IFDOffset = ifdOffset; - TIFFHeaderByteOrder = headerByteOrder; - } - - /// - /// Returns a initialized from the given byte data. - /// - /// The data. - /// The offset into . - /// A initialized from the given byte data. - public static TIFFHeader FromBytes(byte[] data, int offset) - { - var header = default(TIFFHeader); - - // TIFF header - if (data[offset] == 0x49 && data[offset + 1] == 0x49) - { - header.ByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else if (data[offset] == 0x4D && data[offset + 1] == 0x4D) - { - header.ByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - else - { - throw new NotValidTIFFHeader(); - } - - // TIFF header may have a different byte order - if (BitConverterEx.LittleEndian.ToUInt16(data, offset + 2) == 42) - { - header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else if (BitConverterEx.BigEndian.ToUInt16(data, offset + 2) == 42) - { - header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - else - { - throw new NotValidTIFFHeader(); - } - - header.ID = 42; - - // IFD offset - header.IFDOffset = - BitConverterEx.ToUInt32(data, offset + 4, header.TIFFHeaderByteOrder, BitConverterEx.SystemByteOrder); - - return header; - } -} diff --git a/src/Umbraco.Core/Media/Exif/TIFFStrip.cs b/src/Umbraco.Core/Media/Exif/TIFFStrip.cs deleted file mode 100644 index 8bf91abde6..0000000000 --- a/src/Umbraco.Core/Media/Exif/TIFFStrip.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a strip of compressed image data in a TIFF file. -/// -internal class TIFFStrip -{ - /// - /// Initializes a new instance of the class. - /// - /// The byte array to copy strip from. - /// The offset to the beginning of strip. - /// The length of strip. - public TIFFStrip(byte[] data, uint offset, uint length) - { - Data = new byte[length]; - Array.Copy(data, offset, Data, 0, length); - } - - /// - /// Compressed image data contained in this strip. - /// - public byte[] Data { get; } -} diff --git a/src/Umbraco.Core/Media/Exif/Utility.cs b/src/Umbraco.Core/Media/Exif/Utility.cs deleted file mode 100644 index 1ce1b1cdc7..0000000000 --- a/src/Umbraco.Core/Media/Exif/Utility.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Contains utility functions. -/// -internal class Utility -{ - /// - /// Reads the entire stream and returns its contents as a byte array. - /// - /// The to read. - /// Contents of the as a byte array. - public static byte[] GetStreamBytes(Stream stream) - { - using (var mem = new MemoryStream()) - { - stream.Seek(0, SeekOrigin.Begin); - - var b = new byte[32768]; - int r; - while ((r = stream.Read(b, 0, b.Length)) > 0) - { - mem.Write(b, 0, r); - } - - return mem.ToArray(); - } - } -} diff --git a/src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml b/src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml index 7baad05aa0..2095a0c798 100644 --- a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml +++ b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml @@ -1,7 +1,17 @@  - PKV006 - net6.0 + CP0006 + M:Umbraco.Cms.Core.Deploy.IGridCellValueConnector.GetValue(Umbraco.Cms.Core.Models.GridValue.GridControl,System.Collections.Generic.ICollection{Umbraco.Cms.Core.Deploy.ArtifactDependency},Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IGridCellValueConnector.SetValue(Umbraco.Cms.Core.Models.GridValue.GridControl,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true \ No newline at end of file diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 737cd5613b..a77b00814c 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -296,18 +296,7 @@ public static partial class UmbracoBuilderExtensions private static IUmbracoBuilder AddPreValueMigrators(this IUmbracoBuilder builder) { - builder.WithCollectionBuilder() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append(); + builder.WithCollectionBuilder(); return builder; } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index c80d70fa1f..750c65c21a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -1,25 +1,7 @@ -using System.Diagnostics.CodeAnalysis; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration; -using Umbraco.Cms.Core.Semver; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.Common; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_0_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_2_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_1; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_10_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_17_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_7_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_9_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_1_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_2_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_3_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_4_0; -using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade; @@ -29,10 +11,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade; /// public class UmbracoPlan : MigrationPlan { - private const string InitPrefix = "{init-"; - private const string InitSuffix = "}"; - private readonly IUmbracoVersion _umbracoVersion; - /// /// Initializes a new instance of the class. /// @@ -40,7 +18,6 @@ public class UmbracoPlan : MigrationPlan public UmbracoPlan(IUmbracoVersion umbracoVersion) : base(Constants.Conventions.Migrations.UmbracoUpgradePlanName) { - _umbracoVersion = umbracoVersion; DefinePlan(); } @@ -57,74 +34,9 @@ public class UmbracoPlan : MigrationPlan /// upgrades (from a tool old version, or going back in time, etc). /// /// - public override string InitialState - { - get - { - SemVersion currentVersion = _umbracoVersion.SemanticVersion; + public override string InitialState => "{DED98755-4059-41BB-ADBD-3FEAB12D1D7B}"; - // only from 8.0.0 and above - var minVersion = new SemVersion(8); - if (currentVersion < minVersion) - { - throw new InvalidOperationException( - $"Version {currentVersion} cannot be migrated to {_umbracoVersion.SemanticVersion}." - + $" Please upgrade first to at least {minVersion}."); - } - // Force versions between 7.14.*-7.15.* into into 7.14 initial state. Because there is no db-changes, - // and we don't want users to workaround my putting in version 7.14.0 them self. - if (minVersion <= currentVersion && currentVersion < new SemVersion(7, 16)) - { - return GetInitState(minVersion); - } - - // initial state is eg "{init-7.14.0}" - return GetInitState(currentVersion); - } - } - - /// - public override void ThrowOnUnknownInitialState(string state) - { - if (TryGetInitStateVersion(state, out var initVersion)) - { - throw new InvalidOperationException( - $"Version {_umbracoVersion.SemanticVersion} does not support migrating from {initVersion}." - + $" Please verify which versions support migrating from {initVersion}."); - } - - base.ThrowOnUnknownInitialState(state); - } - - /// - /// Gets the initial state corresponding to a version. - /// - /// The version. - /// - /// The initial state. - /// - private static string GetInitState(SemVersion version) => InitPrefix + version + InitSuffix; - - /// - /// Tries to extract a version from an initial state. - /// - /// The state. - /// The version. - /// - /// true when the state contains a version; otherwise, false.D - /// - private static bool TryGetInitStateVersion(string state, [MaybeNullWhen(false)] out string version) - { - if (state.StartsWith(InitPrefix) && state.EndsWith(InitSuffix)) - { - version = state.TrimStart(InitPrefix).TrimEnd(InitSuffix); - return true; - } - - version = null; - return false; - } /// /// Defines the plan. @@ -138,154 +50,23 @@ public class UmbracoPlan : MigrationPlan // * Creating a migration for version 8: // Append the migration to the main chain, using a new guid, before the "//FINAL" comment // - // If the new migration causes a merge conflict, because someone else also added another - // new migration, you NEED to fix the conflict by providing one default path, and paths - // out of the conflict states (see examples below). // - // * Porting from version 7: - // Append the ported migration to the main chain, using a new guid (same as above). - // Create a new special chain from the {init-...} state to the main chain. + // If the new migration causes a merge conflict, because someone else also added another + // new migration, you NEED to fix the conflict by providing one default path, and paths + // out of the conflict states, eg: + // + // .From("state-1") + // .To("state-a") + // .To("state-b") // Some might already be in this state, without having applied ChangeA + // + // .From("state-1") + // .Merge() + // .To("state-a") + // .With() + // .To("state-b") + // .As("state-2"); - // plan starts at 7.14.0 (anything before 7.14.0 is not supported) - From(GetInitState(new SemVersion(7, 14))); - - // begin migrating from v7 - remove all keys and indexes - To("{B36B9ABD-374E-465B-9C5F-26AB0D39326F}"); - - To("{7C447271-CA3F-4A6A-A913-5D77015655CB}"); - To("{CBFF58A2-7B50-4F75-8E98-249920DB0F37}"); - To("{5CB66059-45F4-48BA-BCBD-C5035D79206B}"); - To("{FB0A5429-587E-4BD0-8A67-20F0E7E62FF7}"); - To("{F0C42457-6A3B-4912-A7EA-F27ED85A2092}"); - To("{8640C9E4-A1C0-4C59-99BB-609B4E604981}"); - To("{DD1B99AF-8106-4E00-BAC7-A43003EA07F8}"); - To("{9DF05B77-11D1-475C-A00A-B656AF7E0908}"); - To("{6FE3EF34-44A0-4992-B379-B40BC4EF1C4D}"); - To("{7F59355A-0EC9-4438-8157-EB517E6D2727}"); - ToWithReplace( - "{941B2ABA-2D06-4E04-81F5-74224F1DB037}", - "{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}"); // kill AddVariationTable1 - To("{A7540C58-171D-462A-91C5-7A9AA5CB8BFD}"); - - Merge() - .To("{3E44F712-E2E3-473A-AE49-5D7F8E67CE3F}") - .With() - .To("{65D6B71C-BDD5-4A2E-8D35-8896325E9151}") - .As("{4CACE351-C6B9-4F0C-A6BA-85A02BBD39E4}"); - - To("{1350617A-4930-4D61-852F-E3AA9E692173}"); - To("{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}"); - To("{5F4597F4-A4E0-4AFE-90B5-6D2F896830EB}"); - To("{290C18EE-B3DE-4769-84F1-1F467F3F76DA}"); - To("{6A2C7C1B-A9DB-4EA9-B6AB-78E7D5B722A7}"); - To("{8804D8E8-FE62-4E3A-B8A2-C047C2118C38}"); - To("{23275462-446E-44C7-8C2C-3B8C1127B07D}"); - To("{6B251841-3069-4AD5-8AE9-861F9523E8DA}"); - To("{EE429F1B-9B26-43CA-89F8-A86017C809A3}"); - To("{08919C4B-B431-449C-90EC-2B8445B5C6B1}"); - To("{7EB0254C-CB8B-4C75-B15B-D48C55B449EB}"); - To("{C39BF2A7-1454-4047-BBFE-89E40F66ED63}"); - To("{64EBCE53-E1F0-463A-B40B-E98EFCCA8AE2}"); - To("{0009109C-A0B8-4F3F-8FEB-C137BBDDA268}"); - To("{ED28B66A-E248-4D94-8CDB-9BDF574023F0}"); - To("{38C809D5-6C34-426B-9BEA-EFD39162595C}"); - To("{6017F044-8E70-4E10-B2A3-336949692ADD}"); - - Merge() - .To("{CDBEDEE4-9496-4903-9CF2-4104E00FF960}") - .With() - .To("{940FD19A-00A8-4D5C-B8FF-939143585726}") - .As("{0576E786-5C30-4000-B969-302B61E90CA3}"); - - To("{48AD6CCD-C7A4-4305-A8AB-38728AD23FC5}"); - To("{DF470D86-E5CA-42AC-9780-9D28070E25F9}"); - - // finish migrating from v7 - recreate all keys and indexes - To("{3F9764F5-73D0-4D45-8804-1240A66E43A2}"); - - To("{E0CBE54D-A84F-4A8F-9B13-900945FD7ED9}"); - To("{78BAF571-90D0-4D28-8175-EF96316DA789}"); - - // release-8.0.0 - - // to 8.0.1 - To("{80C0A0CB-0DD5-4573-B000-C4B7C313C70D}"); - - // release-8.0.1 - - // to 8.1.0 - To("{B69B6E8C-A769-4044-A27E-4A4E18D1645A}"); - To("{0372A42B-DECF-498D-B4D1-6379E907EB94}"); - To("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}"); - - // to 8.6.0 - To("{4759A294-9860-46BC-99F9-B4C975CAE580}"); - To("{0BC866BC-0665-487A-9913-0290BD0169AD}"); - To("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}"); - To("{EE288A91-531B-4995-8179-1D62D9AA3E2E}"); - To("{2AB29964-02A1-474D-BD6B-72148D2A53A2}"); - - // to 8.7.0 - To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}"); - - // to 8.9.0 - To("{B5838FF5-1D22-4F6C-BCEB-F83ACB14B575}"); - - // to 8.10.0 - To("{D6A8D863-38EC-44FB-91EC-ACD6A668BD18}"); - - // NOTE: we need to do a merge migration here because as of 'now', - // v9-beta* is already out and 8.15 isn't out yet - // so we need to ensure that migrations from 8.15 are included in the next - // v9*. - - // to 8.15.0 - To("{8DDDCD0B-D7D5-4C97-BD6A-6B38CA65752F}"); - To("{4695D0C9-0729-4976-985B-048D503665D8}"); - To("{5C424554-A32D-4852-8ED1-A13508187901}"); - - // to 8.17.0 - To("{153865E9-7332-4C2A-9F9D-F20AEE078EC7}"); - - // Hack to support migration from 8.18 - To("{03482BB0-CF13-475C-845E-ECB8319DBE3C}"); - - // This should be safe to execute again. We need it with a new name to ensure updates from all the following has executed this step. - // - 8.15.0 RC - Current state: {4695D0C9-0729-4976-985B-048D503665D8} - // - 8.15.0 Final - Current state: {5C424554-A32D-4852-8ED1-A13508187901} - // - 9.0.0 RC1 - Current state: {5060F3D2-88BE-4D30-8755-CF51F28EAD12} - To("{622E5172-42E1-4662-AD80-9504AF5A4E53}"); - To("{10F7BB61-C550-426B-830B-7F954F689CDF}"); - To("{5AAE6276-80DB-4ACF-B845-199BC6C37538}"); - - // to 9.0.0 RC1 - To("{22D801BA-A1FF-4539-BFCC-2139B55594F8}"); - To("{50A43237-A6F4-49E2-A7A6-5DAD65C84669}"); - To("{3D8DADEF-0FDA-4377-A5F0-B52C2110E8F2}"); - To("{1303BDCF-2295-4645-9526-2F32E8B35ABD}"); - To("{5060F3D2-88BE-4D30-8755-CF51F28EAD12}"); - To( - "{A2686B49-A082-4B22-97FD-AAB154D46A57}"); // Re-run this migration to make sure it has executed to account for migrations going out of sync between versions. - - // TO 9.0.0-rc4 - To( - "5E02F241-5253-403D-B5D3-7DB00157E20F"); // Jaddie: This GUID is missing the { }, although this likely can't be changed now as it will break installs going forwards - - // TO 9.1.0 - To("{8BAF5E6C-DCB7-41AE-824F-4215AE4F1F98}"); - - // TO 9.2.0 - To("{0571C395-8F0B-44E9-8E3F-47BDD08D817B}"); - To("{AD3D3B7F-8E74-45A4-85DB-7FFAD57F9243}"); - - // TO 9.3.0 - To("{A2F22F17-5870-4179-8A8D-2362AA4A0A5F}"); - To("{CA7A1D9D-C9D4-4914-BC0A-459E7B9C3C8C}"); - To("{0828F206-DCF7-4F73-ABBB-6792275532EB}"); - - // TO 9.4.0 - To("{DBBA1EA0-25A1-4863-90FB-5D306FB6F1E1}"); - To("{DED98755-4059-41BB-ADBD-3FEAB12D1D7B}"); + From(InitialState); // TO 10.0.0 To("{B7E0D53C-2B0E-418B-AB07-2DDE486E225F}"); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentNuTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentNuTable.cs deleted file mode 100644 index a216abf045..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentNuTable.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; - -internal class AddContentNuTable : MigrationBase -{ - public AddContentNuTable(IMigrationContext context) - : base(context) - { - } - - protected override void Migrate() - { - IEnumerable tables = SqlSyntax.GetTablesInSchema(Context.Database); - if (tables.InvariantContains("cmsContentNu")) - { - return; - } - - Create.Table(true).Do(); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs index 36f4dcb5e0..dbfcebe7c7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddContentTypeIsElementColumn : MigrationBase { public AddContentTypeIsElementColumn(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs index 96937d3991..e30e09b386 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddLockObjects : MigrationBase { public AddLockObjects(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLogTableColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLogTableColumns.cs index 8546566999..9503c0a2c4 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLogTableColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLogTableColumns.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddLogTableColumns : MigrationBase { public AddLogTableColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs index e147d185fe..dc469d139f 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPackagesSectionAccess : MigrationBase { public AddPackagesSectionAccess(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs index f1369db5c3..a8e3157726 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs @@ -6,6 +6,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddTypedLabels : MigrationBase { public AddTypedLabels(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs index 118c7f8bb2..c024459b92 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddVariationTables1A : MigrationBase { public AddVariationTables1A(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs index 76d20b4667..14e3a31fe2 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddVariationTables2 : MigrationBase { public AddVariationTables2(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs index edfeb204f8..bddcc67525 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs @@ -1,8 +1,9 @@ -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.Models; + using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ContentVariationMigration : MigrationBase { public ContentVariationMigration(IMigrationContext context) @@ -12,50 +13,7 @@ public class ContentVariationMigration : MigrationBase protected override void Migrate() { - static byte GetNewValue(byte oldValue) - { - switch (oldValue) - { - case 0: // Unknown - case 1: // InvariantNeutral - return 0; // Unknown - case 2: // CultureNeutral - case 3: // CultureNeutral | InvariantNeutral - return 1; // Culture - case 4: // InvariantSegment - case 5: // InvariantSegment | InvariantNeutral - return 2; // Segment - case 6: // InvariantSegment | CultureNeutral - case 7: // InvariantSegment | CultureNeutral | InvariantNeutral - case 8: // CultureSegment - case 9: // CultureSegment | InvariantNeutral - case 10: // CultureSegment | CultureNeutral - case 11: // CultureSegment | CultureNeutral | InvariantNeutral - case 12: // etc - case 13: - case 14: - case 15: - return 3; // Culture | Segment - default: - throw new NotSupportedException($"Invalid value {oldValue}."); - } - } - List? propertyTypes = - Database.Fetch(Sql().Select().From()); - foreach (PropertyTypeDto80? dto in propertyTypes) - { - dto.Variations = GetNewValue(dto.Variations); - Database.Update(dto); - } - - List? contentTypes = - Database.Fetch(Sql().Select().From()); - foreach (ContentTypeDto80? dto in contentTypes) - { - dto.Variations = GetNewValue(dto.Variations); - Database.Update(dto); - } } // we *need* to use these private DTOs here, which does *not* have extra properties, which would kill the migration diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs index 50ac54436a..14cb4816b5 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs @@ -1,15 +1,6 @@ -using System.Globalization; -using System.Runtime.Serialization; -using Newtonsoft.Json; -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.Models; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ConvertRelatedLinksToMultiUrlPicker : MigrationBase { public ConvertRelatedLinksToMultiUrlPicker(IMigrationContext context) @@ -19,144 +10,7 @@ public class ConvertRelatedLinksToMultiUrlPicker : MigrationBase protected override void Migrate() { - Sql sqlDataTypes = Sql() - .Select() - .From() - .Where(x => x.EditorAlias == Constants.PropertyEditors.Legacy.Aliases.RelatedLinks - || x.EditorAlias == Constants.PropertyEditors.Legacy.Aliases.RelatedLinks2); - List? dataTypes = Database.Fetch(sqlDataTypes); - var dataTypeIds = dataTypes.Select(x => x.NodeId).ToList(); - - if (dataTypeIds.Count == 0) - { - return; - } - - foreach (DataTypeDto? dataType in dataTypes) - { - dataType.EditorAlias = Constants.PropertyEditors.Aliases.MultiUrlPicker; - Database.Update(dataType); - } - - Sql sqlPropertyTpes = Sql() - .Select() - .From() - .Where(x => dataTypeIds.Contains(x.DataTypeId)); - - var propertyTypeIds = Database.Fetch(sqlPropertyTpes).Select(x => x.Id).ToList(); - - if (propertyTypeIds.Count == 0) - { - return; - } - - Sql sqlPropertyData = Sql() - .Select() - .From() - .Where(x => propertyTypeIds.Contains(x.PropertyTypeId)); - - List? properties = Database.Fetch(sqlPropertyData); - - // Create a Multi URL Picker datatype for the converted RelatedLinks data - foreach (PropertyDataDto? property in properties) - { - var value = property.Value?.ToString(); - if (string.IsNullOrWhiteSpace(value)) - { - continue; - } - - List? relatedLinks = JsonConvert.DeserializeObject>(value); - var links = new List(); - if (relatedLinks is null) - { - return; - } - - foreach (RelatedLink relatedLink in relatedLinks) - { - GuidUdi? udi = null; - if (relatedLink.IsInternal) - { - var linkIsUdi = UdiParser.TryParse(relatedLink.Link, out udi); - if (linkIsUdi == false) - { - // oh no.. probably an integer, yikes! - if (int.TryParse(relatedLink.Link, NumberStyles.Integer, CultureInfo.InvariantCulture, - out var intId)) - { - Sql sqlNodeData = Sql() - .Select() - .From() - .Where(x => x.NodeId == intId); - - NodeDto? node = Database.Fetch(sqlNodeData).FirstOrDefault(); - if (node != null) - - // Note: RelatedLinks did not allow for picking media items, - // so if there's a value this will be a content item - hence - // the hardcoded "document" here - { - udi = new GuidUdi("document", node.UniqueId); - } - } - } - } - - var link = new LinkDto - { - Name = relatedLink.Caption, - Target = relatedLink.NewWindow ? "_blank" : null, - Udi = udi, - - // Should only have a URL if it's an external link otherwise it wil be a UDI - Url = relatedLink.IsInternal == false ? relatedLink.Link : null, - }; - - links.Add(link); - } - - var json = JsonConvert.SerializeObject(links); - - // Update existing data - property.TextValue = json; - Database.Update(property); - } } } -internal class RelatedLink -{ - public int? Id { get; internal set; } - - [JsonProperty("caption")] - public string? Caption { get; set; } - - internal bool IsDeleted { get; set; } - - [JsonProperty("link")] - public string? Link { get; set; } - - [JsonProperty("newWindow")] - public bool NewWindow { get; set; } - - [JsonProperty("isInternal")] - public bool IsInternal { get; set; } -} - -[DataContract] -internal class LinkDto -{ - [DataMember(Name = "name")] - public string? Name { get; set; } - - [DataMember(Name = "target")] - public string? Target { get; set; } - - [DataMember(Name = "udi")] - public GuidUdi? Udi { get; set; } - - [DataMember(Name = "url")] - public string? Url { get; set; } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs index d97b7ebcb5..e52d5b5844 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs @@ -10,6 +10,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DataTypeMigration : MigrationBase { private static readonly ISet _legacyAliases = new HashSet @@ -45,100 +46,6 @@ public class DataTypeMigration : MigrationBase protected override void Migrate() { - // drop and create columns - Delete.Column("pk").FromTable("cmsDataType").Do(); - // rename the table - Rename.Table("cmsDataType").To(Constants.DatabaseSchema.Tables.DataType).Do(); - - // create column - AddColumn(Constants.DatabaseSchema.Tables.DataType, "config"); - Execute.Sql(Sql().Update(u => u.Set(x => x.Configuration, string.Empty))).Do(); - - // renames - Execute.Sql(Sql() - .Update(u => u.Set(x => x.EditorAlias, "Umbraco.ColorPicker")) - .Where(x => x.EditorAlias == "Umbraco.ColorPickerAlias")).Do(); - - // from preValues to configuration... - Sql sql = Sql() - .Select() - .AndSelect(x => x.Id, x => x.Alias, x => x.SortOrder, x => x.Value) - .From() - .InnerJoin().On((left, right) => left.NodeId == right.NodeId) - .OrderBy(x => x.NodeId) - .AndBy(x => x.SortOrder); - - IEnumerable> dtos = Database.Fetch(sql).GroupBy(x => x.NodeId); - - foreach (IGrouping group in dtos) - { - DataTypeDto? dataType = Database.Fetch(Sql() - .Select() - .From() - .Where(x => x.NodeId == group.Key)).First(); - - // check for duplicate aliases - var aliases = group.Select(x => x.Alias).Where(x => !string.IsNullOrWhiteSpace(x)).ToArray(); - if (aliases.Distinct().Count() != aliases.Length) - { - throw new InvalidOperationException( - $"Cannot migrate prevalues for datatype id={dataType.NodeId}, editor={dataType.EditorAlias}: duplicate alias."); - } - - // handle null/empty aliases - var index = 0; - var dictionary = group.ToDictionary(x => string.IsNullOrWhiteSpace(x.Alias) ? index++.ToString() : x.Alias); - - // migrate the preValues to configuration - IPreValueMigrator migrator = - _preValueMigrators.GetMigrator(dataType.EditorAlias) ?? new DefaultPreValueMigrator(); - var config = migrator.GetConfiguration(dataType.NodeId, dataType.EditorAlias, dictionary); - var json = _configurationEditorJsonSerializer.Serialize(config); - - // validate - and kill the migration if it fails - var newAlias = migrator.GetNewAlias(dataType.EditorAlias); - if (newAlias == null) - { - if (!_legacyAliases.Contains(dataType.EditorAlias)) - { - _logger.LogWarning( - "Skipping validation of configuration for data type {NodeId} : {EditorAlias}." - + " Please ensure that the configuration is valid. The site may fail to start and / or load data types and run.", - dataType.NodeId, dataType.EditorAlias); - } - } - else if (!_propertyEditors.TryGet(newAlias, out IDataEditor? propertyEditor)) - { - if (!_legacyAliases.Contains(newAlias)) - { - _logger.LogWarning( - "Skipping validation of configuration for data type {NodeId} : {NewEditorAlias} (was: {EditorAlias})" - + " because no property editor with that alias was found." - + " Please ensure that the configuration is valid. The site may fail to start and / or load data types and run.", - dataType.NodeId, newAlias, dataType.EditorAlias); - } - } - else - { - IConfigurationEditor configEditor = propertyEditor.GetConfigurationEditor(); - try - { - var _ = configEditor.FromDatabase(json, _configurationEditorJsonSerializer); - } - catch (Exception e) - { - _logger.LogWarning( - e, - "Failed to validate configuration for data type {NodeId} : {NewEditorAlias} (was: {EditorAlias})." - + " Please fix the configuration and ensure it is valid. The site may fail to start and / or load data types and run.", - dataType.NodeId, newAlias, dataType.EditorAlias); - } - } - - // update - dataType.Configuration = _configurationEditorJsonSerializer.Serialize(config); - Database.Update(dataType); - } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ContentPickerPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ContentPickerPreValueMigrator.cs deleted file mode 100644 index 13c9645ffe..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ContentPickerPreValueMigrator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Umbraco.Cms.Core; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class ContentPickerPreValueMigrator : DefaultPreValueMigrator -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == Constants.PropertyEditors.Legacy.Aliases.ContentPicker2; - - public override string? GetNewAlias(string editorAlias) - => null; - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "showOpenButton" || - preValue.Alias == "ignoreUserStartNodes") - { - return preValue.Value == "1"; - } - - return base.GetPreValueValue(preValue); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DecimalPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DecimalPreValueMigrator.cs deleted file mode 100644 index eb7744cc18..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DecimalPreValueMigrator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class DecimalPreValueMigrator : DefaultPreValueMigrator -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.Decimal"; - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "min" || - preValue.Alias == "step" || - preValue.Alias == "max") - { - return decimal.TryParse(preValue.Value, out var d) ? (decimal?)d : null; - } - - return preValue.Value?.DetectIsJson() ?? false ? JsonConvert.DeserializeObject(preValue.Value) : preValue.Value; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs deleted file mode 100644 index 2faaca6086..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Newtonsoft.Json; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class DefaultPreValueMigrator : IPreValueMigrator -{ - public virtual bool CanMigrate(string editorAlias) - => true; - - public virtual string? GetNewAlias(string editorAlias) - => editorAlias; - - public object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - var preValuesA = preValues.Values.ToList(); - var aliases = preValuesA.Select(x => x.Alias).Distinct().ToArray(); - if (aliases.Length == 1 && string.IsNullOrWhiteSpace(aliases[0])) - { - // array-based prevalues - return new Dictionary - { - ["values"] = preValuesA.OrderBy(x => x.SortOrder).Select(x => x.Value).ToArray(), - }; - } - - // assuming we don't want to fall back to array - if (aliases.Any(string.IsNullOrWhiteSpace)) - { - throw new InvalidOperationException( - $"Cannot migrate prevalues for datatype id={dataTypeId}, editor={editorAlias}: null/empty alias."); - } - - // dictionary-base prevalues - return GetPreValues(preValuesA).ToDictionary(x => x.Alias, GetPreValueValue); - } - - protected virtual IEnumerable GetPreValues(IEnumerable preValues) - => preValues; - - protected virtual object? GetPreValueValue(PreValueDto preValue) => preValue.Value?.DetectIsJson() ?? false - ? JsonConvert.DeserializeObject(preValue.Value) - : preValue.Value; -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DropDownFlexiblePreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DropDownFlexiblePreValueMigrator.cs deleted file mode 100644 index 6588676283..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DropDownFlexiblePreValueMigrator.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Umbraco.Cms.Core.PropertyEditors; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class DropDownFlexiblePreValueMigrator : IPreValueMigrator -{ - public bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.DropDown.Flexible"; - - public virtual string? GetNewAlias(string editorAlias) - => null; - - public object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - var config = new DropDownFlexibleConfiguration(); - foreach (PreValueDto preValue in preValues.Values) - { - if (preValue.Alias == "multiple") - { - config.Multiple = preValue.Value == "1"; - } - else - { - config.Items.Add(new ValueListConfiguration.ValueListItem { Id = preValue.Id, Value = preValue.Value }); - } - } - - return config; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/IPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/IPreValueMigrator.cs index 11a126a60b..1f287b6597 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/IPreValueMigrator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/IPreValueMigrator.cs @@ -3,6 +3,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; /// /// Defines a service migrating preValues. /// +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public interface IPreValueMigrator { /// diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ListViewPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ListViewPreValueMigrator.cs deleted file mode 100644 index 7879e9c67d..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ListViewPreValueMigrator.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Globalization; -using Newtonsoft.Json; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class ListViewPreValueMigrator : DefaultPreValueMigrator -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.ListView"; - - protected override IEnumerable GetPreValues(IEnumerable preValues) => - preValues.Where(preValue => preValue.Alias != "displayAtTabNumber"); - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "pageSize") - { - return int.TryParse(preValue.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var i) - ? (int?)i - : null; - } - - return preValue.Value?.DetectIsJson() ?? false ? JsonConvert.DeserializeObject(preValue.Value) : preValue.Value; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MarkdownEditorPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MarkdownEditorPreValueMigrator.cs deleted file mode 100644 index eff4b82477..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MarkdownEditorPreValueMigrator.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Umbraco.Cms.Core; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class MarkdownEditorPreValueMigrator : DefaultPreValueMigrator // PreValueMigratorBase -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == Constants.PropertyEditors.Aliases.MarkdownEditor; - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "preview") - { - return preValue.Value == "1"; - } - - return base.GetPreValueValue(preValue); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MediaPickerPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MediaPickerPreValueMigrator.cs deleted file mode 100644 index c630693073..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MediaPickerPreValueMigrator.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Umbraco.Cms.Core; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class MediaPickerPreValueMigrator : DefaultPreValueMigrator // PreValueMigratorBase -{ - private readonly string[] _editors = - { - Constants.PropertyEditors.Legacy.Aliases.MediaPicker2, Constants.PropertyEditors.Aliases.MediaPicker, - }; - - public override bool CanMigrate(string editorAlias) - => _editors.Contains(editorAlias); - - public override string GetNewAlias(string editorAlias) - => Constants.PropertyEditors.Aliases.MediaPicker; - - // you wish - but MediaPickerConfiguration lives in Umbraco.Web - /* - public override object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - return new MediaPickerConfiguration { ... }; - } - */ - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "multiPicker" || - preValue.Alias == "onlyImages" || - preValue.Alias == "disableFolderSelect") - { - return preValue.Value == "1"; - } - - return base.GetPreValueValue(preValue); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/NestedContentPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/NestedContentPreValueMigrator.cs deleted file mode 100644 index 72c28dc8ad..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/NestedContentPreValueMigrator.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Globalization; -using Newtonsoft.Json; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class NestedContentPreValueMigrator : DefaultPreValueMigrator // PreValueMigratorBase -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.NestedContent"; - - // you wish - but NestedContentConfiguration lives in Umbraco.Web - /* - public override object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - return new NestedContentConfiguration { ... }; - } - */ - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "confirmDeletes" || - preValue.Alias == "showIcons" || - preValue.Alias == "hideLabel") - { - return preValue.Value == "1"; - } - - if (preValue.Alias == "minItems" || - preValue.Alias == "maxItems") - { - return int.TryParse(preValue.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var i) - ? (int?)i - : null; - } - - return preValue.Value?.DetectIsJson() ?? false ? JsonConvert.DeserializeObject(preValue.Value) : preValue.Value; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueDto.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueDto.cs index d3e639452e..3509ea9f19 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueDto.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueDto.cs @@ -2,6 +2,7 @@ using NPoco; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] [TableName("cmsDataTypePreValues")] [ExplicitColumns] public class PreValueDto diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorBase.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorBase.cs index df9cb27ec3..e658640054 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorBase.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorBase.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public abstract class PreValueMigratorBase : IPreValueMigrator { public abstract bool CanMigrate(string editorAlias); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs index 81a6200991..422bbc1e8c 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class PreValueMigratorCollection : BuilderCollectionBase { private readonly ILogger _logger; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollectionBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollectionBuilder.cs index ba335eef4b..b16d85af6a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollectionBuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollectionBuilder.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class PreValueMigratorCollectionBuilder : OrderedCollectionBuilderBase { diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RenamingPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RenamingPreValueMigrator.cs deleted file mode 100644 index 273c8ae51b..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RenamingPreValueMigrator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Exceptions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class RenamingPreValueMigrator : DefaultPreValueMigrator -{ - private readonly string[] _editors = { "Umbraco.NoEdit" }; - - public override bool CanMigrate(string editorAlias) - => _editors.Contains(editorAlias); - - public override string GetNewAlias(string editorAlias) - { - switch (editorAlias) - { - case "Umbraco.NoEdit": - return Constants.PropertyEditors.Aliases.Label; - default: - throw new PanicException($"The alias {editorAlias} is not supported"); - } - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RichTextPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RichTextPreValueMigrator.cs deleted file mode 100644 index 4e7c5b79f1..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RichTextPreValueMigrator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Newtonsoft.Json; -using Umbraco.Cms.Core; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class RichTextPreValueMigrator : DefaultPreValueMigrator -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.TinyMCEv3"; - - public override string GetNewAlias(string editorAlias) - => Constants.PropertyEditors.Aliases.TinyMce; - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "hideLabel") - { - return preValue.Value == "1"; - } - - return preValue.Value?.DetectIsJson() ?? false ? JsonConvert.DeserializeObject(preValue.Value) : preValue.Value; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/UmbracoSliderPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/UmbracoSliderPreValueMigrator.cs deleted file mode 100644 index 7f8632dd7a..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/UmbracoSliderPreValueMigrator.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Umbraco.Cms.Core.PropertyEditors; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class UmbracoSliderPreValueMigrator : PreValueMigratorBase -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.Slider"; - - public override object GetConfiguration(int dataTypeId, string editorAlias, - Dictionary preValues) => - new SliderConfiguration - { - EnableRange = GetBoolValue(preValues, "enableRange"), - InitialValue = GetDecimalValue(preValues, "initVal1"), - InitialValue2 = GetDecimalValue(preValues, "initVal2"), - MaximumValue = GetDecimalValue(preValues, "maxVal"), - MinimumValue = GetDecimalValue(preValues, "minVal"), - StepIncrements = GetDecimalValue(preValues, "step"), - }; -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ValueListPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ValueListPreValueMigrator.cs deleted file mode 100644 index 9528cadc8b..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ValueListPreValueMigrator.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Umbraco.Cms.Core.PropertyEditors; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class ValueListPreValueMigrator : IPreValueMigrator -{ - private readonly string[] _editors = - { - "Umbraco.RadioButtonList", "Umbraco.CheckBoxList", "Umbraco.DropDown", "Umbraco.DropdownlistPublishingKeys", - "Umbraco.DropDownMultiple", "Umbraco.DropdownlistMultiplePublishKeys", - }; - - public bool CanMigrate(string editorAlias) - => _editors.Contains(editorAlias); - - public virtual string? GetNewAlias(string editorAlias) - => null; - - public object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - var config = new ValueListConfiguration(); - foreach (PreValueDto preValue in preValues.Values) - { - config.Items.Add(new ValueListConfiguration.ValueListItem { Id = preValue.Id, Value = preValue.Value }); - } - - return config; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs index e80dd72765..e2303cf2a2 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs @@ -13,6 +13,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropDownPropertyEditorsMigration : PropertyEditorsMigrationBase { private readonly IConfigurationEditorJsonSerializer _configurationEditorJsonSerializer; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropMigrationsTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropMigrationsTable.cs index 8eebd91772..dc225678f3 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropMigrationsTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropMigrationsTable.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropMigrationsTable : MigrationBase { public DropMigrationsTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs index 64152d0cb3..ff4673b848 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropPreValueTable : MigrationBase { public DropPreValueTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs index e38c0c3292..23dc153b7a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropTaskTables : MigrationBase { public DropTaskTables(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTemplateDesignColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTemplateDesignColumn.cs index 454644b1fb..745efdcf06 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTemplateDesignColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTemplateDesignColumn.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropTemplateDesignColumn : MigrationBase { public DropTemplateDesignColumn(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropXmlTables.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropXmlTables.cs index 1cdb73d410..6dbd91374f 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropXmlTables.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropXmlTables.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropXmlTables : MigrationBase { public DropXmlTables(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs index 4f3b685ef5..ee0d5157ae 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs @@ -5,6 +5,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] /// /// Adds a new, self-joined field to umbracoLanguages to hold the fall-back language for /// a given language. diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FixLanguageIsoCodeLength.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FixLanguageIsoCodeLength.cs index f1bd804c59..606348d179 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FixLanguageIsoCodeLength.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FixLanguageIsoCodeLength.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class FixLanguageIsoCodeLength : MigrationBase { public FixLanguageIsoCodeLength(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/LanguageColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/LanguageColumns.cs index a265195bc9..e883a92e00 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/LanguageColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/LanguageColumns.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class LanguageColumns : MigrationBase { public LanguageColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs index 64c8d4c8b4..2e039543f3 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MakeRedirectUrlVariant : MigrationBase { public MakeRedirectUrlVariant(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeTagsVariant.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeTagsVariant.cs index 630a853aa2..986ac4d3ce 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeTagsVariant.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeTagsVariant.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MakeTagsVariant : MigrationBase { public MakeTagsVariant(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs index f89a6d1497..dfc713f912 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs @@ -11,6 +11,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MergeDateAndDateTimePropertyEditor : MigrationBase { private readonly IConfigurationEditorJsonSerializer _configurationEditorJsonSerializer; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs deleted file mode 100644 index 856c81af52..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs +++ /dev/null @@ -1,63 +0,0 @@ -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; - -namespace Umbraco.Cms.Infrastructure.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.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs deleted file mode 100644 index 4c537472db..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs +++ /dev/null @@ -1,142 +0,0 @@ -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.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) => - 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) => Id == other.Id; - - public override bool Equals(object? other) => - !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 - Id; -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs deleted file mode 100644 index a5a7f48d6d..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs +++ /dev/null @@ -1,76 +0,0 @@ -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; - -namespace Umbraco.Cms.Infrastructure.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; } = null!; - - [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.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs index cef6eb974f..3c0f0ee560 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs @@ -4,6 +4,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class PropertyEditorsMigration : MigrationBase { public PropertyEditorsMigration(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigrationBase.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigrationBase.cs index febf872e34..f8f730c390 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigrationBase.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigrationBase.cs @@ -12,6 +12,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public abstract class PropertyEditorsMigrationBase : MigrationBase { protected PropertyEditorsMigrationBase(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RadioAndCheckboxPropertyEditorsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RadioAndCheckboxPropertyEditorsMigration.cs index ab9b01a3b2..d3359d09fb 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RadioAndCheckboxPropertyEditorsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RadioAndCheckboxPropertyEditorsMigration.cs @@ -13,6 +13,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RadioAndCheckboxPropertyEditorsMigration : PropertyEditorsMigrationBase { private readonly IConfigurationEditorJsonSerializer _configurationEditorJsonSerializer; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs index f8d731e166..00acccb0bf 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RefactorMacroColumns : MigrationBase { public RefactorMacroColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs index 500db8a4bc..2f0592bd40 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs @@ -6,6 +6,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RefactorVariantsModel : MigrationBase { public RefactorVariantsModel(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameLabelAndRichTextPropertyEditorAliases.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameLabelAndRichTextPropertyEditorAliases.cs index a638f17dc4..eda61b80c5 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameLabelAndRichTextPropertyEditorAliases.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameLabelAndRichTextPropertyEditorAliases.cs @@ -4,6 +4,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RenameLabelAndRichTextPropertyEditorAliases : MigrationBase { public RenameLabelAndRichTextPropertyEditorAliases(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs index a6fe5c895b..2948567190 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RenameMediaVersionTable : MigrationBase { public RenameMediaVersionTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameUmbracoDomainsTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameUmbracoDomainsTable.cs index 8611128458..cd65470de7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameUmbracoDomainsTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameUmbracoDomainsTable.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RenameUmbracoDomainsTable : MigrationBase { public RenameUmbracoDomainsTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/SuperZero.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/SuperZero.cs index 135e562fde..7aa92aa585 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/SuperZero.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/SuperZero.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class SuperZero : MigrationBase { public SuperZero(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TablesForScheduledPublishing.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TablesForScheduledPublishing.cs index 013375352e..1db65da66d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TablesForScheduledPublishing.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TablesForScheduledPublishing.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class TablesForScheduledPublishing : MigrationBase { public TablesForScheduledPublishing(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigration.cs index 2f2ac746ab..3ac5609533 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigration.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class TagsMigration : MigrationBase { public TagsMigration(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigrationFix.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigrationFix.cs index eaa745a780..d0c496df01 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigrationFix.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigrationFix.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class TagsMigrationFix : MigrationBase { public TagsMigrationFix(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs index 557f658691..a077ffa3d7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs @@ -6,6 +6,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdateDefaultMandatoryLanguage : MigrationBase { public UpdateDefaultMandatoryLanguage(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdatePickerIntegerValuesToUdi.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdatePickerIntegerValuesToUdi.cs index 18d55aa1e6..2af762cda0 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdatePickerIntegerValuesToUdi.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdatePickerIntegerValuesToUdi.cs @@ -9,6 +9,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdatePickerIntegerValuesToUdi : MigrationBase { public UpdatePickerIntegerValuesToUdi(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UserForeignKeys.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UserForeignKeys.cs index fa19ac284a..06feca0411 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UserForeignKeys.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UserForeignKeys.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] /// /// Creates/Updates non mandatory FK columns to the user table /// diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index db41f70711..75de36ac2d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class VariantsMigration : MigrationBase { public VariantsMigration(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_1/ChangeNuCacheJsonFormat.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_1/ChangeNuCacheJsonFormat.cs index 60e38eca29..83d97d9331 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_1/ChangeNuCacheJsonFormat.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_1/ChangeNuCacheJsonFormat.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Migrations.PostMigrations; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_1; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ChangeNuCacheJsonFormat : MigrationBase { public ChangeNuCacheJsonFormat(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_10_0/AddPropertyTypeLabelOnTopColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_10_0/AddPropertyTypeLabelOnTopColumn.cs index 4e57716c4e..fd7b5b5973 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_10_0/AddPropertyTypeLabelOnTopColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_10_0/AddPropertyTypeLabelOnTopColumn.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_10_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPropertyTypeLabelOnTopColumn : MigrationBase { public AddPropertyTypeLabelOnTopColumn(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs index 4a9a494b76..278d5990da 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddCmsContentNuByteColumn : MigrationBase { private const string TempTableName = Constants.DatabaseSchema.TableNamePrefix + "cms" + "ContentNuTEMP"; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs index 703cfc1474..d6a6a9d6e0 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdateCmsPropertyGroupIdSeed : MigrationBase { public UpdateCmsPropertyGroupIdSeed(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpgradedIncludeIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpgradedIncludeIndexes.cs index 114bb7becc..d97c8d9708 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpgradedIncludeIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpgradedIncludeIndexes.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpgradedIncludeIndexes : MigrationBase { public UpgradedIncludeIndexes(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_17_0/AddPropertyTypeGroupColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_17_0/AddPropertyTypeGroupColumns.cs index cef69d6bd3..41289c6e4e 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_17_0/AddPropertyTypeGroupColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_17_0/AddPropertyTypeGroupColumns.cs @@ -5,6 +5,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_17_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPropertyTypeGroupColumns : MigrationBase { private readonly IShortStringHelper _shortStringHelper; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs index 0f7fe97663..790b5b7010 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs @@ -1,18 +1,8 @@ -using System.Text.RegularExpressions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Infrastructure.Migrations.PostMigrations; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.Models; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ConvertTinyMceAndGridMediaUrlsToLocalLink : MigrationBase { private readonly IMediaService _mediaService; @@ -22,111 +12,6 @@ public class ConvertTinyMceAndGridMediaUrlsToLocalLink : MigrationBase protected override void Migrate() { - var mediaLinkPattern = new Regex( - @"(]*href="")(\/ media[^""\?]*)([^>]*>)", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - Sql 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) - .Where(x => - x.EditorAlias == Constants.PropertyEditors.Aliases.TinyMce || - x.EditorAlias == Constants.PropertyEditors.Aliases.Grid); - - List? properties = Database.Fetch(sqlPropertyData); - - var exceptions = new List(); - foreach (PropertyDataDto80? property in properties) - { - var value = property.TextValue; - if (string.IsNullOrWhiteSpace(value)) - { - continue; - } - - var propertyChanged = false; - if (property.PropertyTypeDto?.DataTypeDto?.EditorAlias == Constants.PropertyEditors.Aliases.Grid) - { - try - { - JObject? obj = JsonConvert.DeserializeObject(value); - IEnumerable? allControls = obj?.SelectTokens("$.sections..rows..areas..controls"); - - if (allControls is not null) - { - foreach (JObject control in allControls.SelectMany(c => c).OfType()) - { - JToken? controlValue = control["value"]; - if (controlValue?.Type == JTokenType.String) - { - control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value()!, - out var controlChanged); - propertyChanged |= controlChanged; - } - } - } - - property.TextValue = JsonConvert.SerializeObject(obj); - } - catch (JsonException e) - { - exceptions.Add(new InvalidOperationException( - "Cannot deserialize the value as json. This can be because the property editor " + - "type is changed from another type into a grid. Old versions of the value in this " + - "property can have the structure from the old property editor type. This needs to be " + - "changed manually before updating the database.\n" + - $"Property info: Id = {property.Id}, LanguageId = {property.LanguageId}, VersionId = {property.VersionId}, Value = {property.Value}", - e)); - continue; - } - } - else - { - property.TextValue = UpdateMediaUrls(mediaLinkPattern, value, out propertyChanged); - } - - if (propertyChanged) - { - Database.Update(property); - } - } - - if (exceptions.Any()) - { - throw new AggregateException( - "One or more errors related to unexpected data in grid values occurred.", - exceptions); - } - - Context.AddPostMigration(); - } - - private string UpdateMediaUrls(Regex mediaLinkPattern, string value, out bool changed) - { - var 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) - // - 3 = anything after group 2 until the a tag is closed - var href = match.Groups[2].Value; - - IMedia? media = _mediaService.GetMediaByPath(href); - return media == null - ? match.Value - : $"{match.Groups[1].Value}/{{localLink:{media.GetUdi()}}}{match.Groups[3].Value}"; - }); - - changed = matched; - - return result; } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/FixContentNuCascade.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/FixContentNuCascade.cs index abb4fbfea8..cd9fdc1303 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/FixContentNuCascade.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/FixContentNuCascade.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class FixContentNuCascade : MigrationBase { public FixContentNuCascade(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/RenameUserLoginDtoDateIndex.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/RenameUserLoginDtoDateIndex.cs index ac2e27b2d6..28e91c1a97 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/RenameUserLoginDtoDateIndex.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/RenameUserLoginDtoDateIndex.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RenameUserLoginDtoDateIndex : MigrationBase { public RenameUserLoginDtoDateIndex(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs index bd76857ab7..1a5633213e 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddMainDomLock : MigrationBase { public AddMainDomLock(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddNewRelationTypes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddNewRelationTypes.cs index c2a447e778..406c02b75a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddNewRelationTypes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddNewRelationTypes.cs @@ -6,6 +6,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; /// /// Ensures the new relation types are created /// +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddNewRelationTypes : MigrationBase { public AddNewRelationTypes(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs index a5aea97fc2..78807672af 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPropertyTypeValidationMessageColumns : MigrationBase { public AddPropertyTypeValidationMessageColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs index 7e7b659401..39b02bd184 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MissingContentVersionsIndexes : MigrationBase { private const string IndexName = "IX_" + ContentVersionDto.TableName + "_NodeId"; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/UpdateRelationTypeTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/UpdateRelationTypeTable.cs index 032359cfcc..a23346a55c 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/UpdateRelationTypeTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/UpdateRelationTypeTable.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdateRelationTypeTable : MigrationBase { public UpdateRelationTypeTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs index 1ab43c2eb7..48274438c5 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_7_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MissingDictionaryIndex : MigrationBase { public MissingDictionaryIndex(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_9_0/ExternalLoginTableUserData.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_9_0/ExternalLoginTableUserData.cs index bf21b6b928..5951b5e9b7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_9_0/ExternalLoginTableUserData.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_9_0/ExternalLoginTableUserData.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_9_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ExternalLoginTableUserData : MigrationBase { public ExternalLoginTableUserData(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/AddPasswordConfigToMemberTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/AddPasswordConfigToMemberTable.cs index 2bf01c55bb..496b1c533f 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/AddPasswordConfigToMemberTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/AddPasswordConfigToMemberTable.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPasswordConfigToMemberTable : MigrationBase { public AddPasswordConfigToMemberTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs index 3556213cd1..08016d9178 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs @@ -8,6 +8,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DictionaryTablesIndexes : MigrationBase { private const string IndexedDictionaryColumn = "key"; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs index 8caa28de03..eb1b7c1b19 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ExternalLoginTableIndexes : MigrationBase { public ExternalLoginTableIndexes(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexesFixup.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexesFixup.cs index 8c508a3d04..15175a4cc5 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexesFixup.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexesFixup.cs @@ -4,6 +4,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; /// Fixes up the original for post RC release to ensure that /// the correct indexes are applied. /// +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ExternalLoginTableIndexesFixup : MigrationBase { public ExternalLoginTableIndexesFixup(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTokenTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTokenTable.cs index 288dbdf23f..7e856e3a44 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTokenTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTokenTable.cs @@ -7,6 +7,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ExternalLoginTokenTable : MigrationBase { public ExternalLoginTokenTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MemberTableColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MemberTableColumns.cs index 50204e4432..32dae76da7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MemberTableColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MemberTableColumns.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MemberTableColumns : MigrationBase { public MemberTableColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs index 641433cee9..b9e6123c15 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs @@ -7,6 +7,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MigrateLogViewerQueriesFromFileToDb : MigrationBase { internal static readonly IEnumerable _defaultLogQueries = new LogViewerQueryDto[] diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/UmbracoServerColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/UmbracoServerColumn.cs index c844606ab2..6ae6c0bb12 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/UmbracoServerColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/UmbracoServerColumn.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UmbracoServerColumn : MigrationBase { public UmbracoServerColumn(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs deleted file mode 100644 index 7a885eca07..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_1_0; - -internal class AddContentVersionCleanupFeature : MigrationBase -{ - public AddContentVersionCleanupFeature(IMigrationContext context) - : base(context) - { - } - - /// - /// The conditionals are useful to enable the same migration to be used in multiple - /// migration paths x.x -> 8.18 and x.x -> 9.x - /// - protected override void Migrate() - { - IEnumerable tables = SqlSyntax.GetTablesInSchema(Context.Database); - if (!tables.InvariantContains(ContentVersionCleanupPolicyDto.TableName)) - { - Create.Table().Do(); - } - - IEnumerable columns = SqlSyntax.GetColumnsInSchema(Context.Database); - AddColumnIfNotExists(columns, "preventCleanup"); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddDefaultForNotificationsToggle.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddDefaultForNotificationsToggle.cs index 9b91c0a372..2641a8304a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddDefaultForNotificationsToggle.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddDefaultForNotificationsToggle.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Infrastructure.Persistence; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_2_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddDefaultForNotificationsToggle : MigrationBase { public AddDefaultForNotificationsToggle(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs deleted file mode 100644 index 41abd07b23..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_2_0; - -internal class AddUserGroup2NodeTable : MigrationBase -{ - public AddUserGroup2NodeTable(IMigrationContext context) - : base(context) - { - } - - protected override void Migrate() - { - IEnumerable tables = SqlSyntax.GetTablesInSchema(Context.Database); - if (!tables.InvariantContains(UserGroup2NodeDto.TableName)) - { - Create.Table().Do(); - } - - // Insert if there exists specific permissions today. Can't do it directly in db in any nice way. - List? allData = Database.Fetch(); - UserGroup2NodeDto[] toInsert = allData - .Select(x => new UserGroup2NodeDto { NodeId = x.NodeId, UserGroupId = x.UserGroupId }).Distinct( - new DelegateEqualityComparer( - (x, y) => x?.NodeId == y?.NodeId && x?.UserGroupId == y?.UserGroupId, - x => x.NodeId.GetHashCode() + x.UserGroupId.GetHashCode())).ToArray(); - Database.InsertBulk(toInsert); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs index 5e781406ae..e1d26aae73 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs @@ -3,6 +3,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_3_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddTwoFactorLoginTable : MigrationBase { public AddTwoFactorLoginTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/MovePackageXMLToDb.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/MovePackageXMLToDb.cs index a2b2b40238..4b963e7c1c 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/MovePackageXMLToDb.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/MovePackageXMLToDb.cs @@ -4,6 +4,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_3_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MovePackageXMLToDb : MigrationBase { private readonly PackagesRepository _packagesRepository; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/UpdateExternalLoginToUseKeyInsteadOfId.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/UpdateExternalLoginToUseKeyInsteadOfId.cs index c3f42a90ab..991e201e0d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/UpdateExternalLoginToUseKeyInsteadOfId.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/UpdateExternalLoginToUseKeyInsteadOfId.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_3_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdateExternalLoginToUseKeyInsteadOfId : MigrationBase { public UpdateExternalLoginToUseKeyInsteadOfId(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/AddScheduledPublishingLock.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/AddScheduledPublishingLock.cs deleted file mode 100644 index 550e67879a..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/AddScheduledPublishingLock.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_4_0; - -internal class AddScheduledPublishingLock : MigrationBase -{ - public AddScheduledPublishingLock(IMigrationContext context) - : base(context) - { - } - - protected override void Migrate() => - Database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.ScheduledPublishing, Name = "ScheduledPublishing" }); -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/UpdateRelationTypesToHandleDependencies.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/UpdateRelationTypesToHandleDependencies.cs deleted file mode 100644 index 44144b93fe..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/UpdateRelationTypesToHandleDependencies.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_4_0; - -internal class UpdateRelationTypesToHandleDependencies : MigrationBase -{ - public UpdateRelationTypesToHandleDependencies(IMigrationContext context) - : base(context) - { - } - - protected override void Migrate() - { - var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); - - AddColumnIfNotExists(columns, "isDependency"); - - var aliasesWithDependencies = new[] - { - Constants.Conventions.RelationTypes.RelatedDocumentAlias, - Constants.Conventions.RelationTypes.RelatedMediaAlias, - }; - - Database.Execute( - Sql() - .Update(u => u.Set(x => x.IsDependency, true)) - .WhereIn(x => x.Alias, aliasesWithDependencies)); - } -} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs index e0140b65b3..7119dd50ff 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs @@ -29,7 +29,8 @@ namespace Umbraco.Cms.Core.PropertyEditors ValueType = ValueTypes.Json, Icon = "icon-layout", Group = Constants.PropertyEditors.Groups.RichContent, - ValueEditorIsReusable = false)] + ValueEditorIsReusable = false, + IsDeprecated = true)] public class GridPropertyEditor : DataEditor { private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs index 230c6e2b59..cf85339d48 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs @@ -21,12 +21,13 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// [DataEditor( Constants.PropertyEditors.Aliases.NestedContent, - "Nested Content", + "Nested Content (legacy)", "nestedcontent", ValueType = ValueTypes.Json, Group = Constants.PropertyEditors.Groups.Lists, Icon = "icon-thumbnail-list", - ValueEditorIsReusable = false)] + ValueEditorIsReusable = false, + IsDeprecated = true)] public class NestedContentPropertyEditor : DataEditor { public const string ContentTypeAliasPropertyKey = "ncContentTypeAlias"; diff --git a/src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml b/src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml b/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Web.Common/CompatibilitySuppressions.xml b/src/Umbraco.Web.Common/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Web.Common/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Web.Website/CompatibilitySuppressions.xml b/src/Umbraco.Web.Website/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Web.Website/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml b/tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml b/tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file From 13308c3610c2cf08ee6dc61a5de9a8820cb56a3e Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 14 Nov 2022 12:40:06 +0100 Subject: [PATCH 33/39] Obsolete old migrations and exif code (#13382) * Obsoleted and deleted internal migrations + Enabled breaking changes checks agains v11.0.0-rc1 * Obsoleted and deleted internal exif stuff * Added CompatibilitySuppressions.xml * Change GenerateCompatibilitySuppressionFile --- .gitignore | 1 + Directory.Build.props | 4 +- .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 42 +- src/Umbraco.Core/Media/Exif/BitConverterEx.cs | 346 ----- .../Media/Exif/ExifBitConverter.cs | 392 ----- src/Umbraco.Core/Media/Exif/ExifEnums.cs | 297 ---- src/Umbraco.Core/Media/Exif/ExifExceptions.cs | 1 + .../Media/Exif/ExifExtendedProperty.cs | 487 ------ .../Media/Exif/ExifFileTypeDescriptor.cs | 108 -- .../Media/Exif/ExifInterOperability.cs | 55 - src/Umbraco.Core/Media/Exif/ExifProperty.cs | 672 --------- .../Media/Exif/ExifPropertyCollection.cs | 493 ------ .../Media/Exif/ExifPropertyFactory.cs | 598 -------- src/Umbraco.Core/Media/Exif/ExifTag.cs | 310 ---- src/Umbraco.Core/Media/Exif/ExifTagFactory.cs | 63 - src/Umbraco.Core/Media/Exif/IFD.cs | 17 - src/Umbraco.Core/Media/Exif/ImageFile.cs | 144 -- .../Media/Exif/ImageFileDirectory.cs | 100 -- .../Media/Exif/ImageFileDirectoryEntry.cs | 144 -- .../Media/Exif/ImageFileFormat.cs | 27 - src/Umbraco.Core/Media/Exif/JFIFEnums.cs | 44 - .../Media/Exif/JFIFExtendedProperty.cs | 76 - src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs | 62 - src/Umbraco.Core/Media/Exif/JPEGExceptions.cs | 1 + src/Umbraco.Core/Media/Exif/JPEGFile.cs | 1110 -------------- src/Umbraco.Core/Media/Exif/JPEGMarker.cs | 95 -- src/Umbraco.Core/Media/Exif/JPEGSection.cs | 66 - src/Umbraco.Core/Media/Exif/MathEx.cs | 1329 ----------------- src/Umbraco.Core/Media/Exif/SvgFile.cs | 31 - src/Umbraco.Core/Media/Exif/TIFFFile.cs | 186 --- src/Umbraco.Core/Media/Exif/TIFFHeader.cs | 98 -- src/Umbraco.Core/Media/Exif/TIFFStrip.cs | 24 - src/Umbraco.Core/Media/Exif/Utility.cs | 29 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 14 +- .../UmbracoBuilder.CoreServices.cs | 13 +- .../Migrations/Upgrade/UmbracoPlan.cs | 251 +--- .../Upgrade/V_8_0_0/AddContentNuTable.cs | 23 - .../V_8_0_0/AddContentTypeIsElementColumn.cs | 1 + .../Upgrade/V_8_0_0/AddLockObjects.cs | 1 + .../Upgrade/V_8_0_0/AddLogTableColumns.cs | 1 + .../V_8_0_0/AddPackagesSectionAccess.cs | 1 + .../Upgrade/V_8_0_0/AddTypedLabels.cs | 1 + .../Upgrade/V_8_0_0/AddVariationTables1A.cs | 1 + .../Upgrade/V_8_0_0/AddVariationTables2.cs | 1 + .../V_8_0_0/ContentVariationMigration.cs | 46 +- .../ConvertRelatedLinksToMultiUrlPicker.cs | 148 +- .../Upgrade/V_8_0_0/DataTypeMigration.cs | 95 +- .../ContentPickerPreValueMigrator.cs | 23 - .../DataTypes/DecimalPreValueMigrator.cs | 22 - .../DataTypes/DefaultPreValueMigrator.cs | 44 - .../DropDownFlexiblePreValueMigrator.cs | 30 - .../V_8_0_0/DataTypes/IPreValueMigrator.cs | 1 + .../DataTypes/ListViewPreValueMigrator.cs | 26 - .../MarkdownEditorPreValueMigrator.cs | 19 - .../DataTypes/MediaPickerPreValueMigrator.cs | 37 - .../NestedContentPreValueMigrator.cs | 39 - .../Upgrade/V_8_0_0/DataTypes/PreValueDto.cs | 1 + .../V_8_0_0/DataTypes/PreValueMigratorBase.cs | 1 + .../DataTypes/PreValueMigratorCollection.cs | 1 + .../PreValueMigratorCollectionBuilder.cs | 1 + .../DataTypes/RenamingPreValueMigrator.cs | 23 - .../DataTypes/RichTextPreValueMigrator.cs | 24 - .../UmbracoSliderPreValueMigrator.cs | 21 - .../DataTypes/ValueListPreValueMigrator.cs | 29 - .../DropDownPropertyEditorsMigration.cs | 1 + .../Upgrade/V_8_0_0/DropMigrationsTable.cs | 1 + .../Upgrade/V_8_0_0/DropPreValueTable.cs | 1 + .../Upgrade/V_8_0_0/DropTaskTables.cs | 1 + .../V_8_0_0/DropTemplateDesignColumn.cs | 1 + .../Upgrade/V_8_0_0/DropXmlTables.cs | 1 + .../Upgrade/V_8_0_0/FallbackLanguage.cs | 1 + .../V_8_0_0/FixLanguageIsoCodeLength.cs | 1 + .../Upgrade/V_8_0_0/LanguageColumns.cs | 1 + .../Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs | 1 + .../Upgrade/V_8_0_0/MakeTagsVariant.cs | 1 + .../MergeDateAndDateTimePropertyEditor.cs | 1 + .../V_8_0_0/Models/ContentTypeDto80.cs | 63 - .../V_8_0_0/Models/PropertyDataDto80.cs | 142 -- .../V_8_0_0/Models/PropertyTypeDto80.cs | 76 - .../V_8_0_0/PropertyEditorsMigration.cs | 1 + .../V_8_0_0/PropertyEditorsMigrationBase.cs | 1 + ...adioAndCheckboxPropertyEditorsMigration.cs | 1 + .../Upgrade/V_8_0_0/RefactorMacroColumns.cs | 1 + .../Upgrade/V_8_0_0/RefactorVariantsModel.cs | 1 + ...meLabelAndRichTextPropertyEditorAliases.cs | 1 + .../V_8_0_0/RenameMediaVersionTable.cs | 1 + .../V_8_0_0/RenameUmbracoDomainsTable.cs | 1 + .../Migrations/Upgrade/V_8_0_0/SuperZero.cs | 1 + .../V_8_0_0/TablesForScheduledPublishing.cs | 1 + .../Upgrade/V_8_0_0/TagsMigration.cs | 1 + .../Upgrade/V_8_0_0/TagsMigrationFix.cs | 1 + .../V_8_0_0/UpdateDefaultMandatoryLanguage.cs | 1 + .../V_8_0_0/UpdatePickerIntegerValuesToUdi.cs | 1 + .../Upgrade/V_8_0_0/UserForeignKeys.cs | 1 + .../Upgrade/V_8_0_0/VariantsMigration.cs | 1 + .../V_8_0_1/ChangeNuCacheJsonFormat.cs | 1 + .../AddPropertyTypeLabelOnTopColumn.cs | 1 + .../V_8_15_0/AddCmsContentNuByteColumn.cs | 1 + .../V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs | 1 + .../V_8_15_0/UpgradedIncludeIndexes.cs | 1 + .../V_8_17_0/AddPropertyTypeGroupColumns.cs | 1 + ...nvertTinyMceAndGridMediaUrlsToLocalLink.cs | 117 +- .../Upgrade/V_8_1_0/FixContentNuCascade.cs | 1 + .../V_8_1_0/RenameUserLoginDtoDateIndex.cs | 1 + .../Upgrade/V_8_6_0/AddMainDomLock.cs | 1 + .../Upgrade/V_8_6_0/AddNewRelationTypes.cs | 1 + ...AddPropertyTypeValidationMessageColumns.cs | 1 + .../V_8_6_0/MissingContentVersionsIndexes.cs | 1 + .../V_8_6_0/UpdateRelationTypeTable.cs | 1 + .../Upgrade/V_8_7_0/MissingDictionaryIndex.cs | 1 + .../V_8_9_0/ExternalLoginTableUserData.cs | 1 + .../V_9_0_0/AddPasswordConfigToMemberTable.cs | 1 + .../V_9_0_0/DictionaryTablesIndexes.cs | 1 + .../V_9_0_0/ExternalLoginTableIndexes.cs | 1 + .../V_9_0_0/ExternalLoginTableIndexesFixup.cs | 1 + .../V_9_0_0/ExternalLoginTokenTable.cs | 1 + .../Upgrade/V_9_0_0/MemberTableColumns.cs | 1 + .../MigrateLogViewerQueriesFromFileToDb.cs | 1 + .../Upgrade/V_9_0_0/UmbracoServerColumn.cs | 1 + .../AddContentVersionCleanupFeature.cs | 29 - .../AddDefaultForNotificationsToggle.cs | 1 + .../Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs | 31 - .../Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs | 1 + .../Upgrade/V_9_3_0/MovePackageXMLToDb.cs | 1 + .../UpdateExternalLoginToUseKeyInsteadOfId.cs | 1 + .../V_9_4_0/AddScheduledPublishingLock.cs | 15 - ...UpdateRelationTypesToHandleDependencies.cs | 31 - .../PropertyEditors/GridPropertyEditor.cs | 3 +- .../NestedContentPropertyEditor.cs | 5 +- .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - .../CompatibilitySuppressions.xml | 7 - 139 files changed, 151 insertions(+), 8877 deletions(-) delete mode 100644 src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Core/Media/Exif/BitConverterEx.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifBitConverter.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifEnums.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifExtendedProperty.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifInterOperability.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifProperty.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifPropertyFactory.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifTag.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ExifTagFactory.cs delete mode 100644 src/Umbraco.Core/Media/Exif/IFD.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ImageFile.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ImageFileDirectory.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ImageFileDirectoryEntry.cs delete mode 100644 src/Umbraco.Core/Media/Exif/ImageFileFormat.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JFIFEnums.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JFIFExtendedProperty.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JPEGFile.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JPEGMarker.cs delete mode 100644 src/Umbraco.Core/Media/Exif/JPEGSection.cs delete mode 100644 src/Umbraco.Core/Media/Exif/MathEx.cs delete mode 100644 src/Umbraco.Core/Media/Exif/SvgFile.cs delete mode 100644 src/Umbraco.Core/Media/Exif/TIFFFile.cs delete mode 100644 src/Umbraco.Core/Media/Exif/TIFFHeader.cs delete mode 100644 src/Umbraco.Core/Media/Exif/TIFFStrip.cs delete mode 100644 src/Umbraco.Core/Media/Exif/Utility.cs delete mode 100644 src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentNuTable.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ContentPickerPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DecimalPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DropDownFlexiblePreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ListViewPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MarkdownEditorPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MediaPickerPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/NestedContentPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RenamingPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RichTextPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/UmbracoSliderPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ValueListPreValueMigrator.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/AddScheduledPublishingLock.cs delete mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/UpdateRelationTypesToHandleDependencies.cs delete mode 100644 src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Web.Common/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Web.Website/CompatibilitySuppressions.xml delete mode 100644 tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml delete mode 100644 tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml diff --git a/.gitignore b/.gitignore index 210ba2ec87..81c60b6019 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,4 @@ preserve.belle /src/Umbraco.Web.UI/appsettings-schema.*.json /tests/Umbraco.Tests.Integration/appsettings-schema.json /tests/Umbraco.Tests.Integration/appsettings-schema.*.json +/src/Umbraco.Cms/appsettings-schema.json diff --git a/Directory.Build.props b/Directory.Build.props index 7b4f5c7794..dd6f8126b1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -29,9 +29,9 @@ + false true - - 10.2.1 + 11.0.0-rc1 true true diff --git a/src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml b/src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Core/CompatibilitySuppressions.xml b/src/Umbraco.Core/CompatibilitySuppressions.xml index 7baad05aa0..e34b117b9a 100644 --- a/src/Umbraco.Core/CompatibilitySuppressions.xml +++ b/src/Umbraco.Core/CompatibilitySuppressions.xml @@ -1,7 +1,45 @@  - PKV006 - net6.0 + CP0006 + M:Umbraco.Cms.Core.Deploy.IDataTypeConfigurationConnector.FromArtifact(Umbraco.Cms.Core.Models.IDataType,System.String,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IDataTypeConfigurationConnector.ToArtifact(Umbraco.Cms.Core.Models.IDataType,System.Collections.Generic.ICollection{Umbraco.Cms.Core.Deploy.ArtifactDependency},Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IServiceConnector.GetArtifact(System.Object,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IServiceConnector.GetArtifact(Umbraco.Cms.Core.Udi,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IValueConnector.FromArtifact(System.String,Umbraco.Cms.Core.Models.IPropertyType,System.Object,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IValueConnector.ToArtifact(System.Object,Umbraco.Cms.Core.Models.IPropertyType,System.Collections.Generic.ICollection{Umbraco.Cms.Core.Deploy.ArtifactDependency},Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Core.dll + lib/net7.0/Umbraco.Core.dll + true \ No newline at end of file diff --git a/src/Umbraco.Core/Media/Exif/BitConverterEx.cs b/src/Umbraco.Core/Media/Exif/BitConverterEx.cs deleted file mode 100644 index f6cc50f801..0000000000 --- a/src/Umbraco.Core/Media/Exif/BitConverterEx.cs +++ /dev/null @@ -1,346 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// An endian-aware converter for converting between base data types -/// and an array of bytes. -/// -internal class BitConverterEx -{ - #region Public Enums - - /// - /// Represents the byte order. - /// - public enum ByteOrder - { - LittleEndian = 1, - BigEndian = 2, - } - - #endregion - - #region Member Variables - - private readonly ByteOrder mFrom; - private readonly ByteOrder mTo; - - #endregion - - #region Constructors - - public BitConverterEx(ByteOrder from, ByteOrder to) - { - mFrom = from; - mTo = to; - } - - #endregion - - #region Properties - - /// - /// Indicates the byte order in which data is stored in this platform. - /// - public static ByteOrder SystemByteOrder => - BitConverter.IsLittleEndian ? ByteOrder.LittleEndian : ByteOrder.BigEndian; - - #endregion - - #region Predefined Values - - /// - /// Returns a bit converter that converts between little-endian and system byte-order. - /// - public static BitConverterEx LittleEndian => new BitConverterEx(ByteOrder.LittleEndian, SystemByteOrder); - - /// - /// Returns a bit converter that converts between big-endian and system byte-order. - /// - public static BitConverterEx BigEndian => new BitConverterEx(ByteOrder.BigEndian, SystemByteOrder); - - /// - /// Returns a bit converter that does not do any byte-order conversion. - /// - public static BitConverterEx SystemEndian => new BitConverterEx(SystemByteOrder, SystemByteOrder); - - #endregion - - #region Static Methods - - /// - /// Converts the given array of bytes to a Unicode character. - /// - public static char ToChar(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 2, from, to); - return BitConverter.ToChar(data, 0); - } - - /// - /// Converts the given array of bytes to a 16-bit unsigned integer. - /// - public static ushort ToUInt16(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 2, from, to); - return BitConverter.ToUInt16(data, 0); - } - - /// - /// Converts the given array of bytes to a 32-bit unsigned integer. - /// - public static uint ToUInt32(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 4, from, to); - return BitConverter.ToUInt32(data, 0); - } - - /// - /// Converts the given array of bytes to a 64-bit unsigned integer. - /// - public static ulong ToUInt64(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 8, from, to); - return BitConverter.ToUInt64(data, 0); - } - - /// - /// Converts the given array of bytes to a 16-bit signed integer. - /// - public static short ToInt16(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 2, from, to); - return BitConverter.ToInt16(data, 0); - } - - /// - /// Converts the given array of bytes to a 32-bit signed integer. - /// - public static int ToInt32(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 4, from, to); - return BitConverter.ToInt32(data, 0); - } - - /// - /// Converts the given array of bytes to a 64-bit signed integer. - /// - public static long ToInt64(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 8, from, to); - return BitConverter.ToInt64(data, 0); - } - - /// - /// Converts the given array of bytes to a single precision floating number. - /// - public static float ToSingle(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 4, from, to); - return BitConverter.ToSingle(data, 0); - } - - /// - /// Converts the given array of bytes to a double precision floating number. - /// - public static double ToDouble(byte[] value, long startIndex, ByteOrder from, ByteOrder to) - { - var data = CheckData(value, startIndex, 8, from, to); - return BitConverter.ToDouble(data, 0); - } - - /// - /// Converts the given 16-bit unsigned integer to an array of bytes. - /// - public static byte[] GetBytes(ushort value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 32-bit unsigned integer to an array of bytes. - /// - public static byte[] GetBytes(uint value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 64-bit unsigned integer to an array of bytes. - /// - public static byte[] GetBytes(ulong value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 16-bit signed integer to an array of bytes. - /// - public static byte[] GetBytes(short value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 32-bit signed integer to an array of bytes. - /// - public static byte[] GetBytes(int value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given 64-bit signed integer to an array of bytes. - /// - public static byte[] GetBytes(long value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given single precision floating-point number to an array of bytes. - /// - public static byte[] GetBytes(float value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - /// - /// Converts the given double precision floating-point number to an array of bytes. - /// - public static byte[] GetBytes(double value, ByteOrder from, ByteOrder to) - { - var data = BitConverter.GetBytes(value); - data = CheckData(data, from, to); - return data; - } - - #endregion - - #region Instance Methods - - /// - /// Converts the given array of bytes to a 16-bit unsigned integer. - /// - public char ToChar(byte[] value, long startIndex) => ToChar(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 16-bit unsigned integer. - /// - public ushort ToUInt16(byte[] value, long startIndex) => ToUInt16(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 32-bit unsigned integer. - /// - public uint ToUInt32(byte[] value, long startIndex) => ToUInt32(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 64-bit unsigned integer. - /// - public ulong ToUInt64(byte[] value, long startIndex) => ToUInt64(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 16-bit signed integer. - /// - public short ToInt16(byte[] value, long startIndex) => ToInt16(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 32-bit signed integer. - /// - public int ToInt32(byte[] value, long startIndex) => ToInt32(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a 64-bit signed integer. - /// - public long ToInt64(byte[] value, long startIndex) => ToInt64(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a single precision floating number. - /// - public float ToSingle(byte[] value, long startIndex) => ToSingle(value, startIndex, mFrom, mTo); - - /// - /// Converts the given array of bytes to a double precision floating number. - /// - public double ToDouble(byte[] value, long startIndex) => ToDouble(value, startIndex, mFrom, mTo); - - /// - /// Converts the given 16-bit unsigned integer to an array of bytes. - /// - public byte[] GetBytes(ushort value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 32-bit unsigned integer to an array of bytes. - /// - public byte[] GetBytes(uint value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 64-bit unsigned integer to an array of bytes. - /// - public byte[] GetBytes(ulong value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 16-bit signed integer to an array of bytes. - /// - public byte[] GetBytes(short value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 32-bit signed integer to an array of bytes. - /// - public byte[] GetBytes(int value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given 64-bit signed integer to an array of bytes. - /// - public byte[] GetBytes(long value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given single precision floating-point number to an array of bytes. - /// - public byte[] GetBytes(float value) => GetBytes(value, mFrom, mTo); - - /// - /// Converts the given double precision floating-point number to an array of bytes. - /// - public byte[] GetBytes(double value) => GetBytes(value, mFrom, mTo); - - #endregion - - #region Private Helpers - - /// - /// Reverse the array of bytes as needed. - /// - private static byte[] CheckData(byte[] value, long startIndex, long length, ByteOrder from, ByteOrder to) - { - var data = new byte[length]; - Array.Copy(value, startIndex, data, 0, length); - if (from != to) - { - Array.Reverse(data); - } - - return data; - } - - /// - /// Reverse the array of bytes as needed. - /// - private static byte[] CheckData(byte[] value, ByteOrder from, ByteOrder to) => - CheckData(value, 0, value.Length, from, to); - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ExifBitConverter.cs b/src/Umbraco.Core/Media/Exif/ExifBitConverter.cs deleted file mode 100644 index e8dc7c4eb9..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifBitConverter.cs +++ /dev/null @@ -1,392 +0,0 @@ -using System.Globalization; -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Converts between exif data types and array of bytes. -/// -internal class ExifBitConverter : BitConverterEx -{ - #region Constructors - - public ExifBitConverter(ByteOrder from, ByteOrder to) - : base(from, to) - { - } - - #endregion - - #region Static Methods - - /// - /// Returns an ASCII string converted from the given byte array. - /// - public static string ToAscii(byte[] data, bool endatfirstnull, Encoding encoding) - { - var len = data.Length; - if (endatfirstnull) - { - len = Array.IndexOf(data, (byte)0); - if (len == -1) - { - len = data.Length; - } - } - - return encoding.GetString(data, 0, len); - } - - /// - /// Returns an ASCII string converted from the given byte array. - /// - public static string ToAscii(byte[] data, Encoding encoding) => ToAscii(data, true, encoding); - - /// - /// Returns a string converted from the given byte array. - /// from the numeric value of each byte. - /// - public static string ToString(byte[] data) - { - var sb = new StringBuilder(); - foreach (var b in data) - { - sb.Append(b); - } - - return sb.ToString(); - } - - /// - /// Returns a DateTime object converted from the given byte array. - /// - public static DateTime ToDateTime(byte[] data, bool hastime) - { - var str = ToAscii(data, Encoding.ASCII); - var parts = str.Split(':', ' '); - try - { - if (hastime && parts.Length == 6) - { - // yyyy:MM:dd HH:mm:ss - // This is the expected format though some cameras - // can use single digits. See Issue 21. - return new DateTime( - int.Parse(parts[0], CultureInfo.InvariantCulture), - int.Parse(parts[1], CultureInfo.InvariantCulture), - int.Parse(parts[2], CultureInfo.InvariantCulture), - int.Parse(parts[3], CultureInfo.InvariantCulture), - int.Parse(parts[4], CultureInfo.InvariantCulture), - int.Parse(parts[5], CultureInfo.InvariantCulture)); - } - - if (!hastime && parts.Length == 3) - { - // yyyy:MM:dd - return new DateTime( - int.Parse(parts[0], CultureInfo.InvariantCulture), - int.Parse(parts[1], CultureInfo.InvariantCulture), - int.Parse(parts[2], CultureInfo.InvariantCulture)); - } - - return DateTime.MinValue; - } - catch (ArgumentOutOfRangeException) - { - return DateTime.MinValue; - } - catch (ArgumentException) - { - return DateTime.MinValue; - } - } - - /// - /// Returns a DateTime object converted from the given byte array. - /// - public static DateTime ToDateTime(byte[] data) => ToDateTime(data, true); - - /// - /// Returns an unsigned rational number converted from the first - /// eight bytes of the given byte array. The first four bytes are - /// assumed to be the numerator and the next four bytes are the - /// denominator. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static MathEx.UFraction32 ToURational(byte[] data, ByteOrder frombyteorder) - { - var num = new byte[4]; - var den = new byte[4]; - Array.Copy(data, 0, num, 0, 4); - Array.Copy(data, 4, den, 0, 4); - return new MathEx.UFraction32( - ToUInt32(num, 0, frombyteorder, SystemByteOrder), - ToUInt32(den, 0, frombyteorder, SystemByteOrder)); - } - - /// - /// Returns a signed rational number converted from the first - /// eight bytes of the given byte array. The first four bytes are - /// assumed to be the numerator and the next four bytes are the - /// denominator. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static MathEx.Fraction32 ToSRational(byte[] data, ByteOrder frombyteorder) - { - var num = new byte[4]; - var den = new byte[4]; - Array.Copy(data, 0, num, 0, 4); - Array.Copy(data, 4, den, 0, 4); - return new MathEx.Fraction32( - ToInt32(num, 0, frombyteorder, SystemByteOrder), - ToInt32(den, 0, frombyteorder, SystemByteOrder)); - } - - /// - /// Returns an array of 16-bit unsigned integers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static ushort[] ToUShortArray(byte[] data, int count, ByteOrder frombyteorder) - { - var numbers = new ushort[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[2]; - Array.Copy(data, i * 2, num, 0, 2); - numbers[i] = ToUInt16(num, 0, frombyteorder, SystemByteOrder); - } - - return numbers; - } - - /// - /// Returns an array of 32-bit unsigned integers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static uint[] ToUIntArray(byte[] data, int count, ByteOrder frombyteorder) - { - var numbers = new uint[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[4]; - Array.Copy(data, i * 4, num, 0, 4); - numbers[i] = ToUInt32(num, 0, frombyteorder, SystemByteOrder); - } - - return numbers; - } - - /// - /// Returns an array of 32-bit signed integers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static int[] ToSIntArray(byte[] data, int count, ByteOrder byteorder) - { - var numbers = new int[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[4]; - Array.Copy(data, i * 4, num, 0, 4); - numbers[i] = ToInt32(num, 0, byteorder, SystemByteOrder); - } - - return numbers; - } - - /// - /// Returns an array of unsigned rational numbers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static MathEx.UFraction32[] ToURationalArray(byte[] data, int count, ByteOrder frombyteorder) - { - var numbers = new MathEx.UFraction32[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[4]; - var den = new byte[4]; - Array.Copy(data, i * 8, num, 0, 4); - Array.Copy(data, (i * 8) + 4, den, 0, 4); - numbers[i].Set( - ToUInt32(num, 0, frombyteorder, SystemByteOrder), - ToUInt32(den, 0, frombyteorder, SystemByteOrder)); - } - - return numbers; - } - - /// - /// Returns an array of signed rational numbers converted from - /// the given byte array. - /// Numbers are converted from the given byte-order to platform byte-order. - /// - public static MathEx.Fraction32[] ToSRationalArray(byte[] data, int count, ByteOrder frombyteorder) - { - var numbers = new MathEx.Fraction32[count]; - for (uint i = 0; i < count; i++) - { - var num = new byte[4]; - var den = new byte[4]; - Array.Copy(data, i * 8, num, 0, 4); - Array.Copy(data, (i * 8) + 4, den, 0, 4); - numbers[i].Set( - ToInt32(num, 0, frombyteorder, SystemByteOrder), - ToInt32(den, 0, frombyteorder, SystemByteOrder)); - } - - return numbers; - } - - /// - /// Converts the given ascii string to an array of bytes optionally adding a null terminator. - /// - public static byte[] GetBytes(string value, bool addnull, Encoding encoding) - { - if (addnull) - { - value += '\0'; - } - - return encoding.GetBytes(value); - } - - /// - /// Converts the given ascii string to an array of bytes without adding a null terminator. - /// - public static byte[] GetBytes(string value, Encoding encoding) => GetBytes(value, false, encoding); - - /// - /// Converts the given datetime to an array of bytes with a null terminator. - /// - public static byte[] GetBytes(DateTime value, bool hastime) - { - var str = string.Empty; - if (hastime) - { - str = value.ToString("yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture); - } - else - { - str = value.ToString("yyyy:MM:dd", CultureInfo.InvariantCulture); - } - - return GetBytes(str, true, Encoding.ASCII); - } - - /// - /// Converts the given unsigned rational number to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(MathEx.UFraction32 value, ByteOrder tobyteorder) - { - var num = GetBytes(value.Numerator, SystemByteOrder, tobyteorder); - var den = GetBytes(value.Denominator, SystemByteOrder, tobyteorder); - var data = new byte[8]; - Array.Copy(num, 0, data, 0, 4); - Array.Copy(den, 0, data, 4, 4); - return data; - } - - /// - /// Converts the given signed rational number to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(MathEx.Fraction32 value, ByteOrder tobyteorder) - { - var num = GetBytes(value.Numerator, SystemByteOrder, tobyteorder); - var den = GetBytes(value.Denominator, SystemByteOrder, tobyteorder); - var data = new byte[8]; - Array.Copy(num, 0, data, 0, 4); - Array.Copy(den, 0, data, 4, 4); - return data; - } - - /// - /// Converts the given array of 16-bit unsigned integers to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(ushort[] value, ByteOrder tobyteorder) - { - var data = new byte[2 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i], SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 2, 2); - } - - return data; - } - - /// - /// Converts the given array of 32-bit unsigned integers to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(uint[] value, ByteOrder tobyteorder) - { - var data = new byte[4 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i], SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 4, 4); - } - - return data; - } - - /// - /// Converts the given array of 32-bit signed integers to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(int[] value, ByteOrder tobyteorder) - { - var data = new byte[4 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i], SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 4, 4); - } - - return data; - } - - /// - /// Converts the given array of unsigned rationals to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(MathEx.UFraction32[] value, ByteOrder tobyteorder) - { - var data = new byte[8 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i].Numerator, SystemByteOrder, tobyteorder); - var den = GetBytes(value[i].Denominator, SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 8, 4); - Array.Copy(den, 0, data, (i * 8) + 4, 4); - } - - return data; - } - - /// - /// Converts the given array of signed rationals to an array of bytes. - /// Numbers are converted from the platform byte-order to the given byte-order. - /// - public static byte[] GetBytes(MathEx.Fraction32[] value, ByteOrder tobyteorder) - { - var data = new byte[8 * value.Length]; - for (var i = 0; i < value.Length; i++) - { - var num = GetBytes(value[i].Numerator, SystemByteOrder, tobyteorder); - var den = GetBytes(value[i].Denominator, SystemByteOrder, tobyteorder); - Array.Copy(num, 0, data, i * 8, 4); - Array.Copy(den, 0, data, (i * 8) + 4, 4); - } - - return data; - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ExifEnums.cs b/src/Umbraco.Core/Media/Exif/ExifEnums.cs deleted file mode 100644 index 5e27b8dd24..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifEnums.cs +++ /dev/null @@ -1,297 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -internal enum Compression : ushort -{ - Uncompressed = 1, - CCITT1D = 2, - Group3Fax = 3, - Group4Fax = 4, - LZW = 5, - JPEG = 6, - PackBits = 32773, -} - -internal enum PhotometricInterpretation : ushort -{ - WhiteIsZero = 0, - BlackIsZero = 1, - RGB = 2, - RGBPalette = 3, - TransparencyMask = 4, - CMYK = 5, - YCbCr = 6, - CIELab = 8, -} - -internal enum Orientation : ushort -{ - Normal = 1, - MirroredVertically = 2, - Rotated180 = 3, - MirroredHorizontally = 4, - RotatedLeftAndMirroredVertically = 5, - RotatedRight = 6, - RotatedLeft = 7, - RotatedRightAndMirroredVertically = 8, -} - -internal enum PlanarConfiguration : ushort -{ - ChunkyFormat = 1, - PlanarFormat = 2, -} - -internal enum YCbCrPositioning : ushort -{ - Centered = 1, - CoSited = 2, -} - -internal enum ResolutionUnit : ushort -{ - Inches = 2, - Centimeters = 3, -} - -internal enum ColorSpace : ushort -{ - SRGB = 1, - Uncalibrated = 0xfff, -} - -internal enum ExposureProgram : ushort -{ - NotDefined = 0, - Manual = 1, - Normal = 2, - AperturePriority = 3, - ShutterPriority = 4, - - /// - /// Biased toward depth of field. - /// - Creative = 5, - - /// - /// Biased toward fast shutter speed. - /// - Action = 6, - - /// - /// For closeup photos with the background out of focus. - /// - Portrait = 7, - - /// - /// For landscape photos with the background in focus. - /// - Landscape = 8, -} - -internal enum MeteringMode : ushort -{ - Unknown = 0, - Average = 1, - CenterWeightedAverage = 2, - Spot = 3, - MultiSpot = 4, - Pattern = 5, - Partial = 6, - Other = 255, -} - -internal enum LightSource : ushort -{ - Unknown = 0, - Daylight = 1, - Fluorescent = 2, - Tungsten = 3, - Flash = 4, - FineWeather = 9, - CloudyWeather = 10, - Shade = 11, - - /// - /// D 5700 – 7100K - /// - DaylightFluorescent = 12, - - /// - /// N 4600 – 5400K - /// - DayWhiteFluorescent = 13, - - /// - /// W 3900 – 4500K - /// - CoolWhiteFluorescent = 14, - - /// - /// WW 3200 – 3700K - /// - WhiteFluorescent = 15, - StandardLightA = 17, - StandardLightB = 18, - StandardLightC = 19, - D55 = 20, - D65 = 21, - D75 = 22, - D50 = 23, - ISOStudioTungsten = 24, - OtherLightSource = 255, -} - -[Flags] -internal enum Flash : ushort -{ - FlashDidNotFire = 0, - StrobeReturnLightNotDetected = 4, - StrobeReturnLightDetected = 2, - FlashFired = 1, - CompulsoryFlashMode = 8, - AutoMode = 16, - NoFlashFunction = 32, - RedEyeReductionMode = 64, -} - -internal enum SensingMethod : ushort -{ - NotDefined = 1, - OneChipColorAreaSensor = 2, - TwoChipColorAreaSensor = 3, - ThreeChipColorAreaSensor = 4, - ColorSequentialAreaSensor = 5, - TriLinearSensor = 7, - ColorSequentialLinearSensor = 8, -} - -internal enum FileSource : byte // UNDEFINED -{ - DSC = 3, -} - -internal enum SceneType : byte // UNDEFINED -{ - DirectlyPhotographedImage = 1, -} - -internal enum CustomRendered : ushort -{ - NormalProcess = 0, - CustomProcess = 1, -} - -internal enum ExposureMode : ushort -{ - Auto = 0, - Manual = 1, - AutoBracket = 2, -} - -internal enum WhiteBalance : ushort -{ - Auto = 0, - Manual = 1, -} - -internal enum SceneCaptureType : ushort -{ - Standard = 0, - Landscape = 1, - Portrait = 2, - NightScene = 3, -} - -internal enum GainControl : ushort -{ - None = 0, - LowGainUp = 1, - HighGainUp = 2, - LowGainDown = 3, - HighGainDown = 4, -} - -internal enum Contrast : ushort -{ - Normal = 0, - Soft = 1, - Hard = 2, -} - -internal enum Saturation : ushort -{ - Normal = 0, - Low = 1, - High = 2, -} - -internal enum Sharpness : ushort -{ - Normal = 0, - Soft = 1, - Hard = 2, -} - -internal enum SubjectDistanceRange : ushort -{ - Unknown = 0, - Macro = 1, - CloseView = 2, - DistantView = 3, -} - -internal enum GPSLatitudeRef : byte // ASCII -{ - North = 78, // 'N' - South = 83, // 'S' -} - -internal enum GPSLongitudeRef : byte // ASCII -{ - West = 87, // 'W' - East = 69, // 'E' -} - -internal enum GPSAltitudeRef : byte -{ - AboveSeaLevel = 0, - BelowSeaLevel = 1, -} - -internal enum GPSStatus : byte // ASCII -{ - MeasurementInProgress = 65, // 'A' - MeasurementInteroperability = 86, // 'V' -} - -internal enum GPSMeasureMode : byte // ASCII -{ - TwoDimensional = 50, // '2' - ThreeDimensional = 51, // '3' -} - -internal enum GPSSpeedRef : byte // ASCII -{ - KilometersPerHour = 75, // 'K' - MilesPerHour = 77, // 'M' - Knots = 78, // 'N' -} - -internal enum GPSDirectionRef : byte // ASCII -{ - TrueDirection = 84, // 'T' - MagneticDirection = 77, // 'M' -} - -internal enum GPSDistanceRef : byte // ASCII -{ - Kilometers = 75, // 'K' - Miles = 77, // 'M' - Knots = 78, // 'N' -} - -internal enum GPSDifferential : ushort -{ - MeasurementWithoutDifferentialCorrection = 0, - DifferentialCorrectionApplied = 1, -} diff --git a/src/Umbraco.Core/Media/Exif/ExifExceptions.cs b/src/Umbraco.Core/Media/Exif/ExifExceptions.cs index bd4426e15a..9f78bd9e15 100644 --- a/src/Umbraco.Core/Media/Exif/ExifExceptions.cs +++ b/src/Umbraco.Core/Media/Exif/ExifExceptions.cs @@ -7,6 +7,7 @@ namespace Umbraco.Cms.Core.Media.Exif; /// /// [Serializable] +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class NotValidExifFileException : Exception { /// diff --git a/src/Umbraco.Core/Media/Exif/ExifExtendedProperty.cs b/src/Umbraco.Core/Media/Exif/ExifExtendedProperty.cs deleted file mode 100644 index ffa31f0cc1..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifExtendedProperty.cs +++ /dev/null @@ -1,487 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents an enumerated value. -/// -internal class ExifEnumProperty : ExifProperty - where T : notnull -{ - protected bool mIsBitField; - protected T mValue; - - public ExifEnumProperty(ExifTag tag, T value, bool isbitfield) - : base(tag) - { - mValue = value; - mIsBitField = isbitfield; - } - - public ExifEnumProperty(ExifTag tag, T value) - : this(tag, value, false) - { - } - - public new T Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (T)value; - } - - public bool IsBitField => mIsBitField; - - public override ExifInterOperability Interoperability - { - get - { - var tagid = ExifTagFactory.GetTagID(mTag); - - Type type = typeof(T); - Type basetype = Enum.GetUnderlyingType(type); - - if (type == typeof(FileSource) || type == typeof(SceneType)) - { - // UNDEFINED - return new ExifInterOperability(tagid, 7, 1, new[] { (byte)(object)mValue }); - } - - if (type == typeof(GPSLatitudeRef) || type == typeof(GPSLongitudeRef) || - type == typeof(GPSStatus) || type == typeof(GPSMeasureMode) || - type == typeof(GPSSpeedRef) || type == typeof(GPSDirectionRef) || - type == typeof(GPSDistanceRef)) - { - // ASCII - return new ExifInterOperability(tagid, 2, 2, new byte[] { (byte)(object)mValue, 0 }); - } - - if (basetype == typeof(byte)) - { - // BYTE - return new ExifInterOperability(tagid, 1, 1, new[] { (byte)(object)mValue }); - } - - if (basetype == typeof(ushort)) - { - // SHORT - return new ExifInterOperability( - tagid, - 3, - 1, - BitConverterEx.GetBytes((ushort)(object)mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder)); - } - - throw new InvalidOperationException( - $"An invalid enum type ({basetype.FullName}) was provided for type {type.FullName}"); - } - } - - public static implicit operator T(ExifEnumProperty obj) => obj.mValue; - - public override string? ToString() => mValue.ToString(); -} - -/// -/// Represents an ASCII string. (EXIF Specification: UNDEFINED) Used for the UserComment field. -/// -internal class ExifEncodedString : ExifProperty -{ - protected string mValue; - - public ExifEncodedString(ExifTag tag, string value, Encoding encoding) - : base(tag) - { - mValue = value; - Encoding = encoding; - } - - public new string Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (string)value; - } - - public Encoding Encoding { get; set; } - - public override ExifInterOperability Interoperability - { - get - { - var enc = string.Empty; - if (Encoding == null) - { - enc = "\0\0\0\0\0\0\0\0"; - } - else if (Encoding.EncodingName == "US-ASCII") - { - enc = "ASCII\0\0\0"; - } - else if (Encoding.EncodingName == "Japanese (JIS 0208-1990 and 0212-1990)") - { - enc = "JIS\0\0\0\0\0"; - } - else if (Encoding.EncodingName == "Unicode") - { - enc = "Unicode\0"; - } - else - { - enc = "\0\0\0\0\0\0\0\0"; - } - - var benc = Encoding.ASCII.GetBytes(enc); - var bstr = Encoding == null ? Encoding.ASCII.GetBytes(mValue) : Encoding.GetBytes(mValue); - var data = new byte[benc.Length + bstr.Length]; - Array.Copy(benc, 0, data, 0, benc.Length); - Array.Copy(bstr, 0, data, benc.Length, bstr.Length); - - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, (uint)data.Length, data); - } - } - - public static implicit operator string(ExifEncodedString obj) => obj.mValue; - - public override string ToString() => mValue; -} - -/// -/// Represents an ASCII string formatted as DateTime. (EXIF Specification: ASCII) Used for the date time fields. -/// -internal class ExifDateTime : ExifProperty -{ - protected DateTime mValue; - - public ExifDateTime(ExifTag tag, DateTime value) - : base(tag) => - mValue = value; - - public new DateTime Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (DateTime)value; - } - - public override ExifInterOperability Interoperability => - new(ExifTagFactory.GetTagID(mTag), 2, 20, ExifBitConverter.GetBytes(mValue, true)); - - public static implicit operator DateTime(ExifDateTime obj) => obj.mValue; - - public override string ToString() => mValue.ToString("yyyy.MM.dd HH:mm:ss"); -} - -/// -/// Represents the exif version as a 4 byte ASCII string. (EXIF Specification: UNDEFINED) -/// Used for the ExifVersion, FlashpixVersion, InteroperabilityVersion and GPSVersionID fields. -/// -internal class ExifVersion : ExifProperty -{ - protected string mValue; - - public ExifVersion(ExifTag tag, string value) - : base(tag) - { - if (value.Length > 4) - { - mValue = value[..4]; - } - else if (value.Length < 4) - { - mValue = value + new string(' ', 4 - value.Length); - } - else - { - mValue = value; - } - } - - public new string Value - { - get => mValue; - set => mValue = value[..4]; - } - - protected override object _Value - { - get => Value; - set => Value = (string)value; - } - - public override ExifInterOperability Interoperability - { - get - { - if (mTag == ExifTag.ExifVersion || mTag == ExifTag.FlashpixVersion || - mTag == ExifTag.InteroperabilityVersion) - { - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, 4, Encoding.ASCII.GetBytes(mValue)); - } - - var data = new byte[4]; - for (var i = 0; i < 4; i++) - { - data[i] = byte.Parse(mValue[0].ToString()); - } - - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, 4, data); - } - } - - public override string ToString() => mValue; -} - -/// -/// Represents the location and area of the subject (EXIF Specification: 2xSHORT) -/// The coordinate values, width, and height are expressed in relation to the -/// upper left as origin, prior to rotation processing as per the Rotation tag. -/// -internal class ExifPointSubjectArea : ExifUShortArray -{ - public ExifPointSubjectArea(ExifTag tag, ushort[] value) - : base(tag, value) - { - } - - public ExifPointSubjectArea(ExifTag tag, ushort x, ushort y) - : base(tag, new[] { x, y }) - { - } - - public ushort X - { - get => mValue[0]; - set => mValue[0] = value; - } - - protected new ushort[] Value - { - get => mValue; - set => mValue = value; - } - - public ushort Y - { - get => mValue[1]; - set => mValue[1] = value; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.AppendFormat("({0:d}, {1:d})", mValue[0], mValue[1]); - return sb.ToString(); - } -} - -/// -/// Represents the location and area of the subject (EXIF Specification: 3xSHORT) -/// The coordinate values, width, and height are expressed in relation to the -/// upper left as origin, prior to rotation processing as per the Rotation tag. -/// -internal class ExifCircularSubjectArea : ExifPointSubjectArea -{ - public ExifCircularSubjectArea(ExifTag tag, ushort[] value) - : base(tag, value) - { - } - - public ExifCircularSubjectArea(ExifTag tag, ushort x, ushort y, ushort d) - : base(tag, new[] { x, y, d }) - { - } - - public ushort Diamater - { - get => mValue[2]; - set => mValue[2] = value; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.AppendFormat("({0:d}, {1:d}) {2:d}", mValue[0], mValue[1], mValue[2]); - return sb.ToString(); - } -} - -/// -/// Represents the location and area of the subject (EXIF Specification: 4xSHORT) -/// The coordinate values, width, and height are expressed in relation to the -/// upper left as origin, prior to rotation processing as per the Rotation tag. -/// -internal class ExifRectangularSubjectArea : ExifPointSubjectArea -{ - public ExifRectangularSubjectArea(ExifTag tag, ushort[] value) - : base(tag, value) - { - } - - public ExifRectangularSubjectArea(ExifTag tag, ushort x, ushort y, ushort w, ushort h) - : base(tag, new[] { x, y, w, h }) - { - } - - public ushort Width - { - get => mValue[2]; - set => mValue[2] = value; - } - - public ushort Height - { - get => mValue[3]; - set => mValue[3] = value; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.AppendFormat("({0:d}, {1:d}) ({2:d} x {3:d})", mValue[0], mValue[1], mValue[2], mValue[3]); - return sb.ToString(); - } -} - -/// -/// Represents GPS latitudes and longitudes (EXIF Specification: 3xRATIONAL) -/// -internal class GPSLatitudeLongitude : ExifURationalArray -{ - public GPSLatitudeLongitude(ExifTag tag, MathEx.UFraction32[] value) - : base(tag, value) - { - } - - public GPSLatitudeLongitude(ExifTag tag, float d, float m, float s) - : base(tag, new[] { new(d), new MathEx.UFraction32(m), new MathEx.UFraction32(s) }) - { - } - - public MathEx.UFraction32 Degrees - { - get => mValue[0]; - set => mValue[0] = value; - } - - protected new MathEx.UFraction32[] Value - { - get => mValue; - set => mValue = value; - } - - public MathEx.UFraction32 Minutes - { - get => mValue[1]; - set => mValue[1] = value; - } - - public MathEx.UFraction32 Seconds - { - get => mValue[2]; - set => mValue[2] = value; - } - - public static explicit operator float(GPSLatitudeLongitude obj) => obj.ToFloat(); - - public float ToFloat() => (float)Degrees + ((float)Minutes / 60.0f) + ((float)Seconds / 3600.0f); - - public override string ToString() => - string.Format("{0:F2}°{1:F2}'{2:F2}\"", (float)Degrees, (float)Minutes, (float)Seconds); -} - -/// -/// Represents a GPS time stamp as UTC (EXIF Specification: 3xRATIONAL) -/// -internal class GPSTimeStamp : ExifURationalArray -{ - public GPSTimeStamp(ExifTag tag, MathEx.UFraction32[] value) - : base(tag, value) - { - } - - public GPSTimeStamp(ExifTag tag, float h, float m, float s) - : base(tag, new[] { new(h), new MathEx.UFraction32(m), new MathEx.UFraction32(s) }) - { - } - - public MathEx.UFraction32 Hour - { - get => mValue[0]; - set => mValue[0] = value; - } - - protected new MathEx.UFraction32[] Value - { - get => mValue; - set => mValue = value; - } - - public MathEx.UFraction32 Minute - { - get => mValue[1]; - set => mValue[1] = value; - } - - public MathEx.UFraction32 Second - { - get => mValue[2]; - set => mValue[2] = value; - } - - public override string ToString() => - string.Format("{0:F2}:{1:F2}:{2:F2}\"", (float)Hour, (float)Minute, (float)Second); -} - -/// -/// Represents an ASCII string. (EXIF Specification: BYTE) -/// Used by Windows XP. -/// -internal class WindowsByteString : ExifProperty -{ - protected string mValue; - - public WindowsByteString(ExifTag tag, string value) - : base(tag) => - mValue = value; - - public new string Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (string)value; - } - - public override ExifInterOperability Interoperability - { - get - { - var data = Encoding.Unicode.GetBytes(mValue); - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)data.Length, data); - } - } - - public static implicit operator string(WindowsByteString obj) => obj.mValue; - - public override string ToString() => mValue; -} diff --git a/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs b/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs deleted file mode 100644 index a07ade9963..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.ComponentModel; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Provides a custom type descriptor for an ExifFile instance. -/// -internal sealed class ExifFileTypeDescriptionProvider : TypeDescriptionProvider -{ - public ExifFileTypeDescriptionProvider() - : this(TypeDescriptor.GetProvider(typeof(ImageFile))) - { - } - - public ExifFileTypeDescriptionProvider(TypeDescriptionProvider parent) - : base(parent) - { - } - - /// - /// Gets a custom type descriptor for the given type and object. - /// - /// The type of object for which to retrieve the type descriptor. - /// - /// An instance of the type. Can be null if no instance was passed to the - /// . - /// - /// - /// An that can provide metadata for the type. - /// - public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object? instance) => - new ExifFileTypeDescriptor(base.GetTypeDescriptor(objectType, instance), instance); -} - -/// -/// Expands ExifProperty objects contained in an ExifFile as separate properties. -/// -internal sealed class ExifFileTypeDescriptor : CustomTypeDescriptor -{ - private readonly ImageFile? owner; - - public ExifFileTypeDescriptor(ICustomTypeDescriptor? parent, object? instance) - : base(parent) => - owner = (ImageFile?)instance; - - public override PropertyDescriptorCollection GetProperties(Attribute[]? attributes) => GetProperties(); - - /// - /// Returns a collection of property descriptors for the object represented by this type descriptor. - /// - /// - /// A containing the property descriptions for the - /// object represented by this type descriptor. The default is - /// . - /// - public override PropertyDescriptorCollection GetProperties() - { - // Enumerate the original set of properties and create our new set with it - var properties = new List(); - - if (owner is not null) - { - foreach (ExifProperty prop in owner.Properties) - { - var pd = new ExifPropertyDescriptor(prop); - properties.Add(pd); - } - } - - // Finally return the list - return new PropertyDescriptorCollection(properties.ToArray(), true); - } -} - -internal sealed class ExifPropertyDescriptor : PropertyDescriptor -{ - private readonly ExifProperty linkedProperty; - private readonly object originalValue; - - public ExifPropertyDescriptor(ExifProperty property) - : base(property.Name, new Attribute[] { new BrowsableAttribute(true) }) - { - linkedProperty = property; - originalValue = property.Value; - } - - public override Type ComponentType => typeof(JPEGFile); - - public override bool IsReadOnly => false; - - public override Type PropertyType => linkedProperty.Value.GetType(); - - public override bool CanResetValue(object component) => true; - - public override object GetValue(object? component) => linkedProperty.Value; - - public override void ResetValue(object component) => linkedProperty.Value = originalValue; - - public override void SetValue(object? component, object? value) - { - if (value is not null) - { - linkedProperty.Value = value; - } - } - - public override bool ShouldSerializeValue(object component) => false; -} diff --git a/src/Umbraco.Core/Media/Exif/ExifInterOperability.cs b/src/Umbraco.Core/Media/Exif/ExifInterOperability.cs deleted file mode 100644 index d2d9f8be6a..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifInterOperability.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents interoperability data for an exif tag in the platform byte order. -/// -internal struct ExifInterOperability -{ - public ExifInterOperability(ushort tagid, ushort typeid, uint count, byte[] data) - { - TagID = tagid; - TypeID = typeid; - Count = count; - Data = data; - } - - /// - /// Gets the tag ID defined in the Exif standard. - /// - public ushort TagID { get; } - - /// - /// Gets the type code defined in the Exif standard. - /// - /// 1 = BYTE (byte) - /// 2 = ASCII (byte array) - /// 3 = SHORT (ushort) - /// 4 = LONG (uint) - /// 5 = RATIONAL (2 x uint: numerator, denominator) - /// 6 = BYTE (sbyte) - /// 7 = UNDEFINED (byte array) - /// 8 = SSHORT (short) - /// 9 = SLONG (int) - /// 10 = SRATIONAL (2 x int: numerator, denominator) - /// 11 = FLOAT (float) - /// 12 = DOUBLE (double) - /// - /// - public ushort TypeID { get; } - - /// - /// Gets the byte count or number of components. - /// - public uint Count { get; } - - /// - /// Gets the field value as an array of bytes. - /// - public byte[] Data { get; } - - /// - /// Returns the string representation of this instance. - /// - /// - public override string ToString() => string.Format("Tag: {0}, Type: {1}, Count: {2}, Data Length: {3}", TagID, TypeID, Count, Data.Length); -} diff --git a/src/Umbraco.Core/Media/Exif/ExifProperty.cs b/src/Umbraco.Core/Media/Exif/ExifProperty.cs deleted file mode 100644 index 6d742bb3ba..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifProperty.cs +++ /dev/null @@ -1,672 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the abstract base class for an Exif property. -/// -internal abstract class ExifProperty -{ - protected IFD mIFD; - protected string? mName; - protected ExifTag mTag; - - public ExifProperty(ExifTag tag) - { - mTag = tag; - mIFD = ExifTagFactory.GetTagIFD(tag); - } - - /// - /// Gets the Exif tag associated with this property. - /// - public ExifTag Tag => mTag; - - /// - /// Gets the IFD section containing this property. - /// - public IFD IFD => mIFD; - - /// - /// Gets or sets the name of this property. - /// - public string Name - { - get - { - if (string.IsNullOrEmpty(mName)) - { - return ExifTagFactory.GetTagName(mTag); - } - - return mName; - } - set => mName = value; - } - - /// - /// Gets or sets the value of this property. - /// - public object Value - { - get => _Value; - set => _Value = value; - } - - protected abstract object _Value { get; set; } - - /// - /// Gets interoperability data for this property. - /// - public abstract ExifInterOperability Interoperability { get; } -} - -/// -/// Represents an 8-bit unsigned integer. (EXIF Specification: BYTE) -/// -internal class ExifByte : ExifProperty -{ - protected byte mValue; - - public ExifByte(ExifTag tag, byte value) - : base(tag) => - mValue = value; - - public new byte Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = Convert.ToByte(value); - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, 1, new[] { mValue }); - - public static implicit operator byte(ExifByte obj) => obj.mValue; - - public override string ToString() => mValue.ToString(); -} - -/// -/// Represents an array of 8-bit unsigned integers. (EXIF Specification: BYTE with count > 1) -/// -internal class ExifByteArray : ExifProperty -{ - protected byte[] mValue; - - public ExifByteArray(ExifTag tag, byte[] value) - : base(tag) => - mValue = value; - - public new byte[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (byte[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.Length, mValue); - - public static implicit operator byte[](ExifByteArray obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents an ASCII string. (EXIF Specification: ASCII) -/// -internal class ExifAscii : ExifProperty -{ - protected string mValue; - - public ExifAscii(ExifTag tag, string value, Encoding encoding) - : base(tag) - { - mValue = value; - Encoding = encoding; - } - - public new string Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (string)value; - } - - public Encoding Encoding { get; } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 2, - (uint)mValue.Length + 1, - ExifBitConverter.GetBytes(mValue, true, Encoding)); - - public static implicit operator string(ExifAscii obj) => obj.mValue; - - public override string ToString() => mValue; -} - -/// -/// Represents a 16-bit unsigned integer. (EXIF Specification: SHORT) -/// -internal class ExifUShort : ExifProperty -{ - protected ushort mValue; - - public ExifUShort(ExifTag tag, ushort value) - : base(tag) => - mValue = value; - - public new ushort Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = Convert.ToUInt16(value); - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 3, - 1, - BitConverterEx.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder)); - - public static implicit operator ushort(ExifUShort obj) => obj.mValue; - - public override string ToString() => mValue.ToString(); -} - -/// -/// Represents an array of 16-bit unsigned integers. -/// (EXIF Specification: SHORT with count > 1) -/// -internal class ExifUShortArray : ExifProperty -{ - protected ushort[] mValue; - - public ExifUShortArray(ExifTag tag, ushort[] value) - : base(tag) => - mValue = value; - - public new ushort[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (ushort[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 3, - (uint)mValue.Length, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static implicit operator ushort[](ExifUShortArray obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a 32-bit unsigned integer. (EXIF Specification: LONG) -/// -internal class ExifUInt : ExifProperty -{ - protected uint mValue; - - public ExifUInt(ExifTag tag, uint value) - : base(tag) => - mValue = value; - - public new uint Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = Convert.ToUInt32(value); - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 4, - 1, - BitConverterEx.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder)); - - public static implicit operator uint(ExifUInt obj) => obj.mValue; - - public override string ToString() => mValue.ToString(); -} - -/// -/// Represents an array of 16-bit unsigned integers. -/// (EXIF Specification: LONG with count > 1) -/// -internal class ExifUIntArray : ExifProperty -{ - protected uint[] mValue; - - public ExifUIntArray(ExifTag tag, uint[] value) - : base(tag) => - mValue = value; - - public new uint[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (uint[])value; - } - - public override ExifInterOperability Interoperability => new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 3, (uint)mValue.Length, ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static implicit operator uint[](ExifUIntArray obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a rational number defined with a 32-bit unsigned numerator -/// and denominator. (EXIF Specification: RATIONAL) -/// -internal class ExifURational : ExifProperty -{ - protected MathEx.UFraction32 mValue; - - public ExifURational(ExifTag tag, uint numerator, uint denominator) - : base(tag) => - mValue = new MathEx.UFraction32(numerator, denominator); - - public ExifURational(ExifTag tag, MathEx.UFraction32 value) - : base(tag) => - mValue = value; - - public new MathEx.UFraction32 Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (MathEx.UFraction32)value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 5, - 1, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static explicit operator float(ExifURational obj) => (float)obj.mValue; - - public override string ToString() => mValue.ToString(); - - public float ToFloat() => (float)mValue; - - public uint[] ToArray() => new[] { mValue.Numerator, mValue.Denominator }; -} - -/// -/// Represents an array of unsigned rational numbers. -/// (EXIF Specification: RATIONAL with count > 1) -/// -internal class ExifURationalArray : ExifProperty -{ - protected MathEx.UFraction32[] mValue; - - public ExifURationalArray(ExifTag tag, MathEx.UFraction32[] value) - : base(tag) => - mValue = value; - - public new MathEx.UFraction32[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (MathEx.UFraction32[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 5, - (uint)mValue.Length, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static explicit operator float[](ExifURationalArray obj) - { - var result = new float[obj.mValue.Length]; - for (var i = 0; i < obj.mValue.Length; i++) - { - result[i] = (float)obj.mValue[i]; - } - - return result; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (MathEx.UFraction32 b in mValue) - { - sb.Append(b.ToString()); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a byte array that can take any value. (EXIF Specification: UNDEFINED) -/// -internal class ExifUndefined : ExifProperty -{ - protected byte[] mValue; - - public ExifUndefined(ExifTag tag, byte[] value) - : base(tag) => - mValue = value; - - public new byte[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (byte[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 7, (uint)mValue.Length, mValue); - - public static implicit operator byte[](ExifUndefined obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a 32-bit signed integer. (EXIF Specification: SLONG) -/// -internal class ExifSInt : ExifProperty -{ - protected int mValue; - - public ExifSInt(ExifTag tag, int value) - : base(tag) => - mValue = value; - - public new int Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = Convert.ToInt32(value); - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 9, - 1, - BitConverterEx.GetBytes(mValue, BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder)); - - public static implicit operator int(ExifSInt obj) => obj.mValue; - - public override string ToString() => mValue.ToString(); -} - -/// -/// Represents an array of 32-bit signed integers. -/// (EXIF Specification: SLONG with count > 1) -/// -internal class ExifSIntArray : ExifProperty -{ - protected int[] mValue; - - public ExifSIntArray(ExifTag tag, int[] value) - : base(tag) => - mValue = value; - - public new int[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (int[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 9, - (uint)mValue.Length, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static implicit operator int[](ExifSIntArray obj) => obj.mValue; - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (var b in mValue) - { - sb.Append(b); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} - -/// -/// Represents a rational number defined with a 32-bit signed numerator -/// and denominator. (EXIF Specification: SRATIONAL) -/// -internal class ExifSRational : ExifProperty -{ - protected MathEx.Fraction32 mValue; - - public ExifSRational(ExifTag tag, int numerator, int denominator) - : base(tag) => - mValue = new MathEx.Fraction32(numerator, denominator); - - public ExifSRational(ExifTag tag, MathEx.Fraction32 value) - : base(tag) => - mValue = value; - - public new MathEx.Fraction32 Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (MathEx.Fraction32)value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 10, - 1, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static explicit operator float(ExifSRational obj) => (float)obj.mValue; - - public override string ToString() => mValue.ToString(); - - public float ToFloat() => (float)mValue; - - public int[] ToArray() => new[] { mValue.Numerator, mValue.Denominator }; -} - -/// -/// Represents an array of signed rational numbers. -/// (EXIF Specification: SRATIONAL with count > 1) -/// -internal class ExifSRationalArray : ExifProperty -{ - protected MathEx.Fraction32[] mValue; - - public ExifSRationalArray(ExifTag tag, MathEx.Fraction32[] value) - : base(tag) => - mValue = value; - - public new MathEx.Fraction32[] Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (MathEx.Fraction32[])value; - } - - public override ExifInterOperability Interoperability => - new ExifInterOperability( - ExifTagFactory.GetTagID(mTag), - 10, - (uint)mValue.Length, - ExifBitConverter.GetBytes(mValue, BitConverterEx.SystemByteOrder)); - - public static explicit operator float[](ExifSRationalArray obj) - { - var result = new float[obj.mValue.Length]; - for (var i = 0; i < obj.mValue.Length; i++) - { - result[i] = (float)obj.mValue[i]; - } - - return result; - } - - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append('['); - foreach (MathEx.Fraction32 b in mValue) - { - sb.Append(b.ToString()); - sb.Append(' '); - } - - sb.Remove(sb.Length - 1, 1); - sb.Append(']'); - return sb.ToString(); - } -} diff --git a/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs b/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs deleted file mode 100644 index f77f0c89cd..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifPropertyCollection.cs +++ /dev/null @@ -1,493 +0,0 @@ -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a collection of objects. -/// -internal class ExifPropertyCollection : IDictionary -{ - #region Constructor - - internal ExifPropertyCollection(ImageFile parentFile) - { - parent = parentFile; - items = new Dictionary(); - } - - #endregion - - #region Member Variables - - private readonly ImageFile parent; - private readonly Dictionary items; - - #endregion - - #region Properties - - /// - /// Gets the number of elements contained in the collection. - /// - public int Count => items.Count; - - /// - /// Gets a collection containing the keys in this collection. - /// - public ICollection Keys => items.Keys; - - /// - /// Gets a collection containing the values in this collection. - /// - public ICollection Values => items.Values; - - /// - /// Gets or sets the with the specified key. - /// - public ExifProperty this[ExifTag key] - { - get => items[key]; - set - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, value); - } - } - - #endregion - - #region ExifProperty Collection Setters - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, byte value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifByte(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, string value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || - key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) - { - items.Add(key, new WindowsByteString(key, value)); - } - else - { - items.Add(key, new ExifAscii(key, value, parent.Encoding)); - } - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, ushort value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifUShort(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, int value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifSInt(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, uint value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifUInt(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, float value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifURational(key, new MathEx.UFraction32(value))); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, double value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifURational(key, new MathEx.UFraction32(value))); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, object value) - { - Type type = value.GetType(); - if (type.IsEnum) - { - Type etype = typeof(ExifEnumProperty<>).MakeGenericType(type); - var prop = Activator.CreateInstance(etype, key, value); - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - if (prop is ExifProperty exifProperty) - { - items.Add(key, exifProperty); - } - } - else - { - throw new ArgumentException("No exif property exists for this tag.", "value"); - } - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - /// String encoding. - public void Set(ExifTag key, string value, Encoding encoding) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifEncodedString(key, value, encoding)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// The value of tag. - public void Set(ExifTag key, DateTime value) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifDateTime(key, value)); - } - - /// - /// Sets the with the specified key. - /// - /// The tag to set. - /// Angular degrees (or clock hours for a timestamp). - /// Angular minutes (or clock minutes for a timestamp). - /// Angular seconds (or clock seconds for a timestamp). - public void Set(ExifTag key, float d, float m, float s) - { - if (items.ContainsKey(key)) - { - items.Remove(key); - } - - items.Add(key, new ExifURationalArray(key, new[] { new(d), new MathEx.UFraction32(m), new MathEx.UFraction32(s) })); - } - - #endregion - - #region Instance Methods - - /// - /// Adds the specified item to the collection. - /// - /// The to add to the collection. - public void Add(ExifProperty item) - { - ExifProperty? oldItem = null; - if (items.TryGetValue(item.Tag, out oldItem)) - { - items[item.Tag] = item; - } - else - { - items.Add(item.Tag, item); - } - } - - /// - /// Removes all items from the collection. - /// - public void Clear() => items.Clear(); - - /// - /// Determines whether the collection contains an element with the specified key. - /// - /// The key to locate in the collection. - /// - /// true if the collection contains an element with the key; otherwise, false. - /// - /// - /// is null. - /// - public bool ContainsKey(ExifTag key) => items.ContainsKey(key); - - /// - /// Removes the element with the specified key from the collection. - /// - /// The key of the element to remove. - /// - /// true if the element is successfully removed; otherwise, false. This method also returns false if - /// was not found in the original collection. - /// - /// - /// is null. - /// - public bool Remove(ExifTag key) => items.Remove(key); - - /// - /// Removes all items with the given IFD from the collection. - /// - /// The IFD section to remove. - public void Remove(IFD ifd) - { - var toRemove = new List(); - foreach (KeyValuePair item in items) - { - if (item.Value.IFD == ifd) - { - toRemove.Add(item.Key); - } - } - - foreach (ExifTag tag in toRemove) - { - items.Remove(tag); - } - } - - /// - /// Gets the value associated with the specified key. - /// - /// The key whose value to get. - /// - /// When this method returns, the value associated with the specified key, if the key is found; - /// otherwise, the default value for the type of the parameter. This parameter is passed - /// uninitialized. - /// - /// - /// true if the collection contains an element with the specified key; otherwise, false. - /// - /// - /// is null. - /// - public bool TryGetValue(ExifTag key, [MaybeNullWhen(false)] out ExifProperty value) => - items.TryGetValue(key, out value); - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator() => Values.GetEnumerator(); - - #endregion - - #region Hidden Interface - - /// - /// Adds an element with the provided key and value to the . - /// - /// The object to use as the key of the element to add. - /// The object to use as the value of the element to add. - /// - /// is null. - /// - /// - /// An element with the same key already exists in the - /// . - /// - /// - /// The is - /// read-only. - /// - void IDictionary.Add(ExifTag key, ExifProperty value) => Add(value); - - /// - /// Adds an item to the . - /// - /// The object to add to the . - /// - /// The is - /// read-only. - /// - void ICollection>.Add(KeyValuePair item) => - Add(item.Value); - - bool ICollection>.Contains(KeyValuePair item) => - throw new NotSupportedException(); - - /// - /// Copies the elements of the to an - /// , starting at a particular index. - /// - /// - /// The one-dimensional that is the destination of the elements copied - /// from . The must have - /// zero-based indexing. - /// - /// The zero-based index in at which copying begins. - /// - /// is null. - /// - /// - /// is less than 0. - /// - /// - /// is multidimensional.-or- is equal to or greater than the - /// length of .-or-The number of elements in the source - /// is greater than the available space from - /// to the end of the destination .-or-Type - /// cannot be cast automatically to the type of the destination . - /// - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (array == null) - { - throw new ArgumentNullException("array"); - } - - if (arrayIndex < 0) - { - throw new ArgumentOutOfRangeException("arrayIndex"); - } - - if (array.Rank > 1) - { - throw new ArgumentException("Destination array is multidimensional.", "array"); - } - - if (arrayIndex >= array.Length) - { - throw new ArgumentException("arrayIndex is equal to or greater than the length of destination array", "array"); - } - - if (arrayIndex + items.Count > array.Length) - { - throw new ArgumentException("There is not enough space in destination array.", "array"); - } - - var i = 0; - foreach (KeyValuePair item in items) - { - if (i >= arrayIndex) - { - array[i] = item; - } - - i++; - } - } - - /// - /// Gets a value indicating whether the is read-only. - /// - /// true if the is read-only; otherwise, false. - bool ICollection>.IsReadOnly => false; - - /// - /// Removes the first occurrence of a specific object from the - /// . - /// - /// The object to remove from the . - /// - /// true if was successfully removed from the - /// ; otherwise, false. This method also returns false if - /// is not found in the original . - /// - /// - /// The is - /// read-only. - /// - bool ICollection>.Remove(KeyValuePair item) => - throw new NotSupportedException(); - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// - /// A that can be used to iterate through the collection. - /// - IEnumerator> IEnumerable>.GetEnumerator() => - items.GetEnumerator(); - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ExifPropertyFactory.cs b/src/Umbraco.Core/Media/Exif/ExifPropertyFactory.cs deleted file mode 100644 index 4290dcaf7c..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifPropertyFactory.cs +++ /dev/null @@ -1,598 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Creates exif properties from interoperability parameters. -/// -internal static class ExifPropertyFactory -{ - #region Static Methods - - /// - /// Creates an ExifProperty from the given interoperability parameters. - /// - /// The tag id of the exif property. - /// The type id of the exif property. - /// Byte or component count. - /// Field data as an array of bytes. - /// Byte order of value. - /// IFD section containing this property. - /// The encoding to be used for text metadata when the source encoding is unknown. - /// an ExifProperty initialized from the interoperability parameters. - public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd, Encoding encoding) - { - var conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder); - - // Find the exif tag corresponding to given tag id - ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag); - - if (ifd == IFD.Zeroth) - { - // Compression - if (tag == 0x103) - { - return new ExifEnumProperty(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0)); - } - - // PhotometricInterpretation - if (tag == 0x106) - { - return new ExifEnumProperty( - ExifTag.PhotometricInterpretation, - (PhotometricInterpretation)conv.ToUInt16(value, 0)); - } - - // Orientation - if (tag == 0x112) - { - return new ExifEnumProperty(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0)); - } - - // PlanarConfiguration - if (tag == 0x11c) - { - return new ExifEnumProperty( - ExifTag.PlanarConfiguration, - (PlanarConfiguration)conv.ToUInt16(value, 0)); - } - - // YCbCrPositioning - if (tag == 0x213) - { - return new ExifEnumProperty( - ExifTag.YCbCrPositioning, - (YCbCrPositioning)conv.ToUInt16(value, 0)); - } - - // ResolutionUnit - if (tag == 0x128) - { - return new ExifEnumProperty( - ExifTag.ResolutionUnit, - (ResolutionUnit)conv.ToUInt16(value, 0)); - } - - // DateTime - if (tag == 0x132) - { - return new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value)); - } - - if (tag == 0x9c9b || tag == 0x9c9c || // Windows tags - tag == 0x9c9d || tag == 0x9c9e || tag == 0x9c9f) - { - return new WindowsByteString( - etag, - Encoding.Unicode.GetString(value).TrimEnd(Constants.CharArrays.NullTerminator)); - } - } - else if (ifd == IFD.EXIF) - { - // ExifVersion - if (tag == 0x9000) - { - return new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)); - } - - // FlashpixVersion - if (tag == 0xa000) - { - return new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)); - } - - // ColorSpace - if (tag == 0xa001) - { - return new ExifEnumProperty(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0)); - } - - // UserComment - if (tag == 0x9286) - { - // Default to ASCII - Encoding enc = Encoding.ASCII; - bool hasenc; - if (value.Length < 8) - { - hasenc = false; - } - else - { - hasenc = true; - var encstr = enc.GetString(value, 0, 8); - if (string.Compare(encstr, "ASCII\0\0\0", StringComparison.OrdinalIgnoreCase) == 0) - { - enc = Encoding.ASCII; - } - else if (string.Compare(encstr, "JIS\0\0\0\0\0", StringComparison.OrdinalIgnoreCase) == 0) - { - enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)"); - } - else if (string.Compare(encstr, "Unicode\0", StringComparison.OrdinalIgnoreCase) == 0) - { - enc = Encoding.Unicode; - } - else - { - hasenc = false; - } - } - - var val = (hasenc ? enc.GetString(value, 8, value.Length - 8) : enc.GetString(value)).Trim( - Constants.CharArrays.NullTerminator); - - return new ExifEncodedString(ExifTag.UserComment, val, enc); - } - - // DateTimeOriginal - if (tag == 0x9003) - { - return new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value)); - } - - // DateTimeDigitized - if (tag == 0x9004) - { - return new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value)); - } - - // ExposureProgram - if (tag == 0x8822) - { - return new ExifEnumProperty( - ExifTag.ExposureProgram, - (ExposureProgram)conv.ToUInt16(value, 0)); - } - - // MeteringMode - if (tag == 0x9207) - { - return new ExifEnumProperty(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0)); - } - - // LightSource - if (tag == 0x9208) - { - return new ExifEnumProperty(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0)); - } - - // Flash - if (tag == 0x9209) - { - return new ExifEnumProperty(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true); - } - - // SubjectArea - if (tag == 0x9214) - { - if (count == 3) - { - return new ExifCircularSubjectArea( - ExifTag.SubjectArea, - ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - if (count == 4) - { - return new ExifRectangularSubjectArea( - ExifTag.SubjectArea, - ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - return new ExifPointSubjectArea( - ExifTag.SubjectArea, - ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - // FocalPlaneResolutionUnit - if (tag == 0xa210) - { - return new ExifEnumProperty( - ExifTag.FocalPlaneResolutionUnit, - (ResolutionUnit)conv.ToUInt16(value, 0), - true); - } - - // SubjectLocation - if (tag == 0xa214) - { - return new ExifPointSubjectArea( - ExifTag.SubjectLocation, - ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - // SensingMethod - if (tag == 0xa217) - { - return new ExifEnumProperty( - ExifTag.SensingMethod, - (SensingMethod)conv.ToUInt16(value, 0), - true); - } - - // FileSource - if (tag == 0xa300) - { - return new ExifEnumProperty(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true); - } - - // SceneType - if (tag == 0xa301) - { - return new ExifEnumProperty(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true); - } - - // CustomRendered - if (tag == 0xa401) - { - return new ExifEnumProperty( - ExifTag.CustomRendered, - (CustomRendered)conv.ToUInt16(value, 0), - true); - } - - // ExposureMode - if (tag == 0xa402) - { - return new ExifEnumProperty(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true); - } - - // WhiteBalance - if (tag == 0xa403) - { - return new ExifEnumProperty(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true); - } - - // SceneCaptureType - if (tag == 0xa406) - { - return new ExifEnumProperty( - ExifTag.SceneCaptureType, - (SceneCaptureType)conv.ToUInt16(value, 0), - true); - } - - // GainControl - if (tag == 0xa407) - { - return new ExifEnumProperty(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true); - } - - // Contrast - if (tag == 0xa408) - { - return new ExifEnumProperty(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true); - } - - // Saturation - if (tag == 0xa409) - { - return new ExifEnumProperty(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true); - } - - // Sharpness - if (tag == 0xa40a) - { - return new ExifEnumProperty(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true); - } - - // SubjectDistanceRange - if (tag == 0xa40c) - { - return new ExifEnumProperty( - ExifTag.SubjectDistanceRange, - (SubjectDistanceRange)conv.ToUInt16(value, 0), - true); - } - } - else if (ifd == IFD.GPS) - { - // GPSVersionID - if (tag == 0) - { - return new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value)); - } - - // GPSLatitudeRef - if (tag == 1) - { - return new ExifEnumProperty(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]); - } - - // GPSLatitude - if (tag == 2) - { - return new GPSLatitudeLongitude( - ExifTag.GPSLatitude, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSLongitudeRef - if (tag == 3) - { - return new ExifEnumProperty(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]); - } - - // GPSLongitude - if (tag == 4) - { - return new GPSLatitudeLongitude( - ExifTag.GPSLongitude, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSAltitudeRef - if (tag == 5) - { - return new ExifEnumProperty(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]); - } - - // GPSTimeStamp - if (tag == 7) - { - return new GPSTimeStamp( - ExifTag.GPSTimeStamp, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSStatus - if (tag == 9) - { - return new ExifEnumProperty(ExifTag.GPSStatus, (GPSStatus)value[0]); - } - - // GPSMeasureMode - if (tag == 10) - { - return new ExifEnumProperty(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]); - } - - // GPSSpeedRef - if (tag == 12) - { - return new ExifEnumProperty(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]); - } - - // GPSTrackRef - if (tag == 14) - { - return new ExifEnumProperty(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]); - } - - // GPSImgDirectionRef - if (tag == 16) - { - return new ExifEnumProperty(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]); - } - - // GPSDestLatitudeRef - if (tag == 19) - { - return new ExifEnumProperty(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]); - } - - // GPSDestLatitude - if (tag == 20) - { - return new GPSLatitudeLongitude( - ExifTag.GPSDestLatitude, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSDestLongitudeRef - if (tag == 21) - { - return new ExifEnumProperty(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]); - } - - // GPSDestLongitude - if (tag == 22) - { - return new GPSLatitudeLongitude( - ExifTag.GPSDestLongitude, - ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // GPSDestBearingRef - if (tag == 23) - { - return new ExifEnumProperty(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]); - } - - // GPSDestDistanceRef - if (tag == 25) - { - return new ExifEnumProperty(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]); - } - - // GPSDate - if (tag == 29) - { - return new ExifDateTime(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false)); - } - - // GPSDifferential - if (tag == 30) - { - return new ExifEnumProperty( - ExifTag.GPSDifferential, - (GPSDifferential)conv.ToUInt16(value, 0)); - } - } - else if (ifd == IFD.Interop) - { - // InteroperabilityIndex - if (tag == 1) - { - return new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value, Encoding.ASCII), Encoding.ASCII); - } - - // InteroperabilityVersion - if (tag == 2) - { - return new ExifVersion( - ExifTag.InteroperabilityVersion, - ExifBitConverter.ToAscii(value, Encoding.ASCII)); - } - } - else if (ifd == IFD.First) - { - // Compression - if (tag == 0x103) - { - return new ExifEnumProperty( - ExifTag.ThumbnailCompression, - (Compression)conv.ToUInt16(value, 0)); - } - - // PhotometricInterpretation - if (tag == 0x106) - { - return new ExifEnumProperty( - ExifTag.ThumbnailPhotometricInterpretation, - (PhotometricInterpretation)conv.ToUInt16(value, 0)); - } - - // Orientation - if (tag == 0x112) - { - return new ExifEnumProperty( - ExifTag.ThumbnailOrientation, - (Orientation)conv.ToUInt16(value, 0)); - } - - // PlanarConfiguration - if (tag == 0x11c) - { - return new ExifEnumProperty( - ExifTag.ThumbnailPlanarConfiguration, - (PlanarConfiguration)conv.ToUInt16(value, 0)); - } - - // YCbCrPositioning - if (tag == 0x213) - { - return new ExifEnumProperty( - ExifTag.ThumbnailYCbCrPositioning, - (YCbCrPositioning)conv.ToUInt16(value, 0)); - } - - // ResolutionUnit - if (tag == 0x128) - { - return new ExifEnumProperty( - ExifTag.ThumbnailResolutionUnit, - (ResolutionUnit)conv.ToUInt16(value, 0)); - } - - // DateTime - if (tag == 0x132) - { - return new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value)); - } - } - - // 1 = BYTE An 8-bit unsigned integer. - if (type == 1) - { - if (count == 1) - { - return new ExifByte(etag, value[0]); - } - - return new ExifByteArray(etag, value); - } - - // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code. - if (type == 2) - { - return new ExifAscii(etag, ExifBitConverter.ToAscii(value, encoding), encoding); - } - - // 3 = SHORT A 16-bit (2-byte) unsigned integer. - if (type == 3) - { - if (count == 1) - { - return new ExifUShort(etag, conv.ToUInt16(value, 0)); - } - - return new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)); - } - - // 4 = LONG A 32-bit (4-byte) unsigned integer. - if (type == 4) - { - if (count == 1) - { - return new ExifUInt(etag, conv.ToUInt32(value, 0)); - } - - return new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder)); - } - - // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator. - if (type == 5) - { - if (count == 1) - { - return new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder)); - } - - return new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)); - } - - // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition. - if (type == 7) - { - return new ExifUndefined(etag, value); - } - - // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation). - if (type == 9) - { - if (count == 1) - { - return new ExifSInt(etag, conv.ToInt32(value, 0)); - } - - return new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder)); - } - - // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator. - if (type == 10) - { - if (count == 1) - { - return new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder)); - } - - return new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder)); - } - - throw new ArgumentException("Unknown property type."); - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ExifTag.cs b/src/Umbraco.Core/Media/Exif/ExifTag.cs deleted file mode 100644 index 0ffd754836..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifTag.cs +++ /dev/null @@ -1,310 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the tags associated with exif fields. -/// -internal enum ExifTag -{ - // **************************** - // Zeroth IFD - // **************************** - NewSubfileType = IFD.Zeroth + 254, - SubfileType = IFD.Zeroth + 255, - ImageWidth = IFD.Zeroth + 256, - ImageLength = IFD.Zeroth + 257, - BitsPerSample = IFD.Zeroth + 258, - Compression = IFD.Zeroth + 259, - PhotometricInterpretation = IFD.Zeroth + 262, - Threshholding = IFD.Zeroth + 263, - CellWidth = IFD.Zeroth + 264, - CellLength = IFD.Zeroth + 265, - FillOrder = IFD.Zeroth + 266, - DocumentName = IFD.Zeroth + 269, - ImageDescription = IFD.Zeroth + 270, - Make = IFD.Zeroth + 271, - Model = IFD.Zeroth + 272, - StripOffsets = IFD.Zeroth + 273, - Orientation = IFD.Zeroth + 274, - SamplesPerPixel = IFD.Zeroth + 277, - RowsPerStrip = IFD.Zeroth + 278, - StripByteCounts = IFD.Zeroth + 279, - MinSampleValue = IFD.Zeroth + 280, - MaxSampleValue = IFD.Zeroth + 281, - XResolution = IFD.Zeroth + 282, - YResolution = IFD.Zeroth + 283, - PlanarConfiguration = IFD.Zeroth + 284, - PageName = IFD.Zeroth + 285, - XPosition = IFD.Zeroth + 286, - YPosition = IFD.Zeroth + 287, - FreeOffsets = IFD.Zeroth + 288, - FreeByteCounts = IFD.Zeroth + 289, - GrayResponseUnit = IFD.Zeroth + 290, - GrayResponseCurve = IFD.Zeroth + 291, - T4Options = IFD.Zeroth + 292, - T6Options = IFD.Zeroth + 293, - ResolutionUnit = IFD.Zeroth + 296, - PageNumber = IFD.Zeroth + 297, - TransferFunction = IFD.Zeroth + 301, - Software = IFD.Zeroth + 305, - DateTime = IFD.Zeroth + 306, - Artist = IFD.Zeroth + 315, - HostComputer = IFD.Zeroth + 316, - Predictor = IFD.Zeroth + 317, - WhitePoint = IFD.Zeroth + 318, - PrimaryChromaticities = IFD.Zeroth + 319, - ColorMap = IFD.Zeroth + 320, - HalftoneHints = IFD.Zeroth + 321, - TileWidth = IFD.Zeroth + 322, - TileLength = IFD.Zeroth + 323, - TileOffsets = IFD.Zeroth + 324, - TileByteCounts = IFD.Zeroth + 325, - InkSet = IFD.Zeroth + 332, - InkNames = IFD.Zeroth + 333, - NumberOfInks = IFD.Zeroth + 334, - DotRange = IFD.Zeroth + 336, - TargetPrinter = IFD.Zeroth + 337, - ExtraSamples = IFD.Zeroth + 338, - SampleFormat = IFD.Zeroth + 339, - SMinSampleValue = IFD.Zeroth + 340, - SMaxSampleValue = IFD.Zeroth + 341, - TransferRange = IFD.Zeroth + 342, - JPEGProc = IFD.Zeroth + 512, - JPEGInterchangeFormat = IFD.Zeroth + 513, - JPEGInterchangeFormatLength = IFD.Zeroth + 514, - JPEGRestartInterval = IFD.Zeroth + 515, - JPEGLosslessPredictors = IFD.Zeroth + 517, - JPEGPointTransforms = IFD.Zeroth + 518, - JPEGQTables = IFD.Zeroth + 519, - JPEGDCTables = IFD.Zeroth + 520, - JPEGACTables = IFD.Zeroth + 521, - YCbCrCoefficients = IFD.Zeroth + 529, - YCbCrSubSampling = IFD.Zeroth + 530, - YCbCrPositioning = IFD.Zeroth + 531, - ReferenceBlackWhite = IFD.Zeroth + 532, - Copyright = IFD.Zeroth + 33432, - - // Pointers to other IFDs - EXIFIFDPointer = IFD.Zeroth + 34665, - GPSIFDPointer = IFD.Zeroth + 34853, - - // Windows Tags - WindowsTitle = IFD.Zeroth + 0x9c9b, - WindowsComment = IFD.Zeroth + 0x9c9c, - WindowsAuthor = IFD.Zeroth + 0x9c9d, - WindowsKeywords = IFD.Zeroth + 0x9c9e, - WindowsSubject = IFD.Zeroth + 0x9c9f, - - // Rating - Rating = IFD.Zeroth + 0x4746, - RatingPercent = IFD.Zeroth + 0x4749, - - // Microsoft specifying padding and offset tags - ZerothIFDPadding = IFD.Zeroth + 0xea1c, - - // **************************** - // EXIF Tags - // **************************** - ExifVersion = IFD.EXIF + 36864, - FlashpixVersion = IFD.EXIF + 40960, - ColorSpace = IFD.EXIF + 40961, - ComponentsConfiguration = IFD.EXIF + 37121, - CompressedBitsPerPixel = IFD.EXIF + 37122, - PixelXDimension = IFD.EXIF + 40962, - PixelYDimension = IFD.EXIF + 40963, - MakerNote = IFD.EXIF + 37500, - UserComment = IFD.EXIF + 37510, - RelatedSoundFile = IFD.EXIF + 40964, - DateTimeOriginal = IFD.EXIF + 36867, - DateTimeDigitized = IFD.EXIF + 36868, - SubSecTime = IFD.EXIF + 37520, - SubSecTimeOriginal = IFD.EXIF + 37521, - SubSecTimeDigitized = IFD.EXIF + 37522, - ExposureTime = IFD.EXIF + 33434, - FNumber = IFD.EXIF + 33437, - ExposureProgram = IFD.EXIF + 34850, - SpectralSensitivity = IFD.EXIF + 34852, - ISOSpeedRatings = IFD.EXIF + 34855, - OECF = IFD.EXIF + 34856, - ShutterSpeedValue = IFD.EXIF + 37377, - ApertureValue = IFD.EXIF + 37378, - BrightnessValue = IFD.EXIF + 37379, - ExposureBiasValue = IFD.EXIF + 37380, - MaxApertureValue = IFD.EXIF + 37381, - SubjectDistance = IFD.EXIF + 37382, - MeteringMode = IFD.EXIF + 37383, - LightSource = IFD.EXIF + 37384, - Flash = IFD.EXIF + 37385, - FocalLength = IFD.EXIF + 37386, - SubjectArea = IFD.EXIF + 37396, - FlashEnergy = IFD.EXIF + 41483, - SpatialFrequencyResponse = IFD.EXIF + 41484, - FocalPlaneXResolution = IFD.EXIF + 41486, - FocalPlaneYResolution = IFD.EXIF + 41487, - FocalPlaneResolutionUnit = IFD.EXIF + 41488, - SubjectLocation = IFD.EXIF + 41492, - ExposureIndex = IFD.EXIF + 41493, - SensingMethod = IFD.EXIF + 41495, - FileSource = IFD.EXIF + 41728, - SceneType = IFD.EXIF + 41729, - CFAPattern = IFD.EXIF + 41730, - CustomRendered = IFD.EXIF + 41985, - ExposureMode = IFD.EXIF + 41986, - WhiteBalance = IFD.EXIF + 41987, - DigitalZoomRatio = IFD.EXIF + 41988, - FocalLengthIn35mmFilm = IFD.EXIF + 41989, - SceneCaptureType = IFD.EXIF + 41990, - GainControl = IFD.EXIF + 41991, - Contrast = IFD.EXIF + 41992, - Saturation = IFD.EXIF + 41993, - Sharpness = IFD.EXIF + 41994, - DeviceSettingDescription = IFD.EXIF + 41995, - SubjectDistanceRange = IFD.EXIF + 41996, - ImageUniqueID = IFD.EXIF + 42016, - InteroperabilityIFDPointer = IFD.EXIF + 40965, - - // Microsoft specifying padding and offset tags - ExifIFDPadding = IFD.EXIF + 0xea1c, - OffsetSchema = IFD.EXIF + 0xea1d, - - // **************************** - // GPS Tags - // **************************** - GPSVersionID = IFD.GPS + 0, - GPSLatitudeRef = IFD.GPS + 1, - GPSLatitude = IFD.GPS + 2, - GPSLongitudeRef = IFD.GPS + 3, - GPSLongitude = IFD.GPS + 4, - GPSAltitudeRef = IFD.GPS + 5, - GPSAltitude = IFD.GPS + 6, - GPSTimeStamp = IFD.GPS + 7, - GPSSatellites = IFD.GPS + 8, - GPSStatus = IFD.GPS + 9, - GPSMeasureMode = IFD.GPS + 10, - GPSDOP = IFD.GPS + 11, - GPSSpeedRef = IFD.GPS + 12, - GPSSpeed = IFD.GPS + 13, - GPSTrackRef = IFD.GPS + 14, - GPSTrack = IFD.GPS + 15, - GPSImgDirectionRef = IFD.GPS + 16, - GPSImgDirection = IFD.GPS + 17, - GPSMapDatum = IFD.GPS + 18, - GPSDestLatitudeRef = IFD.GPS + 19, - GPSDestLatitude = IFD.GPS + 20, - GPSDestLongitudeRef = IFD.GPS + 21, - GPSDestLongitude = IFD.GPS + 22, - GPSDestBearingRef = IFD.GPS + 23, - GPSDestBearing = IFD.GPS + 24, - GPSDestDistanceRef = IFD.GPS + 25, - GPSDestDistance = IFD.GPS + 26, - GPSProcessingMethod = IFD.GPS + 27, - GPSAreaInformation = IFD.GPS + 28, - GPSDateStamp = IFD.GPS + 29, - GPSDifferential = IFD.GPS + 30, - - // **************************** - // InterOp Tags - // **************************** - InteroperabilityIndex = IFD.Interop + 1, - InteroperabilityVersion = IFD.Interop + 2, - RelatedImageWidth = IFD.Interop + 0x1001, - RelatedImageHeight = IFD.Interop + 0x1002, - - // **************************** - // First IFD TIFF Tags - // **************************** - ThumbnailImageWidth = IFD.First + 256, - ThumbnailImageLength = IFD.First + 257, - ThumbnailBitsPerSample = IFD.First + 258, - ThumbnailCompression = IFD.First + 259, - ThumbnailPhotometricInterpretation = IFD.First + 262, - ThumbnailOrientation = IFD.First + 274, - ThumbnailSamplesPerPixel = IFD.First + 277, - ThumbnailPlanarConfiguration = IFD.First + 284, - ThumbnailYCbCrSubSampling = IFD.First + 530, - ThumbnailYCbCrPositioning = IFD.First + 531, - ThumbnailXResolution = IFD.First + 282, - ThumbnailYResolution = IFD.First + 283, - ThumbnailResolutionUnit = IFD.First + 296, - ThumbnailStripOffsets = IFD.First + 273, - ThumbnailRowsPerStrip = IFD.First + 278, - ThumbnailStripByteCounts = IFD.First + 279, - ThumbnailJPEGInterchangeFormat = IFD.First + 513, - ThumbnailJPEGInterchangeFormatLength = IFD.First + 514, - ThumbnailTransferFunction = IFD.First + 301, - ThumbnailWhitePoint = IFD.First + 318, - ThumbnailPrimaryChromaticities = IFD.First + 319, - ThumbnailYCbCrCoefficients = IFD.First + 529, - ThumbnailReferenceBlackWhite = IFD.First + 532, - ThumbnailDateTime = IFD.First + 306, - ThumbnailImageDescription = IFD.First + 270, - ThumbnailMake = IFD.First + 271, - ThumbnailModel = IFD.First + 272, - ThumbnailSoftware = IFD.First + 305, - ThumbnailArtist = IFD.First + 315, - ThumbnailCopyright = IFD.First + 33432, - - // **************************** - // JFIF Tags - // **************************** - - /// - /// Represents the JFIF version. - /// - JFIFVersion = IFD.JFIF + 1, - - /// - /// Represents units for X and Y densities. - /// - JFIFUnits = IFD.JFIF + 101, - - /// - /// Horizontal pixel density. - /// - XDensity = IFD.JFIF + 102, - - /// - /// Vertical pixel density - /// - YDensity = IFD.JFIF + 103, - - /// - /// Thumbnail horizontal pixel count. - /// - JFIFXThumbnail = IFD.JFIF + 201, - - /// - /// Thumbnail vertical pixel count. - /// - JFIFYThumbnail = IFD.JFIF + 202, - - /// - /// JFIF JPEG thumbnail. - /// - JFIFThumbnail = IFD.JFIF + 203, - - /// - /// Code which identifies the JFIF extension. - /// - JFXXExtensionCode = IFD.JFXX + 1, - - /// - /// Thumbnail horizontal pixel count. - /// - JFXXXThumbnail = IFD.JFXX + 101, - - /// - /// Thumbnail vertical pixel count. - /// - JFXXYThumbnail = IFD.JFXX + 102, - - /// - /// The 256-Color RGB palette. - /// - JFXXPalette = IFD.JFXX + 201, - - /// - /// JFIF thumbnail. The thumbnail will be either a JPEG, - /// a 256 color palette bitmap, or a 24-bit RGB bitmap. - /// - JFXXThumbnail = IFD.JFXX + 202, -} diff --git a/src/Umbraco.Core/Media/Exif/ExifTagFactory.cs b/src/Umbraco.Core/Media/Exif/ExifTagFactory.cs deleted file mode 100644 index 726da925aa..0000000000 --- a/src/Umbraco.Core/Media/Exif/ExifTagFactory.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -internal static class ExifTagFactory -{ - #region Static Methods - - /// - /// Returns the ExifTag corresponding to the given tag id. - /// - public static ExifTag GetExifTag(IFD ifd, ushort tagid) => (ExifTag)(ifd + tagid); - - /// - /// Returns the tag id corresponding to the given ExifTag. - /// - public static ushort GetTagID(ExifTag exiftag) - { - IFD ifd = GetTagIFD(exiftag); - return (ushort)((int)exiftag - (int)ifd); - } - - /// - /// Returns the IFD section containing the given tag. - /// - public static IFD GetTagIFD(ExifTag tag) => (IFD)((int)tag / 100000 * 100000); - - /// - /// Returns the string representation for the given exif tag. - /// - public static string GetTagName(ExifTag tag) - { - var name = Enum.GetName(typeof(ExifTag), tag); - if (name == null) - { - return "Unknown"; - } - - return name; - } - - /// - /// Returns the string representation for the given tag id. - /// - public static string GetTagName(IFD ifd, ushort tagid) => GetTagName(GetExifTag(ifd, tagid)); - - /// - /// Returns the string representation for the given exif tag including - /// IFD section and tag id. - /// - public static string GetTagLongName(ExifTag tag) - { - var ifdname = Enum.GetName(typeof(IFD), GetTagIFD(tag)); - var name = Enum.GetName(typeof(ExifTag), tag); - if (name == null) - { - name = "Unknown"; - } - - var tagidname = GetTagID(tag).ToString(); - return ifdname + ": " + name + " (" + tagidname + ")"; - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/IFD.cs b/src/Umbraco.Core/Media/Exif/IFD.cs deleted file mode 100644 index cda3cdcb69..0000000000 --- a/src/Umbraco.Core/Media/Exif/IFD.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the IFD section containing tags. -/// -internal enum IFD -{ - Unknown = 0, - Zeroth = 100000, - EXIF = 200000, - GPS = 300000, - Interop = 400000, - First = 500000, - MakerNote = 600000, - JFIF = 700000, - JFXX = 800000, -} diff --git a/src/Umbraco.Core/Media/Exif/ImageFile.cs b/src/Umbraco.Core/Media/Exif/ImageFile.cs deleted file mode 100644 index 23ea615be9..0000000000 --- a/src/Umbraco.Core/Media/Exif/ImageFile.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System.ComponentModel; -using System.Text; -using Umbraco.Cms.Core.Media.TypeDetector; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the base class for image files. -/// -[TypeDescriptionProvider(typeof(ExifFileTypeDescriptionProvider))] -internal abstract class ImageFile -{ - #region Constructor - - /// - /// Initializes a new instance of the class. - /// - protected ImageFile() - { - Format = ImageFileFormat.Unknown; - Properties = new ExifPropertyCollection(this); - Encoding = Encoding.Default; - } - - #endregion - - #region Properties - - /// - /// Returns the format of the . - /// - public ImageFileFormat Format { get; protected set; } - - /// - /// Gets the collection of Exif properties contained in the . - /// - public ExifPropertyCollection Properties { get; } - - /// - /// Gets or sets the embedded thumbnail image. - /// - public ImageFile? Thumbnail { get; set; } - - /// - /// Gets or sets the Exif property with the given key. - /// - /// The Exif tag associated with the Exif property. - public ExifProperty this[ExifTag key] - { - get => Properties[key]; - set => Properties[key] = value; - } - - /// - /// Gets the encoding used for text metadata when the source encoding is unknown. - /// - public Encoding Encoding { get; protected set; } - - #endregion - - #region Instance Methods - - /// - /// Saves the to the specified file. - /// - /// A string that contains the name of the file. - public virtual void Save(string filename) - { - using (var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None)) - { - Save(stream); - } - } - - /// - /// Saves the to the specified stream. - /// - /// A to save image data to. - public abstract void Save(Stream stream); - - #endregion - - #region Static Methods - - /// - /// Creates an from the specified file. - /// - /// A string that contains the name of the file. - /// The created from the file. - public static ImageFile? FromFile(string filename) => FromFile(filename, Encoding.Default); - - /// - /// Creates an from the specified file. - /// - /// A string that contains the name of the file. - /// The encoding to be used for text metadata when the source encoding is unknown. - /// The created from the file. - public static ImageFile? FromFile(string filename, Encoding encoding) - { - using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - return FromStream(stream, encoding); - } - } - - /// - /// Creates an from the specified data stream. - /// - /// A that contains image data. - /// The created from the file. - public static ImageFile? FromStream(Stream stream) => FromStream(stream, Encoding.Default); - - /// - /// Creates an from the specified data stream. - /// - /// A that contains image data. - /// The encoding to be used for text metadata when the source encoding is unknown. - /// The created from the file. - public static ImageFile? FromStream(Stream stream, Encoding encoding) - { - // JPEG - if (JpegDetector.IsOfType(stream)) - { - return new JPEGFile(stream, encoding); - } - - // TIFF - if (TIFFDetector.IsOfType(stream)) - { - return new TIFFFile(stream, encoding); - } - - // SVG - if (SvgDetector.IsOfType(stream)) - { - return new SvgFile(stream); - } - - // We don't know - return null; - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/ImageFileDirectory.cs b/src/Umbraco.Core/Media/Exif/ImageFileDirectory.cs deleted file mode 100644 index 299e7619f9..0000000000 --- a/src/Umbraco.Core/Media/Exif/ImageFileDirectory.cs +++ /dev/null @@ -1,100 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents an image file directory. -/// -internal class ImageFileDirectory -{ - /// - /// Initializes a new instance of the class. - /// - public ImageFileDirectory() - { - Fields = new List(); - Strips = new List(); - } - - /// - /// The fields contained in this IFD. - /// - public List Fields { get; } - - /// - /// Offset to the next IFD. - /// - public uint NextIFDOffset { get; private set; } - - /// - /// Compressed image data. - /// - public List Strips { get; } - - /// - /// Returns a initialized from the given byte data. - /// - /// The data. - /// The offset into . - /// The byte order of . - /// A initialized from the given byte data. - public static ImageFileDirectory FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder) - { - var ifd = new ImageFileDirectory(); - var conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder); - - var stripOffsets = new List(); - var stripLengths = new List(); - - // Count - var fieldcount = conv.ToUInt16(data, offset); - - // Read fields - for (uint i = 0; i < fieldcount; i++) - { - var fieldoffset = offset + 2 + (12 * i); - var field = ImageFileDirectoryEntry.FromBytes(data, fieldoffset, byteOrder); - ifd.Fields.Add(field); - - // Read strip offsets - if (field.Tag == 273) - { - var baselen = field.Data.Length / (int)field.Count; - for (uint j = 0; j < field.Count; j++) - { - var val = new byte[baselen]; - Array.Copy(field.Data, j * baselen, val, 0, baselen); - var stripOffset = field.Type == 3 ? BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0); - stripOffsets.Add(stripOffset); - } - } - - // Read strip lengths - if (field.Tag == 279) - { - var baselen = field.Data.Length / (int)field.Count; - for (uint j = 0; j < field.Count; j++) - { - var val = new byte[baselen]; - Array.Copy(field.Data, j * baselen, val, 0, baselen); - var stripLength = field.Type == 3 ? BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0); - stripLengths.Add(stripLength); - } - } - } - - // Save strips - if (stripOffsets.Count != stripLengths.Count) - { - throw new NotValidTIFFileException(); - } - - for (var i = 0; i < stripOffsets.Count; i++) - { - ifd.Strips.Add(new TIFFStrip(data, stripOffsets[i], stripLengths[i])); - } - - // Offset to next ifd - ifd.NextIFDOffset = conv.ToUInt32(data, offset + 2 + (12 * fieldcount)); - - return ifd; - } -} diff --git a/src/Umbraco.Core/Media/Exif/ImageFileDirectoryEntry.cs b/src/Umbraco.Core/Media/Exif/ImageFileDirectoryEntry.cs deleted file mode 100644 index a3863b6a69..0000000000 --- a/src/Umbraco.Core/Media/Exif/ImageFileDirectoryEntry.cs +++ /dev/null @@ -1,144 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents an entry in the image file directory. -/// -internal struct ImageFileDirectoryEntry -{ - /// - /// The tag that identifies the field. - /// - public ushort Tag; - - /// - /// Field type identifier. - /// - public ushort Type; - - /// - /// Count of Type. - /// - public uint Count; - - /// - /// Field data. - /// - public byte[] Data; - - /// - /// Initializes a new instance of the struct. - /// - /// The tag that identifies the field. - /// Field type identifier. - /// Count of Type. - /// Field data. - public ImageFileDirectoryEntry(ushort tag, ushort type, uint count, byte[] data) - { - Tag = tag; - Type = type; - Count = count; - Data = data; - } - - /// - /// Returns a initialized from the given byte data. - /// - /// The data. - /// The offset into . - /// The byte order of . - /// A initialized from the given byte data. - public static ImageFileDirectoryEntry FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder) - { - // Tag ID - var tag = BitConverterEx.ToUInt16(data, offset, byteOrder, BitConverterEx.SystemByteOrder); - - // Tag Type - var type = BitConverterEx.ToUInt16(data, offset + 2, byteOrder, BitConverterEx.SystemByteOrder); - - // Count of Type - var count = BitConverterEx.ToUInt32(data, offset + 4, byteOrder, BitConverterEx.SystemByteOrder); - - // Field value or offset to field data - var value = new byte[4]; - Array.Copy(data, offset + 8, value, 0, 4); - - // Calculate the bytes we need to read - var baselength = GetBaseLength(type); - var totallength = count * baselength; - - // If field value does not fit in 4 bytes - // the value field is an offset to the actual - // field value - if (totallength > 4) - { - var dataoffset = BitConverterEx.ToUInt32(value, 0, byteOrder, BitConverterEx.SystemByteOrder); - value = new byte[totallength]; - Array.Copy(data, dataoffset, value, 0, totallength); - } - - // Reverse array order if byte orders are different - if (byteOrder != BitConverterEx.SystemByteOrder) - { - for (uint i = 0; i < count; i++) - { - var val = new byte[baselength]; - Array.Copy(value, i * baselength, val, 0, baselength); - Array.Reverse(val); - Array.Copy(val, 0, value, i * baselength, baselength); - } - } - - return new ImageFileDirectoryEntry(tag, type, count, value); - } - - /// - /// Gets the base byte length for the given type. - /// - /// Type identifier. - private static uint GetBaseLength(ushort type) - { - // BYTE and SBYTE - if (type == 1 || type == 6) - { - return 1; - } - - // ASCII and UNDEFINED - if (type == 2 || type == 7) - { - return 1; - } - - // SHORT and SSHORT - if (type == 3 || type == 8) - { - return 2; - } - - // LONG and SLONG - if (type == 4 || type == 9) - { - return 4; - } - - // RATIONAL (2xLONG) and SRATIONAL (2xSLONG) - if (type == 5 || type == 10) - { - return 8; - } - - // FLOAT - if (type == 11) - { - return 4; - } - - // DOUBLE - if (type == 12) - { - return 8; - } - - throw new ArgumentException("Unknown type identifier.", "type"); - } -} diff --git a/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs b/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs deleted file mode 100644 index fe30c713b2..0000000000 --- a/src/Umbraco.Core/Media/Exif/ImageFileFormat.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the format of the . -/// -internal enum ImageFileFormat -{ - /// - /// The file is not recognized. - /// - Unknown, - - /// - /// The file is a JPEG/Exif or JPEG/JFIF file. - /// - JPEG, - - /// - /// The file is a TIFF File. - /// - TIFF, - - /// - /// The file is a SVG File. - /// - SVG, -} diff --git a/src/Umbraco.Core/Media/Exif/JFIFEnums.cs b/src/Umbraco.Core/Media/Exif/JFIFEnums.cs deleted file mode 100644 index 438d7bf3d4..0000000000 --- a/src/Umbraco.Core/Media/Exif/JFIFEnums.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the units for the X and Y densities -/// for a JFIF file. -/// -internal enum JFIFDensityUnit : byte -{ - /// - /// No units, XDensity and YDensity specify the pixel aspect ratio. - /// - None = 0, - - /// - /// XDensity and YDensity are dots per inch. - /// - DotsPerInch = 1, - - /// - /// XDensity and YDensity are dots per cm. - /// - DotsPerCm = 2, -} - -/// -/// Represents the JFIF extension. -/// -internal enum JFIFExtension : byte -{ - /// - /// Thumbnail coded using JPEG. - /// - ThumbnailJPEG = 0x10, - - /// - /// Thumbnail stored using a 256-Color RGB palette. - /// - ThumbnailPaletteRGB = 0x11, - - /// - /// Thumbnail stored using 3 bytes/pixel (24-bit) RGB values. - /// - Thumbnail24BitRGB = 0x13, -} diff --git a/src/Umbraco.Core/Media/Exif/JFIFExtendedProperty.cs b/src/Umbraco.Core/Media/Exif/JFIFExtendedProperty.cs deleted file mode 100644 index 71ea89228d..0000000000 --- a/src/Umbraco.Core/Media/Exif/JFIFExtendedProperty.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the JFIF version as a 16 bit unsigned integer. (EXIF Specification: SHORT) -/// -internal class JFIFVersion : ExifUShort -{ - public JFIFVersion(ExifTag tag, ushort value) - : base(tag, value) - { - } - - /// - /// Gets the major version. - /// - public byte Major => (byte)(mValue >> 8); - - /// - /// Gets the minor version. - /// - public byte Minor => (byte)(mValue - ((mValue >> 8) * 256)); - - public override string ToString() => string.Format("{0}.{1:00}", Major, Minor); -} - -/// -/// Represents a JFIF thumbnail. (EXIF Specification: BYTE) -/// -internal class JFIFThumbnailProperty : ExifProperty -{ - protected JFIFThumbnail mValue; - - public JFIFThumbnailProperty(ExifTag tag, JFIFThumbnail value) - : base(tag) => - mValue = value; - - public new JFIFThumbnail Value - { - get => mValue; - set => mValue = value; - } - - protected override object _Value - { - get => Value; - set => Value = (JFIFThumbnail)value; - } - - public override ExifInterOperability Interoperability - { - get - { - if (mValue.Format == JFIFThumbnail.ImageFormat.BMP24Bit) - { - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.PixelData.Length, mValue.PixelData); - } - - if (mValue.Format == JFIFThumbnail.ImageFormat.BMPPalette) - { - var data = new byte[mValue.Palette.Length + mValue.PixelData.Length]; - Array.Copy(mValue.Palette, data, mValue.Palette.Length); - Array.Copy(mValue.PixelData, 0, data, mValue.Palette.Length, mValue.PixelData.Length); - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)data.Length, data); - } - - if (mValue.Format == JFIFThumbnail.ImageFormat.JPEG) - { - return new ExifInterOperability(ExifTagFactory.GetTagID(mTag), 1, (uint)mValue.PixelData.Length, mValue.PixelData); - } - - throw new InvalidOperationException("Unknown thumbnail type."); - } - } - - public override string ToString() => mValue.Format.ToString(); -} diff --git a/src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs b/src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs deleted file mode 100644 index cafa804c3a..0000000000 --- a/src/Umbraco.Core/Media/Exif/JFIFThumbnail.cs +++ /dev/null @@ -1,62 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a JFIF thumbnail. -/// -internal class JFIFThumbnail -{ - #region Public Enums - - public enum ImageFormat - { - JPEG, - BMPPalette, - BMP24Bit, - } - - #endregion - - #region Properties - - /// - /// Gets the 256 color RGB palette. - /// - public byte[] Palette { get; } - - /// - /// Gets raw image data. - /// - public byte[] PixelData { get; } - - /// - /// Gets the image format. - /// - public ImageFormat Format { get; } - - #endregion - - #region Constructors - - protected JFIFThumbnail() - { - Palette = new byte[0]; - PixelData = new byte[0]; - } - - public JFIFThumbnail(ImageFormat format, byte[] data) - : this() - { - Format = format; - PixelData = data; - } - - public JFIFThumbnail(byte[] palette, byte[] data) - : this() - { - Format = ImageFormat.BMPPalette; - Palette = palette; - PixelData = data; - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/JPEGExceptions.cs b/src/Umbraco.Core/Media/Exif/JPEGExceptions.cs index c44d6d1db0..4781415d7c 100644 --- a/src/Umbraco.Core/Media/Exif/JPEGExceptions.cs +++ b/src/Umbraco.Core/Media/Exif/JPEGExceptions.cs @@ -7,6 +7,7 @@ namespace Umbraco.Cms.Core.Media.Exif; /// /// [Serializable] +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class NotValidJPEGFileException : Exception { /// diff --git a/src/Umbraco.Core/Media/Exif/JPEGFile.cs b/src/Umbraco.Core/Media/Exif/JPEGFile.cs deleted file mode 100644 index bdf7208ea0..0000000000 --- a/src/Umbraco.Core/Media/Exif/JPEGFile.cs +++ /dev/null @@ -1,1110 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the binary view of a JPEG compressed file. -/// -internal class JPEGFile : ImageFile -{ - #region Constructor - - /// - /// Initializes a new instance of the class. - /// - /// A that contains image data. - /// The encoding to be used for text metadata when the source encoding is unknown. - protected internal JPEGFile(Stream stream, Encoding encoding) - { - Format = ImageFileFormat.JPEG; - Sections = new List(); - TrailingData = new byte[0]; - Encoding = encoding; - - stream.Seek(0, SeekOrigin.Begin); - - // Read the Start of Image (SOI) marker. SOI marker is represented - // with two bytes: 0xFF, 0xD8. - var markerbytes = new byte[2]; - if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] != 0xD8) - { - throw new NotValidJPEGFileException(); - } - - stream.Seek(0, SeekOrigin.Begin); - - // Search and read sections until we reach the end of file. - while (stream.Position != stream.Length) - { - // Read the next section marker. Section markers are two bytes - // with values 0xFF, 0x?? where ?? must not be 0x00 or 0xFF. - if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] == 0x00 || - markerbytes[1] == 0xFF) - { - throw new NotValidJPEGFileException(); - } - - var marker = (JPEGMarker)markerbytes[1]; - - var header = new byte[0]; - - // SOI, EOI and RST markers do not contain any header - if (marker != JPEGMarker.SOI && marker != JPEGMarker.EOI && - !(marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7)) - { - // Length of the header including the length bytes. - // This value is a 16-bit unsigned integer - // in big endian byte-order. - var lengthbytes = new byte[2]; - if (stream.Read(lengthbytes, 0, 2) != 2) - { - throw new NotValidJPEGFileException(); - } - - long length = BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0); - - // Read section header. - header = new byte[length - 2]; - var bytestoread = header.Length; - while (bytestoread > 0) - { - var count = Math.Min(bytestoread, 4 * 1024); - var bytesread = stream.Read(header, header.Length - bytestoread, count); - if (bytesread == 0) - { - throw new NotValidJPEGFileException(); - } - - bytestoread -= bytesread; - } - } - - // Start of Scan (SOS) sections and RST sections are immediately - // followed by entropy coded data. For that, we need to read until - // the next section marker once we reach a SOS or RST. - var entropydata = new byte[0]; - if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7)) - { - var position = stream.Position; - - // Search for the next section marker - while (true) - { - // Search for an 0xFF indicating start of a marker - var nextbyte = 0; - do - { - nextbyte = stream.ReadByte(); - if (nextbyte == -1) - { - throw new NotValidJPEGFileException(); - } - } - while ((byte)nextbyte != 0xFF); - - // Skip filler bytes (0xFF) - do - { - nextbyte = stream.ReadByte(); - if (nextbyte == -1) - { - throw new NotValidJPEGFileException(); - } - } - while ((byte)nextbyte == 0xFF); - - // Looks like a section marker. The next byte must not be 0x00. - if ((byte)nextbyte != 0x00) - { - // We reached a section marker. Calculate the - // length of the entropy coded data. - stream.Seek(-2, SeekOrigin.Current); - var edlength = stream.Position - position; - stream.Seek(-edlength, SeekOrigin.Current); - - // Read entropy coded data - entropydata = new byte[edlength]; - var bytestoread = entropydata.Length; - while (bytestoread > 0) - { - var count = Math.Min(bytestoread, 4 * 1024); - var bytesread = stream.Read(entropydata, entropydata.Length - bytestoread, count); - if (bytesread == 0) - { - throw new NotValidJPEGFileException(); - } - - bytestoread -= bytesread; - } - - break; - } - } - } - - // Store section. - var section = new JPEGSection(marker, header, entropydata); - Sections.Add(section); - - // Some propriety formats store data past the EOI marker - if (marker == JPEGMarker.EOI) - { - var bytestoread = (int)(stream.Length - stream.Position); - TrailingData = new byte[bytestoread]; - while (bytestoread > 0) - { - var count = Math.Min(bytestoread, 4 * 1024); - var bytesread = stream.Read(TrailingData, TrailingData.Length - bytestoread, count); - if (bytesread == 0) - { - throw new NotValidJPEGFileException(); - } - - bytestoread -= bytesread; - } - } - } - - // Read metadata sections - ReadJFIFAPP0(); - ReadJFXXAPP0(); - ReadExifAPP1(); - - // Process the maker note - _makerNoteProcessed = false; - } - - #endregion - - #region Member Variables - - private JPEGSection? _jfifApp0; - private JPEGSection? _jfxxApp0; - private JPEGSection? _exifApp1; - private uint _makerNoteOffset; - private long _exifIfdFieldOffset; - private long _gpsIfdFieldOffset; - private long _interopIfdFieldOffset; - private long _firstIfdFieldOffset; - private long _thumbOffsetLocation; - private long _thumbSizeLocation; - private uint _thumbOffsetValue; - private uint _thumbSizeValue; - private readonly bool _makerNoteProcessed; - - #endregion - - #region Properties - - /// - /// Gets or sets the byte-order of the Exif properties. - /// - public BitConverterEx.ByteOrder ByteOrder { get; set; } - - /// - /// Gets or sets the sections contained in the . - /// - public List Sections { get; } - - /// - /// Gets or sets non-standard trailing data following the End of Image (EOI) marker. - /// - public byte[] TrailingData { get; } - - #endregion - - #region Instance Methods - - /// - /// Saves the JPEG/Exif image to the given stream. - /// - /// The stream of the JPEG/Exif file. - /// - /// Determines whether the maker note offset of - /// the original file will be preserved. - /// - public void Save(Stream stream, bool preserveMakerNote) - { - WriteJFIFApp0(); - WriteJFXXApp0(); - WriteExifApp1(preserveMakerNote); - - // Write sections - foreach (JPEGSection section in Sections) - { - // Section header (including length bytes and section marker) - // must not exceed 64 kB. - if (section.Header.Length + 2 + 2 > 64 * 1024) - { - throw new SectionExceeds64KBException(); - } - - // APP sections must have a header. - // Otherwise skip the entire section. - if (section.Marker >= JPEGMarker.APP0 && section.Marker <= JPEGMarker.APP15 && section.Header.Length == 0) - { - continue; - } - - // Write section marker - stream.Write(new byte[] { 0xFF, (byte)section.Marker }, 0, 2); - - // SOI, EOI and RST markers do not contain any header - if (section.Marker != JPEGMarker.SOI && section.Marker != JPEGMarker.EOI && - !(section.Marker >= JPEGMarker.RST0 && section.Marker <= JPEGMarker.RST7)) - { - // Header length including the length field itself - stream.Write(BitConverterEx.BigEndian.GetBytes((ushort)(section.Header.Length + 2)), 0, 2); - - // Write section header - if (section.Header.Length != 0) - { - stream.Write(section.Header, 0, section.Header.Length); - } - } - - // Write entropy coded data - if (section.EntropyData.Length != 0) - { - stream.Write(section.EntropyData, 0, section.EntropyData.Length); - } - } - - // Write trailing data, if any - if (TrailingData.Length != 0) - { - stream.Write(TrailingData, 0, TrailingData.Length); - } - } - - /// - /// Saves the JPEG/Exif image with the given filename. - /// - /// The path to the JPEG/Exif file. - /// - /// Determines whether the maker note offset of - /// the original file will be preserved. - /// - public void Save(string filename, bool preserveMakerNote) - { - using (var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None)) - { - Save(stream, preserveMakerNote); - } - } - - /// - /// Saves the JPEG/Exif image with the given filename. - /// - /// The path to the JPEG/Exif file. - public override void Save(string filename) => Save(filename, true); - - /// - /// Saves the JPEG/Exif image to the given stream. - /// - /// The stream of the JPEG/Exif file. - public override void Save(Stream stream) => Save(stream, true); - - #endregion - - #region Private Helper Methods - - /// - /// Reads the APP0 section containing JFIF metadata. - /// - private void ReadJFIFAPP0() - { - // Find the APP0 section containing JFIF metadata - _jfifApp0 = Sections.Find(a => a.Marker == JPEGMarker.APP0 && - a.Header.Length >= 5 && - Encoding.ASCII.GetString(a.Header, 0, 5) == "JFIF\0"); - - // If there is no APP0 section, return. - if (_jfifApp0 == null) - { - return; - } - - var header = _jfifApp0.Header; - BitConverterEx jfifConv = BitConverterEx.BigEndian; - - // Version - var version = jfifConv.ToUInt16(header, 5); - Properties.Add(new JFIFVersion(ExifTag.JFIFVersion, version)); - - // Units - var unit = header[7]; - Properties.Add(new ExifEnumProperty(ExifTag.JFIFUnits, (JFIFDensityUnit)unit)); - - // X and Y densities - var xdensity = jfifConv.ToUInt16(header, 8); - Properties.Add(new ExifUShort(ExifTag.XDensity, xdensity)); - var ydensity = jfifConv.ToUInt16(header, 10); - Properties.Add(new ExifUShort(ExifTag.YDensity, ydensity)); - - // Thumbnails pixel count - var xthumbnail = header[12]; - Properties.Add(new ExifByte(ExifTag.JFIFXThumbnail, xthumbnail)); - var ythumbnail = header[13]; - Properties.Add(new ExifByte(ExifTag.JFIFYThumbnail, ythumbnail)); - - // Read JFIF thumbnail - var n = xthumbnail * ythumbnail; - var jfifThumbnail = new byte[n]; - Array.Copy(header, 14, jfifThumbnail, 0, n); - Properties.Add(new JFIFThumbnailProperty(ExifTag.JFIFThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, jfifThumbnail))); - } - - /// - /// Replaces the contents of the APP0 section with the JFIF properties. - /// - private bool WriteJFIFApp0() - { - // Which IFD sections do we have? - var ifdjfef = new List(); - foreach (ExifProperty prop in Properties) - { - if (prop.IFD == IFD.JFIF) - { - ifdjfef.Add(prop); - } - } - - if (ifdjfef.Count == 0) - { - // Nothing to write - return false; - } - - // Create a memory stream to write the APP0 section to - var ms = new MemoryStream(); - - // JFIF identifier - ms.Write(Encoding.ASCII.GetBytes("JFIF\0"), 0, 5); - - // Write tags - foreach (ExifProperty prop in ifdjfef) - { - ExifInterOperability interop = prop.Interoperability; - var data = interop.Data; - if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == 3) - { - Array.Reverse(data); - } - - ms.Write(data, 0, data.Length); - } - - ms.Close(); - - // Return APP0 header - if (_jfifApp0 is not null) - { - _jfifApp0.Header = ms.ToArray(); - return true; - } - - return false; - } - - /// - /// Reads the APP0 section containing JFIF extension metadata. - /// - private void ReadJFXXAPP0() - { - // Find the APP0 section containing JFIF metadata - _jfxxApp0 = Sections.Find(a => a.Marker == JPEGMarker.APP0 && - a.Header.Length >= 5 && - Encoding.ASCII.GetString(a.Header, 0, 5) == "JFXX\0"); - - // If there is no APP0 section, return. - if (_jfxxApp0 == null) - { - return; - } - - var header = _jfxxApp0.Header; - - // Version - var version = (JFIFExtension)header[5]; - Properties.Add(new ExifEnumProperty(ExifTag.JFXXExtensionCode, version)); - - // Read thumbnail - if (version == JFIFExtension.ThumbnailJPEG) - { - var data = new byte[header.Length - 6]; - Array.Copy(header, 6, data, 0, data.Length); - Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, data))); - } - else if (version == JFIFExtension.Thumbnail24BitRGB) - { - // Thumbnails pixel count - var xthumbnail = header[6]; - Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail)); - var ythumbnail = header[7]; - Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail)); - var data = new byte[3 * xthumbnail * ythumbnail]; - Array.Copy(header, 8, data, 0, data.Length); - Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.BMP24Bit, data))); - } - else if (version == JFIFExtension.ThumbnailPaletteRGB) - { - // Thumbnails pixel count - var xthumbnail = header[6]; - Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail)); - var ythumbnail = header[7]; - Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail)); - var palette = new byte[768]; - Array.Copy(header, 8, palette, 0, palette.Length); - var data = new byte[xthumbnail * ythumbnail]; - Array.Copy(header, 8 + 768, data, 0, data.Length); - Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(palette, data))); - } - } - - /// - /// Replaces the contents of the APP0 section with the JFIF extension properties. - /// - private bool WriteJFXXApp0() - { - // Which IFD sections do we have? - var ifdjfef = new List(); - foreach (ExifProperty prop in Properties) - { - if (prop.IFD == IFD.JFXX) - { - ifdjfef.Add(prop); - } - } - - if (ifdjfef.Count == 0) - { - // Nothing to write - return false; - } - - // Create a memory stream to write the APP0 section to - var ms = new MemoryStream(); - - // JFIF identifier - ms.Write(Encoding.ASCII.GetBytes("JFXX\0"), 0, 5); - - // Write tags - foreach (ExifProperty prop in ifdjfef) - { - ExifInterOperability interop = prop.Interoperability; - var data = interop.Data; - if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == 3) - { - Array.Reverse(data); - } - - ms.Write(data, 0, data.Length); - } - - ms.Close(); - - if (_jfxxApp0 is not null) - { - // Return APP0 header - _jfxxApp0.Header = ms.ToArray(); - return true; - } - - return false; - } - - /// - /// Reads the APP1 section containing Exif metadata. - /// - private void ReadExifAPP1() - { - // Find the APP1 section containing Exif metadata - _exifApp1 = Sections.Find(a => a.Marker == JPEGMarker.APP1 && - a.Header.Length >= 6 && - Encoding.ASCII.GetString(a.Header, 0, 6) == "Exif\0\0"); - - // If there is no APP1 section, add a new one after the last APP0 section (if any). - if (_exifApp1 == null) - { - var insertionIndex = Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0); - if (insertionIndex == -1) - { - insertionIndex = 0; - } - - insertionIndex++; - _exifApp1 = new JPEGSection(JPEGMarker.APP1); - Sections.Insert(insertionIndex, _exifApp1); - if (BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian) - { - ByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else - { - ByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - - return; - } - - var header = _exifApp1.Header; - var ifdqueue = new SortedList(); - _makerNoteOffset = 0; - - // TIFF header - var tiffoffset = 6; - if (header[tiffoffset] == 0x49 && header[tiffoffset + 1] == 0x49) - { - ByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else if (header[tiffoffset] == 0x4D && header[tiffoffset + 1] == 0x4D) - { - ByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - else - { - throw new NotValidExifFileException(); - } - - // TIFF header may have a different byte order - BitConverterEx.ByteOrder tiffByteOrder = ByteOrder; - if (BitConverterEx.LittleEndian.ToUInt16(header, tiffoffset + 2) == 42) - { - tiffByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else if (BitConverterEx.BigEndian.ToUInt16(header, tiffoffset + 2) == 42) - { - tiffByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - else - { - throw new NotValidExifFileException(); - } - - // Offset to 0th IFD - var ifd0offset = (int)BitConverterEx.ToUInt32(header, tiffoffset + 4, tiffByteOrder, BitConverterEx.SystemByteOrder); - ifdqueue.Add(ifd0offset, IFD.Zeroth); - - var conv = new BitConverterEx(ByteOrder, BitConverterEx.SystemByteOrder); - var thumboffset = -1; - var thumblength = 0; - var thumbtype = -1; - - // Read IFDs - while (ifdqueue.Count != 0) - { - var ifdoffset = tiffoffset + ifdqueue.Keys[0]; - IFD currentifd = ifdqueue.Values[0]; - ifdqueue.RemoveAt(0); - - // Field count - var fieldcount = conv.ToUInt16(header, ifdoffset); - for (short i = 0; i < fieldcount; i++) - { - // Read field info - var fieldoffset = ifdoffset + 2 + (12 * i); - var tag = conv.ToUInt16(header, fieldoffset); - var type = conv.ToUInt16(header, fieldoffset + 2); - var count = conv.ToUInt32(header, fieldoffset + 4); - var value = new byte[4]; - Array.Copy(header, fieldoffset + 8, value, 0, 4); - - // Fields containing offsets to other IFDs - if (currentifd == IFD.Zeroth && tag == 0x8769) - { - var exififdpointer = (int)conv.ToUInt32(value, 0); - ifdqueue.Add(exififdpointer, IFD.EXIF); - } - else if (currentifd == IFD.Zeroth && tag == 0x8825) - { - var gpsifdpointer = (int)conv.ToUInt32(value, 0); - ifdqueue.Add(gpsifdpointer, IFD.GPS); - } - else if (currentifd == IFD.EXIF && tag == 0xa005) - { - var interopifdpointer = (int)conv.ToUInt32(value, 0); - ifdqueue.Add(interopifdpointer, IFD.Interop); - } - - // Save the offset to maker note data - if (currentifd == IFD.EXIF && tag == 37500) - { - _makerNoteOffset = conv.ToUInt32(value, 0); - } - - // Calculate the bytes we need to read - uint baselength = 0; - if (type == 1 || type == 2 || type == 7) - { - baselength = 1; - } - else if (type == 3) - { - baselength = 2; - } - else if (type == 4 || type == 9) - { - baselength = 4; - } - else if (type == 5 || type == 10) - { - baselength = 8; - } - - var totallength = count * baselength; - - // If field value does not fit in 4 bytes - // the value field is an offset to the actual - // field value - var fieldposition = 0; - if (totallength > 4) - { - fieldposition = tiffoffset + (int)conv.ToUInt32(value, 0); - value = new byte[totallength]; - Array.Copy(header, fieldposition, value, 0, totallength); - } - - // Compressed thumbnail data - if (currentifd == IFD.First && tag == 0x201) - { - thumbtype = 0; - thumboffset = (int)conv.ToUInt32(value, 0); - } - else if (currentifd == IFD.First && tag == 0x202) - { - thumblength = (int)conv.ToUInt32(value, 0); - } - - // Uncompressed thumbnail data - if (currentifd == IFD.First && tag == 0x111) - { - thumbtype = 1; - - // Offset to first strip - if (type == 3) - { - thumboffset = conv.ToUInt16(value, 0); - } - else - { - thumboffset = (int)conv.ToUInt32(value, 0); - } - } - else if (currentifd == IFD.First && tag == 0x117) - { - thumblength = 0; - for (var j = 0; j < count; j++) - { - if (type == 3) - { - thumblength += conv.ToUInt16(value, 0); - } - else - { - thumblength += (int)conv.ToUInt32(value, 0); - } - } - } - - // Create the exif property from the interop data - ExifProperty prop = ExifPropertyFactory.Get(tag, type, count, value, ByteOrder, currentifd, Encoding); - Properties.Add(prop); - } - - // 1st IFD pointer - var firstifdpointer = (int)conv.ToUInt32(header, ifdoffset + 2 + (12 * fieldcount)); - if (firstifdpointer != 0) - { - ifdqueue.Add(firstifdpointer, IFD.First); - } - - // Read thumbnail - if (thumboffset != -1 && thumblength != 0 && Thumbnail == null) - { - if (thumbtype == 0) - { - using (var ts = new MemoryStream(header, tiffoffset + thumboffset, thumblength)) - { - Thumbnail = FromStream(ts); - } - } - } - } - } - - /// - /// Replaces the contents of the APP1 section with the Exif properties. - /// - private bool WriteExifApp1(bool preserveMakerNote) - { - // Zero out IFD field offsets. We will fill those as we write the IFD sections - _exifIfdFieldOffset = 0; - _gpsIfdFieldOffset = 0; - _interopIfdFieldOffset = 0; - _firstIfdFieldOffset = 0; - - // We also do not know the location of the embedded thumbnail yet - _thumbOffsetLocation = 0; - _thumbOffsetValue = 0; - _thumbSizeLocation = 0; - _thumbSizeValue = 0; - - // Write thumbnail tags if they are missing, remove otherwise - if (Thumbnail == null) - { - Properties.Remove(ExifTag.ThumbnailJPEGInterchangeFormat); - Properties.Remove(ExifTag.ThumbnailJPEGInterchangeFormatLength); - } - else - { - if (!Properties.ContainsKey(ExifTag.ThumbnailJPEGInterchangeFormat)) - { - Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormat, 0)); - } - - if (!Properties.ContainsKey(ExifTag.ThumbnailJPEGInterchangeFormatLength)) - { - Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormatLength, 0)); - } - } - - // Which IFD sections do we have? - var ifdzeroth = new Dictionary(); - var ifdexif = new Dictionary(); - var ifdgps = new Dictionary(); - var ifdinterop = new Dictionary(); - var ifdfirst = new Dictionary(); - - foreach (ExifProperty prop in Properties) - { - switch (prop.IFD) - { - case IFD.Zeroth: - ifdzeroth.Add(prop.Tag, prop); - break; - case IFD.EXIF: - ifdexif.Add(prop.Tag, prop); - break; - case IFD.GPS: - ifdgps.Add(prop.Tag, prop); - break; - case IFD.Interop: - ifdinterop.Add(prop.Tag, prop); - break; - case IFD.First: - ifdfirst.Add(prop.Tag, prop); - break; - } - } - - // Add IFD pointers if they are missing - // We will write the pointer values later on - if (ifdexif.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer)) - { - ifdzeroth.Add(ExifTag.EXIFIFDPointer, new ExifUInt(ExifTag.EXIFIFDPointer, 0)); - } - - if (ifdgps.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer)) - { - ifdzeroth.Add(ExifTag.GPSIFDPointer, new ExifUInt(ExifTag.GPSIFDPointer, 0)); - } - - if (ifdinterop.Count != 0 && !ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer)) - { - ifdexif.Add(ExifTag.InteroperabilityIFDPointer, new ExifUInt(ExifTag.InteroperabilityIFDPointer, 0)); - } - - // Remove IFD pointers if IFD sections are missing - if (ifdexif.Count == 0 && ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer)) - { - ifdzeroth.Remove(ExifTag.EXIFIFDPointer); - } - - if (ifdgps.Count == 0 && ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer)) - { - ifdzeroth.Remove(ExifTag.GPSIFDPointer); - } - - if (ifdinterop.Count == 0 && ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer)) - { - ifdexif.Remove(ExifTag.InteroperabilityIFDPointer); - } - - if (ifdzeroth.Count == 0 && ifdgps.Count == 0 && ifdinterop.Count == 0 && ifdfirst.Count == 0 && - Thumbnail == null) - { - // Nothing to write - return false; - } - - // We will need these BitConverters to write byte-ordered data - var bceExif = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder); - - // Create a memory stream to write the APP1 section to - var ms = new MemoryStream(); - - // Exif identifier - ms.Write(Encoding.ASCII.GetBytes("Exif\0\0"), 0, 6); - - // TIFF header - // Byte order - var tiffoffset = ms.Position; - ms.Write(ByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }, 0, 2); - - // TIFF ID - ms.Write(bceExif.GetBytes((ushort)42), 0, 2); - - // Offset to 0th IFD - ms.Write(bceExif.GetBytes((uint)8), 0, 4); - - // Write IFDs - WriteIFD(ms, ifdzeroth, IFD.Zeroth, tiffoffset, preserveMakerNote); - var exififdrelativeoffset = (uint)(ms.Position - tiffoffset); - WriteIFD(ms, ifdexif, IFD.EXIF, tiffoffset, preserveMakerNote); - var gpsifdrelativeoffset = (uint)(ms.Position - tiffoffset); - WriteIFD(ms, ifdgps, IFD.GPS, tiffoffset, preserveMakerNote); - var interopifdrelativeoffset = (uint)(ms.Position - tiffoffset); - WriteIFD(ms, ifdinterop, IFD.Interop, tiffoffset, preserveMakerNote); - var firstifdrelativeoffset = (uint)(ms.Position - tiffoffset); - WriteIFD(ms, ifdfirst, IFD.First, tiffoffset, preserveMakerNote); - - // Now that we now the location of IFDs we can go back and write IFD offsets - if (_exifIfdFieldOffset != 0) - { - ms.Seek(_exifIfdFieldOffset, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(exififdrelativeoffset), 0, 4); - } - - if (_gpsIfdFieldOffset != 0) - { - ms.Seek(_gpsIfdFieldOffset, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(gpsifdrelativeoffset), 0, 4); - } - - if (_interopIfdFieldOffset != 0) - { - ms.Seek(_interopIfdFieldOffset, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(interopifdrelativeoffset), 0, 4); - } - - if (_firstIfdFieldOffset != 0) - { - ms.Seek(_firstIfdFieldOffset, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(firstifdrelativeoffset), 0, 4); - } - - // We can write thumbnail location now - if (_thumbOffsetLocation != 0) - { - ms.Seek(_thumbOffsetLocation, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(_thumbOffsetValue), 0, 4); - } - - if (_thumbSizeLocation != 0) - { - ms.Seek(_thumbSizeLocation, SeekOrigin.Begin); - ms.Write(bceExif.GetBytes(_thumbSizeValue), 0, 4); - } - - ms.Close(); - - if (_exifApp1 is not null) - { - // Return APP1 header - _exifApp1.Header = ms.ToArray(); - return true; - } - - return false; - } - - private void WriteIFD(MemoryStream stream, Dictionary ifd, IFD ifdtype, long tiffoffset, bool preserveMakerNote) - { - var conv = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder); - - // Create a queue of fields to write - var fieldqueue = new Queue(); - foreach (ExifProperty prop in ifd.Values) - { - if (prop.Tag != ExifTag.MakerNote) - { - fieldqueue.Enqueue(prop); - } - } - - // Push the maker note data to the end - if (ifd.ContainsKey(ExifTag.MakerNote)) - { - fieldqueue.Enqueue(ifd[ExifTag.MakerNote]); - } - - // Offset to start of field data from start of TIFF header - var dataoffset = (uint)(2 + (ifd.Count * 12) + 4 + stream.Position - tiffoffset); - var currentdataoffset = dataoffset; - var absolutedataoffset = stream.Position + (2 + (ifd.Count * 12) + 4); - - var makernotewritten = false; - - // Field count - stream.Write(conv.GetBytes((ushort)ifd.Count), 0, 2); - - // Fields - while (fieldqueue.Count != 0) - { - ExifProperty field = fieldqueue.Dequeue(); - ExifInterOperability interop = field.Interoperability; - - uint fillerbytecount = 0; - - // Try to preserve the makernote data offset - if (!makernotewritten && - !_makerNoteProcessed && - _makerNoteOffset != 0 && - ifdtype == IFD.EXIF && - field.Tag != ExifTag.MakerNote && - interop.Data.Length > 4 && - currentdataoffset + interop.Data.Length > _makerNoteOffset && - ifd.ContainsKey(ExifTag.MakerNote)) - { - // Delay writing this field until we write the creator's note data - fieldqueue.Enqueue(field); - continue; - } - - if (field.Tag == ExifTag.MakerNote) - { - makernotewritten = true; - - // We may need to write filler bytes to preserve maker note offset - if (preserveMakerNote && !_makerNoteProcessed && _makerNoteOffset > currentdataoffset) - { - fillerbytecount = _makerNoteOffset - currentdataoffset; - } - else - { - fillerbytecount = 0; - } - } - - // Tag - stream.Write(conv.GetBytes(interop.TagID), 0, 2); - - // Type - stream.Write(conv.GetBytes(interop.TypeID), 0, 2); - - // Count - stream.Write(conv.GetBytes(interop.Count), 0, 4); - - // Field data - var data = interop.Data; - if (ByteOrder != BitConverterEx.SystemByteOrder && - (interop.TypeID == 3 || interop.TypeID == 4 || interop.TypeID == 9 || - interop.TypeID == 5 || interop.TypeID == 10)) - { - var vlen = 4; - if (interop.TypeID == 3) - { - vlen = 2; - } - - var n = data.Length / vlen; - - for (var i = 0; i < n; i++) - { - Array.Reverse(data, i * vlen, vlen); - } - } - - // Fields containing offsets to other IFDs - // Just store their offsets, we will write the values later on when we know the lengths of IFDs - if (ifdtype == IFD.Zeroth && interop.TagID == 0x8769) - { - _exifIfdFieldOffset = stream.Position; - } - else if (ifdtype == IFD.Zeroth && interop.TagID == 0x8825) - { - _gpsIfdFieldOffset = stream.Position; - } - else if (ifdtype == IFD.EXIF && interop.TagID == 0xa005) - { - _interopIfdFieldOffset = stream.Position; - } - else if (ifdtype == IFD.First && interop.TagID == 0x201) - { - _thumbOffsetLocation = stream.Position; - } - else if (ifdtype == IFD.First && interop.TagID == 0x202) - { - _thumbSizeLocation = stream.Position; - } - - // Write 4 byte field value or field data - if (data.Length <= 4) - { - stream.Write(data, 0, data.Length); - for (var i = data.Length; i < 4; i++) - { - stream.WriteByte(0); - } - } - else - { - // Pointer to data area relative to TIFF header - stream.Write(conv.GetBytes(currentdataoffset + fillerbytecount), 0, 4); - - // Actual data - var currentoffset = stream.Position; - stream.Seek(absolutedataoffset, SeekOrigin.Begin); - - // Write filler bytes - for (var i = 0; i < fillerbytecount; i++) - { - stream.WriteByte(0xFF); - } - - stream.Write(data, 0, data.Length); - stream.Seek(currentoffset, SeekOrigin.Begin); - - // Increment pointers - currentdataoffset += fillerbytecount + (uint)data.Length; - absolutedataoffset += fillerbytecount + data.Length; - } - } - - // Offset to 1st IFD - // We will write zeros for now. This will be filled after we write all IFDs - if (ifdtype == IFD.Zeroth) - { - _firstIfdFieldOffset = stream.Position; - } - - stream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4); - - // Seek to end of IFD - stream.Seek(absolutedataoffset, SeekOrigin.Begin); - - // Write thumbnail data - if (ifdtype == IFD.First) - { - if (Thumbnail != null) - { - var ts = new MemoryStream(); - Thumbnail.Save(ts); - ts.Close(); - var thumb = ts.ToArray(); - _thumbOffsetValue = (uint)(stream.Position - tiffoffset); - _thumbSizeValue = (uint)thumb.Length; - stream.Write(thumb, 0, thumb.Length); - ts.Dispose(); - } - else - { - _thumbOffsetValue = 0; - _thumbSizeValue = 0; - } - } - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/JPEGMarker.cs b/src/Umbraco.Core/Media/Exif/JPEGMarker.cs deleted file mode 100644 index 3912d87e82..0000000000 --- a/src/Umbraco.Core/Media/Exif/JPEGMarker.cs +++ /dev/null @@ -1,95 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a JPEG marker byte. -/// -internal enum JPEGMarker : byte -{ - // Start Of Frame markers, non-differential, Huffman coding - SOF0 = 0xc0, - SOF1 = 0xc1, - SOF2 = 0xc2, - SOF3 = 0xc3, - - // Start Of Frame markers, differential, Huffman coding - SOF5 = 0xc5, - SOF6 = 0xc6, - SOF7 = 0xc7, - - // Start Of Frame markers, non-differential, arithmetic coding - JPG = 0xc8, - SOF9 = 0xc9, - SOF10 = 0xca, - SOF11 = 0xcb, - - // Start Of Frame markers, differential, arithmetic coding - SOF13 = 0xcd, - SOF14 = 0xce, - SOF15 = 0xcf, - - // Huffman table specification - DHT = 0xc4, - - // Arithmetic coding conditioning specification - DAC = 0xcc, - - // Restart interval termination - RST0 = 0xd0, - RST1 = 0xd1, - RST2 = 0xd2, - RST3 = 0xd3, - RST4 = 0xd4, - RST5 = 0xd5, - RST6 = 0xd6, - RST7 = 0xd7, - - // Other markers - SOI = 0xd8, - EOI = 0xd9, - SOS = 0xda, - DQT = 0xdb, - DNL = 0xdc, - DRI = 0xdd, - DHP = 0xde, - EXP = 0xdf, - - // application segments - APP0 = 0xe0, - APP1 = 0xe1, - APP2 = 0xe2, - APP3 = 0xe3, - APP4 = 0xe4, - APP5 = 0xe5, - APP6 = 0xe6, - APP7 = 0xe7, - APP8 = 0xe8, - APP9 = 0xe9, - APP10 = 0xea, - APP11 = 0xeb, - APP12 = 0xec, - APP13 = 0xed, - APP14 = 0xee, - APP15 = 0xef, - - // JPEG extensions - JPG0 = 0xf0, - JPG1 = 0xf1, - JPG2 = 0xf2, - JPG3 = 0xf3, - JPG4 = 0xf4, - JPG5 = 0xf5, - JPG6 = 0xf6, - JPG7 = 0xf7, - JPG8 = 0xf8, - JPG9 = 0xf9, - JPG10 = 0xfa, - JPG11 = 0xfb, - JP1G2 = 0xfc, - JPG13 = 0xfd, - - // Comment - COM = 0xfe, - - // Temporary - TEM = 0x01, -} diff --git a/src/Umbraco.Core/Media/Exif/JPEGSection.cs b/src/Umbraco.Core/Media/Exif/JPEGSection.cs deleted file mode 100644 index 787b04b056..0000000000 --- a/src/Umbraco.Core/Media/Exif/JPEGSection.cs +++ /dev/null @@ -1,66 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the memory view of a JPEG section. -/// A JPEG section is the data between markers of the JPEG file. -/// -internal class JPEGSection -{ - #region Instance Methods - - /// - /// Returns a string representation of the current section. - /// - /// A System.String that represents the current section. - public override string ToString() => string.Format("{0} => Header: {1} bytes, Entropy Data: {2} bytes", Marker, Header.Length, EntropyData.Length); - - #endregion - - #region Properties - - /// - /// The marker byte representing the section. - /// - public JPEGMarker Marker { get; } - - /// - /// Section header as a byte array. This is different from the header - /// definition in JPEG specification in that it does not include the - /// two byte section length. - /// - public byte[] Header { get; set; } - - /// - /// For the SOS and RST markers, this contains the entropy coded data. - /// - public byte[] EntropyData { get; set; } - - #endregion - - #region Constructors - - /// - /// Constructs a JPEGSection represented by the marker byte and containing - /// the given data. - /// - /// The marker byte representing the section. - /// Section data. - /// Entropy coded data. - public JPEGSection(JPEGMarker marker, byte[] data, byte[] entropydata) - { - Marker = marker; - Header = data; - EntropyData = entropydata; - } - - /// - /// Constructs a JPEGSection represented by the marker byte. - /// - /// The marker byte representing the section. - public JPEGSection(JPEGMarker marker) - : this(marker, new byte[0], new byte[0]) - { - } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/MathEx.cs b/src/Umbraco.Core/Media/Exif/MathEx.cs deleted file mode 100644 index fbf5f2dbde..0000000000 --- a/src/Umbraco.Core/Media/Exif/MathEx.cs +++ /dev/null @@ -1,1329 +0,0 @@ -using System.Globalization; -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Contains extended Math functions. -/// -internal static class MathEx -{ - /// - /// Returns the greatest common divisor of two numbers. - /// - /// First number. - /// Second number. - public static uint GCD(uint a, uint b) - { - while (b != 0) - { - var rem = a % b; - a = b; - b = rem; - } - - return a; - } - - /// - /// Returns the greatest common divisor of two numbers. - /// - /// First number. - /// Second number. - public static ulong GCD(ulong a, ulong b) - { - while (b != 0) - { - var rem = a % b; - a = b; - b = rem; - } - - return a; - } - - /// - /// Represents a generic rational number represented by 32-bit signed numerator and denominator. - /// - public struct Fraction32 : IComparable, IFormattable, IComparable, IEquatable - { - #region Constants - - private const uint MaximumIterations = 10000000; - - #endregion - - #region Member Variables - - private int mNumerator; - private int mDenominator; - - #endregion - - #region Properties - - /// - /// Gets or sets the numerator. - /// - public int Numerator - { - get => (IsNegative ? -1 : 1) * mNumerator; - set - { - if (value < 0) - { - IsNegative = true; - mNumerator = -1 * value; - } - else - { - IsNegative = false; - mNumerator = value; - } - - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Gets or sets the denominator. - /// - public int Denominator - { - get => mDenominator; - set - { - mDenominator = Math.Abs(value); - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Gets the error term. - /// - public double Error { get; } - - /// - /// Gets or sets a value determining id the fraction is a negative value. - /// - public bool IsNegative { get; set; } - - #endregion - - #region Predefined Values - - public static readonly Fraction32 NaN = new(0, 0); - public static readonly Fraction32 NegativeInfinity = new(-1, 0); - public static readonly Fraction32 PositiveInfinity = new(1, 0); - - #endregion - - #region Static Methods - - /// - /// Returns a value indicating whether the specified number evaluates to a value - /// that is not a number. - /// - /// A fraction. - /// true if f evaluates to Fraction.NaN; otherwise, false. - public static bool IsNan(Fraction32 f) => f.Numerator == 0 && f.Denominator == 0; - - /// - /// Returns a value indicating whether the specified number evaluates to negative - /// infinity. - /// - /// A fraction. - /// true if f evaluates to Fraction.NegativeInfinity; otherwise, false. - public static bool IsNegativeInfinity(Fraction32 f) => f.Numerator < 0 && f.Denominator == 0; - - /// - /// Returns a value indicating whether the specified number evaluates to positive - /// infinity. - /// - /// A fraction. - /// true if f evaluates to Fraction.PositiveInfinity; otherwise, false. - public static bool IsPositiveInfinity(Fraction32 f) => f.Numerator > 0 && f.Denominator == 0; - - /// - /// Returns a value indicating whether the specified number evaluates to negative - /// or positive infinity. - /// - /// A fraction. - /// true if f evaluates to Fraction.NegativeInfinity or Fraction.PositiveInfinity; otherwise, false. - public static bool IsInfinity(Fraction32 f) => f.Denominator == 0; - - /// - /// Returns the multiplicative inverse of a given value. - /// - /// A fraction. - /// Multiplicative inverse of f. - public static Fraction32 Inverse(Fraction32 f) => new(f.Denominator, f.Numerator); - - /// - /// Converts the string representation of a fraction to a fraction object. - /// - /// A string formatted as numerator/denominator - /// A fraction object converted from s. - /// s is null - /// s is not in the correct format - /// - /// s represents a number less than System.UInt32.MinValue or greater than - /// System.UInt32.MaxValue. - /// - public static Fraction32 Parse(string s) => FromString(s); - - /// - /// Converts the string representation of a fraction to a fraction object. - /// A return value indicates whether the conversion succeeded. - /// - /// A string formatted as numerator/denominator - /// true if s was converted successfully; otherwise, false. - public static bool TryParse(string s, out Fraction32 f) - { - try - { - f = Parse(s); - return true; - } - catch - { - f = new Fraction32(); - return false; - } - } - - #endregion - - #region Operators - - #region Arithmetic Operators - - // Multiplication - public static Fraction32 operator *(Fraction32 f, int n) => new(f.Numerator * n, f.Denominator * Math.Abs(n)); - - public static Fraction32 operator *(int n, Fraction32 f) => f * n; - - public static Fraction32 operator *(Fraction32 f, float n) => new((float)f * n); - - public static Fraction32 operator *(float n, Fraction32 f) => f * n; - - public static Fraction32 operator *(Fraction32 f, double n) => new((double)f * n); - - public static Fraction32 operator *(double n, Fraction32 f) => f * n; - - public static Fraction32 operator *(Fraction32 f1, Fraction32 f2) => - new(f1.Numerator * f2.Numerator, f1.Denominator * f2.Denominator); - - // Division - public static Fraction32 operator /(Fraction32 f, int n) => new(f.Numerator / n, f.Denominator / Math.Abs(n)); - - public static Fraction32 operator /(Fraction32 f, float n) => new((float)f / n); - - public static Fraction32 operator /(Fraction32 f, double n) => new((double)f / n); - - public static Fraction32 operator /(Fraction32 f1, Fraction32 f2) => f1 * Inverse(f2); - - // Addition - public static Fraction32 operator +(Fraction32 f, int n) => f + new Fraction32(n, 1); - - public static Fraction32 operator +(int n, Fraction32 f) => f + n; - - public static Fraction32 operator +(Fraction32 f, float n) => new((float)f + n); - - public static Fraction32 operator +(float n, Fraction32 f) => f + n; - - public static Fraction32 operator +(Fraction32 f, double n) => new((double)f + n); - - public static Fraction32 operator +(double n, Fraction32 f) => f + n; - - public static Fraction32 operator +(Fraction32 f1, Fraction32 f2) - { - int n1 = f1.Numerator, d1 = f1.Denominator; - int n2 = f2.Numerator, d2 = f2.Denominator; - - return new Fraction32((n1 * d2) + (n2 * d1), d1 * d2); - } - - // Subtraction - public static Fraction32 operator -(Fraction32 f, int n) => f - new Fraction32(n, 1); - - public static Fraction32 operator -(int n, Fraction32 f) => new Fraction32(n, 1) - f; - - public static Fraction32 operator -(Fraction32 f, float n) => new((float)f - n); - - public static Fraction32 operator -(float n, Fraction32 f) => new Fraction32(n) - f; - - public static Fraction32 operator -(Fraction32 f, double n) => new((double)f - n); - - public static Fraction32 operator -(double n, Fraction32 f) => new Fraction32(n) - f; - - public static Fraction32 operator -(Fraction32 f1, Fraction32 f2) - { - int n1 = f1.Numerator, d1 = f1.Denominator; - int n2 = f2.Numerator, d2 = f2.Denominator; - - return new Fraction32((n1 * d2) - (n2 * d1), d1 * d2); - } - - // Increment - public static Fraction32 operator ++(Fraction32 f) => f + new Fraction32(1, 1); - - // Decrement - public static Fraction32 operator --(Fraction32 f) => f - new Fraction32(1, 1); - - #endregion - - #region Casts To Integral Types - - public static explicit operator int(Fraction32 f) => f.Numerator / f.Denominator; - - public static explicit operator float(Fraction32 f) => f.Numerator / (float)f.Denominator; - - public static explicit operator double(Fraction32 f) => f.Numerator / (double)f.Denominator; - - #endregion - - #region Comparison Operators - - public static bool operator ==(Fraction32 f1, Fraction32 f2) => - f1.Numerator == f2.Numerator && f1.Denominator == f2.Denominator; - - public static bool operator !=(Fraction32 f1, Fraction32 f2) => - f1.Numerator != f2.Numerator || f1.Denominator != f2.Denominator; - - public static bool operator <(Fraction32 f1, Fraction32 f2) => - f1.Numerator * f2.Denominator < f2.Numerator * f1.Denominator; - - public static bool operator >(Fraction32 f1, Fraction32 f2) => - f1.Numerator * f2.Denominator > f2.Numerator * f1.Denominator; - - #endregion - - #endregion - - #region Constructors - - private Fraction32(int numerator, int denominator, double error) - { - IsNegative = false; - if (numerator < 0) - { - numerator = -numerator; - IsNegative = !IsNegative; - } - - if (denominator < 0) - { - denominator = -denominator; - IsNegative = !IsNegative; - } - - mNumerator = numerator; - mDenominator = denominator; - Error = error; - - if (mDenominator != 0) - { - Reduce(ref mNumerator, ref mDenominator); - } - } - - public Fraction32(int numerator, int denominator) - : this(numerator, denominator, 0) - { - } - - public Fraction32(int numerator) - : this(numerator, 1) - { - } - - public Fraction32(Fraction32 f) - : this(f.Numerator, f.Denominator, f.Error) - { - } - - public Fraction32(float value) - : this((double)value) - { - } - - public Fraction32(double value) - : this(FromDouble(value)) - { - } - - public Fraction32(string s) - : this(FromString(s)) - { - } - - #endregion - - #region Instance Methods - - /// - /// Sets the value of this instance to the fraction represented - /// by the given numerator and denominator. - /// - /// The new numerator. - /// The new denominator. - public void Set(int numerator, int denominator) - { - IsNegative = false; - if (numerator < 0) - { - IsNegative = !IsNegative; - numerator = -numerator; - } - - if (denominator < 0) - { - IsNegative = !IsNegative; - denominator = -denominator; - } - - mNumerator = numerator; - mDenominator = denominator; - - if (mDenominator != 0) - { - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Indicates whether this instance and a specified object are equal value-wise. - /// - /// Another object to compare to. - /// - /// true if obj and this instance are the same type and represent - /// the same value; otherwise, false. - /// - public override bool Equals(object? obj) - { - if (obj == null) - { - return false; - } - - if (obj is Fraction32) - { - return Equals((Fraction32)obj); - } - - return false; - } - - /// - /// Indicates whether this instance and a specified object are equal value-wise. - /// - /// Another fraction object to compare to. - /// - /// true if obj and this instance represent the same value; - /// otherwise, false. - /// - public bool Equals(Fraction32 obj) => IsNegative == obj.IsNegative && mNumerator == obj.Numerator && - mDenominator == obj.Denominator; - - /// - /// Returns the hash code for this instance. - /// - /// A 32-bit signed integer that is the hash code for this instance. - public override int GetHashCode() => mDenominator ^ ((IsNegative ? -1 : 1) * mNumerator); - - /// - /// Returns a string representation of the fraction. - /// - /// A numeric format string. - /// - /// An System.IFormatProvider that supplies culture-specific - /// formatting information. - /// - /// - /// The string representation of the value of this instance as - /// specified by format and provider. - /// - /// - /// format is invalid or not supported. - /// - public string ToString(string? format, IFormatProvider? formatProvider) - { - var sb = new StringBuilder(); - sb.Append(((IsNegative ? -1 : 1) * mNumerator).ToString(format, formatProvider)); - sb.Append('/'); - sb.Append(mDenominator.ToString(format, formatProvider)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// A numeric format string. - /// - /// The string representation of the value of this instance as - /// specified by format. - /// - /// - /// format is invalid or not supported. - /// - public string ToString(string format) - { - var sb = new StringBuilder(); - sb.Append(((IsNegative ? -1 : 1) * mNumerator).ToString(format)); - sb.Append('/'); - sb.Append(mDenominator.ToString(format)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// - /// An System.IFormatProvider that supplies culture-specific - /// formatting information. - /// - /// - /// The string representation of the value of this instance as - /// specified by provider. - /// - public string ToString(IFormatProvider formatProvider) - { - var sb = new StringBuilder(); - sb.Append(((IsNegative ? -1 : 1) * mNumerator).ToString(formatProvider)); - sb.Append('/'); - sb.Append(mDenominator.ToString(formatProvider)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// A string formatted as numerator/denominator. - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append(((IsNegative ? -1 : 1) * mNumerator).ToString()); - sb.Append('/'); - sb.Append(mDenominator.ToString()); - return sb.ToString(); - } - - /// - /// Compares this instance to a specified object and returns an indication of - /// their relative values. - /// - /// An object to compare, or null. - /// - /// A signed number indicating the relative values of this instance and value. - /// Less than zero: This instance is less than obj. - /// Zero: This instance is equal to obj. - /// Greater than zero: This instance is greater than obj or obj is null. - /// - /// obj is not a Fraction. - public int CompareTo(object? obj) - { - if (!(obj is Fraction32)) - { - throw new ArgumentException("obj must be of type Fraction", "obj"); - } - - return CompareTo((Fraction32)obj); - } - - /// - /// Compares this instance to a specified object and returns an indication of - /// their relative values. - /// - /// An fraction to compare with this instance. - /// - /// A signed number indicating the relative values of this instance and value. - /// Less than zero: This instance is less than obj. - /// Zero: This instance is equal to obj. - /// Greater than zero: This instance is greater than obj or obj is null. - /// - public int CompareTo(Fraction32 obj) - { - if (this < obj) - { - return -1; - } - - if (this > obj) - { - return 1; - } - - return 0; - } - - #endregion - - #region Private Helper Methods - - /// - /// Converts the given floating-point number to its rational representation. - /// - /// The floating-point number to be converted. - /// The rational representation of value. - private static Fraction32 FromDouble(double value) - { - if (double.IsNaN(value)) - { - return NaN; - } - - if (double.IsNegativeInfinity(value)) - { - return NegativeInfinity; - } - - if (double.IsPositiveInfinity(value)) - { - return PositiveInfinity; - } - - var isneg = value < 0; - if (isneg) - { - value = -value; - } - - var f = value; - var forg = f; - var lnum = 0; - var lden = 1; - var num = 1; - var den = 0; - var lasterr = 1.0; - var a = 0; - var currIteration = 0; - while (true) - { - if (++currIteration > MaximumIterations) - { - break; - } - - a = (int)Math.Floor(f); - f = f - a; - if (Math.Abs(f) < double.Epsilon) - { - break; - } - - f = 1.0 / f; - if (double.IsInfinity(f)) - { - break; - } - - var cnum = (num * a) + lnum; - var cden = (den * a) + lden; - if (Math.Abs((cnum / (double)cden) - forg) < double.Epsilon) - { - break; - } - - var err = ((cnum / (double)cden) - (num / (double)den)) / (num / (double)den); - - // Are we converging? - if (err >= lasterr) - { - break; - } - - lasterr = err; - lnum = num; - lden = den; - num = cnum; - den = cden; - } - - if (den > 0) - { - lasterr = value - (num / (double)den); - } - else - { - lasterr = double.PositiveInfinity; - } - - return new Fraction32((isneg ? -1 : 1) * num, den, lasterr); - } - - /// Converts the string representation of a fraction to a Fraction type. - /// The input string formatted as numerator/denominator. - /// s is null. - /// s is not formatted as numerator/denominator. - /// - /// s represents numbers less than System.Int32.MinValue or greater than - /// System.Int32.MaxValue. - /// - private static Fraction32 FromString(string s) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - var sa = s.Split(Constants.CharArrays.ForwardSlash); - var numerator = 1; - var denominator = 1; - - if (sa.Length == 1) - { - // Try to parse as int - if (int.TryParse(sa[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out numerator)) - { - denominator = 1; - } - else - { - // Parse as double - var dval = double.Parse(sa[0]); - return FromDouble(dval); - } - } - else if (sa.Length == 2) - { - numerator = int.Parse(sa[0], CultureInfo.InvariantCulture); - denominator = int.Parse(sa[1], CultureInfo.InvariantCulture); - } - else - { - throw new FormatException("The input string must be formatted as n/d where n and d are integers"); - } - - return new Fraction32(numerator, denominator); - } - - /// - /// Reduces the given numerator and denominator by dividing with their - /// greatest common divisor. - /// - /// numerator to be reduced. - /// denominator to be reduced. - private static void Reduce(ref int numerator, ref int denominator) - { - var gcd = GCD((uint)numerator, (uint)denominator); - if (gcd == 0) - { - gcd = 1; - } - - numerator = numerator / (int)gcd; - denominator = denominator / (int)gcd; - } - - #endregion - } - - /// - /// Represents a generic rational number represented by 32-bit unsigned numerator and denominator. - /// - public struct UFraction32 : IComparable, IFormattable, IComparable, IEquatable - { - #region Constants - - private const uint MaximumIterations = 10000000; - - #endregion - - #region Member Variables - - private uint mNumerator; - private uint mDenominator; - - #endregion - - #region Properties - - /// - /// Gets or sets the numerator. - /// - public uint Numerator - { - get => mNumerator; - set - { - mNumerator = value; - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Gets or sets the denominator. - /// - public uint Denominator - { - get => mDenominator; - set - { - mDenominator = value; - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Gets the error term. - /// - public double Error { get; } - - #endregion - - #region Predefined Values - - public static readonly UFraction32 NaN = new(0, 0); - public static readonly UFraction32 Infinity = new(1, 0); - - #endregion - - #region Static Methods - - /// - /// Returns a value indicating whether the specified number evaluates to a value - /// that is not a number. - /// - /// A fraction. - /// true if f evaluates to Fraction.NaN; otherwise, false. - public static bool IsNan(UFraction32 f) => f.Numerator == 0 && f.Denominator == 0; - - /// - /// Returns a value indicating whether the specified number evaluates to infinity. - /// - /// A fraction. - /// true if f evaluates to Fraction.Infinity; otherwise, false. - public static bool IsInfinity(UFraction32 f) => f.Denominator == 0; - - /// - /// Converts the string representation of a fraction to a fraction object. - /// - /// A string formatted as numerator/denominator - /// A fraction object converted from s. - /// s is null - /// s is not in the correct format - /// - /// s represents a number less than System.UInt32.MinValue or greater than - /// System.UInt32.MaxValue. - /// - public static UFraction32 Parse(string s) => FromString(s); - - /// - /// Converts the string representation of a fraction to a fraction object. - /// A return value indicates whether the conversion succeeded. - /// - /// A string formatted as numerator/denominator - /// true if s was converted successfully; otherwise, false. - public static bool TryParse(string s, out UFraction32 f) - { - try - { - f = Parse(s); - return true; - } - catch - { - f = new UFraction32(); - return false; - } - } - - #endregion - - #region Operators - - #region Arithmetic Operators - - // Multiplication - public static UFraction32 operator *(UFraction32 f, uint n) => new(f.Numerator * n, f.Denominator * n); - - public static UFraction32 operator *(uint n, UFraction32 f) => f * n; - - public static UFraction32 operator *(UFraction32 f, float n) => new((float)f * n); - - public static UFraction32 operator *(float n, UFraction32 f) => f * n; - - public static UFraction32 operator *(UFraction32 f, double n) => new((double)f * n); - - public static UFraction32 operator *(double n, UFraction32 f) => f * n; - - public static UFraction32 operator *(UFraction32 f1, UFraction32 f2) => - new(f1.Numerator * f2.Numerator, f1.Denominator * f2.Denominator); - - // Division - public static UFraction32 operator /(UFraction32 f, uint n) => new(f.Numerator / n, f.Denominator / n); - - public static UFraction32 operator /(UFraction32 f, float n) => new((float)f / n); - - public static UFraction32 operator /(UFraction32 f, double n) => new((double)f / n); - - public static UFraction32 operator /(UFraction32 f1, UFraction32 f2) => f1 * Inverse(f2); - - // Addition - public static UFraction32 operator +(UFraction32 f, uint n) => f + new UFraction32(n, 1); - - public static UFraction32 operator +(uint n, UFraction32 f) => f + n; - - public static UFraction32 operator +(UFraction32 f, float n) => new((float)f + n); - - public static UFraction32 operator +(float n, UFraction32 f) => f + n; - - public static UFraction32 operator +(UFraction32 f, double n) => new((double)f + n); - - public static UFraction32 operator +(double n, UFraction32 f) => f + n; - - public static UFraction32 operator +(UFraction32 f1, UFraction32 f2) - { - uint n1 = f1.Numerator, d1 = f1.Denominator; - uint n2 = f2.Numerator, d2 = f2.Denominator; - - return new UFraction32((n1 * d2) + (n2 * d1), d1 * d2); - } - - // Subtraction - public static UFraction32 operator -(UFraction32 f, uint n) => f - new UFraction32(n, 1); - - public static UFraction32 operator -(uint n, UFraction32 f) => new UFraction32(n, 1) - f; - - public static UFraction32 operator -(UFraction32 f, float n) => new((float)f - n); - - public static UFraction32 operator -(float n, UFraction32 f) => new UFraction32(n) - f; - - public static UFraction32 operator -(UFraction32 f, double n) => new((double)f - n); - - public static UFraction32 operator -(double n, UFraction32 f) => new UFraction32(n) - f; - - public static UFraction32 operator -(UFraction32 f1, UFraction32 f2) - { - uint n1 = f1.Numerator, d1 = f1.Denominator; - uint n2 = f2.Numerator, d2 = f2.Denominator; - - return new UFraction32((n1 * d2) - (n2 * d1), d1 * d2); - } - - // Increment - public static UFraction32 operator ++(UFraction32 f) => f + new UFraction32(1, 1); - - // Decrement - public static UFraction32 operator --(UFraction32 f) => f - new UFraction32(1, 1); - - #endregion - - #region Casts To Integral Types - - public static explicit operator uint(UFraction32 f) => f.Numerator / f.Denominator; - - public static explicit operator float(UFraction32 f) => f.Numerator / (float)f.Denominator; - public static explicit operator double(UFraction32 f) => f.Numerator / (double)f.Denominator; - - #endregion - - #region Comparison Operators - - public static bool operator ==(UFraction32 f1, UFraction32 f2) => - f1.Numerator == f2.Numerator && f1.Denominator == f2.Denominator; - - public static bool operator !=(UFraction32 f1, UFraction32 f2) => - f1.Numerator != f2.Numerator || f1.Denominator != f2.Denominator; - - public static bool operator <(UFraction32 f1, UFraction32 f2) => - f1.Numerator * f2.Denominator < f2.Numerator * f1.Denominator; - - public static bool operator >(UFraction32 f1, UFraction32 f2) => - f1.Numerator * f2.Denominator > f2.Numerator * f1.Denominator; - - #endregion - - #endregion - - #region Constructors - - public UFraction32(uint numerator, uint denominator, double error) - { - mNumerator = numerator; - mDenominator = denominator; - Error = error; - - if (mDenominator != 0) - { - Reduce(ref mNumerator, ref mDenominator); - } - } - - public UFraction32(uint numerator, uint denominator) - : this(numerator, denominator, 0) - { - } - - public UFraction32(uint numerator) - : this(numerator, 1) - { - } - - public UFraction32(UFraction32 f) - : this(f.Numerator, f.Denominator, f.Error) - { - } - - public UFraction32(float value) - : this((double)value) - { - } - - public UFraction32(double value) - : this(FromDouble(value)) - { - } - - public UFraction32(string s) - : this(FromString(s)) - { - } - - #endregion - - #region Instance Methods - - /// - /// Sets the value of this instance to the fraction represented - /// by the given numerator and denominator. - /// - /// The new numerator. - /// The new denominator. - public void Set(uint numerator, uint denominator) - { - mNumerator = numerator; - mDenominator = denominator; - - if (mDenominator != 0) - { - Reduce(ref mNumerator, ref mDenominator); - } - } - - /// - /// Returns the multiplicative inverse of a given value. - /// - /// A fraction. - /// Multiplicative inverse of f. - public static UFraction32 Inverse(UFraction32 f) => new(f.Denominator, f.Numerator); - - /// - /// Indicates whether this instance and a specified object are equal value-wise. - /// - /// Another object to compare to. - /// - /// true if obj and this instance are the same type and represent - /// the same value; otherwise, false. - /// - public override bool Equals(object? obj) - { - if (obj == null) - { - return false; - } - - if (obj is UFraction32) - { - return Equals((UFraction32)obj); - } - - return false; - } - - /// - /// Indicates whether this instance and a specified object are equal value-wise. - /// - /// Another fraction object to compare to. - /// - /// true if obj and this instance represent the same value; - /// otherwise, false. - /// - public bool Equals(UFraction32 obj) => mNumerator == obj.Numerator && mDenominator == obj.Denominator; - - /// - /// Returns the hash code for this instance. - /// - /// A 32-bit signed integer that is the hash code for this instance. - public override int GetHashCode() => (int)mDenominator ^ (int)mNumerator; - - /// - /// Returns a string representation of the fraction. - /// - /// A numeric format string. - /// - /// An System.IFormatProvider that supplies culture-specific - /// formatting information. - /// - /// - /// The string representation of the value of this instance as - /// specified by format and provider. - /// - /// - /// format is invalid or not supported. - /// - public string ToString(string? format, IFormatProvider? formatProvider) - { - var sb = new StringBuilder(); - sb.Append(mNumerator.ToString(format, formatProvider)); - sb.Append('/'); - sb.Append(mDenominator.ToString(format, formatProvider)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// A numeric format string. - /// - /// The string representation of the value of this instance as - /// specified by format. - /// - /// - /// format is invalid or not supported. - /// - public string ToString(string format) - { - var sb = new StringBuilder(); - sb.Append(mNumerator.ToString(format)); - sb.Append('/'); - sb.Append(mDenominator.ToString(format)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// - /// An System.IFormatProvider that supplies culture-specific - /// formatting information. - /// - /// - /// The string representation of the value of this instance as - /// specified by provider. - /// - public string ToString(IFormatProvider formatProvider) - { - var sb = new StringBuilder(); - sb.Append(mNumerator.ToString(formatProvider)); - sb.Append('/'); - sb.Append(mDenominator.ToString(formatProvider)); - return sb.ToString(); - } - - /// - /// Returns a string representation of the fraction. - /// - /// A string formatted as numerator/denominator. - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append(mNumerator.ToString()); - sb.Append('/'); - sb.Append(mDenominator.ToString()); - return sb.ToString(); - } - - /// - /// Compares this instance to a specified object and returns an indication of - /// their relative values. - /// - /// An object to compare, or null. - /// - /// A signed number indicating the relative values of this instance and value. - /// Less than zero: This instance is less than obj. - /// Zero: This instance is equal to obj. - /// Greater than zero: This instance is greater than obj or obj is null. - /// - /// obj is not a Fraction. - public int CompareTo(object? obj) - { - if (!(obj is UFraction32)) - { - throw new ArgumentException("obj must be of type UFraction32", "obj"); - } - - return CompareTo((UFraction32)obj); - } - - /// - /// Compares this instance to a specified object and returns an indication of - /// their relative values. - /// - /// An fraction to compare with this instance. - /// - /// A signed number indicating the relative values of this instance and value. - /// Less than zero: This instance is less than obj. - /// Zero: This instance is equal to obj. - /// Greater than zero: This instance is greater than obj or obj is null. - /// - public int CompareTo(UFraction32 obj) - { - if (this < obj) - { - return -1; - } - - if (this > obj) - { - return 1; - } - - return 0; - } - - #endregion - - #region Private Helper Methods - - /// - /// Converts the given floating-point number to its rational representation. - /// - /// The floating-point number to be converted. - /// The rational representation of value. - private static UFraction32 FromDouble(double value) - { - if (value < 0) - { - throw new ArgumentException("value cannot be negative.", "value"); - } - - if (double.IsNaN(value)) - { - return NaN; - } - - if (double.IsInfinity(value)) - { - return Infinity; - } - - var f = value; - var forg = f; - uint lnum = 0; - uint lden = 1; - uint num = 1; - uint den = 0; - var lasterr = 1.0; - uint a = 0; - var currIteration = 0; - while (true) - { - if (++currIteration > MaximumIterations) - { - break; - } - - a = (uint)Math.Floor(f); - f = f - a; - if (Math.Abs(f) < double.Epsilon) - { - break; - } - - f = 1.0 / f; - if (double.IsInfinity(f)) - { - break; - } - - var cnum = (num * a) + lnum; - var cden = (den * a) + lden; - if (Math.Abs((cnum / (double)cden) - forg) < double.Epsilon) - { - break; - } - - var err = ((cnum / (double)cden) - (num / (double)den)) / (num / (double)den); - - // Are we converging? - if (err >= lasterr) - { - break; - } - - lasterr = err; - lnum = num; - lden = den; - num = cnum; - den = cden; - } - - var fnum = (num * a) + lnum; - var fden = (den * a) + lden; - - if (fden > 0) - { - lasterr = value - (fnum / (double)fden); - } - else - { - lasterr = double.PositiveInfinity; - } - - return new UFraction32(fnum, fden, lasterr); - } - - /// Converts the string representation of a fraction to a Fraction type. - /// The input string formatted as numerator/denominator. - /// s is null. - /// s is not formatted as numerator/denominator. - /// - /// s represents numbers less than System.UInt32.MinValue or greater than - /// System.UInt32.MaxValue. - /// - private static UFraction32 FromString(string s) - { - if (s == null) - { - throw new ArgumentNullException("s"); - } - - var sa = s.Split(Constants.CharArrays.ForwardSlash); - uint numerator = 1; - uint denominator = 1; - - if (sa.Length == 1) - { - // Try to parse as uint - if (uint.TryParse(sa[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out numerator)) - { - denominator = 1; - } - else - { - // Parse as double - var dval = double.Parse(sa[0]); - return FromDouble(dval); - } - } - else if (sa.Length == 2) - { - numerator = uint.Parse(sa[0], CultureInfo.InvariantCulture); - denominator = uint.Parse(sa[1], CultureInfo.InvariantCulture); - } - else - { - throw new FormatException("The input string must be formatted as n/d where n and d are integers"); - } - - return new UFraction32(numerator, denominator); - } - - /// - /// Reduces the given numerator and denominator by dividing with their - /// greatest common divisor. - /// - /// numerator to be reduced. - /// denominator to be reduced. - private static void Reduce(ref uint numerator, ref uint denominator) - { - var gcd = GCD(numerator, denominator); - numerator = numerator / gcd; - denominator = denominator / gcd; - } - - #endregion - } -} diff --git a/src/Umbraco.Core/Media/Exif/SvgFile.cs b/src/Umbraco.Core/Media/Exif/SvgFile.cs deleted file mode 100644 index 08326e634c..0000000000 --- a/src/Umbraco.Core/Media/Exif/SvgFile.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Globalization; -using System.Xml.Linq; - -namespace Umbraco.Cms.Core.Media.Exif; - -internal class SvgFile : ImageFile -{ - public SvgFile(Stream fileStream) - { - fileStream.Position = 0; - - var document = - XDocument.Load(fileStream); // if it throws an exception the ugly try catch in MediaFileSystem will catch it - - var width = document.Root?.Attributes().Where(x => x.Name == "width").Select(x => x.Value).FirstOrDefault(); - var height = document.Root?.Attributes().Where(x => x.Name == "height").Select(x => x.Value).FirstOrDefault(); - - Properties.Add(new ExifSInt( - ExifTag.PixelYDimension, - height == null ? Constants.Conventions.Media.DefaultSize : int.Parse(height, CultureInfo.InvariantCulture))); - Properties.Add(new ExifSInt( - ExifTag.PixelXDimension, - width == null ? Constants.Conventions.Media.DefaultSize : int.Parse(width, CultureInfo.InvariantCulture))); - - Format = ImageFileFormat.SVG; - } - - public override void Save(Stream stream) - { - } -} diff --git a/src/Umbraco.Core/Media/Exif/TIFFFile.cs b/src/Umbraco.Core/Media/Exif/TIFFFile.cs deleted file mode 100644 index 2ae27c46dc..0000000000 --- a/src/Umbraco.Core/Media/Exif/TIFFFile.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System.Text; - -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents the binary view of a TIFF file. -/// -internal class TIFFFile : ImageFile -{ - #region Constructor - - /// - /// Initializes a new instance of the class from the - /// specified data stream. - /// - /// A that contains image data. - /// The encoding to be used for text metadata when the source encoding is unknown. - protected internal TIFFFile(Stream stream, Encoding encoding) - { - Format = ImageFileFormat.TIFF; - IFDs = new List(); - Encoding = encoding; - - // Read the entire stream - var data = Utility.GetStreamBytes(stream); - - // Read the TIFF header - TIFFHeader = TIFFHeader.FromBytes(data, 0); - var nextIFDOffset = TIFFHeader.IFDOffset; - if (nextIFDOffset == 0) - { - throw new NotValidTIFFileException("The first IFD offset is zero."); - } - - // Read IFDs in order - while (nextIFDOffset != 0) - { - var ifd = ImageFileDirectory.FromBytes(data, nextIFDOffset, TIFFHeader.ByteOrder); - nextIFDOffset = ifd.NextIFDOffset; - IFDs.Add(ifd); - } - - // Process IFDs - // TODO: Add support for multiple frames - foreach (ImageFileDirectoryEntry field in IFDs[0].Fields) - { - Properties.Add(ExifPropertyFactory.Get(field.Tag, field.Type, field.Count, field.Data, BitConverterEx.SystemByteOrder, IFD.Zeroth, Encoding)); - } - } - - #endregion - - #region Properties - - /// - /// Gets the TIFF header. - /// - public TIFFHeader TIFFHeader { get; } - - #endregion - - #region Instance Methods - - /// - /// Saves the to the given stream. - /// - /// The data stream used to save the image. - public override void Save(Stream stream) - { - BitConverterEx conv = BitConverterEx.SystemEndian; - - // Write TIFF header - uint ifdoffset = 8; - - // Byte order - stream.Write( - BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian - ? new byte[] { 0x49, 0x49 } - : new byte[] { 0x4D, 0x4D }, - 0, - 2); - - // TIFF ID - stream.Write(conv.GetBytes((ushort)42), 0, 2); - - // Offset to 0th IFD, will be corrected below - stream.Write(conv.GetBytes(ifdoffset), 0, 4); - - // Write IFD sections - for (var i = 0; i < IFDs.Count; i++) - { - ImageFileDirectory ifd = IFDs[i]; - - // Save the location of IFD offset - var ifdLocation = stream.Position - 4; - - // Write strips first - var stripOffsets = new byte[4 * ifd.Strips.Count]; - var stripLengths = new byte[4 * ifd.Strips.Count]; - var stripOffset = ifdoffset; - for (var j = 0; j < ifd.Strips.Count; j++) - { - var stripData = ifd.Strips[j].Data; - var oBytes = BitConverter.GetBytes(stripOffset); - var lBytes = BitConverter.GetBytes((uint)stripData.Length); - Array.Copy(oBytes, 0, stripOffsets, 4 * j, 4); - Array.Copy(lBytes, 0, stripLengths, 4 * j, 4); - stream.Write(stripData, 0, stripData.Length); - stripOffset += (uint)stripData.Length; - } - - // Remove old strip tags - for (var j = ifd.Fields.Count - 1; j > 0; j--) - { - var tag = ifd.Fields[j].Tag; - if (tag == 273 || tag == 279) - { - ifd.Fields.RemoveAt(j); - } - } - - // Write new strip tags - ifd.Fields.Add(new ImageFileDirectoryEntry(273, 4, (uint)ifd.Strips.Count, stripOffsets)); - ifd.Fields.Add(new ImageFileDirectoryEntry(279, 4, (uint)ifd.Strips.Count, stripLengths)); - - // Write fields after strips - ifdoffset = stripOffset; - - // Correct IFD offset - var currentLocation = stream.Position; - stream.Seek(ifdLocation, SeekOrigin.Begin); - stream.Write(conv.GetBytes(ifdoffset), 0, 4); - stream.Seek(currentLocation, SeekOrigin.Begin); - - // Offset to field data - var dataOffset = ifdoffset + 2 + ((uint)ifd.Fields.Count * 12) + 4; - - // Field count - stream.Write(conv.GetBytes((ushort)ifd.Fields.Count), 0, 2); - - // Fields - foreach (ImageFileDirectoryEntry field in ifd.Fields) - { - // Tag - stream.Write(conv.GetBytes(field.Tag), 0, 2); - - // Type - stream.Write(conv.GetBytes(field.Type), 0, 2); - - // Count - stream.Write(conv.GetBytes(field.Count), 0, 4); - - // Field data - var data = field.Data; - if (data.Length <= 4) - { - stream.Write(data, 0, data.Length); - for (var j = data.Length; j < 4; j++) - { - stream.WriteByte(0); - } - } - else - { - stream.Write(conv.GetBytes(dataOffset), 0, 4); - var currentOffset = stream.Position; - stream.Seek(dataOffset, SeekOrigin.Begin); - stream.Write(data, 0, data.Length); - dataOffset += (uint)data.Length; - stream.Seek(currentOffset, SeekOrigin.Begin); - } - } - - // Offset to next IFD - ifdoffset = dataOffset; - stream.Write(conv.GetBytes(i == IFDs.Count - 1 ? 0 : ifdoffset), 0, 4); - } - } - - /// - /// Gets the image file directories. - /// - public List IFDs { get; } - - #endregion -} diff --git a/src/Umbraco.Core/Media/Exif/TIFFHeader.cs b/src/Umbraco.Core/Media/Exif/TIFFHeader.cs deleted file mode 100644 index 54a79d90b4..0000000000 --- a/src/Umbraco.Core/Media/Exif/TIFFHeader.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a TIFF Header. -/// -internal struct TIFFHeader -{ - /// - /// The byte order of the image file. - /// - public BitConverterEx.ByteOrder ByteOrder; - - /// - /// TIFF ID. This value should always be 42. - /// - public byte ID; - - /// - /// The offset to the first IFD section from the - /// start of the TIFF header. - /// - public uint IFDOffset; - - /// - /// The byte order of the TIFF header itself. - /// - public BitConverterEx.ByteOrder TIFFHeaderByteOrder; - - /// - /// Initializes a new instance of the struct. - /// - /// The byte order. - /// The TIFF ID. This value should always be 42. - /// - /// The offset to the first IFD section from the - /// start of the TIFF header. - /// - /// The byte order of the TIFF header itself. - public TIFFHeader(BitConverterEx.ByteOrder byteOrder, byte id, uint ifdOffset, BitConverterEx.ByteOrder headerByteOrder) - { - if (id != 42) - { - throw new NotValidTIFFHeader(); - } - - ByteOrder = byteOrder; - ID = id; - IFDOffset = ifdOffset; - TIFFHeaderByteOrder = headerByteOrder; - } - - /// - /// Returns a initialized from the given byte data. - /// - /// The data. - /// The offset into . - /// A initialized from the given byte data. - public static TIFFHeader FromBytes(byte[] data, int offset) - { - var header = default(TIFFHeader); - - // TIFF header - if (data[offset] == 0x49 && data[offset + 1] == 0x49) - { - header.ByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else if (data[offset] == 0x4D && data[offset + 1] == 0x4D) - { - header.ByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - else - { - throw new NotValidTIFFHeader(); - } - - // TIFF header may have a different byte order - if (BitConverterEx.LittleEndian.ToUInt16(data, offset + 2) == 42) - { - header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.LittleEndian; - } - else if (BitConverterEx.BigEndian.ToUInt16(data, offset + 2) == 42) - { - header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.BigEndian; - } - else - { - throw new NotValidTIFFHeader(); - } - - header.ID = 42; - - // IFD offset - header.IFDOffset = - BitConverterEx.ToUInt32(data, offset + 4, header.TIFFHeaderByteOrder, BitConverterEx.SystemByteOrder); - - return header; - } -} diff --git a/src/Umbraco.Core/Media/Exif/TIFFStrip.cs b/src/Umbraco.Core/Media/Exif/TIFFStrip.cs deleted file mode 100644 index 8bf91abde6..0000000000 --- a/src/Umbraco.Core/Media/Exif/TIFFStrip.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Represents a strip of compressed image data in a TIFF file. -/// -internal class TIFFStrip -{ - /// - /// Initializes a new instance of the class. - /// - /// The byte array to copy strip from. - /// The offset to the beginning of strip. - /// The length of strip. - public TIFFStrip(byte[] data, uint offset, uint length) - { - Data = new byte[length]; - Array.Copy(data, offset, Data, 0, length); - } - - /// - /// Compressed image data contained in this strip. - /// - public byte[] Data { get; } -} diff --git a/src/Umbraco.Core/Media/Exif/Utility.cs b/src/Umbraco.Core/Media/Exif/Utility.cs deleted file mode 100644 index 1ce1b1cdc7..0000000000 --- a/src/Umbraco.Core/Media/Exif/Utility.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Umbraco.Cms.Core.Media.Exif; - -/// -/// Contains utility functions. -/// -internal class Utility -{ - /// - /// Reads the entire stream and returns its contents as a byte array. - /// - /// The to read. - /// Contents of the as a byte array. - public static byte[] GetStreamBytes(Stream stream) - { - using (var mem = new MemoryStream()) - { - stream.Seek(0, SeekOrigin.Begin); - - var b = new byte[32768]; - int r; - while ((r = stream.Read(b, 0, b.Length)) > 0) - { - mem.Write(b, 0, r); - } - - return mem.ToArray(); - } - } -} diff --git a/src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml b/src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml index 7baad05aa0..2095a0c798 100644 --- a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml +++ b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml @@ -1,7 +1,17 @@  - PKV006 - net6.0 + CP0006 + M:Umbraco.Cms.Core.Deploy.IGridCellValueConnector.GetValue(Umbraco.Cms.Core.Models.GridValue.GridControl,System.Collections.Generic.ICollection{Umbraco.Cms.Core.Deploy.ArtifactDependency},Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true + + + CP0006 + M:Umbraco.Cms.Core.Deploy.IGridCellValueConnector.SetValue(Umbraco.Cms.Core.Models.GridValue.GridControl,Umbraco.Cms.Core.Deploy.IContextCache) + lib/net7.0/Umbraco.Infrastructure.dll + lib/net7.0/Umbraco.Infrastructure.dll + true \ No newline at end of file diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 737cd5613b..a77b00814c 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -296,18 +296,7 @@ public static partial class UmbracoBuilderExtensions private static IUmbracoBuilder AddPreValueMigrators(this IUmbracoBuilder builder) { - builder.WithCollectionBuilder() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append(); + builder.WithCollectionBuilder(); return builder; } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index c80d70fa1f..750c65c21a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -1,25 +1,7 @@ -using System.Diagnostics.CodeAnalysis; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration; -using Umbraco.Cms.Core.Semver; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.Common; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_0_0; using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_10_2_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_1; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_10_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_17_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_7_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_9_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_1_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_2_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_3_0; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_4_0; -using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade; @@ -29,10 +11,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade; /// public class UmbracoPlan : MigrationPlan { - private const string InitPrefix = "{init-"; - private const string InitSuffix = "}"; - private readonly IUmbracoVersion _umbracoVersion; - /// /// Initializes a new instance of the class. /// @@ -40,7 +18,6 @@ public class UmbracoPlan : MigrationPlan public UmbracoPlan(IUmbracoVersion umbracoVersion) : base(Constants.Conventions.Migrations.UmbracoUpgradePlanName) { - _umbracoVersion = umbracoVersion; DefinePlan(); } @@ -57,74 +34,9 @@ public class UmbracoPlan : MigrationPlan /// upgrades (from a tool old version, or going back in time, etc). /// /// - public override string InitialState - { - get - { - SemVersion currentVersion = _umbracoVersion.SemanticVersion; + public override string InitialState => "{DED98755-4059-41BB-ADBD-3FEAB12D1D7B}"; - // only from 8.0.0 and above - var minVersion = new SemVersion(8); - if (currentVersion < minVersion) - { - throw new InvalidOperationException( - $"Version {currentVersion} cannot be migrated to {_umbracoVersion.SemanticVersion}." - + $" Please upgrade first to at least {minVersion}."); - } - // Force versions between 7.14.*-7.15.* into into 7.14 initial state. Because there is no db-changes, - // and we don't want users to workaround my putting in version 7.14.0 them self. - if (minVersion <= currentVersion && currentVersion < new SemVersion(7, 16)) - { - return GetInitState(minVersion); - } - - // initial state is eg "{init-7.14.0}" - return GetInitState(currentVersion); - } - } - - /// - public override void ThrowOnUnknownInitialState(string state) - { - if (TryGetInitStateVersion(state, out var initVersion)) - { - throw new InvalidOperationException( - $"Version {_umbracoVersion.SemanticVersion} does not support migrating from {initVersion}." - + $" Please verify which versions support migrating from {initVersion}."); - } - - base.ThrowOnUnknownInitialState(state); - } - - /// - /// Gets the initial state corresponding to a version. - /// - /// The version. - /// - /// The initial state. - /// - private static string GetInitState(SemVersion version) => InitPrefix + version + InitSuffix; - - /// - /// Tries to extract a version from an initial state. - /// - /// The state. - /// The version. - /// - /// true when the state contains a version; otherwise, false.D - /// - private static bool TryGetInitStateVersion(string state, [MaybeNullWhen(false)] out string version) - { - if (state.StartsWith(InitPrefix) && state.EndsWith(InitSuffix)) - { - version = state.TrimStart(InitPrefix).TrimEnd(InitSuffix); - return true; - } - - version = null; - return false; - } /// /// Defines the plan. @@ -138,154 +50,23 @@ public class UmbracoPlan : MigrationPlan // * Creating a migration for version 8: // Append the migration to the main chain, using a new guid, before the "//FINAL" comment // - // If the new migration causes a merge conflict, because someone else also added another - // new migration, you NEED to fix the conflict by providing one default path, and paths - // out of the conflict states (see examples below). // - // * Porting from version 7: - // Append the ported migration to the main chain, using a new guid (same as above). - // Create a new special chain from the {init-...} state to the main chain. + // If the new migration causes a merge conflict, because someone else also added another + // new migration, you NEED to fix the conflict by providing one default path, and paths + // out of the conflict states, eg: + // + // .From("state-1") + // .To("state-a") + // .To("state-b") // Some might already be in this state, without having applied ChangeA + // + // .From("state-1") + // .Merge() + // .To("state-a") + // .With() + // .To("state-b") + // .As("state-2"); - // plan starts at 7.14.0 (anything before 7.14.0 is not supported) - From(GetInitState(new SemVersion(7, 14))); - - // begin migrating from v7 - remove all keys and indexes - To("{B36B9ABD-374E-465B-9C5F-26AB0D39326F}"); - - To("{7C447271-CA3F-4A6A-A913-5D77015655CB}"); - To("{CBFF58A2-7B50-4F75-8E98-249920DB0F37}"); - To("{5CB66059-45F4-48BA-BCBD-C5035D79206B}"); - To("{FB0A5429-587E-4BD0-8A67-20F0E7E62FF7}"); - To("{F0C42457-6A3B-4912-A7EA-F27ED85A2092}"); - To("{8640C9E4-A1C0-4C59-99BB-609B4E604981}"); - To("{DD1B99AF-8106-4E00-BAC7-A43003EA07F8}"); - To("{9DF05B77-11D1-475C-A00A-B656AF7E0908}"); - To("{6FE3EF34-44A0-4992-B379-B40BC4EF1C4D}"); - To("{7F59355A-0EC9-4438-8157-EB517E6D2727}"); - ToWithReplace( - "{941B2ABA-2D06-4E04-81F5-74224F1DB037}", - "{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}"); // kill AddVariationTable1 - To("{A7540C58-171D-462A-91C5-7A9AA5CB8BFD}"); - - Merge() - .To("{3E44F712-E2E3-473A-AE49-5D7F8E67CE3F}") - .With() - .To("{65D6B71C-BDD5-4A2E-8D35-8896325E9151}") - .As("{4CACE351-C6B9-4F0C-A6BA-85A02BBD39E4}"); - - To("{1350617A-4930-4D61-852F-E3AA9E692173}"); - To("{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}"); - To("{5F4597F4-A4E0-4AFE-90B5-6D2F896830EB}"); - To("{290C18EE-B3DE-4769-84F1-1F467F3F76DA}"); - To("{6A2C7C1B-A9DB-4EA9-B6AB-78E7D5B722A7}"); - To("{8804D8E8-FE62-4E3A-B8A2-C047C2118C38}"); - To("{23275462-446E-44C7-8C2C-3B8C1127B07D}"); - To("{6B251841-3069-4AD5-8AE9-861F9523E8DA}"); - To("{EE429F1B-9B26-43CA-89F8-A86017C809A3}"); - To("{08919C4B-B431-449C-90EC-2B8445B5C6B1}"); - To("{7EB0254C-CB8B-4C75-B15B-D48C55B449EB}"); - To("{C39BF2A7-1454-4047-BBFE-89E40F66ED63}"); - To("{64EBCE53-E1F0-463A-B40B-E98EFCCA8AE2}"); - To("{0009109C-A0B8-4F3F-8FEB-C137BBDDA268}"); - To("{ED28B66A-E248-4D94-8CDB-9BDF574023F0}"); - To("{38C809D5-6C34-426B-9BEA-EFD39162595C}"); - To("{6017F044-8E70-4E10-B2A3-336949692ADD}"); - - Merge() - .To("{CDBEDEE4-9496-4903-9CF2-4104E00FF960}") - .With() - .To("{940FD19A-00A8-4D5C-B8FF-939143585726}") - .As("{0576E786-5C30-4000-B969-302B61E90CA3}"); - - To("{48AD6CCD-C7A4-4305-A8AB-38728AD23FC5}"); - To("{DF470D86-E5CA-42AC-9780-9D28070E25F9}"); - - // finish migrating from v7 - recreate all keys and indexes - To("{3F9764F5-73D0-4D45-8804-1240A66E43A2}"); - - To("{E0CBE54D-A84F-4A8F-9B13-900945FD7ED9}"); - To("{78BAF571-90D0-4D28-8175-EF96316DA789}"); - - // release-8.0.0 - - // to 8.0.1 - To("{80C0A0CB-0DD5-4573-B000-C4B7C313C70D}"); - - // release-8.0.1 - - // to 8.1.0 - To("{B69B6E8C-A769-4044-A27E-4A4E18D1645A}"); - To("{0372A42B-DECF-498D-B4D1-6379E907EB94}"); - To("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}"); - - // to 8.6.0 - To("{4759A294-9860-46BC-99F9-B4C975CAE580}"); - To("{0BC866BC-0665-487A-9913-0290BD0169AD}"); - To("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}"); - To("{EE288A91-531B-4995-8179-1D62D9AA3E2E}"); - To("{2AB29964-02A1-474D-BD6B-72148D2A53A2}"); - - // to 8.7.0 - To("{a78e3369-8ea3-40ec-ad3f-5f76929d2b20}"); - - // to 8.9.0 - To("{B5838FF5-1D22-4F6C-BCEB-F83ACB14B575}"); - - // to 8.10.0 - To("{D6A8D863-38EC-44FB-91EC-ACD6A668BD18}"); - - // NOTE: we need to do a merge migration here because as of 'now', - // v9-beta* is already out and 8.15 isn't out yet - // so we need to ensure that migrations from 8.15 are included in the next - // v9*. - - // to 8.15.0 - To("{8DDDCD0B-D7D5-4C97-BD6A-6B38CA65752F}"); - To("{4695D0C9-0729-4976-985B-048D503665D8}"); - To("{5C424554-A32D-4852-8ED1-A13508187901}"); - - // to 8.17.0 - To("{153865E9-7332-4C2A-9F9D-F20AEE078EC7}"); - - // Hack to support migration from 8.18 - To("{03482BB0-CF13-475C-845E-ECB8319DBE3C}"); - - // This should be safe to execute again. We need it with a new name to ensure updates from all the following has executed this step. - // - 8.15.0 RC - Current state: {4695D0C9-0729-4976-985B-048D503665D8} - // - 8.15.0 Final - Current state: {5C424554-A32D-4852-8ED1-A13508187901} - // - 9.0.0 RC1 - Current state: {5060F3D2-88BE-4D30-8755-CF51F28EAD12} - To("{622E5172-42E1-4662-AD80-9504AF5A4E53}"); - To("{10F7BB61-C550-426B-830B-7F954F689CDF}"); - To("{5AAE6276-80DB-4ACF-B845-199BC6C37538}"); - - // to 9.0.0 RC1 - To("{22D801BA-A1FF-4539-BFCC-2139B55594F8}"); - To("{50A43237-A6F4-49E2-A7A6-5DAD65C84669}"); - To("{3D8DADEF-0FDA-4377-A5F0-B52C2110E8F2}"); - To("{1303BDCF-2295-4645-9526-2F32E8B35ABD}"); - To("{5060F3D2-88BE-4D30-8755-CF51F28EAD12}"); - To( - "{A2686B49-A082-4B22-97FD-AAB154D46A57}"); // Re-run this migration to make sure it has executed to account for migrations going out of sync between versions. - - // TO 9.0.0-rc4 - To( - "5E02F241-5253-403D-B5D3-7DB00157E20F"); // Jaddie: This GUID is missing the { }, although this likely can't be changed now as it will break installs going forwards - - // TO 9.1.0 - To("{8BAF5E6C-DCB7-41AE-824F-4215AE4F1F98}"); - - // TO 9.2.0 - To("{0571C395-8F0B-44E9-8E3F-47BDD08D817B}"); - To("{AD3D3B7F-8E74-45A4-85DB-7FFAD57F9243}"); - - // TO 9.3.0 - To("{A2F22F17-5870-4179-8A8D-2362AA4A0A5F}"); - To("{CA7A1D9D-C9D4-4914-BC0A-459E7B9C3C8C}"); - To("{0828F206-DCF7-4F73-ABBB-6792275532EB}"); - - // TO 9.4.0 - To("{DBBA1EA0-25A1-4863-90FB-5D306FB6F1E1}"); - To("{DED98755-4059-41BB-ADBD-3FEAB12D1D7B}"); + From(InitialState); // TO 10.0.0 To("{B7E0D53C-2B0E-418B-AB07-2DDE486E225F}"); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentNuTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentNuTable.cs deleted file mode 100644 index a216abf045..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentNuTable.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; - -internal class AddContentNuTable : MigrationBase -{ - public AddContentNuTable(IMigrationContext context) - : base(context) - { - } - - protected override void Migrate() - { - IEnumerable tables = SqlSyntax.GetTablesInSchema(Context.Database); - if (tables.InvariantContains("cmsContentNu")) - { - return; - } - - Create.Table(true).Do(); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs index 36f4dcb5e0..dbfcebe7c7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddContentTypeIsElementColumn : MigrationBase { public AddContentTypeIsElementColumn(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs index 96937d3991..e30e09b386 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLockObjects.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddLockObjects : MigrationBase { public AddLockObjects(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLogTableColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLogTableColumns.cs index 8546566999..9503c0a2c4 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLogTableColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddLogTableColumns.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddLogTableColumns : MigrationBase { public AddLogTableColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs index e147d185fe..dc469d139f 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddPackagesSectionAccess.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPackagesSectionAccess : MigrationBase { public AddPackagesSectionAccess(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs index f1369db5c3..a8e3157726 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddTypedLabels.cs @@ -6,6 +6,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddTypedLabels : MigrationBase { public AddTypedLabels(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs index 118c7f8bb2..c024459b92 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddVariationTables1A : MigrationBase { public AddVariationTables1A(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs index 76d20b4667..14e3a31fe2 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddVariationTables2 : MigrationBase { public AddVariationTables2(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs index edfeb204f8..bddcc67525 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ContentVariationMigration.cs @@ -1,8 +1,9 @@ -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.Models; + using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ContentVariationMigration : MigrationBase { public ContentVariationMigration(IMigrationContext context) @@ -12,50 +13,7 @@ public class ContentVariationMigration : MigrationBase protected override void Migrate() { - static byte GetNewValue(byte oldValue) - { - switch (oldValue) - { - case 0: // Unknown - case 1: // InvariantNeutral - return 0; // Unknown - case 2: // CultureNeutral - case 3: // CultureNeutral | InvariantNeutral - return 1; // Culture - case 4: // InvariantSegment - case 5: // InvariantSegment | InvariantNeutral - return 2; // Segment - case 6: // InvariantSegment | CultureNeutral - case 7: // InvariantSegment | CultureNeutral | InvariantNeutral - case 8: // CultureSegment - case 9: // CultureSegment | InvariantNeutral - case 10: // CultureSegment | CultureNeutral - case 11: // CultureSegment | CultureNeutral | InvariantNeutral - case 12: // etc - case 13: - case 14: - case 15: - return 3; // Culture | Segment - default: - throw new NotSupportedException($"Invalid value {oldValue}."); - } - } - List? propertyTypes = - Database.Fetch(Sql().Select().From()); - foreach (PropertyTypeDto80? dto in propertyTypes) - { - dto.Variations = GetNewValue(dto.Variations); - Database.Update(dto); - } - - List? contentTypes = - Database.Fetch(Sql().Select().From()); - foreach (ContentTypeDto80? dto in contentTypes) - { - dto.Variations = GetNewValue(dto.Variations); - Database.Update(dto); - } } // we *need* to use these private DTOs here, which does *not* have extra properties, which would kill the migration diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs index 50ac54436a..14cb4816b5 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/ConvertRelatedLinksToMultiUrlPicker.cs @@ -1,15 +1,6 @@ -using System.Globalization; -using System.Runtime.Serialization; -using Newtonsoft.Json; -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.Models; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ConvertRelatedLinksToMultiUrlPicker : MigrationBase { public ConvertRelatedLinksToMultiUrlPicker(IMigrationContext context) @@ -19,144 +10,7 @@ public class ConvertRelatedLinksToMultiUrlPicker : MigrationBase protected override void Migrate() { - Sql sqlDataTypes = Sql() - .Select() - .From() - .Where(x => x.EditorAlias == Constants.PropertyEditors.Legacy.Aliases.RelatedLinks - || x.EditorAlias == Constants.PropertyEditors.Legacy.Aliases.RelatedLinks2); - List? dataTypes = Database.Fetch(sqlDataTypes); - var dataTypeIds = dataTypes.Select(x => x.NodeId).ToList(); - - if (dataTypeIds.Count == 0) - { - return; - } - - foreach (DataTypeDto? dataType in dataTypes) - { - dataType.EditorAlias = Constants.PropertyEditors.Aliases.MultiUrlPicker; - Database.Update(dataType); - } - - Sql sqlPropertyTpes = Sql() - .Select() - .From() - .Where(x => dataTypeIds.Contains(x.DataTypeId)); - - var propertyTypeIds = Database.Fetch(sqlPropertyTpes).Select(x => x.Id).ToList(); - - if (propertyTypeIds.Count == 0) - { - return; - } - - Sql sqlPropertyData = Sql() - .Select() - .From() - .Where(x => propertyTypeIds.Contains(x.PropertyTypeId)); - - List? properties = Database.Fetch(sqlPropertyData); - - // Create a Multi URL Picker datatype for the converted RelatedLinks data - foreach (PropertyDataDto? property in properties) - { - var value = property.Value?.ToString(); - if (string.IsNullOrWhiteSpace(value)) - { - continue; - } - - List? relatedLinks = JsonConvert.DeserializeObject>(value); - var links = new List(); - if (relatedLinks is null) - { - return; - } - - foreach (RelatedLink relatedLink in relatedLinks) - { - GuidUdi? udi = null; - if (relatedLink.IsInternal) - { - var linkIsUdi = UdiParser.TryParse(relatedLink.Link, out udi); - if (linkIsUdi == false) - { - // oh no.. probably an integer, yikes! - if (int.TryParse(relatedLink.Link, NumberStyles.Integer, CultureInfo.InvariantCulture, - out var intId)) - { - Sql sqlNodeData = Sql() - .Select() - .From() - .Where(x => x.NodeId == intId); - - NodeDto? node = Database.Fetch(sqlNodeData).FirstOrDefault(); - if (node != null) - - // Note: RelatedLinks did not allow for picking media items, - // so if there's a value this will be a content item - hence - // the hardcoded "document" here - { - udi = new GuidUdi("document", node.UniqueId); - } - } - } - } - - var link = new LinkDto - { - Name = relatedLink.Caption, - Target = relatedLink.NewWindow ? "_blank" : null, - Udi = udi, - - // Should only have a URL if it's an external link otherwise it wil be a UDI - Url = relatedLink.IsInternal == false ? relatedLink.Link : null, - }; - - links.Add(link); - } - - var json = JsonConvert.SerializeObject(links); - - // Update existing data - property.TextValue = json; - Database.Update(property); - } } } -internal class RelatedLink -{ - public int? Id { get; internal set; } - - [JsonProperty("caption")] - public string? Caption { get; set; } - - internal bool IsDeleted { get; set; } - - [JsonProperty("link")] - public string? Link { get; set; } - - [JsonProperty("newWindow")] - public bool NewWindow { get; set; } - - [JsonProperty("isInternal")] - public bool IsInternal { get; set; } -} - -[DataContract] -internal class LinkDto -{ - [DataMember(Name = "name")] - public string? Name { get; set; } - - [DataMember(Name = "target")] - public string? Target { get; set; } - - [DataMember(Name = "udi")] - public GuidUdi? Udi { get; set; } - - [DataMember(Name = "url")] - public string? Url { get; set; } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs index d97b7ebcb5..e52d5b5844 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypeMigration.cs @@ -10,6 +10,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DataTypeMigration : MigrationBase { private static readonly ISet _legacyAliases = new HashSet @@ -45,100 +46,6 @@ public class DataTypeMigration : MigrationBase protected override void Migrate() { - // drop and create columns - Delete.Column("pk").FromTable("cmsDataType").Do(); - // rename the table - Rename.Table("cmsDataType").To(Constants.DatabaseSchema.Tables.DataType).Do(); - - // create column - AddColumn(Constants.DatabaseSchema.Tables.DataType, "config"); - Execute.Sql(Sql().Update(u => u.Set(x => x.Configuration, string.Empty))).Do(); - - // renames - Execute.Sql(Sql() - .Update(u => u.Set(x => x.EditorAlias, "Umbraco.ColorPicker")) - .Where(x => x.EditorAlias == "Umbraco.ColorPickerAlias")).Do(); - - // from preValues to configuration... - Sql sql = Sql() - .Select() - .AndSelect(x => x.Id, x => x.Alias, x => x.SortOrder, x => x.Value) - .From() - .InnerJoin().On((left, right) => left.NodeId == right.NodeId) - .OrderBy(x => x.NodeId) - .AndBy(x => x.SortOrder); - - IEnumerable> dtos = Database.Fetch(sql).GroupBy(x => x.NodeId); - - foreach (IGrouping group in dtos) - { - DataTypeDto? dataType = Database.Fetch(Sql() - .Select() - .From() - .Where(x => x.NodeId == group.Key)).First(); - - // check for duplicate aliases - var aliases = group.Select(x => x.Alias).Where(x => !string.IsNullOrWhiteSpace(x)).ToArray(); - if (aliases.Distinct().Count() != aliases.Length) - { - throw new InvalidOperationException( - $"Cannot migrate prevalues for datatype id={dataType.NodeId}, editor={dataType.EditorAlias}: duplicate alias."); - } - - // handle null/empty aliases - var index = 0; - var dictionary = group.ToDictionary(x => string.IsNullOrWhiteSpace(x.Alias) ? index++.ToString() : x.Alias); - - // migrate the preValues to configuration - IPreValueMigrator migrator = - _preValueMigrators.GetMigrator(dataType.EditorAlias) ?? new DefaultPreValueMigrator(); - var config = migrator.GetConfiguration(dataType.NodeId, dataType.EditorAlias, dictionary); - var json = _configurationEditorJsonSerializer.Serialize(config); - - // validate - and kill the migration if it fails - var newAlias = migrator.GetNewAlias(dataType.EditorAlias); - if (newAlias == null) - { - if (!_legacyAliases.Contains(dataType.EditorAlias)) - { - _logger.LogWarning( - "Skipping validation of configuration for data type {NodeId} : {EditorAlias}." - + " Please ensure that the configuration is valid. The site may fail to start and / or load data types and run.", - dataType.NodeId, dataType.EditorAlias); - } - } - else if (!_propertyEditors.TryGet(newAlias, out IDataEditor? propertyEditor)) - { - if (!_legacyAliases.Contains(newAlias)) - { - _logger.LogWarning( - "Skipping validation of configuration for data type {NodeId} : {NewEditorAlias} (was: {EditorAlias})" - + " because no property editor with that alias was found." - + " Please ensure that the configuration is valid. The site may fail to start and / or load data types and run.", - dataType.NodeId, newAlias, dataType.EditorAlias); - } - } - else - { - IConfigurationEditor configEditor = propertyEditor.GetConfigurationEditor(); - try - { - var _ = configEditor.FromDatabase(json, _configurationEditorJsonSerializer); - } - catch (Exception e) - { - _logger.LogWarning( - e, - "Failed to validate configuration for data type {NodeId} : {NewEditorAlias} (was: {EditorAlias})." - + " Please fix the configuration and ensure it is valid. The site may fail to start and / or load data types and run.", - dataType.NodeId, newAlias, dataType.EditorAlias); - } - } - - // update - dataType.Configuration = _configurationEditorJsonSerializer.Serialize(config); - Database.Update(dataType); - } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ContentPickerPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ContentPickerPreValueMigrator.cs deleted file mode 100644 index 13c9645ffe..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ContentPickerPreValueMigrator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Umbraco.Cms.Core; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class ContentPickerPreValueMigrator : DefaultPreValueMigrator -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == Constants.PropertyEditors.Legacy.Aliases.ContentPicker2; - - public override string? GetNewAlias(string editorAlias) - => null; - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "showOpenButton" || - preValue.Alias == "ignoreUserStartNodes") - { - return preValue.Value == "1"; - } - - return base.GetPreValueValue(preValue); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DecimalPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DecimalPreValueMigrator.cs deleted file mode 100644 index eb7744cc18..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DecimalPreValueMigrator.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class DecimalPreValueMigrator : DefaultPreValueMigrator -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.Decimal"; - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "min" || - preValue.Alias == "step" || - preValue.Alias == "max") - { - return decimal.TryParse(preValue.Value, out var d) ? (decimal?)d : null; - } - - return preValue.Value?.DetectIsJson() ?? false ? JsonConvert.DeserializeObject(preValue.Value) : preValue.Value; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs deleted file mode 100644 index 2faaca6086..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DefaultPreValueMigrator.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Newtonsoft.Json; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class DefaultPreValueMigrator : IPreValueMigrator -{ - public virtual bool CanMigrate(string editorAlias) - => true; - - public virtual string? GetNewAlias(string editorAlias) - => editorAlias; - - public object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - var preValuesA = preValues.Values.ToList(); - var aliases = preValuesA.Select(x => x.Alias).Distinct().ToArray(); - if (aliases.Length == 1 && string.IsNullOrWhiteSpace(aliases[0])) - { - // array-based prevalues - return new Dictionary - { - ["values"] = preValuesA.OrderBy(x => x.SortOrder).Select(x => x.Value).ToArray(), - }; - } - - // assuming we don't want to fall back to array - if (aliases.Any(string.IsNullOrWhiteSpace)) - { - throw new InvalidOperationException( - $"Cannot migrate prevalues for datatype id={dataTypeId}, editor={editorAlias}: null/empty alias."); - } - - // dictionary-base prevalues - return GetPreValues(preValuesA).ToDictionary(x => x.Alias, GetPreValueValue); - } - - protected virtual IEnumerable GetPreValues(IEnumerable preValues) - => preValues; - - protected virtual object? GetPreValueValue(PreValueDto preValue) => preValue.Value?.DetectIsJson() ?? false - ? JsonConvert.DeserializeObject(preValue.Value) - : preValue.Value; -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DropDownFlexiblePreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DropDownFlexiblePreValueMigrator.cs deleted file mode 100644 index 6588676283..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/DropDownFlexiblePreValueMigrator.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Umbraco.Cms.Core.PropertyEditors; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class DropDownFlexiblePreValueMigrator : IPreValueMigrator -{ - public bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.DropDown.Flexible"; - - public virtual string? GetNewAlias(string editorAlias) - => null; - - public object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - var config = new DropDownFlexibleConfiguration(); - foreach (PreValueDto preValue in preValues.Values) - { - if (preValue.Alias == "multiple") - { - config.Multiple = preValue.Value == "1"; - } - else - { - config.Items.Add(new ValueListConfiguration.ValueListItem { Id = preValue.Id, Value = preValue.Value }); - } - } - - return config; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/IPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/IPreValueMigrator.cs index 11a126a60b..1f287b6597 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/IPreValueMigrator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/IPreValueMigrator.cs @@ -3,6 +3,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; /// /// Defines a service migrating preValues. /// +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public interface IPreValueMigrator { /// diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ListViewPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ListViewPreValueMigrator.cs deleted file mode 100644 index 7879e9c67d..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ListViewPreValueMigrator.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Globalization; -using Newtonsoft.Json; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class ListViewPreValueMigrator : DefaultPreValueMigrator -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.ListView"; - - protected override IEnumerable GetPreValues(IEnumerable preValues) => - preValues.Where(preValue => preValue.Alias != "displayAtTabNumber"); - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "pageSize") - { - return int.TryParse(preValue.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var i) - ? (int?)i - : null; - } - - return preValue.Value?.DetectIsJson() ?? false ? JsonConvert.DeserializeObject(preValue.Value) : preValue.Value; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MarkdownEditorPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MarkdownEditorPreValueMigrator.cs deleted file mode 100644 index eff4b82477..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MarkdownEditorPreValueMigrator.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Umbraco.Cms.Core; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class MarkdownEditorPreValueMigrator : DefaultPreValueMigrator // PreValueMigratorBase -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == Constants.PropertyEditors.Aliases.MarkdownEditor; - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "preview") - { - return preValue.Value == "1"; - } - - return base.GetPreValueValue(preValue); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MediaPickerPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MediaPickerPreValueMigrator.cs deleted file mode 100644 index c630693073..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/MediaPickerPreValueMigrator.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Umbraco.Cms.Core; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class MediaPickerPreValueMigrator : DefaultPreValueMigrator // PreValueMigratorBase -{ - private readonly string[] _editors = - { - Constants.PropertyEditors.Legacy.Aliases.MediaPicker2, Constants.PropertyEditors.Aliases.MediaPicker, - }; - - public override bool CanMigrate(string editorAlias) - => _editors.Contains(editorAlias); - - public override string GetNewAlias(string editorAlias) - => Constants.PropertyEditors.Aliases.MediaPicker; - - // you wish - but MediaPickerConfiguration lives in Umbraco.Web - /* - public override object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - return new MediaPickerConfiguration { ... }; - } - */ - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "multiPicker" || - preValue.Alias == "onlyImages" || - preValue.Alias == "disableFolderSelect") - { - return preValue.Value == "1"; - } - - return base.GetPreValueValue(preValue); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/NestedContentPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/NestedContentPreValueMigrator.cs deleted file mode 100644 index 72c28dc8ad..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/NestedContentPreValueMigrator.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Globalization; -using Newtonsoft.Json; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class NestedContentPreValueMigrator : DefaultPreValueMigrator // PreValueMigratorBase -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.NestedContent"; - - // you wish - but NestedContentConfiguration lives in Umbraco.Web - /* - public override object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - return new NestedContentConfiguration { ... }; - } - */ - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "confirmDeletes" || - preValue.Alias == "showIcons" || - preValue.Alias == "hideLabel") - { - return preValue.Value == "1"; - } - - if (preValue.Alias == "minItems" || - preValue.Alias == "maxItems") - { - return int.TryParse(preValue.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var i) - ? (int?)i - : null; - } - - return preValue.Value?.DetectIsJson() ?? false ? JsonConvert.DeserializeObject(preValue.Value) : preValue.Value; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueDto.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueDto.cs index d3e639452e..3509ea9f19 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueDto.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueDto.cs @@ -2,6 +2,7 @@ using NPoco; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] [TableName("cmsDataTypePreValues")] [ExplicitColumns] public class PreValueDto diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorBase.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorBase.cs index df9cb27ec3..e658640054 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorBase.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorBase.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public abstract class PreValueMigratorBase : IPreValueMigrator { public abstract bool CanMigrate(string editorAlias); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs index 81a6200991..422bbc1e8c 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class PreValueMigratorCollection : BuilderCollectionBase { private readonly ILogger _logger; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollectionBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollectionBuilder.cs index ba335eef4b..b16d85af6a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollectionBuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollectionBuilder.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class PreValueMigratorCollectionBuilder : OrderedCollectionBuilderBase { diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RenamingPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RenamingPreValueMigrator.cs deleted file mode 100644 index 273c8ae51b..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RenamingPreValueMigrator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Exceptions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class RenamingPreValueMigrator : DefaultPreValueMigrator -{ - private readonly string[] _editors = { "Umbraco.NoEdit" }; - - public override bool CanMigrate(string editorAlias) - => _editors.Contains(editorAlias); - - public override string GetNewAlias(string editorAlias) - { - switch (editorAlias) - { - case "Umbraco.NoEdit": - return Constants.PropertyEditors.Aliases.Label; - default: - throw new PanicException($"The alias {editorAlias} is not supported"); - } - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RichTextPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RichTextPreValueMigrator.cs deleted file mode 100644 index 4e7c5b79f1..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/RichTextPreValueMigrator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Newtonsoft.Json; -using Umbraco.Cms.Core; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class RichTextPreValueMigrator : DefaultPreValueMigrator -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.TinyMCEv3"; - - public override string GetNewAlias(string editorAlias) - => Constants.PropertyEditors.Aliases.TinyMce; - - protected override object? GetPreValueValue(PreValueDto preValue) - { - if (preValue.Alias == "hideLabel") - { - return preValue.Value == "1"; - } - - return preValue.Value?.DetectIsJson() ?? false ? JsonConvert.DeserializeObject(preValue.Value) : preValue.Value; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/UmbracoSliderPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/UmbracoSliderPreValueMigrator.cs deleted file mode 100644 index 7f8632dd7a..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/UmbracoSliderPreValueMigrator.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Umbraco.Cms.Core.PropertyEditors; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class UmbracoSliderPreValueMigrator : PreValueMigratorBase -{ - public override bool CanMigrate(string editorAlias) - => editorAlias == "Umbraco.Slider"; - - public override object GetConfiguration(int dataTypeId, string editorAlias, - Dictionary preValues) => - new SliderConfiguration - { - EnableRange = GetBoolValue(preValues, "enableRange"), - InitialValue = GetDecimalValue(preValues, "initVal1"), - InitialValue2 = GetDecimalValue(preValues, "initVal2"), - MaximumValue = GetDecimalValue(preValues, "maxVal"), - MinimumValue = GetDecimalValue(preValues, "minVal"), - StepIncrements = GetDecimalValue(preValues, "step"), - }; -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ValueListPreValueMigrator.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ValueListPreValueMigrator.cs deleted file mode 100644 index 9528cadc8b..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/ValueListPreValueMigrator.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Umbraco.Cms.Core.PropertyEditors; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes; - -internal class ValueListPreValueMigrator : IPreValueMigrator -{ - private readonly string[] _editors = - { - "Umbraco.RadioButtonList", "Umbraco.CheckBoxList", "Umbraco.DropDown", "Umbraco.DropdownlistPublishingKeys", - "Umbraco.DropDownMultiple", "Umbraco.DropdownlistMultiplePublishKeys", - }; - - public bool CanMigrate(string editorAlias) - => _editors.Contains(editorAlias); - - public virtual string? GetNewAlias(string editorAlias) - => null; - - public object GetConfiguration(int dataTypeId, string editorAlias, Dictionary preValues) - { - var config = new ValueListConfiguration(); - foreach (PreValueDto preValue in preValues.Values) - { - config.Items.Add(new ValueListConfiguration.ValueListItem { Id = preValue.Id, Value = preValue.Value }); - } - - return config; - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs index e80dd72765..e2303cf2a2 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropDownPropertyEditorsMigration.cs @@ -13,6 +13,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropDownPropertyEditorsMigration : PropertyEditorsMigrationBase { private readonly IConfigurationEditorJsonSerializer _configurationEditorJsonSerializer; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropMigrationsTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropMigrationsTable.cs index 8eebd91772..dc225678f3 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropMigrationsTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropMigrationsTable.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropMigrationsTable : MigrationBase { public DropMigrationsTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs index 64152d0cb3..ff4673b848 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropPreValueTable.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropPreValueTable : MigrationBase { public DropPreValueTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs index e38c0c3292..23dc153b7a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTaskTables.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropTaskTables : MigrationBase { public DropTaskTables(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTemplateDesignColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTemplateDesignColumn.cs index 454644b1fb..745efdcf06 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTemplateDesignColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropTemplateDesignColumn.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropTemplateDesignColumn : MigrationBase { public DropTemplateDesignColumn(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropXmlTables.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropXmlTables.cs index 1cdb73d410..6dbd91374f 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropXmlTables.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DropXmlTables.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DropXmlTables : MigrationBase { public DropXmlTables(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs index 4f3b685ef5..ee0d5157ae 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FallbackLanguage.cs @@ -5,6 +5,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] /// /// Adds a new, self-joined field to umbracoLanguages to hold the fall-back language for /// a given language. diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FixLanguageIsoCodeLength.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FixLanguageIsoCodeLength.cs index f1bd804c59..606348d179 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FixLanguageIsoCodeLength.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/FixLanguageIsoCodeLength.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class FixLanguageIsoCodeLength : MigrationBase { public FixLanguageIsoCodeLength(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/LanguageColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/LanguageColumns.cs index a265195bc9..e883a92e00 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/LanguageColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/LanguageColumns.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class LanguageColumns : MigrationBase { public LanguageColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs index 64c8d4c8b4..2e039543f3 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeRedirectUrlVariant.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MakeRedirectUrlVariant : MigrationBase { public MakeRedirectUrlVariant(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeTagsVariant.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeTagsVariant.cs index 630a853aa2..986ac4d3ce 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeTagsVariant.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MakeTagsVariant.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MakeTagsVariant : MigrationBase { public MakeTagsVariant(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs index f89a6d1497..dfc713f912 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs @@ -11,6 +11,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MergeDateAndDateTimePropertyEditor : MigrationBase { private readonly IConfigurationEditorJsonSerializer _configurationEditorJsonSerializer; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs deleted file mode 100644 index 856c81af52..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/ContentTypeDto80.cs +++ /dev/null @@ -1,63 +0,0 @@ -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; - -namespace Umbraco.Cms.Infrastructure.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.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs deleted file mode 100644 index 4c537472db..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyDataDto80.cs +++ /dev/null @@ -1,142 +0,0 @@ -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.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) => - 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) => Id == other.Id; - - public override bool Equals(object? other) => - !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 - Id; -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs deleted file mode 100644 index a5a7f48d6d..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/Models/PropertyTypeDto80.cs +++ /dev/null @@ -1,76 +0,0 @@ -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; - -namespace Umbraco.Cms.Infrastructure.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; } = null!; - - [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.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs index cef6eb974f..3c0f0ee560 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigration.cs @@ -4,6 +4,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class PropertyEditorsMigration : MigrationBase { public PropertyEditorsMigration(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigrationBase.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigrationBase.cs index febf872e34..f8f730c390 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigrationBase.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/PropertyEditorsMigrationBase.cs @@ -12,6 +12,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public abstract class PropertyEditorsMigrationBase : MigrationBase { protected PropertyEditorsMigrationBase(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RadioAndCheckboxPropertyEditorsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RadioAndCheckboxPropertyEditorsMigration.cs index ab9b01a3b2..d3359d09fb 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RadioAndCheckboxPropertyEditorsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RadioAndCheckboxPropertyEditorsMigration.cs @@ -13,6 +13,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RadioAndCheckboxPropertyEditorsMigration : PropertyEditorsMigrationBase { private readonly IConfigurationEditorJsonSerializer _configurationEditorJsonSerializer; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs index f8d731e166..00acccb0bf 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RefactorMacroColumns : MigrationBase { public RefactorMacroColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs index 500db8a4bc..2f0592bd40 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RefactorVariantsModel.cs @@ -6,6 +6,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RefactorVariantsModel : MigrationBase { public RefactorVariantsModel(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameLabelAndRichTextPropertyEditorAliases.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameLabelAndRichTextPropertyEditorAliases.cs index a638f17dc4..eda61b80c5 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameLabelAndRichTextPropertyEditorAliases.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameLabelAndRichTextPropertyEditorAliases.cs @@ -4,6 +4,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RenameLabelAndRichTextPropertyEditorAliases : MigrationBase { public RenameLabelAndRichTextPropertyEditorAliases(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs index a6fe5c895b..2948567190 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameMediaVersionTable.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RenameMediaVersionTable : MigrationBase { public RenameMediaVersionTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameUmbracoDomainsTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameUmbracoDomainsTable.cs index 8611128458..cd65470de7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameUmbracoDomainsTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/RenameUmbracoDomainsTable.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RenameUmbracoDomainsTable : MigrationBase { public RenameUmbracoDomainsTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/SuperZero.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/SuperZero.cs index 135e562fde..7aa92aa585 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/SuperZero.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/SuperZero.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class SuperZero : MigrationBase { public SuperZero(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TablesForScheduledPublishing.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TablesForScheduledPublishing.cs index 013375352e..1db65da66d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TablesForScheduledPublishing.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TablesForScheduledPublishing.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class TablesForScheduledPublishing : MigrationBase { public TablesForScheduledPublishing(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigration.cs index 2f2ac746ab..3ac5609533 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigration.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class TagsMigration : MigrationBase { public TagsMigration(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigrationFix.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigrationFix.cs index eaa745a780..d0c496df01 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigrationFix.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/TagsMigrationFix.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class TagsMigrationFix : MigrationBase { public TagsMigrationFix(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs index 557f658691..a077ffa3d7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdateDefaultMandatoryLanguage.cs @@ -6,6 +6,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdateDefaultMandatoryLanguage : MigrationBase { public UpdateDefaultMandatoryLanguage(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdatePickerIntegerValuesToUdi.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdatePickerIntegerValuesToUdi.cs index 18d55aa1e6..2af762cda0 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdatePickerIntegerValuesToUdi.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UpdatePickerIntegerValuesToUdi.cs @@ -9,6 +9,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdatePickerIntegerValuesToUdi : MigrationBase { public UpdatePickerIntegerValuesToUdi(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UserForeignKeys.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UserForeignKeys.cs index fa19ac284a..06feca0411 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UserForeignKeys.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/UserForeignKeys.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] /// /// Creates/Updates non mandatory FK columns to the user table /// diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index db41f70711..75de36ac2d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class VariantsMigration : MigrationBase { public VariantsMigration(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_1/ChangeNuCacheJsonFormat.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_1/ChangeNuCacheJsonFormat.cs index 60e38eca29..83d97d9331 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_1/ChangeNuCacheJsonFormat.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_1/ChangeNuCacheJsonFormat.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Migrations.PostMigrations; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_1; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ChangeNuCacheJsonFormat : MigrationBase { public ChangeNuCacheJsonFormat(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_10_0/AddPropertyTypeLabelOnTopColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_10_0/AddPropertyTypeLabelOnTopColumn.cs index 4e57716c4e..fd7b5b5973 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_10_0/AddPropertyTypeLabelOnTopColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_10_0/AddPropertyTypeLabelOnTopColumn.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_10_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPropertyTypeLabelOnTopColumn : MigrationBase { public AddPropertyTypeLabelOnTopColumn(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs index 4a9a494b76..278d5990da 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddCmsContentNuByteColumn : MigrationBase { private const string TempTableName = Constants.DatabaseSchema.TableNamePrefix + "cms" + "ContentNuTEMP"; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs index 703cfc1474..d6a6a9d6e0 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdateCmsPropertyGroupIdSeed : MigrationBase { public UpdateCmsPropertyGroupIdSeed(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpgradedIncludeIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpgradedIncludeIndexes.cs index 114bb7becc..d97c8d9708 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpgradedIncludeIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_15_0/UpgradedIncludeIndexes.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_15_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpgradedIncludeIndexes : MigrationBase { public UpgradedIncludeIndexes(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_17_0/AddPropertyTypeGroupColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_17_0/AddPropertyTypeGroupColumns.cs index cef69d6bd3..41289c6e4e 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_17_0/AddPropertyTypeGroupColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_17_0/AddPropertyTypeGroupColumns.cs @@ -5,6 +5,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_17_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPropertyTypeGroupColumns : MigrationBase { private readonly IShortStringHelper _shortStringHelper; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs index 0f7fe97663..790b5b7010 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/ConvertTinyMceAndGridMediaUrlsToLocalLink.cs @@ -1,18 +1,8 @@ -using System.Text.RegularExpressions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Infrastructure.Migrations.PostMigrations; -using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.Models; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ConvertTinyMceAndGridMediaUrlsToLocalLink : MigrationBase { private readonly IMediaService _mediaService; @@ -22,111 +12,6 @@ public class ConvertTinyMceAndGridMediaUrlsToLocalLink : MigrationBase protected override void Migrate() { - var mediaLinkPattern = new Regex( - @"(]*href="")(\/ media[^""\?]*)([^>]*>)", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - Sql 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) - .Where(x => - x.EditorAlias == Constants.PropertyEditors.Aliases.TinyMce || - x.EditorAlias == Constants.PropertyEditors.Aliases.Grid); - - List? properties = Database.Fetch(sqlPropertyData); - - var exceptions = new List(); - foreach (PropertyDataDto80? property in properties) - { - var value = property.TextValue; - if (string.IsNullOrWhiteSpace(value)) - { - continue; - } - - var propertyChanged = false; - if (property.PropertyTypeDto?.DataTypeDto?.EditorAlias == Constants.PropertyEditors.Aliases.Grid) - { - try - { - JObject? obj = JsonConvert.DeserializeObject(value); - IEnumerable? allControls = obj?.SelectTokens("$.sections..rows..areas..controls"); - - if (allControls is not null) - { - foreach (JObject control in allControls.SelectMany(c => c).OfType()) - { - JToken? controlValue = control["value"]; - if (controlValue?.Type == JTokenType.String) - { - control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value()!, - out var controlChanged); - propertyChanged |= controlChanged; - } - } - } - - property.TextValue = JsonConvert.SerializeObject(obj); - } - catch (JsonException e) - { - exceptions.Add(new InvalidOperationException( - "Cannot deserialize the value as json. This can be because the property editor " + - "type is changed from another type into a grid. Old versions of the value in this " + - "property can have the structure from the old property editor type. This needs to be " + - "changed manually before updating the database.\n" + - $"Property info: Id = {property.Id}, LanguageId = {property.LanguageId}, VersionId = {property.VersionId}, Value = {property.Value}", - e)); - continue; - } - } - else - { - property.TextValue = UpdateMediaUrls(mediaLinkPattern, value, out propertyChanged); - } - - if (propertyChanged) - { - Database.Update(property); - } - } - - if (exceptions.Any()) - { - throw new AggregateException( - "One or more errors related to unexpected data in grid values occurred.", - exceptions); - } - - Context.AddPostMigration(); - } - - private string UpdateMediaUrls(Regex mediaLinkPattern, string value, out bool changed) - { - var 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) - // - 3 = anything after group 2 until the a tag is closed - var href = match.Groups[2].Value; - - IMedia? media = _mediaService.GetMediaByPath(href); - return media == null - ? match.Value - : $"{match.Groups[1].Value}/{{localLink:{media.GetUdi()}}}{match.Groups[3].Value}"; - }); - - changed = matched; - - return result; } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/FixContentNuCascade.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/FixContentNuCascade.cs index abb4fbfea8..cd9fdc1303 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/FixContentNuCascade.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/FixContentNuCascade.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class FixContentNuCascade : MigrationBase { public FixContentNuCascade(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/RenameUserLoginDtoDateIndex.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/RenameUserLoginDtoDateIndex.cs index ac2e27b2d6..28e91c1a97 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/RenameUserLoginDtoDateIndex.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_1_0/RenameUserLoginDtoDateIndex.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_1_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class RenameUserLoginDtoDateIndex : MigrationBase { public RenameUserLoginDtoDateIndex(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs index bd76857ab7..1a5633213e 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddMainDomLock.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddMainDomLock : MigrationBase { public AddMainDomLock(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddNewRelationTypes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddNewRelationTypes.cs index c2a447e778..406c02b75a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddNewRelationTypes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddNewRelationTypes.cs @@ -6,6 +6,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; /// /// Ensures the new relation types are created /// +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddNewRelationTypes : MigrationBase { public AddNewRelationTypes(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs index a5aea97fc2..78807672af 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/AddPropertyTypeValidationMessageColumns.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPropertyTypeValidationMessageColumns : MigrationBase { public AddPropertyTypeValidationMessageColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs index 7e7b659401..39b02bd184 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/MissingContentVersionsIndexes.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MissingContentVersionsIndexes : MigrationBase { private const string IndexName = "IX_" + ContentVersionDto.TableName + "_NodeId"; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/UpdateRelationTypeTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/UpdateRelationTypeTable.cs index 032359cfcc..a23346a55c 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/UpdateRelationTypeTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_6_0/UpdateRelationTypeTable.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_6_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdateRelationTypeTable : MigrationBase { public UpdateRelationTypeTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs index 1ab43c2eb7..48274438c5 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_7_0/MissingDictionaryIndex.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_7_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MissingDictionaryIndex : MigrationBase { public MissingDictionaryIndex(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_9_0/ExternalLoginTableUserData.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_9_0/ExternalLoginTableUserData.cs index bf21b6b928..5951b5e9b7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_9_0/ExternalLoginTableUserData.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_9_0/ExternalLoginTableUserData.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_9_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ExternalLoginTableUserData : MigrationBase { public ExternalLoginTableUserData(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/AddPasswordConfigToMemberTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/AddPasswordConfigToMemberTable.cs index 2bf01c55bb..496b1c533f 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/AddPasswordConfigToMemberTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/AddPasswordConfigToMemberTable.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddPasswordConfigToMemberTable : MigrationBase { public AddPasswordConfigToMemberTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs index 3556213cd1..08016d9178 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs @@ -8,6 +8,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class DictionaryTablesIndexes : MigrationBase { private const string IndexedDictionaryColumn = "key"; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs index 8caa28de03..eb1b7c1b19 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs @@ -1,5 +1,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ExternalLoginTableIndexes : MigrationBase { public ExternalLoginTableIndexes(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexesFixup.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexesFixup.cs index 8c508a3d04..15175a4cc5 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexesFixup.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexesFixup.cs @@ -4,6 +4,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; /// Fixes up the original for post RC release to ensure that /// the correct indexes are applied. /// +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ExternalLoginTableIndexesFixup : MigrationBase { public ExternalLoginTableIndexesFixup(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTokenTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTokenTable.cs index 288dbdf23f..7e856e3a44 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTokenTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTokenTable.cs @@ -7,6 +7,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class ExternalLoginTokenTable : MigrationBase { public ExternalLoginTokenTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MemberTableColumns.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MemberTableColumns.cs index 50204e4432..32dae76da7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MemberTableColumns.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MemberTableColumns.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MemberTableColumns : MigrationBase { public MemberTableColumns(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs index 641433cee9..b9e6123c15 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/MigrateLogViewerQueriesFromFileToDb.cs @@ -7,6 +7,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MigrateLogViewerQueriesFromFileToDb : MigrationBase { internal static readonly IEnumerable _defaultLogQueries = new LogViewerQueryDto[] diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/UmbracoServerColumn.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/UmbracoServerColumn.cs index c844606ab2..6ae6c0bb12 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/UmbracoServerColumn.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/UmbracoServerColumn.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UmbracoServerColumn : MigrationBase { public UmbracoServerColumn(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs deleted file mode 100644 index 7a885eca07..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_1_0/AddContentVersionCleanupFeature.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_1_0; - -internal class AddContentVersionCleanupFeature : MigrationBase -{ - public AddContentVersionCleanupFeature(IMigrationContext context) - : base(context) - { - } - - /// - /// The conditionals are useful to enable the same migration to be used in multiple - /// migration paths x.x -> 8.18 and x.x -> 9.x - /// - protected override void Migrate() - { - IEnumerable tables = SqlSyntax.GetTablesInSchema(Context.Database); - if (!tables.InvariantContains(ContentVersionCleanupPolicyDto.TableName)) - { - Create.Table().Do(); - } - - IEnumerable columns = SqlSyntax.GetColumnsInSchema(Context.Database); - AddColumnIfNotExists(columns, "preventCleanup"); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddDefaultForNotificationsToggle.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddDefaultForNotificationsToggle.cs index 9b91c0a372..2641a8304a 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddDefaultForNotificationsToggle.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddDefaultForNotificationsToggle.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Infrastructure.Persistence; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_2_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddDefaultForNotificationsToggle : MigrationBase { public AddDefaultForNotificationsToggle(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs deleted file mode 100644 index 41abd07b23..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_2_0/AddUserGroup2NodeTable.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_2_0; - -internal class AddUserGroup2NodeTable : MigrationBase -{ - public AddUserGroup2NodeTable(IMigrationContext context) - : base(context) - { - } - - protected override void Migrate() - { - IEnumerable tables = SqlSyntax.GetTablesInSchema(Context.Database); - if (!tables.InvariantContains(UserGroup2NodeDto.TableName)) - { - Create.Table().Do(); - } - - // Insert if there exists specific permissions today. Can't do it directly in db in any nice way. - List? allData = Database.Fetch(); - UserGroup2NodeDto[] toInsert = allData - .Select(x => new UserGroup2NodeDto { NodeId = x.NodeId, UserGroupId = x.UserGroupId }).Distinct( - new DelegateEqualityComparer( - (x, y) => x?.NodeId == y?.NodeId && x?.UserGroupId == y?.UserGroupId, - x => x.NodeId.GetHashCode() + x.UserGroupId.GetHashCode())).ToArray(); - Database.InsertBulk(toInsert); - } -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs index 5e781406ae..e1d26aae73 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/AddTwoFactorLoginTable.cs @@ -3,6 +3,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_3_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class AddTwoFactorLoginTable : MigrationBase { public AddTwoFactorLoginTable(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/MovePackageXMLToDb.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/MovePackageXMLToDb.cs index a2b2b40238..4b963e7c1c 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/MovePackageXMLToDb.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/MovePackageXMLToDb.cs @@ -4,6 +4,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_3_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class MovePackageXMLToDb : MigrationBase { private readonly PackagesRepository _packagesRepository; diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/UpdateExternalLoginToUseKeyInsteadOfId.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/UpdateExternalLoginToUseKeyInsteadOfId.cs index c3f42a90ab..991e201e0d 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/UpdateExternalLoginToUseKeyInsteadOfId.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_3_0/UpdateExternalLoginToUseKeyInsteadOfId.cs @@ -2,6 +2,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_3_0; +[Obsolete("This is not used anymore and will be removed in Umbraco 13")] public class UpdateExternalLoginToUseKeyInsteadOfId : MigrationBase { public UpdateExternalLoginToUseKeyInsteadOfId(IMigrationContext context) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/AddScheduledPublishingLock.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/AddScheduledPublishingLock.cs deleted file mode 100644 index 550e67879a..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/AddScheduledPublishingLock.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_4_0; - -internal class AddScheduledPublishingLock : MigrationBase -{ - public AddScheduledPublishingLock(IMigrationContext context) - : base(context) - { - } - - protected override void Migrate() => - Database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.ScheduledPublishing, Name = "ScheduledPublishing" }); -} diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/UpdateRelationTypesToHandleDependencies.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/UpdateRelationTypesToHandleDependencies.cs deleted file mode 100644 index 44144b93fe..0000000000 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_4_0/UpdateRelationTypesToHandleDependencies.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Umbraco.Cms.Core; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_4_0; - -internal class UpdateRelationTypesToHandleDependencies : MigrationBase -{ - public UpdateRelationTypesToHandleDependencies(IMigrationContext context) - : base(context) - { - } - - protected override void Migrate() - { - var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); - - AddColumnIfNotExists(columns, "isDependency"); - - var aliasesWithDependencies = new[] - { - Constants.Conventions.RelationTypes.RelatedDocumentAlias, - Constants.Conventions.RelationTypes.RelatedMediaAlias, - }; - - Database.Execute( - Sql() - .Update(u => u.Set(x => x.IsDependency, true)) - .WhereIn(x => x.Alias, aliasesWithDependencies)); - } -} diff --git a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs index e0140b65b3..7119dd50ff 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs @@ -29,7 +29,8 @@ namespace Umbraco.Cms.Core.PropertyEditors ValueType = ValueTypes.Json, Icon = "icon-layout", Group = Constants.PropertyEditors.Groups.RichContent, - ValueEditorIsReusable = false)] + ValueEditorIsReusable = false, + IsDeprecated = true)] public class GridPropertyEditor : DataEditor { private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs index 230c6e2b59..cf85339d48 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs @@ -21,12 +21,13 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// [DataEditor( Constants.PropertyEditors.Aliases.NestedContent, - "Nested Content", + "Nested Content (legacy)", "nestedcontent", ValueType = ValueTypes.Json, Group = Constants.PropertyEditors.Groups.Lists, Icon = "icon-thumbnail-list", - ValueEditorIsReusable = false)] + ValueEditorIsReusable = false, + IsDeprecated = true)] public class NestedContentPropertyEditor : DataEditor { public const string ContentTypeAliasPropertyKey = "ncContentTypeAlias"; diff --git a/src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml b/src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml b/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Web.Common/CompatibilitySuppressions.xml b/src/Umbraco.Web.Common/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Web.Common/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/src/Umbraco.Web.Website/CompatibilitySuppressions.xml b/src/Umbraco.Web.Website/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/src/Umbraco.Web.Website/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml b/tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file diff --git a/tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml b/tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml deleted file mode 100644 index 7baad05aa0..0000000000 --- a/tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - PKV006 - net6.0 - - \ No newline at end of file From c2047d1d6ec8729f454b57f80d0baa46ad14c32b Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Thu, 10 Nov 2022 14:13:10 +0100 Subject: [PATCH 34/39] Remove nullability from AdditionalData on ContentItemDisplay (#13380) @Zeegaan We have a use case where we need to set a value in the AdditionalData dictionary in a NotificationHandler, but as it can be null, it requires reflection to make sure that is not the case. As such, it would be nice if the following change could be implemented to ensure it is never null. --- src/Umbraco.Core/Models/ContentEditing/ContentItemDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/ContentItemDisplay.cs index eb800791a2..d1a8d10970 100644 --- a/src/Umbraco.Core/Models/ContentEditing/ContentItemDisplay.cs +++ b/src/Umbraco.Core/Models/ContentEditing/ContentItemDisplay.cs @@ -201,7 +201,7 @@ public class ContentItemDisplay : /// [DataMember(Name = "metaData")] [ReadOnly(true)] - public IDictionary? AdditionalData { get; private set; } + public IDictionary AdditionalData { get; private set; } = new Dictionary(); /// /// This is used for validation of a content item. From 0356aade30ba5f44aa27ac702add46a4309cebeb Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Mon, 14 Nov 2022 12:44:26 +0100 Subject: [PATCH 35/39] Bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 4043ae827d..47bcc5fc24 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "11.0.0-rc3", + "version": "11.0.0-rc4", "assemblyVersion": { "precision": "build" }, From 3a8024c0c64561c9a3880493ced4eeb83965c379 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 14 Nov 2022 16:01:01 +0100 Subject: [PATCH 36/39] v11: Rich text editor - remove distraction-free mode and optimise inline mode (#13394) * combine 'inline' and 'distraction-free' modes with the quickbars plugin * remove 'distraction-free' and map to 'inline' * remove container overrides for 'distraction-free' --- .../src/common/services/tinymce.service.js | 14 ++++++++------ .../views/propertyeditors/rte/rte.controller.js | 6 +++--- .../rte/rte.prevalues.controller.js | 4 ++++ .../views/propertyeditors/rte/rte.prevalues.html | 5 ++--- 4 files changed, 17 insertions(+), 12 deletions(-) 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 65c4a50e43..8614173af8 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 @@ -347,18 +347,18 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s plugins.push("autoresize"); var modeInline = false; - var toolbar = args.toolbar.join(" "); + var toolbarActions = args.toolbar.join(" "); + var toolbar = toolbarActions; + var quickbar = toolbarActions; // Based on mode set // classic = Theme: modern, inline: false // inline = Theme: modern, inline: true, - // distraction-free = Theme: inlite, inline: true - if (args.mode === "inline") { - modeInline = true; - } - else if (args.mode === "distraction-free") { + // distraction-free = Same as inline - kept for legacy reasons due to older versions of Umbraco having this as a mode + if (args.mode === "inline" || args.mode === 'distraction-free') { modeInline = true; toolbar = false; + plugins.push('quickbars'); } //create a baseline Config to extend upon @@ -379,6 +379,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s //this would be for a theme other than inlite toolbar: toolbar, + quickbars_insert_toolbar: quickbar, + quickbars_selection_toolbar: quickbar, body_class: "umb-rte", diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index ac3c2f9e7e..4973bede7a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -20,9 +20,9 @@ angular.module("umbraco") var width = editorConfig.dimensions ? parseInt(editorConfig.dimensions.width, 10) || null : null; var height = editorConfig.dimensions ? parseInt(editorConfig.dimensions.height, 10) || null : null; - $scope.containerWidth = editorConfig.mode === "distraction-free" ? (width ? width : "auto") : "auto"; - $scope.containerHeight = editorConfig.mode === "distraction-free" ? (height ? height : "auto") : "auto"; - $scope.containerOverflow = editorConfig.mode === "distraction-free" ? (height ? "auto" : "inherit") : "inherit"; + $scope.containerWidth = "auto"; + $scope.containerHeight = "auto"; + $scope.containerOverflow = "inherit"; var promises = []; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js index 47d1f401c7..4e90dd063b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js @@ -22,6 +22,10 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", if (!$scope.model.value.mode) { $scope.model.value.mode = "classic"; } + else if ($scope.model.value.mode === 'distraction-free') { + // Due to legacy reasons, the older 'distraction-free' mode is kept and remapped to 'inline' + $scope.model.value.mode = 'inline'; + } tinyMceService.configuration().then(function(config){ $scope.tinyMceConfig = config; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html index 0926efd6a4..eac505040c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html @@ -40,9 +40,8 @@
From 7af8a407d315013df7235bfa2b987882d02d417b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 15 Nov 2022 15:43:12 +0100 Subject: [PATCH 37/39] Management API operation IDs (#13374) * add a custom operationId generator to swagger operations based on the name of the action * mark 'total' and 'items' as required in all paged models seeing as they should be there always non-nullable * fix action typo collect -> rebuild to avoid duplicates * fix operations for server status + version * add factory to format custom operation ids * Revert "fix operations for server status + version" This reverts commit 066a59e1c282e055f25ccb13437040d7ec9e5c62. * Throw exception if no relative path and remove template placeholders completely * Revert "Throw exception if no relative path and remove template placeholders completely" This reverts commit 40af0721d6cfcd021326d6c2c54fbead7df53e7c. * Move source generated regex statements to its own file (#13410) Co-authored-by: Mole --- .../RebuildPublishedCacheController.cs | 2 +- .../ManagementApiComposer.cs | 28 ++- src/Umbraco.Cms.ManagementApi/OpenApi.json | 233 ++++++++++++++---- .../OpenApi/OperationIdRegexes.cs | 21 ++ .../ViewModels/Pagination/PagedViewModel.cs | 6 +- 5 files changed, 243 insertions(+), 47 deletions(-) create mode 100644 src/Umbraco.Cms.ManagementApi/OpenApi/OperationIdRegexes.cs diff --git a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/RebuildPublishedCacheController.cs b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/RebuildPublishedCacheController.cs index 85a6602a52..ed841344bd 100644 --- a/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/RebuildPublishedCacheController.cs +++ b/src/Umbraco.Cms.ManagementApi/Controllers/PublishedCache/RebuildPublishedCacheController.cs @@ -14,7 +14,7 @@ public class RebuildPublishedCacheController : PublishedCacheControllerBase [HttpPost("rebuild")] [MapToApiVersion("1.0")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task Collect() + public async Task Rebuild() { _publishedSnapshotService.Rebuild(); return await Task.FromResult(Ok()); diff --git a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs index fa1d34a30a..43949c62ae 100644 --- a/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs +++ b/src/Umbraco.Cms.ManagementApi/ManagementApiComposer.cs @@ -59,6 +59,30 @@ public class ManagementApiComposer : IComposer services.AddSwaggerGen(swaggerGenOptions => { + swaggerGenOptions.CustomOperationIds(e => + { + var relativePath = e.RelativePath; + + if (string.IsNullOrWhiteSpace(relativePath)) + { + throw new Exception($"There is no relative path for controller action {e.ActionDescriptor.RouteValues["controller"]}"); + } + + var httpMethod = e.HttpMethod?.ToLower().ToFirstUpper() ?? "Get"; + + // Remove the prefixed base path with version, e.g. /umbraco/management/api/v1/tracked-reference/{id} => tracked-reference/{id} + var unprefixedRelativePath = OperationIdRegexes.VersionPrefixRegex().Replace(relativePath, string.Empty); + + // Remove template placeholders, e.g. tracked-reference/{id} => tracked-reference/Id + var formattedOperationId = OperationIdRegexes.TemplatePlaceholdersRegex().Replace(unprefixedRelativePath, m => $"By{m.Groups[1].Value.ToFirstUpper()}"); + + // Remove dashes (-) and slashes (/) and convert the following letter to uppercase with + // the word "By" in front, e.g. tracked-reference/Id => TrackedReferenceById + formattedOperationId = OperationIdRegexes.ToCamelCaseRegex().Replace(formattedOperationId, m => m.Groups[1].Value.ToUpper()); + + // Return the operation ID with the formatted http method verb in front, e.g. GetTrackedReferenceById + return $"{httpMethod}{formattedOperationId.ToFirstUpper()}"; + }); swaggerGenOptions.SwaggerDoc( ApiDefaultDocumentName, new OpenApiInfo @@ -207,9 +231,9 @@ public class ManagementApiComposer : IComposer endpoints.MapControllers(); // Serve contract - endpoints.MapGet($"{officePath}/management/api/openapi.json",async context => + endpoints.MapGet($"{officePath}/management/api/openapi.json", async context => { - await context.Response.SendFileAsync(new EmbeddedFileProvider(this.GetType().Assembly).GetFileInfo("OpenApi.json")); + await context.Response.SendFileAsync(new EmbeddedFileProvider(GetType().Assembly).GetFileInfo("OpenApi.json")); }); }); } diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi.json b/src/Umbraco.Cms.ManagementApi/OpenApi.json index c0b9f4ef0b..1e5a80b5a1 100644 --- a/src/Umbraco.Cms.ManagementApi/OpenApi.json +++ b/src/Umbraco.Cms.ManagementApi/OpenApi.json @@ -11,6 +11,7 @@ "tags": [ "Culture" ], + "operationId": "GetCulture", "parameters": [ { "name": "skip", @@ -48,6 +49,7 @@ "tags": [ "Data Type" ], + "operationId": "GetTreeDataTypeChildren", "parameters": [ { "name": "parentKey", @@ -103,6 +105,7 @@ "tags": [ "Data Type" ], + "operationId": "GetTreeDataTypeItem", "parameters": [ { "name": "key", @@ -138,6 +141,7 @@ "tags": [ "Data Type" ], + "operationId": "GetTreeDataTypeRoot", "parameters": [ { "name": "skip", @@ -185,6 +189,7 @@ "tags": [ "Dictionary" ], + "operationId": "GetDictionary", "parameters": [ { "name": "skip", @@ -222,6 +227,7 @@ "tags": [ "Dictionary" ], + "operationId": "PatchDictionaryById", "parameters": [ { "name": "id", @@ -274,6 +280,7 @@ "tags": [ "Dictionary" ], + "operationId": "GetDictionaryByKey", "parameters": [ { "name": "key", @@ -312,6 +319,7 @@ "tags": [ "Dictionary" ], + "operationId": "DeleteDictionaryByKey", "parameters": [ { "name": "key", @@ -345,6 +353,7 @@ "tags": [ "Dictionary" ], + "operationId": "PostDictionaryCreate", "requestBody": { "content": { "application/json": { @@ -383,6 +392,7 @@ "tags": [ "Dictionary" ], + "operationId": "GetDictionaryExportByKey", "parameters": [ { "name": "key", @@ -432,6 +442,7 @@ "tags": [ "Dictionary" ], + "operationId": "PostDictionaryImport", "parameters": [ { "name": "file", @@ -478,8 +489,9 @@ "tags": [ "Dictionary" ], + "operationId": "PostDictionaryUpload", "requestBody": { - "content": { } + "content": {} }, "responses": { "200": { @@ -510,6 +522,7 @@ "tags": [ "Dictionary" ], + "operationId": "GetTreeDictionaryChildren", "parameters": [ { "name": "parentKey", @@ -557,6 +570,7 @@ "tags": [ "Dictionary" ], + "operationId": "GetTreeDictionaryItem", "parameters": [ { "name": "key", @@ -592,6 +606,7 @@ "tags": [ "Dictionary" ], + "operationId": "GetTreeDictionaryRoot", "parameters": [ { "name": "skip", @@ -631,6 +646,7 @@ "tags": [ "Document Blueprint" ], + "operationId": "GetTreeDocumentBlueprintItem", "parameters": [ { "name": "key", @@ -666,6 +682,7 @@ "tags": [ "Document Blueprint" ], + "operationId": "GetTreeDocumentBlueprintRoot", "parameters": [ { "name": "skip", @@ -705,6 +722,7 @@ "tags": [ "Document Type" ], + "operationId": "GetTreeDocumentTypeChildren", "parameters": [ { "name": "parentKey", @@ -760,6 +778,7 @@ "tags": [ "Document Type" ], + "operationId": "GetTreeDocumentTypeItem", "parameters": [ { "name": "key", @@ -795,6 +814,7 @@ "tags": [ "Document Type" ], + "operationId": "GetTreeDocumentTypeRoot", "parameters": [ { "name": "skip", @@ -842,6 +862,7 @@ "tags": [ "Document" ], + "operationId": "GetRecycleBinDocumentChildren", "parameters": [ { "name": "parentKey", @@ -899,6 +920,7 @@ "tags": [ "Document" ], + "operationId": "GetRecycleBinDocumentRoot", "parameters": [ { "name": "skip", @@ -948,6 +970,7 @@ "tags": [ "Document" ], + "operationId": "GetTreeDocumentChildren", "parameters": [ { "name": "parentKey", @@ -1010,6 +1033,7 @@ "tags": [ "Document" ], + "operationId": "GetTreeDocumentItem", "parameters": [ { "name": "key", @@ -1060,6 +1084,7 @@ "tags": [ "Document" ], + "operationId": "GetTreeDocumentRoot", "parameters": [ { "name": "skip", @@ -1114,6 +1139,7 @@ "tags": [ "Help" ], + "operationId": "GetHelp", "parameters": [ { "name": "section", @@ -1183,6 +1209,7 @@ "tags": [ "Install" ], + "operationId": "GetInstallSettings", "responses": { "400": { "description": "Bad Request", @@ -1222,6 +1249,7 @@ "tags": [ "Install" ], + "operationId": "PostInstallSetup", "requestBody": { "content": { "application/json": { @@ -1263,6 +1291,7 @@ "tags": [ "Install" ], + "operationId": "PostInstallValidateDatabase", "requestBody": { "content": { "application/json": { @@ -1294,6 +1323,7 @@ "tags": [ "Language" ], + "operationId": "GetLanguage", "parameters": [ { "name": "skip", @@ -1331,6 +1361,7 @@ "tags": [ "Language" ], + "operationId": "GetLanguageById", "parameters": [ { "name": "id", @@ -1369,6 +1400,7 @@ "tags": [ "Language" ], + "operationId": "DeleteLanguageById", "parameters": [ { "name": "id", @@ -1412,6 +1444,7 @@ "tags": [ "Language" ], + "operationId": "PostLanguageCreate", "requestBody": { "content": { "application/json": { @@ -1443,6 +1476,7 @@ "tags": [ "Language" ], + "operationId": "PutLanguageUpdate", "requestBody": { "content": { "application/json": { @@ -1484,6 +1518,7 @@ "tags": [ "Media Type" ], + "operationId": "GetTreeMediaTypeChildren", "parameters": [ { "name": "parentKey", @@ -1539,6 +1574,7 @@ "tags": [ "Media Type" ], + "operationId": "GetTreeMediaTypeItem", "parameters": [ { "name": "key", @@ -1574,6 +1610,7 @@ "tags": [ "Media Type" ], + "operationId": "GetTreeMediaTypeRoot", "parameters": [ { "name": "skip", @@ -1621,6 +1658,7 @@ "tags": [ "Media" ], + "operationId": "GetRecycleBinMediaChildren", "parameters": [ { "name": "parentKey", @@ -1678,6 +1716,7 @@ "tags": [ "Media" ], + "operationId": "GetRecycleBinMediaRoot", "parameters": [ { "name": "skip", @@ -1727,6 +1766,7 @@ "tags": [ "Media" ], + "operationId": "GetTreeMediaChildren", "parameters": [ { "name": "parentKey", @@ -1782,6 +1822,7 @@ "tags": [ "Media" ], + "operationId": "GetTreeMediaItem", "parameters": [ { "name": "key", @@ -1825,6 +1866,7 @@ "tags": [ "Media" ], + "operationId": "GetTreeMediaRoot", "parameters": [ { "name": "skip", @@ -1872,6 +1914,7 @@ "tags": [ "Member Group" ], + "operationId": "GetTreeMemberGroupItem", "parameters": [ { "name": "key", @@ -1907,6 +1950,7 @@ "tags": [ "Member Group" ], + "operationId": "GetTreeMemberGroupRoot", "parameters": [ { "name": "skip", @@ -1946,6 +1990,7 @@ "tags": [ "Member Type" ], + "operationId": "GetTreeMemberTypeItem", "parameters": [ { "name": "key", @@ -1981,6 +2026,7 @@ "tags": [ "Member Type" ], + "operationId": "GetTreeMemberTypeRoot", "parameters": [ { "name": "skip", @@ -2020,6 +2066,7 @@ "tags": [ "Models Builder" ], + "operationId": "PostModelsBuilderBuild", "responses": { "201": { "description": "Created", @@ -2049,6 +2096,7 @@ "tags": [ "Models Builder" ], + "operationId": "GetModelsBuilderDashboard", "responses": { "200": { "description": "Success", @@ -2068,6 +2116,7 @@ "tags": [ "Models Builder" ], + "operationId": "GetModelsBuilderStatus", "responses": { "200": { "description": "Success", @@ -2087,6 +2136,7 @@ "tags": [ "Partial View" ], + "operationId": "GetTreePartialViewChildren", "parameters": [ { "name": "path", @@ -2133,6 +2183,7 @@ "tags": [ "Partial View" ], + "operationId": "GetTreePartialViewItem", "parameters": [ { "name": "path", @@ -2167,6 +2218,7 @@ "tags": [ "Partial View" ], + "operationId": "GetTreePartialViewRoot", "parameters": [ { "name": "skip", @@ -2206,6 +2258,7 @@ "tags": [ "Profiling" ], + "operationId": "GetProfilingStatus", "responses": { "200": { "description": "Success", @@ -2225,6 +2278,7 @@ "tags": [ "Published Cache" ], + "operationId": "PostPublishedCacheCollect", "responses": { "200": { "description": "Success" @@ -2237,6 +2291,7 @@ "tags": [ "Published Cache" ], + "operationId": "PostPublishedCacheRebuild", "responses": { "200": { "description": "Success" @@ -2249,6 +2304,7 @@ "tags": [ "Published Cache" ], + "operationId": "PostPublishedCacheReload", "responses": { "200": { "description": "Success" @@ -2261,6 +2317,7 @@ "tags": [ "Published Cache" ], + "operationId": "GetPublishedCacheStatus", "responses": { "200": { "description": "Success", @@ -2280,6 +2337,7 @@ "tags": [ "Relation Type" ], + "operationId": "GetTreeRelationTypeItem", "parameters": [ { "name": "key", @@ -2315,6 +2373,7 @@ "tags": [ "Relation Type" ], + "operationId": "GetTreeRelationTypeRoot", "parameters": [ { "name": "skip", @@ -2354,6 +2413,7 @@ "tags": [ "Relation" ], + "operationId": "GetRelationById", "parameters": [ { "name": "id", @@ -2394,6 +2454,7 @@ "tags": [ "Relation" ], + "operationId": "GetRelationChildRelationByChildId", "parameters": [ { "name": "childId", @@ -2448,6 +2509,7 @@ "tags": [ "Script" ], + "operationId": "GetTreeScriptChildren", "parameters": [ { "name": "path", @@ -2494,6 +2556,7 @@ "tags": [ "Script" ], + "operationId": "GetTreeScriptItem", "parameters": [ { "name": "path", @@ -2528,6 +2591,7 @@ "tags": [ "Script" ], + "operationId": "GetTreeScriptRoot", "parameters": [ { "name": "skip", @@ -2567,6 +2631,7 @@ "tags": [ "Search" ], + "operationId": "GetSearchIndex", "parameters": [ { "name": "skip", @@ -2604,6 +2669,7 @@ "tags": [ "Search" ], + "operationId": "GetSearchIndexByIndexName", "parameters": [ { "name": "indexName", @@ -2643,6 +2709,7 @@ "tags": [ "Search" ], + "operationId": "PostSearchIndexByIndexNameRebuild", "parameters": [ { "name": "indexName", @@ -2682,6 +2749,7 @@ "tags": [ "Search" ], + "operationId": "GetSearchSearcher", "parameters": [ { "name": "skip", @@ -2719,6 +2787,7 @@ "tags": [ "Search" ], + "operationId": "GetSearchSearcherBySearcherNameSearch", "parameters": [ { "name": "searcherName", @@ -2781,6 +2850,7 @@ "tags": [ "Security" ], + "operationId": "GetSecurityBackOfficeAuthorize", "responses": { "200": { "description": "Success" @@ -2791,6 +2861,7 @@ "tags": [ "Security" ], + "operationId": "PostSecurityBackOfficeAuthorize", "responses": { "200": { "description": "Success" @@ -2803,6 +2874,7 @@ "tags": [ "Server" ], + "operationId": "GetServerStatus", "responses": { "400": { "description": "Bad Request", @@ -2832,6 +2904,7 @@ "tags": [ "Server" ], + "operationId": "GetServerVersion", "responses": { "400": { "description": "Bad Request", @@ -2861,6 +2934,7 @@ "tags": [ "Static File" ], + "operationId": "GetTreeStaticFileChildren", "parameters": [ { "name": "path", @@ -2907,6 +2981,7 @@ "tags": [ "Static File" ], + "operationId": "GetTreeStaticFileItem", "parameters": [ { "name": "path", @@ -2941,6 +3016,7 @@ "tags": [ "Static File" ], + "operationId": "GetTreeStaticFileRoot", "parameters": [ { "name": "skip", @@ -2980,6 +3056,7 @@ "tags": [ "Stylesheet" ], + "operationId": "GetTreeStylesheetChildren", "parameters": [ { "name": "path", @@ -3026,6 +3103,7 @@ "tags": [ "Stylesheet" ], + "operationId": "GetTreeStylesheetItem", "parameters": [ { "name": "path", @@ -3060,6 +3138,7 @@ "tags": [ "Stylesheet" ], + "operationId": "GetTreeStylesheetRoot", "parameters": [ { "name": "skip", @@ -3099,6 +3178,7 @@ "tags": [ "Telemetry" ], + "operationId": "GetTelemetry", "parameters": [ { "name": "skip", @@ -3136,6 +3216,7 @@ "tags": [ "Telemetry" ], + "operationId": "GetTelemetryLevel", "responses": { "200": { "description": "Success", @@ -3153,6 +3234,7 @@ "tags": [ "Telemetry" ], + "operationId": "PostTelemetryLevel", "requestBody": { "content": { "application/json": { @@ -3184,6 +3266,7 @@ "tags": [ "Template" ], + "operationId": "GetTreeTemplateChildren", "parameters": [ { "name": "parentKey", @@ -3231,6 +3314,7 @@ "tags": [ "Template" ], + "operationId": "GetTreeTemplateItem", "parameters": [ { "name": "key", @@ -3266,6 +3350,7 @@ "tags": [ "Template" ], + "operationId": "GetTreeTemplateRoot", "parameters": [ { "name": "skip", @@ -3305,6 +3390,7 @@ "tags": [ "Tracked Reference" ], + "operationId": "GetTrackedReferenceById", "parameters": [ { "name": "id", @@ -3358,6 +3444,7 @@ "tags": [ "Tracked Reference" ], + "operationId": "GetTrackedReferenceDescendantsByParentId", "parameters": [ { "name": "parentId", @@ -3411,6 +3498,7 @@ "tags": [ "Tracked Reference" ], + "operationId": "GetTrackedReferenceItem", "parameters": [ { "name": "ids", @@ -3466,6 +3554,7 @@ "tags": [ "Upgrade" ], + "operationId": "PostUpgradeAuthorize", "responses": { "200": { "description": "Success" @@ -3498,6 +3587,7 @@ "tags": [ "Upgrade" ], + "operationId": "GetUpgradeSettings", "responses": { "200": { "description": "Success", @@ -5425,6 +5515,10 @@ "format": "int32" }, "PagedContentTreeItem": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5435,13 +5529,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/ContentTreeItem" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedCulture": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5452,13 +5549,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/Culture" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedDictionaryOverview": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5469,13 +5569,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/DictionaryOverview" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedDocumentBlueprintTreeItem": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5486,13 +5589,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/DocumentBlueprintTreeItem" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedDocumentTreeItem": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5503,13 +5609,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/DocumentTreeItem" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedDocumentTypeTreeItem": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5520,13 +5629,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/DocumentTypeTreeItem" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedEntityTreeItem": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5537,13 +5649,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/EntityTreeItem" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedFileSystemTreeItem": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5554,13 +5669,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/FileSystemTreeItem" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedFolderTreeItem": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5571,13 +5689,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/FolderTreeItem" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedHelpPage": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5588,13 +5709,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/HelpPage" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedIndex": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5605,13 +5729,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/Index" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedLanguage": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5622,13 +5749,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/Language" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedPaged": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5639,13 +5769,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/PagedSearchResult" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedRecycleBinItem": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5656,13 +5789,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/RecycleBinItem" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedRelation": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5673,13 +5809,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/Relation" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedRelationItem": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5690,13 +5829,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/RelationItem" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedSearchResult": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5707,13 +5849,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/SearchResult" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedSearcher": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5724,13 +5869,16 @@ "type": "array", "items": { "$ref": "#/components/schemas/Searcher" - }, - "nullable": true + } } }, "additionalProperties": false }, "PagedTelemetry": { + "required": [ + "items", + "total" + ], "type": "object", "properties": { "total": { @@ -5741,8 +5889,7 @@ "type": "array", "items": { "$ref": "#/components/schemas/Telemetry" - }, - "nullable": true + } } }, "additionalProperties": false @@ -5859,7 +6006,7 @@ "nullable": true } }, - "additionalProperties": { } + "additionalProperties": {} }, "ProfilingStatus": { "type": "object", @@ -6965,7 +7112,7 @@ "authorizationCode": { "authorizationUrl": "/umbraco/management/api/v1.0/security/back-office/authorize", "tokenUrl": "/umbraco/management/api/v1.0/security/back-office/token", - "scopes": { } + "scopes": {} } } } @@ -6973,7 +7120,7 @@ }, "security": [ { - "OAuth": [ ] + "OAuth": [] } ] -} +} \ No newline at end of file diff --git a/src/Umbraco.Cms.ManagementApi/OpenApi/OperationIdRegexes.cs b/src/Umbraco.Cms.ManagementApi/OpenApi/OperationIdRegexes.cs new file mode 100644 index 0000000000..1903863f6b --- /dev/null +++ b/src/Umbraco.Cms.ManagementApi/OpenApi/OperationIdRegexes.cs @@ -0,0 +1,21 @@ +using System.Text.RegularExpressions; + +namespace Umbraco.Cms.ManagementApi.OpenApi; + +/// +/// This is the regexes used to generate the operation IDs, the benefit of this being partial with GeneratedRegex +/// source generators is that it will be pre-compiled at startup +/// See: https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/#source-generation for more info. +/// +internal static partial class OperationIdRegexes +{ + // Your IDE may be showing errors here, this is because it's a new dotnet 7 feature (it's fixed in the EAP of Rider) + [GeneratedRegex(".*?\\/v[1-9]+/")] + public static partial Regex VersionPrefixRegex(); + + [GeneratedRegex("\\{(.*?)\\:?\\}")] + public static partial Regex TemplatePlaceholdersRegex(); + + [GeneratedRegex("[\\/\\-](\\w{1})")] + public static partial Regex ToCamelCaseRegex(); +} diff --git a/src/Umbraco.Cms.ManagementApi/ViewModels/Pagination/PagedViewModel.cs b/src/Umbraco.Cms.ManagementApi/ViewModels/Pagination/PagedViewModel.cs index 9e3b6cf21b..8a21cecb93 100644 --- a/src/Umbraco.Cms.ManagementApi/ViewModels/Pagination/PagedViewModel.cs +++ b/src/Umbraco.Cms.ManagementApi/ViewModels/Pagination/PagedViewModel.cs @@ -1,9 +1,13 @@ -namespace Umbraco.Cms.ManagementApi.ViewModels.Pagination; +using System.ComponentModel.DataAnnotations; + +namespace Umbraco.Cms.ManagementApi.ViewModels.Pagination; public class PagedViewModel { + [Required] public long Total { get; set; } + [Required] public IEnumerable Items { get; set; } = Enumerable.Empty(); public static PagedViewModel Empty() => new(); From f2de42f38e0a155550dd324cef16d0a5e8f5f51f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 16 Nov 2022 11:48:18 +0100 Subject: [PATCH 38/39] v11: Fix for TinyMCE dropdowns (#13417) * Set higher z-index for TinyMCE overlays as they were set below that of our property editor overlays Fixes #13414 * replace !important with more specificity --- src/Umbraco.Web.UI.Client/src/less/rte.less | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index ed25ce4e90..522d387c93 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -140,6 +140,12 @@ } } +.tox { + &.tox-tinymce-aux { + z-index: 65535; + } +} + .tox-tinymce-inline { z-index: 999; } From 9b13a260e99cc68db26bc109b90d2f775c35284e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 16 Nov 2022 11:52:40 +0100 Subject: [PATCH 39/39] Block Grid Editor: Removal of the forced placement feature (#13400) * removal of the forceLeft/forceRight code * removal of forced placement in css * bring back removed code --- .../EmbeddedResources/BlockGrid/items.cshtml | 4 - .../EmbeddedResources/Lang/da.xml | 6 - .../EmbeddedResources/Lang/en.xml | 6 - .../EmbeddedResources/Lang/en_us.xml | 6 - .../Models/Blocks/BlockGridItem.cs | 12 -- .../Models/Blocks/BlockGridLayoutItem.cs | 6 - .../BlockGridPropertyValueConverter.cs | 2 - .../blockgrid/blockgridui.less | 151 +----------------- .../blockgrid/razorhtml.temp.html | 2 - .../blockgrid/umb-block-grid-entries.html | 2 - .../blockgrid/umb-block-grid-entry.html | 32 ---- .../umbblockgridentries.component.js | 148 ++++------------- .../blockgrid/umbblockgridentry.component.js | 14 -- .../umbraco-blockgridlayout-flexbox.css | 12 -- .../blockgrid/umbraco-blockgridlayout.css | 6 - .../Views/Partials/blockgrid/items.cshtml | 5 +- 16 files changed, 31 insertions(+), 383 deletions(-) diff --git a/src/Umbraco.Core/EmbeddedResources/BlockGrid/items.cshtml b/src/Umbraco.Core/EmbeddedResources/BlockGrid/items.cshtml index 165044553a..f00e2481ac 100644 --- a/src/Umbraco.Core/EmbeddedResources/BlockGrid/items.cshtml +++ b/src/Umbraco.Core/EmbeddedResources/BlockGrid/items.cshtml @@ -7,8 +7,6 @@
@foreach (var item in Model) { - bool attrForceLeft = item.ForceLeft; - bool attrForceRight = item.ForceRight;
@{ var partialViewName = "blockgrid/Components/" + item.Content.ContentType.Alias; diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml index 049b617f93..1c23c53e38 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml @@ -2244,12 +2244,6 @@ Mange hilsner fra Umbraco robotten - Påtving placering i venstre side - Påtving placering i højre side - Fjern påtvungen placering i venstre side - Fjern påtvungen placering i højre side - Skift påtvungen placering i venstre side - Skift påtvungen placering i højre side Træk for at skalere Tilføj indhold label Overskriv labellen for tilføj indholds knappen i dette område. diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index 3d3116afc7..7bb861fb3d 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -2796,12 +2796,6 @@ To manage your website, simply open the Umbraco backoffice and start adding cont - Force placement at left side - Force placement at right side - Remove forced placement at left side - Remove forced placement at right side - Toggle forced placement at left side - Toggle forced placement at right side Drag to scale Create Button Label Overwrite the label on the create button of this Area. diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index 29817fc8ed..1099795a73 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -2899,12 +2899,6 @@ To manage your website, simply open the Umbraco backoffice and start adding cont - Force placement at left side - Force placement at right side - Remove forced placement at left side - Remove forced placement at right side - Toggle forced placement at left side - Toggle forced placement at right side Drag to scale Create Button Label Overwrite the label on the create button of this Area. diff --git a/src/Umbraco.Core/Models/Blocks/BlockGridItem.cs b/src/Umbraco.Core/Models/Blocks/BlockGridItem.cs index 097882c5ab..eb554ef90d 100644 --- a/src/Umbraco.Core/Models/Blocks/BlockGridItem.cs +++ b/src/Umbraco.Core/Models/Blocks/BlockGridItem.cs @@ -81,18 +81,6 @@ namespace Umbraco.Cms.Core.Models.Blocks [DataMember(Name = "columnSpan")] public int ColumnSpan { get; set; } - /// - /// Forcing the item to be located in the left side. - /// - [DataMember(Name = "forceLeft")] - public bool ForceLeft { get; set; } - - /// - /// Forcing the item to be located in the right side. - /// - [DataMember(Name = "forceRight")] - public bool ForceRight { get; set; } - /// /// The grid areas within this item /// diff --git a/src/Umbraco.Infrastructure/Models/Blocks/BlockGridLayoutItem.cs b/src/Umbraco.Infrastructure/Models/Blocks/BlockGridLayoutItem.cs index 9014e2789a..30494b03f7 100644 --- a/src/Umbraco.Infrastructure/Models/Blocks/BlockGridLayoutItem.cs +++ b/src/Umbraco.Infrastructure/Models/Blocks/BlockGridLayoutItem.cs @@ -25,12 +25,6 @@ public class BlockGridLayoutItem : IBlockLayoutItem [JsonProperty("rowSpan", NullValueHandling = NullValueHandling.Ignore)] public int? RowSpan { get; set; } - [JsonProperty("forceLeft")] - public bool ForceLeft { get; set; } - - [JsonProperty("forceRight")] - public bool ForceRight { get; set; } - [JsonProperty("areas", NullValueHandling = NullValueHandling.Ignore)] public BlockGridLayoutAreaItem[] Areas { get; set; } = Array.Empty(); } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockGridPropertyValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockGridPropertyValueConverter.cs index 6cd2d4bd38..ae330870fa 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockGridPropertyValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/BlockGridPropertyValueConverter.cs @@ -49,8 +49,6 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters blockItem.RowSpan = layoutItem.RowSpan!.Value; blockItem.ColumnSpan = layoutItem.ColumnSpan!.Value; - blockItem.ForceLeft = layoutItem.ForceLeft; - blockItem.ForceRight = layoutItem.ForceRight; blockItem.AreaGridColumns = blockConfig.AreaGridColumns; blockItem.GridColumns = configuration.GridColumns; blockItem.Areas = layoutItem.Areas.Select(area => diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less index 38ff8086c6..b6205d73ac 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/blockgridui.less @@ -28,9 +28,7 @@ > ng-form > .umb-block-grid__block--inline-create-button, > ng-form > .umb-block-grid__block--validation-border, - > ng-form > .umb-block-grid__block--actions, - > ng-form > .umb-block-grid__force-left, - > ng-form > .umb-block-grid__force-right { + > ng-form > .umb-block-grid__block--actions { z-index: 3; } } @@ -80,11 +78,6 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti opacity: 0; transition: opacity 120ms; } - > .umb-block-grid__force-left, - > .umb-block-grid__force-right { - opacity: 0; - transition: opacity 120ms; - } > .umb-block-grid__block--context { opacity: 0; transition: opacity 120ms; @@ -149,10 +142,6 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti > .umb-block-grid__scale-label { opacity: 1; } - > .umb-block-grid__force-left, - > .umb-block-grid__force-right { - opacity: 1; - } } /** make sure to hide child block ui: */ @@ -169,10 +158,6 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti > .umb-block-grid__block--actions { display: none; } - > .umb-block-grid__force-left, - > .umb-block-grid__force-right { - display: none; - } } &.--block-ui-visible { @@ -221,10 +206,6 @@ ng-form.ng-invalid-val-server-match-content > .umb-block-grid__block:not(.--acti > .umb-block-grid__scale-handler { opacity: var(--umb-block-grid--block-ui-opacity); } - > .umb-block-grid__force-left, - > .umb-block-grid__force-right { - opacity: var(--umb-block-grid--block-ui-opacity); - } } } @@ -372,106 +353,6 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl } } -.umb-block-grid__force-left, -.umb-block-grid__force-right { - position: absolute; - z-index: 2; - top: 50%; - height: 30px; - width: 15px; - margin-top:-15px; - background-color: transparent; - color: @blueDark; - border: 1px solid rgba(255, 255, 255, .2); - font-size: 12px; - padding: 0; - cursor: pointer; - box-sizing: border-box; - border-radius: 8px; - display: var(--umb-block-grid--block-ui-display, flex); - justify-content: center; - align-items: center; - pointer-events: all; - - opacity: 0; - transition: background-color 120ms, border-color 120ms, color 120ms, opacity 120ms; - - .icon { - position: relative; - display: inline-block; - pointer-events: none; - opacity: 0; - transition: transform 120ms ease-in-out, opacity 120ms; - ::before { - content: ''; - position: absolute; - background-color:currentColor; - width:2px; - height: 8px; - top: 2px; - transition: transform 120ms ease-in-out; - } - } - - &:hover { - opacity: 1; - color: @blueDark; - background-color: @white; - } - &:hover, - &.--active { - .icon { - opacity: 1; - transform: translateX(0); - ::before { - transform: translateX(0); - } - } - } - - &.--active { - background-color: @blueDark; - color: white; - &:hover { - color: white; - } - } -} - -.umb-block-grid__force-left { - left: 0; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - .icon { - transform: translateX(3px); - ::before { - left: 2px; - transform: translateX(-3px); - } - } - &:hover, - &.--active { - border-left-color: @blueDark; - } -} -.umb-block-grid__force-right { - right: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - .icon { - margin-right: 1px; - transform: translateX(-3px); - ::before { - right: 2px; - transform: translateX(3px); - } - } - &:hover, - &.--active { - border-right-color: @blueDark; - } -} - .umb-block-grid__scale-handler { cursor: nwse-resize; @@ -745,36 +626,6 @@ ng-form.ng-invalid > .umb-block-grid__block:not(.--active) > .umb-block-grid__bl 100% { background-color: rgba(@blueMidLight, 0.22); } } } -.umb-block-grid__layout-item-placeholder .indicateForceLeft, -.umb-block-grid__layout-item-placeholder .indicateForceRight { - position:absolute; - - z-index: 2; - height: 100%; - width: 15px; - - background-color: @blueDark; - - background-position: center center; - background-repeat: no-repeat; - display: block !important; - - animation: umb-block-grid__indicateForce__pulse 400ms ease-in-out alternate infinite; -} - -.umb-block-grid__layout-item-placeholder .indicateForceLeft { - left:0; - background-image: url("data:image/svg+xml;utf8,"); -} -.umb-block-grid__layout-item-placeholder .indicateForceRight { - right:0; - background-image: url("data:image/svg+xml;utf8,"); -} - -@keyframes umb-block-grid__indicateForce__pulse { - 0% { background-color: rgba(@blueDark, 1); } - 100% { background-color: rgba(@blueDark, 0.5); } -} .umb-block-grid__area { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/razorhtml.temp.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/razorhtml.temp.html index 9c7a95a101..5c64d795d1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/razorhtml.temp.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/razorhtml.temp.html @@ -14,8 +14,6 @@ data-element-udi="{{layoutEntry.contentUdi}}" data-col-span="{{layoutEntry.columnSpan}}" data-row-span="{{layoutEntry.rowSpan}}" - ng-attr-data-force-left="{{layoutEntry.forceLeft || undefined}}" - ng-attr-data-force-right="{{layoutEntry.forceRight || undefined}}" style=" --umb-block-grid--item-column-span: {{layoutEntry.columnSpan}}; --umb-block-grid--item-row-span: {{layoutEntry.rowSpan}}; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html index 301208c6f2..4aed670ffd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entries.html @@ -14,8 +14,6 @@ data-element-udi="{{layoutEntry.contentUdi}}" data-col-span="{{layoutEntry.columnSpan}}" data-row-span="{{layoutEntry.rowSpan}}" - ng-attr-data-force-left="{{layoutEntry.forceLeft || undefined}}" - ng-attr-data-force-right="{{layoutEntry.forceRight || undefined}}" style=" --umb-block-grid--item-column-span: {{layoutEntry.columnSpan}}; --umb-block-grid--item-row-span: {{layoutEntry.rowSpan}}; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html index e3f439a891..0caebd35c2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blockgrid/umb-block-grid-entry.html @@ -132,38 +132,6 @@
- - - -