diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/ByKeyMemberTypeFolderController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/ByKeyMemberTypeFolderController.cs new file mode 100644 index 000000000000..81ba0fcbb9e5 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/ByKeyMemberTypeFolderController.cs @@ -0,0 +1,25 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.ViewModels.Folder; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder; + +[ApiVersion("1.0")] +public class ByKeyMemberTypeFolderController : MemberTypeFolderControllerBase +{ + public ByKeyMemberTypeFolderController( + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IMemberTypeContainerService memberTypeContainerService) + : base(backOfficeSecurityAccessor, memberTypeContainerService) + { + } + + [HttpGet("{id:guid}")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(FolderResponseModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task ByKey(CancellationToken cancellationToken, Guid id) => await GetFolderAsync(id); +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/CreateMemberTypeFolderController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/CreateMemberTypeFolderController.cs new file mode 100644 index 000000000000..3379cb2e9b0e --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/CreateMemberTypeFolderController.cs @@ -0,0 +1,31 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.ViewModels.Folder; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder; + +[ApiVersion("1.0")] +public class CreateMemberTypeFolderController : MemberTypeFolderControllerBase +{ + public CreateMemberTypeFolderController( + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IMemberTypeContainerService memberTypeContainerService) + : base(backOfficeSecurityAccessor, memberTypeContainerService) + { + } + + [HttpPost] + [MapToApiVersion("1.0")] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task Create( + CancellationToken cancellationToken, + CreateFolderRequestModel createFolderRequestModel) + => await CreateFolderAsync( + createFolderRequestModel, + controller => nameof(controller.ByKey)).ConfigureAwait(false); +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/DeleteMemberTypeFolderController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/DeleteMemberTypeFolderController.cs new file mode 100644 index 000000000000..6bdc679e2d70 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/DeleteMemberTypeFolderController.cs @@ -0,0 +1,25 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder; + +[ApiVersion("1.0")] +public class DeleteMemberTypeFolderController : MemberTypeFolderControllerBase +{ + public DeleteMemberTypeFolderController( + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IMemberTypeContainerService memberTypeContainerService) + : base(backOfficeSecurityAccessor, memberTypeContainerService) + { + } + + [HttpDelete("{id:guid}")] + [MapToApiVersion("1.0")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task Delete(CancellationToken cancellationToken, Guid id) => await DeleteFolderAsync(id); +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/MemberTypeFolderControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/MemberTypeFolderControllerBase.cs new file mode 100644 index 000000000000..867544c697fc --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/MemberTypeFolderControllerBase.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Routing; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.Common.Authorization; + +namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder; + +[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.MemberType}/folder")] +[ApiExplorerSettings(GroupName = "Member Type")] +[Authorize(Policy = AuthorizationPolicies.TreeAccessMemberTypes)] +public abstract class MemberTypeFolderControllerBase : FolderManagementControllerBase +{ + protected MemberTypeFolderControllerBase( + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IMemberTypeContainerService memberTypeContainerService) + : base(backOfficeSecurityAccessor, memberTypeContainerService) + { + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/UpdateMemberTypeFolderController.cs b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/UpdateMemberTypeFolderController.cs new file mode 100644 index 000000000000..acc0acab5b78 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/MemberType/Folder/UpdateMemberTypeFolderController.cs @@ -0,0 +1,30 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.ViewModels.Folder; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.MemberType.Folder; + +[ApiVersion("1.0")] +public class UpdateMemberTypeFolderController : MemberTypeFolderControllerBase +{ + public UpdateMemberTypeFolderController( + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IMemberTypeContainerService memberTypeContainerService) + : base(backOfficeSecurityAccessor, memberTypeContainerService) + { + } + + [HttpPut("{id:guid}")] + [MapToApiVersion("1.0")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task Update( + CancellationToken cancellationToken, + Guid id, + UpdateFolderResponseModel updateFolderResponseModel) + => await UpdateFolderAsync(id, updateFolderResponseModel); +} diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs index 5f57548ffd99..a6b3b2966bd8 100644 --- a/src/Umbraco.Core/Constants-ObjectTypes.cs +++ b/src/Umbraco.Core/Constants-ObjectTypes.cs @@ -19,6 +19,8 @@ public static class ObjectTypes public static readonly Guid MediaTypeContainer = new(Strings.MediaTypeContainer); + public static readonly Guid MemberTypeContainer = new(Strings.MemberTypeContainer); + public static readonly Guid DocumentBlueprintContainer = new(Strings.DocumentBlueprintContainer); public static readonly Guid DataType = new(Strings.DataType); @@ -75,6 +77,8 @@ public static class Strings public const string MediaTypeContainer = "42AEF799-B288-4744-9B10-BE144B73CDC4"; + public const string MemberTypeContainer = "59EF5767-7223-4ABC-B229-72821DC711B9"; + public const string DocumentBlueprintContainer = "A7EFF71B-FA69-4552-93FC-038F7DEEE453"; public const string ContentItem = "10E2B09F-C28B-476D-B77A-AA686435E44A"; diff --git a/src/Umbraco.Core/Constants-UdiEntityType.cs b/src/Umbraco.Core/Constants-UdiEntityType.cs index 9c2a25b3148d..c5d511e03be1 100644 --- a/src/Umbraco.Core/Constants-UdiEntityType.cs +++ b/src/Umbraco.Core/Constants-UdiEntityType.cs @@ -19,22 +19,27 @@ public static class UdiEntityType // GUID entity types public const string AnyGuid = "any-guid"; // that one is for tests - public const string DataType = "data-type"; - public const string DataTypeContainer = "data-type-container"; public const string DictionaryItem = "dictionary-item"; public const string Document = "document"; public const string DocumentBlueprint = "document-blueprint"; public const string DocumentBlueprintContainer = "document-blueprint-container"; public const string DocumentType = "document-type"; public const string DocumentTypeContainer = "document-type-container"; + + public const string MemberType = "member-type"; + public const string MemberTypeContainer = "member-type-container"; + public const string MemberGroup = "member-group"; + public const string Member = "member"; + + public const string DataType = "data-type"; + public const string DataTypeContainer = "data-type-container"; + public const string Element = "element"; public const string Media = "media"; public const string MediaType = "media-type"; public const string MediaTypeContainer = "media-type-container"; - public const string Member = "member"; - public const string MemberGroup = "member-group"; - public const string MemberType = "member-type"; public const string Relation = "relation"; + public const string RelationType = "relation-type"; public const string Template = "template"; public const string User = "user"; diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 5ad01c0d1613..701aad924a0c 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -329,6 +329,7 @@ private void AddCoreServices() Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); + Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs index 6c8e8d746662..4339ea404f33 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs @@ -35,14 +35,6 @@ public enum UmbracoObjectTypes [UmbracoUdiType(Constants.UdiEntityType.Media)] Media, - /// - /// Member Type - /// - [UmbracoObjectType(Constants.ObjectTypes.Strings.MemberType, typeof(IMemberType))] - [FriendlyName("Member Type")] - [UmbracoUdiType(Constants.UdiEntityType.MemberType)] - MemberType, - /// /// Template /// @@ -52,12 +44,12 @@ public enum UmbracoObjectTypes Template, /// - /// Member Group + /// Document Type /// - [UmbracoObjectType(Constants.ObjectTypes.Strings.MemberGroup, typeof(IMemberGroup))] - [FriendlyName("Member Group")] - [UmbracoUdiType(Constants.UdiEntityType.MemberGroup)] - MemberGroup, + [UmbracoObjectType(Constants.ObjectTypes.Strings.DocumentType, typeof(IContentType))] + [FriendlyName("Document Type")] + [UmbracoUdiType(Constants.UdiEntityType.DocumentType)] + DocumentType, /// /// "Media Type @@ -68,12 +60,20 @@ public enum UmbracoObjectTypes MediaType, /// - /// Document Type + /// Member Type /// - [UmbracoObjectType(Constants.ObjectTypes.Strings.DocumentType, typeof(IContentType))] - [FriendlyName("Document Type")] - [UmbracoUdiType(Constants.UdiEntityType.DocumentType)] - DocumentType, + [UmbracoObjectType(Constants.ObjectTypes.Strings.MemberType, typeof(IMemberType))] + [FriendlyName("Member Type")] + [UmbracoUdiType(Constants.UdiEntityType.MemberType)] + MemberType, + + /// + /// Member Group + /// + [UmbracoObjectType(Constants.ObjectTypes.Strings.MemberGroup, typeof(IMemberGroup))] + [FriendlyName("Member Group")] + [UmbracoUdiType(Constants.UdiEntityType.MemberGroup)] + MemberGroup, /// /// Recycle Bin @@ -114,6 +114,14 @@ public enum UmbracoObjectTypes [UmbracoUdiType(Constants.UdiEntityType.MediaTypeContainer)] MediaTypeContainer, + /// + /// Member type container + /// + [UmbracoObjectType(Constants.ObjectTypes.Strings.MemberTypeContainer)] + [FriendlyName("Member Type Container")] + [UmbracoUdiType(Constants.UdiEntityType.MemberTypeContainer)] + MemberTypeContainer, + /// /// Media type container /// diff --git a/src/Umbraco.Core/Services/IMemberTypeContainerService.cs b/src/Umbraco.Core/Services/IMemberTypeContainerService.cs new file mode 100644 index 000000000000..d84b5781c098 --- /dev/null +++ b/src/Umbraco.Core/Services/IMemberTypeContainerService.cs @@ -0,0 +1,7 @@ +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Core.Services; + +public interface IMemberTypeContainerService : IEntityTypeContainerService +{ +} diff --git a/src/Umbraco.Core/Services/Locking/MemberTypeLocks.cs b/src/Umbraco.Core/Services/Locking/MemberTypeLocks.cs new file mode 100644 index 000000000000..5c852d51770c --- /dev/null +++ b/src/Umbraco.Core/Services/Locking/MemberTypeLocks.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Cms.Core.Services.Locking; + +internal static class MemberTypeLocks +{ + // beware! order is important to avoid deadlocks + internal static int[] ReadLockIds { get; } = { Constants.Locks.MemberTypes }; + + internal static int[] WriteLockIds { get; } = { Constants.Locks.MemberTree, Constants.Locks.MemberTypes }; +} diff --git a/src/Umbraco.Core/Services/MemberTypeContainerService.cs b/src/Umbraco.Core/Services/MemberTypeContainerService.cs new file mode 100644 index 000000000000..e75d9cd603d1 --- /dev/null +++ b/src/Umbraco.Core/Services/MemberTypeContainerService.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Persistence.Repositories; +using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Services.Locking; + +namespace Umbraco.Cms.Core.Services; + +internal sealed class MemberTypeContainerService : EntityTypeContainerService, IMemberTypeContainerService +{ + public MemberTypeContainerService( + ICoreScopeProvider provider, + ILoggerFactory loggerFactory, + IEventMessagesFactory eventMessagesFactory, + IMemberTypeContainerRepository entityContainerRepository, + IAuditService auditService, + IEntityRepository entityRepository, + IUserIdKeyResolver userIdKeyResolver) + : base(provider, loggerFactory, eventMessagesFactory, entityContainerRepository, auditService, entityRepository, userIdKeyResolver) + { + } + + protected override Guid ContainedObjectType => Constants.ObjectTypes.MemberType; + + protected override UmbracoObjectTypes ContainerObjectType => UmbracoObjectTypes.MemberTypeContainer; + + protected override int[] ReadLockIds => MemberTypeLocks.ReadLockIds; + + protected override int[] WriteLockIds => MemberTypeLocks.WriteLockIds; +} diff --git a/src/Umbraco.Core/UdiParser.cs b/src/Umbraco.Core/UdiParser.cs index ed1f16ae2881..c2c6b01d0d58 100644 --- a/src/Umbraco.Core/UdiParser.cs +++ b/src/Umbraco.Core/UdiParser.cs @@ -217,6 +217,7 @@ public static Dictionary GetKnownUdiTypes() => { Constants.UdiEntityType.Member, UdiType.GuidUdi }, { Constants.UdiEntityType.MemberGroup, UdiType.GuidUdi }, { Constants.UdiEntityType.MemberType, UdiType.GuidUdi }, + { Constants.UdiEntityType.MemberTypeContainer, UdiType.GuidUdi }, { Constants.UdiEntityType.Relation, UdiType.GuidUdi }, { Constants.UdiEntityType.RelationType, UdiType.GuidUdi }, { Constants.UdiEntityType.Template, UdiType.GuidUdi },