diff --git a/zero.Core/Collections/FixedCollectionBase.cs b/zero.Core/Collections/CollectionConfigBase.cs similarity index 87% rename from zero.Core/Collections/FixedCollectionBase.cs rename to zero.Core/Collections/CollectionConfigBase.cs index beae31e4..0dc8d631 100644 --- a/zero.Core/Collections/FixedCollectionBase.cs +++ b/zero.Core/Collections/CollectionConfigBase.cs @@ -11,9 +11,9 @@ using zero.Core.Options; namespace zero.Core.Collections { - public abstract class FixedCollectionBase : CollectionBase, IFixedCollectionBase, IDisposable where T : IZeroTypedEntity + public abstract class CollectionConfigBase : CollectionBase, IFixedCollectionBase, IDisposable where T : IZeroConfigEntity { - public FixedCollectionBase(IZeroContext context, ICollectionInterceptorHandler interceptorHandler = null, IValidator validator = null) : base(context, interceptorHandler, validator) { } + public CollectionConfigBase(IZeroContext context, ICollectionInterceptorHandler interceptorHandler = null, IValidator validator = null) : base(context, interceptorHandler, validator) { } protected abstract IEnumerable GetDefinedTypes(); @@ -62,7 +62,7 @@ namespace zero.Core.Collections TSpecific model = await Session.Query().FirstOrDefaultAsync(x => x.TypeAlias == type.Alias); - if (model == null && type.IsAutoActivated) + if (model == null) //&& type.IsAutoActivated) { return new TSpecific(); } diff --git a/zero.Core/Collections/CollectionSession.cs b/zero.Core/Collections/CollectionSession.cs new file mode 100644 index 00000000..7f537b47 --- /dev/null +++ b/zero.Core/Collections/CollectionSession.cs @@ -0,0 +1,99 @@ +using Raven.Client.Documents.Session; +using System; +using zero.Core.Database; + +namespace zero.Core.Collections +{ + public abstract class CollectionSession : ICollectionSession + { + private IAsyncDocumentSession _session; + private string _database; + + public CollectionSession(IZeroContext context) + { + Context = context; + Store = context.Store; + Database = Store.ResolvedDatabase; + } + + + /// + /// Zero context + /// + protected readonly IZeroContext Context; + + /// + /// Document store + /// + protected readonly IZeroStore Store; + + /// + /// Create an an async document session + /// + protected IAsyncDocumentSession Session + { + get + { + if (_session != null) + { + return _session; + } + _session = Store.OpenAsyncSession(Database ?? Store.ResolvedDatabase); + _session.Advanced.WaitForIndexesAfterSaveChanges(throwOnTimeout: false); + return _session; + } + } + + /// + public string Database + { + get => _database; + set + { + if (value != _database) + { + _session?.Dispose(); + _session = null; + _database = value; + } + } + } + + /// + public Guid Guid { get; private set; } = Guid.NewGuid(); + + + /// + public virtual void ApplyScope(string scope) + { + Database = scope is "shared" or "core" ? Context.Options.Raven.Database : Store.ResolvedDatabase; + } + + + /// + public void Dispose() + { + Session?.Dispose(); + } + } + + + public interface ICollectionSession : IDisposable + { + /// + /// Guid for this instance + /// + Guid Guid { get; } + + /// + /// The database to operate on. + /// Is null by default, which uses the database from the resolved application. + /// + string Database { get; set; } + + /// + /// Applies the scope to the service instance + /// + void ApplyScope(string scope); + } +} diff --git a/zero.Core/Entities/IZeroTypedEntity.cs b/zero.Core/Entities/IZeroConfigEntity.cs similarity index 74% rename from zero.Core/Entities/IZeroTypedEntity.cs rename to zero.Core/Entities/IZeroConfigEntity.cs index 84146bf9..d10906ab 100644 --- a/zero.Core/Entities/IZeroTypedEntity.cs +++ b/zero.Core/Entities/IZeroConfigEntity.cs @@ -1,6 +1,6 @@ namespace zero.Core.Entities { - public interface IZeroTypedEntity : IZeroEntity + public interface IZeroConfigEntity : IZeroEntity { /// /// Alias of the used type diff --git a/zero.Core/Entities/ListResult.cs b/zero.Core/Entities/ListResult.cs index ab981a02..c3a65473 100644 --- a/zero.Core/Entities/ListResult.cs +++ b/zero.Core/Entities/ListResult.cs @@ -39,7 +39,7 @@ namespace zero.Core.Entities Statistics = Statistics }; } - public IList Items { get; set; } + public IList Items { get; set; } = new List(); /// diff --git a/zero.Core/Extensions/EnumerableExtensions.cs b/zero.Core/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..3d6ba7c9 --- /dev/null +++ b/zero.Core/Extensions/EnumerableExtensions.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using zero.Core.Entities; + +namespace zero.Core.Extensions +{ + public static class EnumerableExtensions + { + /// + /// + /// + public static ListResult ToQueriedList(this IEnumerable items, ListQuery query) where T : IZeroEntity + { + //queryable = queryable.Statistics(out QueryStatistics stats); + + if (query != null) + { + if (!query.IncludeInactive) + { + items = items.Where(x => x.IsActive); + } + + if (!query.Search.IsNullOrEmpty()) + { + items = items.Where(x => x.Name.Contains(query.Search, StringComparison.InvariantCultureIgnoreCase)); + } + //if (!query.Search.IsNullOrEmpty() && query.SearchSelector != null) + //{ + // items = items.SearchIf(query.SearchSelector, query.Search, "*", "*", Raven.Client.Documents.Queries.SearchOperator.And); + //} + //if (!query.Search.IsNullOrEmpty() && query.SearchSelectors.Length > 0) + //{ + // foreach (var selector in query.SearchSelectors) + // { + // items = items.SearchIf(selector, query.Search, "*", "*", Raven.Client.Documents.Queries.SearchOperator.And); + // } + //} + + //if (!query.OrderBy.IsNullOrEmpty()) + //{ + // items = items.OrderBy(query.OrderBy, query.OrderIsDescending); + //} + + if (query.PageSize > 0) + { + items = items.Paging(query.Page, query.PageSize); + } + } + + List result = items.ToList(); + + return new ListResult(result, result.Count, query.Page, query.PageSize); + } + + + + public static IEnumerable Paging(this IEnumerable source, int pageNumber, int pageSize) + { + pageNumber = pageNumber.Limit(1, 10_000_000); + pageSize = pageSize.Limit(1, 1_000); + + if (pageNumber <= 0 || pageSize <= 0) + { + throw new NotSupportedException("Both pageNumber and pageSize must be greater than zero"); + } + + return source.Skip((pageNumber - 1) * pageSize).Take(pageSize); + } + } +} diff --git a/zero.Core/Integrations/Integration.cs b/zero.Core/Integrations/Integration.cs index 9ebead05..a683b73b 100644 --- a/zero.Core/Integrations/Integration.cs +++ b/zero.Core/Integrations/Integration.cs @@ -17,5 +17,5 @@ namespace zero.Core.Integrations /// It's up to the user to provide functionality. /// [Collection("Integrations")] - public interface IIntegration : IZeroTypedEntity, IZeroDbConventions { } + public interface IIntegration : IZeroConfigEntity, IZeroDbConventions { } } \ No newline at end of file diff --git a/zero.Core/Integrations/IntegrationService.cs b/zero.Core/Integrations/IntegrationService.cs index 171589af..fa1f0857 100644 --- a/zero.Core/Integrations/IntegrationService.cs +++ b/zero.Core/Integrations/IntegrationService.cs @@ -54,10 +54,10 @@ namespace zero.Core.Integrations return false; } - if (types.Any(x => x.IsAutoActivated)) - { - return true; - } + //if (types.Any(x => x.IsAutoActivated)) + //{ + // return true; + //} string[] aliases = types.Select(x => x.Alias).ToArray(); @@ -79,7 +79,7 @@ namespace zero.Core.Integrations using IAsyncDocumentSession session = Store.OpenAsyncSession(); T integration = await session.Query().FirstOrDefaultAsync(x => x.TypeAlias == type.Alias && x.IsActive); - if (integration == null && type.IsAutoActivated) + if (integration == null)// && type.IsAutoActivated) { return new T(); } diff --git a/zero.Core/Integrations/IntegrationType.cs b/zero.Core/Integrations/IntegrationType.cs index 5de96a2b..b4212925 100644 --- a/zero.Core/Integrations/IntegrationType.cs +++ b/zero.Core/Integrations/IntegrationType.cs @@ -1,5 +1,4 @@ -using FluentValidation; -using System; +using System; using System.Collections.Generic; using zero.Core.Options; @@ -25,11 +24,6 @@ namespace zero.Core.Integrations /// public List Tags { get; set; } = new(); - /// - /// The name of the integration (either a string or a translation key with @ prefix) - /// - public string Name { get; set; } - /// /// Optional description /// @@ -54,7 +48,6 @@ namespace zero.Core.Integrations Name = model.Name, Description = model.Description, ImagePath = model.ImagePath, - IsAutoActivated = model.IsAutoActivated, Tags = model.Tags, Validator = model.Validator }; diff --git a/zero.Core/Integrations/IntegrationsCollection.cs b/zero.Core/Integrations/IntegrationsCollection.cs index 5e4fbc0c..f8fc62c3 100644 --- a/zero.Core/Integrations/IntegrationsCollection.cs +++ b/zero.Core/Integrations/IntegrationsCollection.cs @@ -9,7 +9,7 @@ using zero.Core.Options; namespace zero.Core.Integrations { - public class IntegrationsCollection : FixedCollectionBase, IIntegrationsCollection + public class IntegrationsCollection : CollectionConfigBase, IIntegrationsCollection { public IntegrationsCollection(IZeroContext context, ICollectionInterceptorHandler interceptorHandler) : base(context, interceptorHandler) { } @@ -27,10 +27,10 @@ namespace zero.Core.Integrations return false; } - if (types.Any(x => x.IsAutoActivated)) - { - return true; - } + //if (types.Any(x => x.IsAutoActivated)) + //{ + // return true; + //} string[] aliases = types.Select(x => x.Alias).ToArray(); return await Session.Query().AnyAsync(x => x.TypeAlias.In(aliases) && x.IsActive); diff --git a/zero.Core/Options/IntegrationOptions.cs b/zero.Core/Options/IntegrationOptions.cs index da83eafe..ee2a6907 100644 --- a/zero.Core/Options/IntegrationOptions.cs +++ b/zero.Core/Options/IntegrationOptions.cs @@ -1,7 +1,6 @@ using FluentValidation; using System; using System.Collections.Generic; -using zero.Core.Entities; using zero.Core.Integrations; namespace zero.Core.Options @@ -14,14 +13,13 @@ namespace zero.Core.Options } - public void Add(string alias, string name, string description, bool isAutoActivated = false, List tags = default, string imagePath = null, IValidator validator = null) where T : Integration, new() + 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)) { Alias = alias, Name = name, Description = description, - IsAutoActivated = isAutoActivated, ImagePath = imagePath, Tags = tags, Validator = validator @@ -29,14 +27,13 @@ namespace zero.Core.Options } - public void Add(Type type, string alias, string name, string description, bool isAutoActivated = false, List tags = default, string imagePath = null, IValidator validator = null) + 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) { Alias = alias, Name = name, Description = description, - IsAutoActivated = isAutoActivated, ImagePath = imagePath, Tags = tags, Validator = validator diff --git a/zero.Core/Options/OptionsType.cs b/zero.Core/Options/OptionsType.cs index eb6528ef..e1317592 100644 --- a/zero.Core/Options/OptionsType.cs +++ b/zero.Core/Options/OptionsType.cs @@ -16,12 +16,12 @@ namespace zero.Core.Options public string Alias { get; set; } /// - /// Do never return null when it has not been configured yet but the default instance for a settings object + /// The name of the options type /// - public bool IsAutoActivated { get; set; } + public string Name { get; set; } /// - /// Set a validator for the integration editor + /// Set a validator for the editor /// public IValidator Validator { get; set; } } diff --git a/zero.Core/zero.Core.csproj b/zero.Core/zero.Core.csproj index 8f858739..5970ca0d 100644 --- a/zero.Core/zero.Core.csproj +++ b/zero.Core/zero.Core.csproj @@ -28,6 +28,7 @@ + \ No newline at end of file diff --git a/zero.Web.UI/App/components/forms/form-header.vue b/zero.Web.UI/App/components/forms/form-header.vue index 4d7bd262..c52db763 100644 --- a/zero.Web.UI/App/components/forms/form-header.vue +++ b/zero.Web.UI/App/components/forms/form-header.vue @@ -9,7 +9,7 @@
-
+
@@ -49,6 +49,10 @@ type: Boolean, default: false }, + activeDisabled: { + type: Boolean, + default: false + }, state: { type: String }, diff --git a/zero.Web.UI/App/components/tables/table.scss b/zero.Web.UI/App/components/tables/table.scss index a427f791..701ddce3 100644 --- a/zero.Web.UI/App/components/tables/table.scss +++ b/zero.Web.UI/App/components/tables/table.scss @@ -189,6 +189,13 @@ a.ui-table-row:hover padding: 0 20px; } +.ui-table.is-inline .ui-table-empty, +.ui-table.is-inline .ui-table-loading +{ + height: auto; + padding: var(--padding) 20px; +} + .ui-table-empty-icon { font-size: 34px; diff --git a/zero.Web.UI/App/components/tables/table.vue b/zero.Web.UI/App/components/tables/table.vue index 4b51a3be..bc10771b 100644 --- a/zero.Web.UI/App/components/tables/table.vue +++ b/zero.Web.UI/App/components/tables/table.vue @@ -115,7 +115,7 @@ }; }); this.query = { ...this.listConfig.query, ...this.listConfig.queryToParams(this.$route.query) }; - this.component = typeof !!this.listConfig.link ? 'router-link' : 'div'; + this.component = !!this.listConfig.link ? 'router-link' : 'div'; this.filter = { ...this.listConfig.filterOptions }; this.$nextTick(() => { diff --git a/zero.Web.UI/App/editor/editor-infos.vue b/zero.Web.UI/App/editor/editor-infos.vue index 39209e43..49754f34 100644 --- a/zero.Web.UI/App/editor/editor-infos.vue +++ b/zero.Web.UI/App/editor/editor-infos.vue @@ -1,5 +1,5 @@