diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs index e0742b345..c1ee42557 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs @@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Apps case AssignContributor assigneContributor: return UpdateReturnAsync(assigneContributor, async c => { - await GuardAppContributors.CanAssign(Snapshot.Contributors, c, userResolver, appPlansProvider.GetPlan(Snapshot.Plan?.PlanId)); + await GuardAppContributors.CanAssign(Snapshot.Contributors, c, userResolver, appPlansProvider.GetPlan(Snapshot.Plan?.PlanId), Snapshot.Roles); AssignContributor(c); @@ -96,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Apps case UpdateClient updateClient: return UpdateAsync(updateClient, c => { - GuardAppClients.CanUpdate(Snapshot.Clients, c); + GuardAppClients.CanUpdate(Snapshot.Clients, c, Snapshot.Roles); UpdateClient(c); }); diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs index 535515f3e..9a5e52a01 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs @@ -63,6 +63,15 @@ namespace Squidex.Domain.Apps.Entities.Apps AddEventMessage( "updated pattern {[Name]}"); + + AddEventMessage( + "added role {[Name]}"); + + AddEventMessage( + "deleted role {[Name]}"); + + AddEventMessage( + "updated role {[Name]}"); } protected Task On(AppContributorRemoved @event) @@ -173,6 +182,33 @@ namespace Squidex.Domain.Apps.Entities.Apps .AddParameter("PatternId", @event.PatternId)); } + protected Task On(AppRoleAdded @event) + { + const string channel = "settings.roles"; + + return Task.FromResult( + ForEvent(@event, channel) + .AddParameter("Name", @event.Name)); + } + + protected Task On(AppRoleUpdated @event) + { + const string channel = "settings.roles"; + + return Task.FromResult( + ForEvent(@event, channel) + .AddParameter("Name", @event.Name)); + } + + protected Task On(AppRoleDeleted @event) + { + const string channel = "settings.roles"; + + return Task.FromResult( + ForEvent(@event, channel) + .AddParameter("Name", @event.Name)); + } + protected override Task CreateEventCoreAsync(Envelope @event) { return this.DispatchFuncAsync(@event.Payload, (HistoryEventToStore)null); diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs index bbe63278e..d33ad053c 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs @@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards }); } - public static void CanUpdate(AppClients clients, UpdateClient command) + public static void CanUpdate(AppClients clients, UpdateClient command, Roles roles) { Guard.NotNull(command, nameof(command)); @@ -63,7 +63,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards e("Either name or role must be defined.", nameof(command.Name), nameof(command.Role)); } - if (command.Role != null && !Role.IsDefaultRole(command.Role)) + if (command.Role != null && !roles.ContainsKey(command.Role)) { e("Role is not valid.", nameof(command.Role)); } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs index c2184fd4e..efb47de73 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs @@ -18,13 +18,13 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { public static class GuardAppContributors { - public static Task CanAssign(AppContributors contributors, AssignContributor command, IUserResolver users, IAppLimitsPlan plan) + public static Task CanAssign(AppContributors contributors, AssignContributor command, IUserResolver users, IAppLimitsPlan plan, Roles roles) { Guard.NotNull(command, nameof(command)); return Validate.It(() => "Cannot assign contributor.", async e => { - if (!Role.IsDefaultRole(command.Role)) + if (!roles.ContainsKey(command.Role)) { e("Role is not valid.", nameof(command.Role)); } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs index 0991e4b46..4ee092e69 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.ComponentModel.DataAnnotations; using System.Linq; using Squidex.Domain.Apps.Core.Apps; @@ -20,17 +21,33 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models [Required] public string Name { get; set; } + /// + /// The number of clients with this role. + /// + public int NumClients { get; set; } + + /// + /// The number of contributors with this role. + /// + public int NumContributors { get; set; } + /// /// Associated list of permissions. /// [Required] public string[] Permissions { get; set; } - public static RoleDto FromRole(Role role, string appName) + public static RoleDto FromRole(Role role, IAppEntity app) { - var permissions = role.Permissions.WithoutApp(appName); + var permissions = role.Permissions.WithoutApp(app.Name); - return new RoleDto { Name = role.Name, Permissions = permissions.ToIds().ToArray() }; + return new RoleDto + { + Name = role.Name, + NumClients = app.Clients.Count(x => string.Equals(x.Value.Role, role.Name, StringComparison.OrdinalIgnoreCase)), + NumContributors = app.Contributors.Count(x => string.Equals(x.Value, role.Name, StringComparison.OrdinalIgnoreCase)), + Permissions = permissions.ToIds().ToArray() + }; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs index c34cfbbdf..64390c625 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using Squidex.Domain.Apps.Entities.Apps; @@ -15,14 +14,14 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models public sealed class RolesDto { /// - /// The roles. + /// The app roles. /// [Required] public RoleDto[] Roles { get; set; } public static RolesDto FromApp(IAppEntity app) { - var roles = app.Roles.Values.Select(x => RoleDto.FromRole(x, app.Name)).ToArray(); + var roles = app.Roles.Values.Select(x => RoleDto.FromRole(x, app)).ToArray(); return new RolesDto { Roles = roles }; } diff --git a/src/Squidex/app/features/settings/pages/languages/language.component.html b/src/Squidex/app/features/settings/pages/languages/language.component.html index b4cdc839e..75b2e0cec 100644 --- a/src/Squidex/app/features/settings/pages/languages/language.component.html +++ b/src/Squidex/app/features/settings/pages/languages/language.component.html @@ -7,7 +7,7 @@
{{language.englishName}}
-
+
-