From 649791d6488e124e91397c0221e173d7c93cc159 Mon Sep 17 00:00:00 2001 From: Tobias Klika Date: Wed, 24 Nov 2021 15:49:25 +0100 Subject: [PATCH] RE 7 --- .../Configuration/BackofficeOptions.cs | 10 + zero.Backoffice/Plugin.cs | 3 + .../ServiceCollectionExtensions.cs | 2 + .../UIComposition/Settings/SettingsOptions.cs | 49 ++ .../Blueprints/BlueprintOptions.cs | 19 +- .../Handlers/IModuleTypeHandler.cs | 6 - .../ConfigurationParts/SettingsOptions.cs | 52 -- zero.Core/Configuration/Features/Feature.cs | 22 + .../FeatureOptions.cs | 8 +- .../IntegrationOptions.cs | 8 +- .../Integrations/IntegrationService.cs | 20 +- .../Integrations/IntegrationsCollection.cs | 448 +++++++++--------- zero.Core/Configuration/OptionsEnumerable.cs | 27 -- .../ServiceCollectionExtensions.cs | 1 + zero.Core/Configuration/ZeroOptions.cs | 101 +--- zero.Core/FileStorage/Paths.cs | 17 +- zero.Core/Identity/RavenUserStore(TUser).cs | 12 +- .../Services/AuthenticationService.cs | 77 +-- .../Identity/Services/AuthorizationService.cs | 2 +- zero.Core/Identity/Services/UserService.cs | 39 ++ zero.Core/Localization/CultureService.cs | 2 +- zero.Core/Media/MediaCollection.cs | 31 +- zero.Core/Pages/Modules/IModuleTypeHandler.cs | 6 + zero.Core/Pages/Modules/Module.cs | 24 - zero.Core/Pages/Modules/ModulePermissions.cs | 30 +- zero.Core/Pages/Modules/ModuleType.cs | 81 ---- zero.Core/Pages/Modules/ModulesApi.cs | 12 +- zero.Core/Pages/Modules/PageModule.cs | 23 + .../Modules/PageModuleOptions.cs} | 20 +- zero.Core/Pages/Modules/PageModuleType.cs | 77 +++ .../PageOptions.cs | 21 +- .../Pages/ServiceCollectionExtensions.cs | 9 +- zero.Core/Persistence/RavenOptions.cs | 22 +- .../ServiceCollectionExtensions.cs | 8 +- .../SpaceOptions.cs | 25 +- zero.Core/Stores/EntityStore.cs | 34 +- zero.Core/Stores/EntityStoreExtensions.cs | 46 ++ zero.Core/Stores/StoreOperations.Delete.cs | 40 +- zero.Core/Stores/StoreOperations.Read.cs | 6 +- zero.Core/Stores/StoreOperations.Tree.cs | 6 +- zero.Core/Stores/StoreOperations.Write.cs | 2 +- zero.Core/Stores/StoreOperations.cs | 27 +- zero.Core/Stores/StoreOperationsExtensions.cs | 45 ++ zero.Core/Stores/TreeEntityStore.cs | 4 +- zero.Core/Unimplemented/Feature.cs | 23 - zero.Core/Utils/ObjectCopycat.cs | 2 +- zero.Core/ZeroBuilder.cs | 5 +- 47 files changed, 708 insertions(+), 846 deletions(-) create mode 100644 zero.Backoffice/UIComposition/Settings/SettingsOptions.cs delete mode 100644 zero.Core/Communication/Handlers/IModuleTypeHandler.cs delete mode 100644 zero.Core/Configuration/ConfigurationParts/SettingsOptions.cs create mode 100644 zero.Core/Configuration/Features/Feature.cs rename zero.Core/Configuration/{ConfigurationParts => Features}/FeatureOptions.cs (65%) rename zero.Core/Configuration/{ConfigurationParts => Integrations}/IntegrationOptions.cs (78%) delete mode 100644 zero.Core/Configuration/OptionsEnumerable.cs create mode 100644 zero.Core/Pages/Modules/IModuleTypeHandler.cs delete mode 100644 zero.Core/Pages/Modules/Module.cs delete mode 100644 zero.Core/Pages/Modules/ModuleType.cs create mode 100644 zero.Core/Pages/Modules/PageModule.cs rename zero.Core/{Configuration/ConfigurationParts/ModuleOptions.cs => Pages/Modules/PageModuleOptions.cs} (64%) create mode 100644 zero.Core/Pages/Modules/PageModuleType.cs rename zero.Core/{Configuration/ConfigurationParts => Pages}/PageOptions.cs (62%) rename zero.Core/{Configuration/ConfigurationParts => Spaces}/SpaceOptions.cs (79%) create mode 100644 zero.Core/Stores/EntityStoreExtensions.cs create mode 100644 zero.Core/Stores/StoreOperationsExtensions.cs delete mode 100644 zero.Core/Unimplemented/Feature.cs diff --git a/zero.Backoffice/Configuration/BackofficeOptions.cs b/zero.Backoffice/Configuration/BackofficeOptions.cs index 688f6901..b10fbc24 100644 --- a/zero.Backoffice/Configuration/BackofficeOptions.cs +++ b/zero.Backoffice/Configuration/BackofficeOptions.cs @@ -26,4 +26,14 @@ public class BackofficeOptions /// Options for configuring the vite development server /// public ZeroDevOptions DevServer { get; set; } = new(); + + /// + /// Default language ISO code + /// + public string DefaultLanguage { get; set; } + + /// + /// Language ISO codes which are supported by the zero backoffice + /// + public string[] SupportedLanguages { get; internal set; } } \ No newline at end of file diff --git a/zero.Backoffice/Plugin.cs b/zero.Backoffice/Plugin.cs index dee88f6c..a9836fee 100644 --- a/zero.Backoffice/Plugin.cs +++ b/zero.Backoffice/Plugin.cs @@ -56,6 +56,9 @@ public class ZeroBackofficePlugin : ZeroPlugin Prefix = "fth" }); + options.SupportedLanguages = new string[2] { "en-US", "de-DE" }; + options.DefaultLanguage = options.SupportedLanguages[0]; + //Map().Display((x, res, opts) => //{ // PageType pageType = opts.Pages.GetByAlias(x.PageTypeAlias); diff --git a/zero.Backoffice/UIComposition/ServiceCollectionExtensions.cs b/zero.Backoffice/UIComposition/ServiceCollectionExtensions.cs index 1a0bf8da..f6a5ff32 100644 --- a/zero.Backoffice/UIComposition/ServiceCollectionExtensions.cs +++ b/zero.Backoffice/UIComposition/ServiceCollectionExtensions.cs @@ -17,6 +17,8 @@ public static class ServiceCollectionExtensions services.AddScoped(); + services.AddOptions(); + return services; } } \ No newline at end of file diff --git a/zero.Backoffice/UIComposition/Settings/SettingsOptions.cs b/zero.Backoffice/UIComposition/Settings/SettingsOptions.cs new file mode 100644 index 00000000..ea18b15d --- /dev/null +++ b/zero.Backoffice/UIComposition/Settings/SettingsOptions.cs @@ -0,0 +1,49 @@ +namespace zero.Backoffice.UIComposition; + +public class BackofficeSettingsOptions : List +{ + public void AddGroup(int index = -1) where T : SettingsGroup, new() + { + if (index > -1 && index < Count) + { + Insert(index, new T()); + } + else + { + Add(new T()); + } + } + + + public void AddGroup(SettingsGroup group, int index = -1) + { + if (index > -1 && index < Count) + { + Insert(index, group); + } + else + { + Add(group); + } + } + + + public void AddToDefaultGroup(SettingsArea item, int index = -1) + { + SettingsGroup group = this.FirstOrDefault(x => x.Name == "@settings.groups.system"); + + if (group == null) + { + return; + } + + if (index > -1 && index < group.Areas.Count) + { + group.Areas.Insert(index, item); + } + else + { + group.Areas.Add(item); + } + } +} diff --git a/zero.Core/Architecture/Blueprints/BlueprintOptions.cs b/zero.Core/Architecture/Blueprints/BlueprintOptions.cs index daddb9ea..94a07ea9 100644 --- a/zero.Core/Architecture/Blueprints/BlueprintOptions.cs +++ b/zero.Core/Architecture/Blueprints/BlueprintOptions.cs @@ -1,30 +1,21 @@ namespace zero.Architecture; -public class BlueprintOptions : OptionsEnumerable, IOptionsEnumerable +public class BlueprintOptions : List { - public BlueprintOptions() - { - Enabled = false; - } - - - public bool Enabled { get; set; } - + public bool Enabled { get; set; } = false; public void Add() where T : Blueprint, new() { - Items.Add(new T()); + Add(new T()); } - public void Add(Blueprint implementation) where T : ZeroEntity, new() { - Items.Add(implementation); + Add(implementation); } - public void Add(Action> createExpression = null) where T : ZeroEntity, new() { - Items.Add(new DefaultBlueprint(createExpression)); + Add(new DefaultBlueprint(createExpression)); } } diff --git a/zero.Core/Communication/Handlers/IModuleTypeHandler.cs b/zero.Core/Communication/Handlers/IModuleTypeHandler.cs deleted file mode 100644 index e42ef0e3..00000000 --- a/zero.Core/Communication/Handlers/IModuleTypeHandler.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace zero.Communication; - -public interface IModuleTypeHandler : IHandler -{ - IEnumerable GetAllowedModuleTypes(Application application, IEnumerable registeredTypes, Page page = default, string[] tags = default); -} diff --git a/zero.Core/Configuration/ConfigurationParts/SettingsOptions.cs b/zero.Core/Configuration/ConfigurationParts/SettingsOptions.cs deleted file mode 100644 index 9b431d31..00000000 --- a/zero.Core/Configuration/ConfigurationParts/SettingsOptions.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Linq; -using zero.Core.Entities; - -namespace zero.Configuration; - -public class SettingsOptions : OptionsEnumerable, IOptionsEnumerable -{ - public void AddGroup(int index = -1) where T : SettingsGroup, new() - { - if (index > -1 && index < Items.Count) - { - Items.Insert(index, new T()); - } - else - { - Items.Add(new T()); - } - } - - - public void AddGroup(SettingsGroup group, int index = -1) - { - if (index > -1 && index < Items.Count) - { - Items.Insert(index, group); - } - else - { - Items.Add(group); - } - } - - - public void AddToDefaultGroup(SettingsArea item, int index = -1) - { - SettingsGroup group = Items.FirstOrDefault(x => x.Name == "@settings.groups.system"); - - if (group == null) - { - return; - } - - if (index > -1 && index < group.Items.Count) - { - group.Items.Insert(index, item); - } - else - { - group.Items.Add(item); - } - } -} diff --git a/zero.Core/Configuration/Features/Feature.cs b/zero.Core/Configuration/Features/Feature.cs new file mode 100644 index 00000000..35a8d20d --- /dev/null +++ b/zero.Core/Configuration/Features/Feature.cs @@ -0,0 +1,22 @@ +namespace zero.Configuration; + +/// +/// A feature can affect both the backoffice and the frontend +/// +public class Feature +{ + /// + /// The alias + /// + public string Alias { get; set; } + + /// + /// The name of the feature + /// + public string Name { get; set; } + + /// + /// Additional description + /// + public string Description { get; set; } +} \ No newline at end of file diff --git a/zero.Core/Configuration/ConfigurationParts/FeatureOptions.cs b/zero.Core/Configuration/Features/FeatureOptions.cs similarity index 65% rename from zero.Core/Configuration/ConfigurationParts/FeatureOptions.cs rename to zero.Core/Configuration/Features/FeatureOptions.cs index bb01a67a..e222a407 100644 --- a/zero.Core/Configuration/ConfigurationParts/FeatureOptions.cs +++ b/zero.Core/Configuration/Features/FeatureOptions.cs @@ -1,19 +1,19 @@ namespace zero.Configuration; -public class FeatureOptions : OptionsEnumerable, IOptionsEnumerable +public class FeatureOptions : List { public void Add() where T : Feature, new() { - Items.Add(new T()); + Add(new T()); } public void Add(string alias, string name, string description) { - Items.Add(new Feature() + Add(new Feature() { Alias = alias, Name = name, Description = description }); } -} +} \ No newline at end of file diff --git a/zero.Core/Configuration/ConfigurationParts/IntegrationOptions.cs b/zero.Core/Configuration/Integrations/IntegrationOptions.cs similarity index 78% rename from zero.Core/Configuration/ConfigurationParts/IntegrationOptions.cs rename to zero.Core/Configuration/Integrations/IntegrationOptions.cs index c244b621..2d8373fe 100644 --- a/zero.Core/Configuration/ConfigurationParts/IntegrationOptions.cs +++ b/zero.Core/Configuration/Integrations/IntegrationOptions.cs @@ -2,17 +2,17 @@ namespace zero.Configuration; -public class IntegrationOptions : OptionsEnumerable, IOptionsEnumerable +public class IntegrationOptions : List { public void Add(IntegrationType integration) where T : Integration, new() { - Items.Add(IntegrationType.Convert(integration)); + Add(IntegrationType.Convert(integration)); } public void Add(string alias, string name, string description, List tags = default, string imagePath = null, IValidator validator = null) where T : Integration, new() { - Items.Add(new IntegrationType(typeof(T)) + Add(new IntegrationType(typeof(T)) { Alias = alias, Name = name, @@ -26,7 +26,7 @@ public class IntegrationOptions : OptionsEnumerable, IOptionsEn public void Add(Type type, string alias, string name, string description, List tags = default, string imagePath = null, IValidator validator = null) { - Items.Add(new IntegrationType(type) + Add(new IntegrationType(type) { Alias = alias, Name = name, diff --git a/zero.Core/Configuration/Integrations/IntegrationService.cs b/zero.Core/Configuration/Integrations/IntegrationService.cs index b48f888b..ca3e3de9 100644 --- a/zero.Core/Configuration/Integrations/IntegrationService.cs +++ b/zero.Core/Configuration/Integrations/IntegrationService.cs @@ -1,14 +1,14 @@ -using Microsoft.Extensions.Logging; +//using Microsoft.Extensions.Logging; -namespace zero.Configuration; +//namespace zero.Configuration; -public class IntegrationService : IntegrationsCollection, IIntegrationService -{ - public IntegrationService(IStoreContext context, ILogger logger) : base(context, logger) - { - Options = new(false); - } -} +//public class IntegrationService : IntegrationsCollection, IIntegrationService +//{ +// public IntegrationService(IStoreContext context, ILogger logger) : base(context, logger) +// { +// Options = new(false); +// } +//} -public interface IIntegrationService : IIntegrationsCollection { } \ No newline at end of file +//public interface IIntegrationService : IIntegrationsCollection { } \ No newline at end of file diff --git a/zero.Core/Configuration/Integrations/IntegrationsCollection.cs b/zero.Core/Configuration/Integrations/IntegrationsCollection.cs index bf105bb3..b0404d1b 100644 --- a/zero.Core/Configuration/Integrations/IntegrationsCollection.cs +++ b/zero.Core/Configuration/Integrations/IntegrationsCollection.cs @@ -1,287 +1,287 @@ -using Microsoft.Extensions.Logging; -using Raven.Client.Documents; -using Raven.Client.Documents.Linq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using zero.Core.Integrations; +//using Microsoft.Extensions.Logging; +//using Raven.Client.Documents; +//using Raven.Client.Documents.Linq; +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Threading.Tasks; +//using zero.Core.Integrations; -namespace zero.Core.Collections -{ - public class IntegrationsCollection : EntityStore, IIntegrationsCollection - { - /// - public IReadOnlyCollection RegisteredTypes { get; private set; } +//namespace zero.Core.Collections +//{ +// public class IntegrationsCollection : EntityStore, IIntegrationsCollection +// { +// /// +// public IReadOnlyCollection RegisteredTypes { get; private set; } - protected ILogger Logger { get; private set; } +// protected ILogger Logger { get; private set; } - public IntegrationsCollection(IStoreContext context, ILogger logger) : base(context) - { - Options = new(true); - RegisteredTypes = Context.Options.Integrations.GetAllItems(); - Logger = logger; - } +// public IntegrationsCollection(IStoreContext context, ILogger logger) : base(context) +// { +// Options = new(true); +// RegisteredTypes = Context.Options.Integrations.GetAllItems(); +// Logger = logger; +// } - /// - public Integration GetEmpty(string alias) - { - IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase)); +// /// +// public Integration GetEmpty(string alias) +// { +// IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase)); - if (type == null) - { - return null; - } +// if (type == null) +// { +// return null; +// } - try - { - Integration model = Activator.CreateInstance(type.ContentType) as Integration; - model.TypeAlias = type.Alias; - return model; - } - catch - { - Logger.LogWarning("Could not create integration with type {alias}", alias); - } +// try +// { +// Integration model = Activator.CreateInstance(type.ContentType) as Integration; +// model.TypeAlias = type.Alias; +// return model; +// } +// catch +// { +// Logger.LogWarning("Could not create integration with type {alias}", alias); +// } - return null; - } +// return null; +// } - /// - public async Task GetByAlias(string alias) - { - IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase)); - return await Load(type); - } +// /// +// public async Task GetByAlias(string alias) +// { +// IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase)); +// return await Load(type); +// } - /// - public async Task Get(string alias = null) where T : Integration, new() - { - IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.ContentType == typeof(T) && (alias.IsNullOrEmpty() || x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase))); - return await Load(type); - } +// /// +// public async Task Get(string alias = null) where T : Integration, new() +// { +// IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.ContentType == typeof(T) && (alias.IsNullOrEmpty() || x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase))); +// return await Load(type); +// } - /// - public async Task> GetByTag(string tag) - { - IEnumerable types = RegisteredTypes.Where(x => x.Tags.Contains(tag, StringComparer.InvariantCultureIgnoreCase)); +// /// +// public async Task> GetByTag(string tag) +// { +// IEnumerable types = RegisteredTypes.Where(x => x.Tags.Contains(tag, StringComparer.InvariantCultureIgnoreCase)); - if (!types.Any()) - { - return new List(); - } +// if (!types.Any()) +// { +// return new List(); +// } - string[] aliases = types.Select(x => x.Alias).ToArray(); - return await Session.Query().Where(x => x.TypeAlias.In(aliases)).ToListAsync(); - } +// string[] aliases = types.Select(x => x.Alias).ToArray(); +// return await Session.Query().Where(x => x.TypeAlias.In(aliases)).ToListAsync(); +// } - /// - public async Task Any(string tag) - { - return (await GetByTag(tag)).Any(x => x.IsActive); - } +// /// +// public async Task Any(string tag) +// { +// return (await GetByTag(tag)).Any(x => x.IsActive); +// } - /// - public override async Task> Load(ListQuery query) - { - List result = new(); - List models = await Session.Query().ToListAsync(); +// /// +// public override async Task> Load(ListQuery query) +// { +// List result = new(); +// List models = await Session.Query().ToListAsync(); - foreach (IntegrationType type in RegisteredTypes) - { - Integration model = models.FirstOrDefault(x => x.TypeAlias == type.Alias); +// foreach (IntegrationType type in RegisteredTypes) +// { +// Integration model = models.FirstOrDefault(x => x.TypeAlias == type.Alias); - if (model != null) - { - model.Name = type.Name; - result.Add(model); - } - } +// if (model != null) +// { +// model.Name = type.Name; +// result.Add(model); +// } +// } - return result.ToQueriedList(query); - } +// return result.ToQueriedList(query); +// } - /// - public async Task> GetTypesWithStatus() - { - List result = new(); - List models = await Session.Query().ToListAsync(); +// /// +// public async Task> GetTypesWithStatus() +// { +// List result = new(); +// List models = await Session.Query().ToListAsync(); - foreach (IntegrationType type in RegisteredTypes) - { - Integration model = models.FirstOrDefault(x => x.TypeAlias == type.Alias); +// foreach (IntegrationType type in RegisteredTypes) +// { +// Integration model = models.FirstOrDefault(x => x.TypeAlias == type.Alias); - result.Add(new() - { - Type = type, - Id = model?.Id, - IsActive = model?.IsActive ?? false, - IsConfigured = model != null - }); - } +// result.Add(new() +// { +// Type = type, +// Id = model?.Id, +// IsActive = model?.IsActive ?? false, +// IsConfigured = model != null +// }); +// } - return result.OrderByDescending(x => x.IsActive).ThenByDescending(x => x.IsConfigured).ThenByDescending(x => x.Type.Name).ToList(); - } +// return result.OrderByDescending(x => x.IsActive).ThenByDescending(x => x.IsConfigured).ThenByDescending(x => x.Type.Name).ToList(); +// } - /// - public override async Task> Save(Integration model) - { - if (model == null) - { - return EntityResult.Fail("@integration.errors.notfound"); - } +// /// +// public override async Task> Save(Integration model) +// { +// if (model == null) +// { +// return EntityResult.Fail("@integration.errors.notfound"); +// } - IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.Alias.Equals(model.TypeAlias, StringComparison.InvariantCultureIgnoreCase)); +// IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.Alias.Equals(model.TypeAlias, StringComparison.InvariantCultureIgnoreCase)); - if (type == null) - { - return EntityResult.Fail("@integration.errors.typenotfound"); - } +// if (type == null) +// { +// return EntityResult.Fail("@integration.errors.typenotfound"); +// } - string existingId = await Session.Query().Where(x => x.TypeAlias == type.Alias).Select(x => x.Id).FirstOrDefaultAsync(); +// string existingId = await Session.Query().Where(x => x.TypeAlias == type.Alias).Select(x => x.Id).FirstOrDefaultAsync(); - if (!existingId.IsNullOrEmpty() && existingId != model.Id) - { - return EntityResult.Fail("@integration.errors.multiplenotallowed"); - } +// if (!existingId.IsNullOrEmpty() && existingId != model.Id) +// { +// return EntityResult.Fail("@integration.errors.multiplenotallowed"); +// } - model.Alias = type.Alias; - model.Name = null; +// model.Alias = type.Alias; +// model.Name = null; - EntityResult result = await base.Save(model); +// EntityResult result = await base.Save(model); - if (result.IsSuccess) - { - result.Model.Name = type.Name; - } +// if (result.IsSuccess) +// { +// result.Model.Name = type.Name; +// } - return result; - } +// return result; +// } - /// - public async Task> Activate(string alias) - { - Integration model = await GetByAlias(alias); - if (model != null) - { - model.IsActive = true; - } - return await Save(model); - } +// /// +// public async Task> Activate(string alias) +// { +// Integration model = await GetByAlias(alias); +// if (model != null) +// { +// model.IsActive = true; +// } +// return await Save(model); +// } - /// - public async Task> Deactivate(string alias) - { - Integration model = await GetByAlias(alias); - if (model != null) - { - model.IsActive = false; - } - return await Save(model); - } +// /// +// public async Task> Deactivate(string alias) +// { +// Integration model = await GetByAlias(alias); +// if (model != null) +// { +// model.IsActive = false; +// } +// return await Save(model); +// } - /// - public async Task> DeleteByAlias(string alias) - { - return await base.Delete(await GetByAlias(alias)); - } +// /// +// public async Task> DeleteByAlias(string alias) +// { +// return await base.Delete(await GetByAlias(alias)); +// } - /// - /// Get integration data from database - /// - protected async Task Load(IntegrationType type) where T : Integration, new() - { - if (type == null) - { - return default; - } +// /// +// /// Get integration data from database +// /// +// protected async Task Load(IntegrationType type) where T : Integration, new() +// { +// if (type == null) +// { +// return default; +// } - T entity = await Session.Query().FirstOrDefaultAsync(x => x.TypeAlias == type.Alias); +// T entity = await Session.Query().FirstOrDefaultAsync(x => x.TypeAlias == type.Alias); - if (entity != null) - { - entity.Name = type.Name; - entity.TypeAlias = type.Alias; - } +// if (entity != null) +// { +// entity.Name = type.Name; +// entity.TypeAlias = type.Alias; +// } - return WhenActive(entity) as T; - } - } +// return WhenActive(entity) as T; +// } +// } - public interface IIntegrationsCollection - { - /// - /// Get all registered integration types - /// - IReadOnlyCollection RegisteredTypes { get; } +// public interface IIntegrationsCollection +// { +// /// +// /// Get all registered integration types +// /// +// IReadOnlyCollection RegisteredTypes { get; } - /// - /// Get new integration model for the specified integration type alias - /// - Integration GetEmpty(string alias); +// /// +// /// Get new integration model for the specified integration type alias +// /// +// Integration GetEmpty(string alias); - /// - /// Get integration by an alias - /// - Task GetByAlias(string alias); +// /// +// /// Get integration by an alias +// /// +// Task GetByAlias(string alias); - /// - /// Get an integration by type and optional alias - /// - Task Get(string alias = null) where T : Integration, new(); +// /// +// /// Get an integration by type and optional alias +// /// +// Task Get(string alias = null) where T : Integration, new(); - /// - /// Get all integrations by a certain tag - /// - Task> GetByTag(string tag); +// /// +// /// Get all integrations by a certain tag +// /// +// Task> GetByTag(string tag); - /// - /// Check if any integrations of certain tag are activated - /// - Task Any(string tag); +// /// +// /// Check if any integrations of certain tag are activated +// /// +// Task Any(string tag); - /// - /// Get all integrations with the specified query - /// - Task> Load(ListQuery query); +// /// +// /// Get all integrations with the specified query +// /// +// Task> Load(ListQuery query); - /// - /// Get all integration types with their configuration status - /// - Task> GetTypesWithStatus(); +// /// +// /// Get all integration types with their configuration status +// /// +// Task> GetTypesWithStatus(); - /// - /// Saves an integration - /// - Task> Save(Integration model); +// /// +// /// Saves an integration +// /// +// Task> Save(Integration model); - /// - /// Activates a configured integration - /// - Task> Activate(string alias); +// /// +// /// Activates a configured integration +// /// +// Task> Activate(string alias); - /// - /// Disables a configured integration - /// - Task> Deactivate(string alias); +// /// +// /// Disables a configured integration +// /// +// Task> Deactivate(string alias); - /// - /// Deletes configuration of an integration and disables it - /// - Task> DeleteByAlias(string alias); - } -} +// /// +// /// Deletes configuration of an integration and disables it +// /// +// Task> DeleteByAlias(string alias); +// } +//} diff --git a/zero.Core/Configuration/OptionsEnumerable.cs b/zero.Core/Configuration/OptionsEnumerable.cs deleted file mode 100644 index ba517c52..00000000 --- a/zero.Core/Configuration/OptionsEnumerable.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace zero.Configuration; - -public abstract class OptionsEnumerable -{ - protected List Items { get; set; } = new List(); - - - public IReadOnlyCollection GetAllItems() - { - return Items.AsReadOnly(); - } - - - public void RemoveAt(int index) - { - Items.RemoveAt(index); - } - - - public bool Remove(T item) - { - return Items.Remove(item); - } -} - - -public interface IOptionsEnumerable { } \ No newline at end of file diff --git a/zero.Core/Configuration/ServiceCollectionExtensions.cs b/zero.Core/Configuration/ServiceCollectionExtensions.cs index a89ad3a6..a48201ce 100644 --- a/zero.Core/Configuration/ServiceCollectionExtensions.cs +++ b/zero.Core/Configuration/ServiceCollectionExtensions.cs @@ -12,6 +12,7 @@ internal static class ServiceCollectionExtensions { opts.Version = "0.0.1-alpha.1"; opts.ZeroPath = "/zero"; + opts.TokenExpiration = TimeSpan.FromHours(3); }); services.AddTransient(factory => factory.GetService>().CurrentValue); return services; diff --git a/zero.Core/Configuration/ZeroOptions.cs b/zero.Core/Configuration/ZeroOptions.cs index c97ed144..1960ecf8 100644 --- a/zero.Core/Configuration/ZeroOptions.cs +++ b/zero.Core/Configuration/ZeroOptions.cs @@ -6,6 +6,19 @@ namespace zero.Configuration; public class ZeroOptions : IZeroOptions { + /// + public bool Initialized => !String.IsNullOrEmpty(For()?.Database); + + /// + public string Version { get; set; } + + /// + public string ZeroPath { get; set; } + + /// + public TimeSpan TokenExpiration { get; set; } + + protected IServiceProvider ServiceProvider { get; set; } protected ConcurrentDictionary OptionsCache { get; private set; } = new(); @@ -14,16 +27,6 @@ public class ZeroOptions : IZeroOptions public ZeroOptions(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; - - //SupportedLanguages = new string[2] { "en-US", "de-DE" }; - //DefaultLanguage = SupportedLanguages[0]; - //TokenExpiration = 60 * 3; - //BackofficePath = "/zero"; - //ExcludedPaths = new() { }; - //Raven = new() - //{ - // CollectionPrefix = String.Empty - //}; } @@ -41,54 +44,6 @@ public class ZeroOptions : IZeroOptions return value as TOptions; } - - - - /// - public bool Initialized => !String.IsNullOrEmpty(For()?.Database); - - /// - public string Version { get; set; } - - /// - public string ZeroPath { get; set; } - - ///// - //public string DefaultLanguage { get; set; } - - ///// - //public string[] SupportedLanguages { get; private set; } - - ///// - //public int TokenExpiration { get; set; } - - ///// - //public RavenOptions Raven { get; set; } - - ///// - //public ApplicationOptions Applications { get; set; } - - - ///// - //public FeatureOptions Features { get; private set; } - - ///// - //public PageOptions Pages { get; private set; } - - ///// - //public ModuleOptions Modules { get; private set; } - - ///// - //public SettingsOptions Settings { get; private set; } - - ///// - //public SpaceOptions Spaces { get; private set; } - - ///// - //public IntegrationOptions Integrations { get; private set; } - - ///// - //public BlueprintOptions Blueprints { get; private set; } } @@ -110,33 +65,13 @@ public interface IZeroOptions /// bool Initialized { get; } + /// + /// Expiration of a generated change token for an entity + /// + TimeSpan TokenExpiration { get; set; } + /// /// Get typed options (proxy to IOptions) /// TOptions For() where TOptions : class; - - ///// - ///// Default language ISO code - ///// - //string DefaultLanguage { get; set; } - - ///// - ///// Language ISO codes which are supported by the zero backoffice - ///// - //string[] SupportedLanguages { get; } - - ///// - ///// Expiration in minutes of a generated change token for an entity - ///// - //int TokenExpiration { get; set; } - - ///// - ///// RavenDB configuration data - ///// - //RavenOptions Raven { get; set; } - - ///// - ///// Application options - ///// - //ApplicationOptions Applications { get; set; } } diff --git a/zero.Core/FileStorage/Paths.cs b/zero.Core/FileStorage/Paths.cs index 435a482c..602ab9de 100644 --- a/zero.Core/FileStorage/Paths.cs +++ b/zero.Core/FileStorage/Paths.cs @@ -17,19 +17,17 @@ public class Paths : IPaths protected IWebHostEnvironment Env { get; set; } - private bool IsDebug { get; set; } - public const string MEDIA_FOLDER = "Uploads"; public const char PATH_SEPARATOR = '/'; - private static char[] InvalidFilenameChars = null; + static char[] InvalidFilenameChars = null; - private const char REPLACEMENT_CHAR = '-'; + const char REPLACEMENT_CHAR = '-'; - private FileExtensionContentTypeProvider FileExtensionContentTypeProvider { get; set; } + FileExtensionContentTypeProvider FileExtensionContentTypeProvider { get; set; } - private static Dictionary Replacements = new Dictionary() + static readonly Dictionary Replacements = new() { { 'ä', "ae" }, { 'ü', "ue" }, @@ -38,10 +36,9 @@ public class Paths : IPaths }; - public Paths(IWebHostEnvironment env, bool isDebug) + public Paths(IWebHostEnvironment env) { Env = env; - IsDebug = isDebug; WebRoot = env.WebRootPath; ContentRoot = env.ContentRootPath; SecureRoot = Path.Combine(ContentRoot, "wwwroot.secure"); @@ -133,9 +130,9 @@ public class Paths : IPaths // lowercase for string value = value.ToLower(); - StringBuilder sb = new StringBuilder(value.Length); + StringBuilder sb = new(value.Length); - var invalids = InvalidFilenameChars ?? (InvalidFilenameChars = Path.GetInvalidFileNameChars()); + var invalids = InvalidFilenameChars ??= Path.GetInvalidFileNameChars(); bool changed = false; for (int i = 0; i < value.Length; i++) diff --git a/zero.Core/Identity/RavenUserStore(TUser).cs b/zero.Core/Identity/RavenUserStore(TUser).cs index 27f66d50..666994a1 100644 --- a/zero.Core/Identity/RavenUserStore(TUser).cs +++ b/zero.Core/Identity/RavenUserStore(TUser).cs @@ -57,7 +57,7 @@ public partial class RavenUserStore : { IZeroDocumentSession session = Store.Session(Global); - if (await IsEmailReserved(session, user)) + if (await IsEmailReserved(session, user, cancellationToken)) { return IdentityResult.Failed(new IdentityError { @@ -103,7 +103,7 @@ public partial class RavenUserStore : }); } - if (source.Email != user.Email && await IsEmailReserved(Store.Session(Global), user)) + if (source.Email != user.Email && await IsEmailReserved(Store.Session(Global), user, cancellationToken)) { return IdentityResult.Failed(new IdentityError { @@ -337,8 +337,8 @@ public partial class RavenUserStore : /// public async Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken) { - UserClaim userClaim = new UserClaim(claim); - return await ScopeQuery(Store.Session(Global).Query()).Where(x => x.Claims.Any(c => c.Type == userClaim.Type && c.Value == userClaim.Value)).ToListAsync(); + UserClaim userClaim = new(claim); + return await ScopeQuery(Store.Session(Global).Query()).Where(x => x.Claims.Any(c => c.Type == userClaim.Type && c.Value == userClaim.Value)).ToListAsync(token: cancellationToken); } @@ -360,8 +360,8 @@ public partial class RavenUserStore : public async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken) { IZeroDocumentSession session = Store.Session(Global); - UserClaim userClaim = new UserClaim(claim); - UserClaim newUserClaim = new UserClaim(newClaim); + UserClaim userClaim = new(claim); + UserClaim newUserClaim = new(newClaim); user.Claims.Remove(userClaim); user.Claims.Add(newUserClaim); diff --git a/zero.Core/Identity/Services/AuthenticationService.cs b/zero.Core/Identity/Services/AuthenticationService.cs index 403b26cf..163b40f1 100644 --- a/zero.Core/Identity/Services/AuthenticationService.cs +++ b/zero.Core/Identity/Services/AuthenticationService.cs @@ -22,6 +22,12 @@ public class AuthenticationService : IAuthenticationService /// public bool IsLoggedIn() { + //ClaimsPrincipal principal = HttpContextAccessor.HttpContext.User; + //bool isAuthenticated = principal.Identity.IsAuthenticated; + //bool isZeroUser = principal.HasClaim(Constants.Auth.Claims.IsZero, PermissionsValue.True); + //bool isSignedIn = SignInManager.IsSignedIn(principal); + //return isAuthenticated && isZeroUser && isSignedIn; + return SignInManager.IsSignedIn(Context.BackofficeUser); } @@ -47,30 +53,10 @@ public class AuthenticationService : IAuthenticationService } - /// - public IList GetPermissions(string prefix = null) - { - return new List(); // TODO - //return Context.BackofficeUser.Claims - // .Where(claim => claim.Type == Constants.Auth.Claims.Permission && (prefix == null || claim.Value.StartsWith(prefix))) - // .Select(claim => Permission.FromClaim(claim, prefix)) - // .ToList(); - } - - - /// - public Permission GetPermission(string key = null) - { - return new Permission(); - //Claim claim = Context.BackofficeUser.Claims.FirstOrDefault(claim => claim.Type == Constants.Auth.Claims.Permission && claim.Value.StartsWith(key + ":")); - //return Permission.FromClaim(claim); - } - - /// public async Task Login(string email, string password, bool isPersistent) { - EntityResult result = new EntityResult(); + EntityResult result = new(); ZeroUser user = await SignInManager.UserManager.FindByNameAsync(email); @@ -127,40 +113,6 @@ public class AuthenticationService : IAuthenticationService { return SignInManager.UserManager.GetUserId(Context.BackofficeUser); } - - - /// - public async Task TrySwitchApp(string appId) - { - IZeroDocumentSession session = Store.Session(global: true); - ZeroUser user = await GetUser(); - - if (user == null || appId.IsNullOrEmpty()) - { - return false; - } - - string[] allowedAppIds = user.GetAllowedAppIds(); - - bool isMainId = appId.Equals(user.AppId, StringComparison.InvariantCultureIgnoreCase); - bool isAllowedId = allowedAppIds.Contains(appId, StringComparer.InvariantCultureIgnoreCase); - - if (user.IsSuper || isMainId || isAllowedId) - { - user.CurrentAppId = appId; - - //byte[] bytes = new byte[20]; - //RandomNumberGenerator.Fill(bytes); - //user.SecurityStamp = Base32.ToBase32(bytes); // TODO update security stamp but Base32 is .net core internal - - await session.StoreAsync(user); - await session.SaveChangesAsync(); - - return true; - } - - return false; - } } @@ -200,19 +152,4 @@ public interface IAuthenticationService /// Get the ID of the currently logged in user /// string GetUserId(); - - /// - /// Get all permissions for the current user with an optional prefix - /// - IList GetPermissions(string prefix = null); - - /// - /// Get a single permissions by key - /// - Permission GetPermission(string key = null); - - /// - /// Tries to switch the currently loaded backoffice application for the current user - /// - Task TrySwitchApp(string appId); } \ No newline at end of file diff --git a/zero.Core/Identity/Services/AuthorizationService.cs b/zero.Core/Identity/Services/AuthorizationService.cs index 09f9f899..e7c772dc 100644 --- a/zero.Core/Identity/Services/AuthorizationService.cs +++ b/zero.Core/Identity/Services/AuthorizationService.cs @@ -71,7 +71,7 @@ public class AuthorizationService : IAuthorizationService public EntityPermission GetPermissionForEntity(T model, string permissionKey) where T : ZeroEntity { - EntityPermission result = new EntityPermission(); + EntityPermission result = new(); if (!IsLoggedIn()) { diff --git a/zero.Core/Identity/Services/UserService.cs b/zero.Core/Identity/Services/UserService.cs index dc8df408..77692739 100644 --- a/zero.Core/Identity/Services/UserService.cs +++ b/zero.Core/Identity/Services/UserService.cs @@ -212,6 +212,40 @@ public class UserService : BackofficeApi, IUserService return EntityResult.Success(user); } + + + /// + public async Task TrySwitchApp(string appId) + { + IZeroDocumentSession session = Store.Session(global: true); + ZeroUser user = await GetUser(); + + if (user == null || appId.IsNullOrEmpty()) + { + return false; + } + + string[] allowedAppIds = user.GetAllowedAppIds(); + + bool isMainId = appId.Equals(user.AppId, StringComparison.InvariantCultureIgnoreCase); + bool isAllowedId = allowedAppIds.Contains(appId, StringComparer.InvariantCultureIgnoreCase); + + if (user.IsSuper || isMainId || isAllowedId) + { + user.CurrentAppId = appId; + + //byte[] bytes = new byte[20]; + //RandomNumberGenerator.Fill(bytes); + //user.SecurityStamp = Base32.ToBase32(bytes); // TODO update security stamp but Base32 is .net core internal + + await session.StoreAsync(user); + await session.SaveChangesAsync(); + + return true; + } + + return false; + } } @@ -272,4 +306,9 @@ public interface IUserService : IBackofficeApi /// Disables a user /// Task> Disable(ZeroUser user); + + /// + /// Tries to switch the currently loaded backoffice application for the current user + /// + Task TrySwitchApp(string appId); } \ No newline at end of file diff --git a/zero.Core/Localization/CultureService.cs b/zero.Core/Localization/CultureService.cs index 18f56d1c..b1a111a4 100644 --- a/zero.Core/Localization/CultureService.cs +++ b/zero.Core/Localization/CultureService.cs @@ -10,7 +10,7 @@ public class CultureService : ICultureService return CultureInfo.GetCultures(CultureTypes.AllCultures) .Where(x => !x.Name.IsNullOrWhiteSpace()) .Select(x => new CultureInfo(x.Name)) - .Where(x => codes.Length > 0 ? codes.Contains(x.Name, StringComparer.InvariantCultureIgnoreCase) : true) + .Where(x => codes.Length == 0 || codes.Contains(x.Name, StringComparer.InvariantCultureIgnoreCase)) .OrderBy(x => x.DisplayName) .Select(x => new Culture() { diff --git a/zero.Core/Media/MediaCollection.cs b/zero.Core/Media/MediaCollection.cs index c8b3c6a6..ff1d1526 100644 --- a/zero.Core/Media/MediaCollection.cs +++ b/zero.Core/Media/MediaCollection.cs @@ -137,24 +137,23 @@ namespace zero.Core.Collections // write additional image data + thumbnail if (media.Type == MediaType.Image) { - using (Image image = Image.Load(filePath)) + using Image image = Image.Load(filePath); + + media.ImageMeta = GetImageMeta(image); + + Image imageFrame = media.ImageMeta.Frames > 1 ? image.Frames.CloneFrame(0) : image; + + media.PreviewSource = SaveThumbnail(media, imageFrame, PREVIEW_EXTENSION, new ResizeOptions() { - media.ImageMeta = GetImageMeta(image); + Size = new Size(210, 210), + Mode = ResizeMode.Min + }); - Image imageFrame = media.ImageMeta.Frames > 1 ? image.Frames.CloneFrame(0) : image; - - media.PreviewSource = SaveThumbnail(media, imageFrame, PREVIEW_EXTENSION, new ResizeOptions() - { - Size = new Size(210, 210), - Mode = ResizeMode.Min - }); - - media.ThumbnailSource = SaveThumbnail(media, imageFrame, THUMB_EXTENSION, new ResizeOptions() - { - Size = new Size(100, 100), - Mode = ResizeMode.Max - }); - } + media.ThumbnailSource = SaveThumbnail(media, imageFrame, THUMB_EXTENSION, new ResizeOptions() + { + Size = new Size(100, 100), + Mode = ResizeMode.Max + }); } return media; diff --git a/zero.Core/Pages/Modules/IModuleTypeHandler.cs b/zero.Core/Pages/Modules/IModuleTypeHandler.cs new file mode 100644 index 00000000..c7ca1700 --- /dev/null +++ b/zero.Core/Pages/Modules/IModuleTypeHandler.cs @@ -0,0 +1,6 @@ +namespace zero.Pages; + +public interface IModuleTypeHandler : IHandler +{ + IEnumerable GetAllowedModuleTypes(Application application, IEnumerable registeredTypes, Page page = default, string[] tags = default); +} \ No newline at end of file diff --git a/zero.Core/Pages/Modules/Module.cs b/zero.Core/Pages/Modules/Module.cs deleted file mode 100644 index b91a6cee..00000000 --- a/zero.Core/Pages/Modules/Module.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace zero.Core.Entities -{ - /// - /// A module can consist of unlimited properties and be rendered as you wish - /// The backoffice rendering is done by an IRenderer - /// - public class Module : ZeroIdEntity - { - /// - /// Sort order - /// - public uint Sort { get; set; } - - /// - /// Whether the module is visible in the frontend - /// - public bool IsActive { get; set; } - - /// - /// Alias of the used module type - /// - public string ModuleTypeAlias { get; set; } - } -} \ No newline at end of file diff --git a/zero.Core/Pages/Modules/ModulePermissions.cs b/zero.Core/Pages/Modules/ModulePermissions.cs index 3adccf27..5b37ad4d 100644 --- a/zero.Core/Pages/Modules/ModulePermissions.cs +++ b/zero.Core/Pages/Modules/ModulePermissions.cs @@ -1,17 +1,17 @@ -using zero.Core; -using zero.Core.Identity; +//using zero.Core; +//using zero.Core.Identity; -namespace zero.Web.Defaults -{ - public class ModulePermissions : PermissionCollection - { - public ModulePermissions() - { - Alias = Constants.PermissionCollections.Modules; - Label = "@permission.collections.modules"; - Description = "@permission.collections.modules_description"; +//namespace zero.Web.Defaults +//{ +// public class ModulePermissions : PermissionCollection +// { +// public ModulePermissions() +// { +// Alias = Constants.PermissionCollections.Modules; +// Label = "@permission.collections.modules"; +// Description = "@permission.collections.modules_description"; - // Items get added at runtime - } - } -} +// // Items get added at runtime +// } +// } +//} diff --git a/zero.Core/Pages/Modules/ModuleType.cs b/zero.Core/Pages/Modules/ModuleType.cs deleted file mode 100644 index 0f2cd5c8..00000000 --- a/zero.Core/Pages/Modules/ModuleType.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace zero.Core.Entities -{ - /// - /// A module type holds information about a Module implementation - /// - public class ModuleType : ModuleType where T : Module, new() - { - public ModuleType() : base(typeof(T)) { } - } - - - /// - /// A module type holds information about a Module implementation - /// - public class ModuleType - { - /// - /// Type of the associated module class - /// - public Type ContentType { get; private set; } - - /// - /// Alias for querying - /// - public string Alias { get; set; } - - /// - /// Name of the module type - /// - public string Name { get; set; } - - /// - /// Optional description - /// - public string Description { get; set; } - - /// - /// Icon of the module type - /// - public string Icon { get; set; } - - /// - /// Optionally group modules together - /// - public string Group { get; set; } - - /// - /// Defining tags for a module type allows you to query for them - /// - public List Tags { get; set; } = new List(); - - /// - /// Page types where this module type is not allowed. - /// This will only work when operating within the page context. - /// - public List DisallowedPageTypes { get; set; } = new List(); - - - public ModuleType(Type type) - { - ContentType = type; - } - - public static ModuleType Convert(ModuleType model) where T : Module, new() - { - return new ModuleType(model.ContentType) - { - Alias = model.Alias, - Name = model.Name, - Description = model.Description, - Icon = model.Icon, - Group = model.Group, - Tags = model.Tags, - DisallowedPageTypes = model.DisallowedPageTypes - }; - } - } -} diff --git a/zero.Core/Pages/Modules/ModulesApi.cs b/zero.Core/Pages/Modules/ModulesApi.cs index 9f038912..b67dc7b9 100644 --- a/zero.Core/Pages/Modules/ModulesApi.cs +++ b/zero.Core/Pages/Modules/ModulesApi.cs @@ -24,10 +24,10 @@ namespace zero.Core.Api /// - public async Task> GetModuleTypes(string[] tags = default, string pageId = default) + public async Task> GetModuleTypes(string[] tags = default, string pageId = default) { - IEnumerable types = Options.Modules.GetAllItems(); - List modules = types.ToList(); + IEnumerable types = Options.Modules.GetAllItems(); + List modules = types.ToList(); Page page = null; if (!pageId.IsNullOrEmpty()) @@ -53,7 +53,7 @@ namespace zero.Core.Api /// - public ModuleType GetModuleType(string alias) + public PageModuleType GetModuleType(string alias) { return Options.Modules.GetAllItems().FirstOrDefault(x => x.Alias == alias); } @@ -65,11 +65,11 @@ namespace zero.Core.Api /// /// Get all available module types (can be limited to the passed tags) /// - Task> GetModuleTypes(string[] tags = default, string pageId = default); + Task> GetModuleTypes(string[] tags = default, string pageId = default); /// /// Get a specific module type by alias /// - ModuleType GetModuleType(string alias); + PageModuleType GetModuleType(string alias); } } diff --git a/zero.Core/Pages/Modules/PageModule.cs b/zero.Core/Pages/Modules/PageModule.cs new file mode 100644 index 00000000..08811191 --- /dev/null +++ b/zero.Core/Pages/Modules/PageModule.cs @@ -0,0 +1,23 @@ +namespace zero.Pages; + +/// +/// A module can consist of unlimited properties and be rendered as you wish +/// The backoffice rendering is done by an IRenderer +/// +public class PageModule : ZeroIdEntity +{ + /// + /// Sort order + /// + public uint Sort { get; set; } + + /// + /// Whether the module is visible in the frontend + /// + public bool IsActive { get; set; } + + /// + /// Alias of the used module type + /// + public string ModuleTypeAlias { get; set; } +} \ No newline at end of file diff --git a/zero.Core/Configuration/ConfigurationParts/ModuleOptions.cs b/zero.Core/Pages/Modules/PageModuleOptions.cs similarity index 64% rename from zero.Core/Configuration/ConfigurationParts/ModuleOptions.cs rename to zero.Core/Pages/Modules/PageModuleOptions.cs index b2682a5c..2b39de7a 100644 --- a/zero.Core/Configuration/ConfigurationParts/ModuleOptions.cs +++ b/zero.Core/Pages/Modules/PageModuleOptions.cs @@ -1,20 +1,16 @@ -using System; -using System.Collections.Generic; -using zero.Core.Entities; +namespace zero.Pages; -namespace zero.Configuration; - -public class ModuleOptions : OptionsEnumerable, IOptionsEnumerable +public class PageModuleOptions : List { - public void Add(ModuleType moduleType) where T : Module, new() + public void Add(PageModuleType moduleType) where T : PageModule, new() { - Items.Add(ModuleType.Convert(moduleType)); + Add(PageModuleType.Convert(moduleType)); } - public void Add(string alias, string name, string description, string icon, string group = null, List tags = null, List disallowedPageTypes = null) where T : Module, new() + public void Add(string alias, string name, string description, string icon, string group = null, List tags = null, List disallowedPageTypes = null) where T : PageModule, new() { - Items.Add(new ModuleType(typeof(T)) + Add(new PageModuleType(typeof(T)) { Alias = alias, Name = name, @@ -29,7 +25,7 @@ public class ModuleOptions : OptionsEnumerable, IOptionsEnumerable public void Add(Type type, string alias, string name, string description, string icon, string group = null, List tags = null, List disallowedPageTypes = null) { - Items.Add(new ModuleType(type) + Add(new PageModuleType(type) { Alias = alias, Name = name, @@ -40,4 +36,4 @@ public class ModuleOptions : OptionsEnumerable, IOptionsEnumerable DisallowedPageTypes = disallowedPageTypes ?? new List() }); } -} +} \ No newline at end of file diff --git a/zero.Core/Pages/Modules/PageModuleType.cs b/zero.Core/Pages/Modules/PageModuleType.cs new file mode 100644 index 00000000..6090ae65 --- /dev/null +++ b/zero.Core/Pages/Modules/PageModuleType.cs @@ -0,0 +1,77 @@ +namespace zero.Pages; + +/// +/// A module type holds information about a Module implementation +/// +public class PageModuleType : PageModuleType where T : PageModule, new() +{ + public PageModuleType() : base(typeof(T)) { } +} + + +/// +/// A module type holds information about a Module implementation +/// +public class PageModuleType +{ + /// + /// Type of the associated module class + /// + public Type ContentType { get; private set; } + + /// + /// Alias for querying + /// + public string Alias { get; set; } + + /// + /// Name of the module type + /// + public string Name { get; set; } + + /// + /// Optional description + /// + public string Description { get; set; } + + /// + /// Icon of the module type + /// + public string Icon { get; set; } + + /// + /// Optionally group modules together + /// + public string Group { get; set; } + + /// + /// Defining tags for a module type allows you to query for them + /// + public List Tags { get; set; } = new List(); + + /// + /// Page types where this module type is not allowed. + /// This will only work when operating within the page context. + /// + public List DisallowedPageTypes { get; set; } = new List(); + + + public PageModuleType(Type type) + { + ContentType = type; + } + + public static PageModuleType Convert(PageModuleType model) where T : PageModule, new() + { + return new PageModuleType(model.ContentType) + { + Alias = model.Alias, + Name = model.Name, + Description = model.Description, + Icon = model.Icon, + Group = model.Group, + Tags = model.Tags, + DisallowedPageTypes = model.DisallowedPageTypes + }; + } +} \ No newline at end of file diff --git a/zero.Core/Configuration/ConfigurationParts/PageOptions.cs b/zero.Core/Pages/PageOptions.cs similarity index 62% rename from zero.Core/Configuration/ConfigurationParts/PageOptions.cs rename to zero.Core/Pages/PageOptions.cs index 132f1664..6086671a 100644 --- a/zero.Core/Configuration/ConfigurationParts/PageOptions.cs +++ b/zero.Core/Pages/PageOptions.cs @@ -1,28 +1,19 @@ -using System; -using System.Linq; -using zero.Core.Entities; +namespace zero.Pages; -namespace zero.Configuration; - -public class PageOptions : OptionsEnumerable, IOptionsEnumerable +public class PageOptions : List { - public PageOptions() - { - - } - public string Root { get; set; } = Constants.Pages.DefaultRootPageTypeAlias; public void Add(PageType pageType) where T : Page, new() { - Items.Add(PageType.Convert(pageType)); + Add(PageType.Convert(pageType)); } public void Add(string alias, string name, string description, string icon) where T : Page, new() { - Items.Add(new PageType(typeof(T)) + Add(new PageType(typeof(T)) { Alias = alias, Name = name, @@ -34,7 +25,7 @@ public class PageOptions : OptionsEnumerable, IOptionsEnumerable public void Add(Type type, string alias, string name, string description, string icon) { - Items.Add(new PageType(type) + Add(new PageType(type) { Alias = alias, Name = name, @@ -45,6 +36,6 @@ public class PageOptions : OptionsEnumerable, IOptionsEnumerable public PageType GetByAlias(string alias) { - return Items.FirstOrDefault(x => x.Alias == alias); + return this.FirstOrDefault(x => x.Alias == alias); } } diff --git a/zero.Core/Pages/ServiceCollectionExtensions.cs b/zero.Core/Pages/ServiceCollectionExtensions.cs index 6174ef35..8cc88fd6 100644 --- a/zero.Core/Pages/ServiceCollectionExtensions.cs +++ b/zero.Core/Pages/ServiceCollectionExtensions.cs @@ -1,14 +1,19 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; namespace zero.Pages; internal static class ServiceCollectionExtensions { - public static IServiceCollection AddZeroPages(this IServiceCollection services) + public static IServiceCollection AddZeroPages(this IServiceCollection services, IConfiguration config) { services.AddScoped(); services.AddScoped(); + services.AddOptions().Bind(config.GetSection("Zero:Pages")); + services.AddOptions().Bind(config.GetSection("Zero:PageModules")); + services.Configure(opts => { RavenOptions raven = opts.For(); diff --git a/zero.Core/Persistence/RavenOptions.cs b/zero.Core/Persistence/RavenOptions.cs index a45909a2..fb61c851 100644 --- a/zero.Core/Persistence/RavenOptions.cs +++ b/zero.Core/Persistence/RavenOptions.cs @@ -9,13 +9,13 @@ public class RavenOptions public string Database { get; set; } - public string CollectionPrefix { get; set; } + public string CollectionPrefix { get; set; } = String.Empty; public RavenIndexesOptions Indexes { get; set; } = new(); } -public class RavenIndexesOptions : OptionsEnumerable, IOptionsEnumerable +public class RavenIndexesOptions : List { public class Map { @@ -35,17 +35,17 @@ public class RavenIndexesOptions : OptionsEnumerable, I public void Add() where T : IZeroIndexDefinition, new() { - Items.Add(new(typeof(T), () => new T())); + base.Add(new Map(typeof(T), () => new T())); } public void Add(Type indexType) { - Items.Add(new(indexType, () => (IZeroIndexDefinition)Activator.CreateInstance(indexType))); + base.Add(new Map(indexType, () => (IZeroIndexDefinition)Activator.CreateInstance(indexType))); } public void Add(T index) where T : IZeroIndexDefinition { - Items.Add(new(typeof(T), () => index)); + base.Add(new Map(typeof(T), () => index)); } public void AddRange(params Type[] indexes) @@ -65,17 +65,17 @@ public class RavenIndexesOptions : OptionsEnumerable, I public void Replace(Type origin, Type replaceWith) { - var item = Items.FirstOrDefault(x => x.Type == origin); + var item = this.FirstOrDefault(x => x.Type == origin); if (item != null) { - Items.Remove(item); + Remove(item); } Add(replaceWith); } public IEnumerable BuildAll(IZeroOptions options, IDocumentStore store) { - foreach (Map map in Items) + foreach (Map map in this) { IZeroIndexDefinition index = map.CreateIndex.Compile().Invoke(); index.Setup(options, store); @@ -86,7 +86,7 @@ public class RavenIndexesOptions : OptionsEnumerable, I } -public class RavenIndexModifiersOptions : OptionsEnumerable, IOptionsEnumerable +public class RavenIndexModifiersOptions : List { public class Modifier { @@ -97,7 +97,7 @@ public class RavenIndexModifiersOptions : OptionsEnumerable(Action modify) where T : IZeroIndexDefinition, new() { - Items.Add(new() + Add(new() { Type = typeof(T), Modify = x => modify((T)x) @@ -110,7 +110,7 @@ public class RavenIndexModifiersOptions : OptionsEnumerable GetAllForType(Type type) { - foreach (Modifier modifier in Items.Where(x => x.Type.IsAssignableFrom(type))) + foreach (Modifier modifier in this.Where(x => x.Type.IsAssignableFrom(type))) { yield return modifier; } diff --git a/zero.Core/Persistence/ServiceCollectionExtensions.cs b/zero.Core/Persistence/ServiceCollectionExtensions.cs index c21f0696..442d71f9 100644 --- a/zero.Core/Persistence/ServiceCollectionExtensions.cs +++ b/zero.Core/Persistence/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Raven.Client.Documents; using Raven.Client.Http; @@ -6,12 +7,15 @@ namespace zero.Persistence; internal static class ServiceCollectionExtensions { - public static IServiceCollection AddZeroPersistence(this IServiceCollection services) + public static IServiceCollection AddZeroPersistence(this IServiceCollection services, IConfiguration config) { services.AddSingleton(); services.AddSingleton(CreateRavenStore); services.AddScoped(); services.AddScoped(); + + services.AddOptions().Bind(config.GetSection("Zero:Raven")); + return services; } diff --git a/zero.Core/Configuration/ConfigurationParts/SpaceOptions.cs b/zero.Core/Spaces/SpaceOptions.cs similarity index 79% rename from zero.Core/Configuration/ConfigurationParts/SpaceOptions.cs rename to zero.Core/Spaces/SpaceOptions.cs index f0f789ea..62c21151 100644 --- a/zero.Core/Configuration/ConfigurationParts/SpaceOptions.cs +++ b/zero.Core/Spaces/SpaceOptions.cs @@ -1,25 +1,18 @@ -using System.Linq; -using zero.Core.Entities; +using zero.Core.Entities; -namespace zero.Configuration; +namespace zero.Spaces; -public class SpaceOptions : OptionsEnumerable, IOptionsEnumerable +public class SpaceOptions : List { - public SpaceOptions() - { - - } - - public void Add() where T : Space, new() { - Items.Add(new T()); + Add(new T()); } public void AddList(string alias, string name, string description, string icon) where T : SpaceContent, new() { - Items.Add(new Space() + Add(new Space() { Alias = alias, View = SpaceView.List, @@ -33,7 +26,7 @@ public class SpaceOptions : OptionsEnumerable, IOptionsEnumerable public void AddEditor(string alias, string name, string description, string icon) where T : SpaceContent, new() { - Items.Add(new Space() + Add(new Space() { Alias = alias, View = SpaceView.Editor, @@ -47,7 +40,7 @@ public class SpaceOptions : OptionsEnumerable, IOptionsEnumerable public void AddSeparator() { - Space lastSpace = Items.LastOrDefault(); + Space lastSpace = this.LastOrDefault(); if (lastSpace != null) { @@ -58,7 +51,7 @@ public class SpaceOptions : OptionsEnumerable, IOptionsEnumerable public void AddCustom(string componentPath, string alias, string name, string description, string icon) where T : SpaceContent, new() { - Items.Add(new Space() + Add(new Space() { Alias = alias, View = SpaceView.Custom, @@ -73,7 +66,7 @@ public class SpaceOptions : OptionsEnumerable, IOptionsEnumerable public void AddCustom(string componentPath, string alias, string name, string description, string icon) { - Items.Add(new Space() + Add(new Space() { Alias = alias, View = SpaceView.Custom, diff --git a/zero.Core/Stores/EntityStore.cs b/zero.Core/Stores/EntityStore.cs index d78e6264..d49750e9 100644 --- a/zero.Core/Stores/EntityStore.cs +++ b/zero.Core/Stores/EntityStore.cs @@ -50,9 +50,6 @@ public abstract class EntityStore : IEntityStore where T : ZeroIdEntity, n /// public virtual Task> LoadAll() => Operations.LoadAll(); - /// - public virtual IAsyncEnumerable Stream() => Operations.Stream(); - /// public virtual IAsyncEnumerable Stream(Func, IQueryable> expression) => Operations.Stream(expression); @@ -65,15 +62,6 @@ public abstract class EntityStore : IEntityStore where T : ZeroIdEntity, n /// public virtual Task> Delete(T model) => Operations.Delete(model); - /// - public virtual Task Delete(IEnumerable models) => Operations.Delete(models); - - /// - public virtual Task> Delete(string id) => Operations.Delete(id); - - /// - public virtual Task Delete(IEnumerable ids) => Operations.Delete(ids); - /// public virtual async Task Validate(T model) { @@ -92,7 +80,7 @@ public abstract class EntityStore : IEntityStore where T : ZeroIdEntity, n /// /// Do only return the model when it is set to active or inactive entities are included with IncludeInactive() /// - protected virtual T WhenActive(T model) => model != null && (Config.IncludeInactive || (model is ZeroEntity ? (model as ZeroEntity).IsActive : true)) ? model : default; + protected virtual T WhenActive(T model) => model != null && (Config.IncludeInactive || (model is not ZeroEntity || (model as ZeroEntity).IsActive)) ? model : default; } @@ -140,11 +128,6 @@ public interface IEntityStore where T : ZeroIdEntity, new() /// Task> LoadAll(); - /// - /// Stream the collection - /// - IAsyncEnumerable Stream(); - /// /// Stream the collection /// @@ -169,19 +152,4 @@ public interface IEntityStore where T : ZeroIdEntity, new() /// Deletes an entity /// Task> Delete(T model); - - /// - /// Deletes entities - /// - Task Delete(IEnumerable models); - - /// - /// Deletes an entity by Id - /// - Task> Delete(string id); - - /// - /// Deletes entities by Id - /// - Task Delete(IEnumerable ids); } \ No newline at end of file diff --git a/zero.Core/Stores/EntityStoreExtensions.cs b/zero.Core/Stores/EntityStoreExtensions.cs new file mode 100644 index 00000000..5414c52d --- /dev/null +++ b/zero.Core/Stores/EntityStoreExtensions.cs @@ -0,0 +1,46 @@ +using Raven.Client.Documents.Linq; + +namespace zero.Stores; + +public static class EntityStoreExtensions +{ + /// + /// Stream the collection + /// + public static IAsyncEnumerable Stream(this IEntityStore store) where T : ZeroIdEntity, new() => store.Stream(null); + + + /// + /// Deletes an entity by Id + /// + public static async Task> Delete(this IEntityStore store, string id) where T : ZeroIdEntity, new() => await store.Delete(await store.Load(id)); + + + /// + /// Deletes entities by Id + /// + public static async Task Delete(this IEntityStore store, IEnumerable ids) where T : ZeroIdEntity, new() => await store.Delete((await store.Load(ids)).Select(x => x.Value)); + + + /// + /// Deletes entities + /// + public static async Task Delete(this IEntityStore store, IEnumerable models) where T : ZeroIdEntity, new() + { + int successCount = 0; + + foreach (T model in models) + { + EntityResult result = await store.Delete(model); + successCount += result.IsSuccess ? 1 : 0; + } + + return successCount; + } + + + /// + /// Deletes an entity by Id with all descendents + /// + public static async Task> DeleteWithDescendants(this ITreeEntityStore store, string id) where T : ZeroIdEntity, IZeroTreeEntity, new() => await store.DeleteWithDescendants(await store.Load(id)); +} \ No newline at end of file diff --git a/zero.Core/Stores/StoreOperations.Delete.cs b/zero.Core/Stores/StoreOperations.Delete.cs index 281e365a..8482c1a9 100644 --- a/zero.Core/Stores/StoreOperations.Delete.cs +++ b/zero.Core/Stores/StoreOperations.Delete.cs @@ -3,54 +3,24 @@ public abstract partial class StoreOperations { /// - public virtual async Task> Delete(string id) where T : ZeroIdEntity, new() + public virtual async Task> Delete(T model) where T : ZeroIdEntity, new() { - if (id.IsNullOrEmpty()) + if (model == null) { return EntityResult.Fail("@errors.ondelete.idnotfound"); } - T entity = await Session.LoadAsync(id); - - if (entity == null) - { - return EntityResult.Fail("@errors.ondelete.idnotfound"); - } - - InterceptorInstruction instruction = Interceptors.ForDelete(entity); + InterceptorInstruction instruction = Interceptors.ForDelete(model); if (!await instruction.Start()) - { + { return instruction.Result; } - Session.Delete(entity); + Session.Delete(model); await instruction.Complete(); await Session.SaveChangesAsync(); return EntityResult.Success(); } - - - /// - public virtual async Task> Delete(T model) where T : ZeroIdEntity, new() => await Delete(model?.Id); - - - /// - public virtual async Task Delete(IEnumerable models) where T : ZeroIdEntity, new() => await Delete(models.Select(x => x.Id)); - - - /// - public virtual async Task Delete(IEnumerable ids) where T : ZeroIdEntity, new() - { - int successCount = 0; - - foreach (string id in ids) - { - EntityResult result = await Delete(id); - successCount += result.IsSuccess ? 1 : 0; - } - - return successCount; - } } \ No newline at end of file diff --git a/zero.Core/Stores/StoreOperations.Read.cs b/zero.Core/Stores/StoreOperations.Read.cs index 5ad628cd..77a1ad77 100644 --- a/zero.Core/Stores/StoreOperations.Read.cs +++ b/zero.Core/Stores/StoreOperations.Read.cs @@ -70,7 +70,7 @@ public abstract partial class StoreOperations { List items = new(); - await foreach (T item in Stream()) + await foreach (T item in Stream(null)) { items.Add(item); } @@ -79,10 +79,6 @@ public abstract partial class StoreOperations } - /// - public virtual IAsyncEnumerable Stream() where T : ZeroIdEntity, new() => Stream(null); - - /// public virtual async IAsyncEnumerable Stream(Func, IQueryable> expression) where T : ZeroIdEntity, new() { diff --git a/zero.Core/Stores/StoreOperations.Tree.cs b/zero.Core/Stores/StoreOperations.Tree.cs index 5c19f369..e5654eea 100644 --- a/zero.Core/Stores/StoreOperations.Tree.cs +++ b/zero.Core/Stores/StoreOperations.Tree.cs @@ -162,15 +162,11 @@ public abstract partial class StoreOperations return EntityResult.Fail("@errors.ondelete.idnotfound"); } - await Delete(pages.ToArray()); + await this.Delete(pages.ToArray()); return EntityResult.Success(pages.Select(x => x.Id).ToArray()); } - /// - public async Task> DeleteWithDescendants(string id) where T : ZeroIdEntity, IZeroTreeEntity, new() => await DeleteWithDescendants(await Load(id)); - - /// /// Get an entity with all its descendants /// diff --git a/zero.Core/Stores/StoreOperations.Write.cs b/zero.Core/Stores/StoreOperations.Write.cs index 65eb704c..4aebd8f8 100644 --- a/zero.Core/Stores/StoreOperations.Write.cs +++ b/zero.Core/Stores/StoreOperations.Write.cs @@ -18,7 +18,7 @@ public abstract partial class StoreOperations return EntityResult.Fail("@errors.onsave.empty"); } - bool isUpdate = model.Id.IsNullOrEmpty() ? false : await Session.Advanced.ExistsAsync(model.Id); + bool isUpdate = !model.Id.IsNullOrEmpty() && await Session.Advanced.ExistsAsync(model.Id); // prepare model PrepareForSave(model); diff --git a/zero.Core/Stores/StoreOperations.cs b/zero.Core/Stores/StoreOperations.cs index 61bad221..1177b4f4 100644 --- a/zero.Core/Stores/StoreOperations.cs +++ b/zero.Core/Stores/StoreOperations.cs @@ -109,7 +109,7 @@ public abstract partial class StoreOperations : IStoreOperations /// protected virtual T WhenActive(T model) where T : ZeroIdEntity, new() { - return model != null && (Options.IncludeInactive || (model is ZeroEntity ? (model as ZeroEntity).IsActive : true)) ? model : default; + return model != null && (Options.IncludeInactive || (model is not ZeroEntity || (model as ZeroEntity).IsActive)) ? model : default; } } @@ -163,11 +163,6 @@ public interface IStoreOperations /// Task> LoadAll() where T : ZeroIdEntity, new(); - /// - /// Stream the collection - /// - IAsyncEnumerable Stream() where T : ZeroIdEntity, new(); - /// /// Stream the collection /// @@ -188,21 +183,6 @@ public interface IStoreOperations /// Task> Delete(T model) where T : ZeroIdEntity, new(); - /// - /// Deletes entities - /// - Task Delete(IEnumerable models) where T : ZeroIdEntity, new(); - - /// - /// Deletes an entity by Id - /// - Task> Delete(string id) where T : ZeroIdEntity, new(); - - /// - /// Deletes entities by Id - /// - Task Delete(IEnumerable ids) where T : ZeroIdEntity, new(); - /// /// Loads all children for an entity /// @@ -237,9 +217,4 @@ public interface IStoreOperations /// Deletes an entity with all descendents /// Task> DeleteWithDescendants(T model) where T : ZeroIdEntity, IZeroTreeEntity, new(); - - /// - /// Deletes an entity by Id with all descendents - /// - Task> DeleteWithDescendants(string id) where T : ZeroIdEntity, IZeroTreeEntity, new(); } \ No newline at end of file diff --git a/zero.Core/Stores/StoreOperationsExtensions.cs b/zero.Core/Stores/StoreOperationsExtensions.cs new file mode 100644 index 00000000..7b761c33 --- /dev/null +++ b/zero.Core/Stores/StoreOperationsExtensions.cs @@ -0,0 +1,45 @@ +using Raven.Client.Documents.Linq; + +namespace zero.Stores; + +public static class StoreOperationsExtensions +{ + /// + /// Stream the collection + /// + public static IAsyncEnumerable Stream(this IStoreOperations ops) where T : ZeroIdEntity, new() => ops.Stream(null); + + + /// + /// Deletes an entity by Id + /// + public static async Task> Delete(this IStoreOperations ops, string id) where T : ZeroIdEntity, new() => await ops.Delete(await ops.Load(id)); + + + /// + /// Deletes entities by Id + /// + public static async Task Delete(this IStoreOperations ops, IEnumerable ids) where T : ZeroIdEntity, new() => await ops.Delete((await ops.Load(ids)).Select(x => x.Value)); + + + /// + /// Deletes entities + /// + public static async Task Delete(this IStoreOperations ops, IEnumerable models) where T : ZeroIdEntity, new() + { + int successCount = 0; + + foreach (T model in models) + { + EntityResult result = await ops.Delete(model); + successCount += result.IsSuccess ? 1 : 0; + } + + return successCount; + } + + /// + /// Deletes an entity by Id with all descendents + /// + public static async Task> DeleteWithDescendants(this IStoreOperations ops, string id) where T : ZeroIdEntity, IZeroTreeEntity, new() => await ops.DeleteWithDescendants(await ops.Load(id)); +} \ No newline at end of file diff --git a/zero.Core/Stores/TreeEntityStore.cs b/zero.Core/Stores/TreeEntityStore.cs index e49d90cf..a9a44020 100644 --- a/zero.Core/Stores/TreeEntityStore.cs +++ b/zero.Core/Stores/TreeEntityStore.cs @@ -14,10 +14,10 @@ public abstract class TreeEntityStore : EntityStore, ITreeEntityStore w public virtual Task> Copy(string id, string newParentId) => Operations.Copy(id, newParentId, async (model, parentId) => await IsAllowedAsChild(model, parentId)); /// - public virtual Task> CopyWithDescendants(string id, string newParentId) => Operations.Copy(id, newParentId, async (model, parentId) => await IsAllowedAsChild(model, parentId)); + public virtual Task> CopyWithDescendants(string id, string newParentId) => Operations.CopyWithDescendants(id, newParentId, async (model, parentId) => await IsAllowedAsChild(model, parentId)); /// - public virtual Task> Move(string id, string newParentId) => Operations.Copy(id, newParentId, async (model, parentId) => await IsAllowedAsChild(model, parentId)); + public virtual Task> Move(string id, string newParentId) => Operations.Move(id, newParentId, async (model, parentId) => await IsAllowedAsChild(model, parentId)); /// public virtual Task>> Sort(string[] sortedIds) => Operations.Sort(sortedIds); diff --git a/zero.Core/Unimplemented/Feature.cs b/zero.Core/Unimplemented/Feature.cs deleted file mode 100644 index 3b5238ef..00000000 --- a/zero.Core/Unimplemented/Feature.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace zero.Core.Entities -{ - /// - /// A feature can affect both the backoffice and the frontend - /// - public class Feature - { - /// - /// The alias - /// - public string Alias { get; set; } - - /// - /// The name of the feature - /// - public string Name { get; set; } - - /// - /// Additional description - /// - public string Description { get; set; } - } -} diff --git a/zero.Core/Utils/ObjectCopycat.cs b/zero.Core/Utils/ObjectCopycat.cs index a3b6b15a..9e45a1a1 100644 --- a/zero.Core/Utils/ObjectCopycat.cs +++ b/zero.Core/Utils/ObjectCopycat.cs @@ -8,7 +8,7 @@ public class ObjectCopycat { const string SEPARATOR = "."; - static Type STRING_TYPE = typeof(string); + static readonly Type STRING_TYPE = typeof(string); static ConcurrentDictionary> PublicPropertiesPerType { get; set; } = new(); diff --git a/zero.Core/ZeroBuilder.cs b/zero.Core/ZeroBuilder.cs index c5e04116..f42526cf 100644 --- a/zero.Core/ZeroBuilder.cs +++ b/zero.Core/ZeroBuilder.cs @@ -13,9 +13,8 @@ public class ZeroBuilder public virtual IMvcBuilder Mvc { get; } - IConfiguration Configuration; - - IZeroStartupOptions StartupOptions; + readonly IConfiguration Configuration; + readonly IZeroStartupOptions StartupOptions; public ZeroBuilder(IServiceCollection services, IConfiguration configuration, Action setupAction)