From 3df8948fe3f02ed87bd04ca1fa21a3a98b9ad30c Mon Sep 17 00:00:00 2001 From: Tobias Klika Date: Fri, 15 Jan 2021 13:56:50 +0100 Subject: [PATCH] integrations work now! --- zero.Core/Collections/CollectionBase.cs | 36 ++- .../Integrations/IntegrationsCollection.cs | 297 ++++++++++++++++++ zero.Core/Integrations/IntegrationService.cs | 105 +------ .../Integrations/IntegrationTypeWithStatus.cs | 13 + .../Integrations/IntegrationsCollection.cs | 96 ------ zero.Web.UI/App/api/integrations.js | 15 +- .../components/pickers/iconPicker/overlay.vue | 2 +- zero.Web.UI/App/editor/fields/password.vue | 27 ++ .../App/pages/settings/integration.vue | 19 +- .../App/pages/settings/integrations-item.vue | 100 +++--- .../App/pages/settings/integrations.vue | 49 ++- zero.Web.UI/app/core/editor-field.ts | 13 + .../Controllers/IntegrationsController.cs | 68 ++-- zero.Web/Defaults/ZeroBackofficePlugin.cs | 4 +- .../Resources/Localization/zero.en-us.json | 6 + 15 files changed, 537 insertions(+), 313 deletions(-) create mode 100644 zero.Core/Collections/Integrations/IntegrationsCollection.cs create mode 100644 zero.Core/Integrations/IntegrationTypeWithStatus.cs delete mode 100644 zero.Core/Integrations/IntegrationsCollection.cs create mode 100644 zero.Web.UI/App/editor/fields/password.vue diff --git a/zero.Core/Collections/CollectionBase.cs b/zero.Core/Collections/CollectionBase.cs index e6e2e245..2145a800 100644 --- a/zero.Core/Collections/CollectionBase.cs +++ b/zero.Core/Collections/CollectionBase.cs @@ -26,6 +26,8 @@ namespace zero.Core.Collections protected virtual Action PreSave { get; set; } + protected bool OnlyActive { get; set; } = false; // TODO do we really need this? + public CollectionBase(IZeroContext context, ICollectionInterceptorHandler interceptorHandler = null, IValidator validator = null) { @@ -98,6 +100,13 @@ namespace zero.Core.Collections } + /// + public virtual void WithInactive(bool includeInactive = true) + { + OnlyActive = !includeInactive; + } + + /// public virtual async Task GetById(string id) { @@ -106,7 +115,7 @@ namespace zero.Core.Collections return default; } - return await Session.LoadAsync(id); + return WhenActive(await Session.LoadAsync(id)); } @@ -119,7 +128,7 @@ namespace zero.Core.Collections foreach (string id in ids) { models.TryGetValue(id, out T model); - result.Add(id, model); + result.Add(id, WhenActive(model)); } return result; @@ -129,7 +138,7 @@ namespace zero.Core.Collections /// public virtual async Task> GetByQuery(ListQuery query) { - return await Session.Query().OrderByDescending(x => x.CreatedDate).ToQueriedListAsync(query); + return await Session.Query().WhereIf(x => x.IsActive, OnlyActive).OrderByDescending(x => x.CreatedDate).ToQueriedListAsync(query); } @@ -154,7 +163,7 @@ namespace zero.Core.Collections /// public virtual async IAsyncEnumerable Stream(Func, IRavenQueryable> expression) { - IRavenQueryable query = Session.Query(); + IRavenQueryable query = Session.Query().WhereIf(x => x.IsActive, OnlyActive); if (expression != null) { @@ -308,6 +317,11 @@ namespace zero.Core.Collections /// public virtual async Task> DeleteById(string id) { + if (String.IsNullOrEmpty(id)) + { + return EntityResult.Fail("@errors.ondelete.idnotfound"); + } + T entity = await Session.LoadAsync(id); if (entity == null) @@ -397,6 +411,15 @@ namespace zero.Core.Collections configure?.Invoke(parameters); return parameters; } + + + /// + /// Do only return the model when it is set to active or inactive entities are included with IncludeInactive() + /// + protected T WhenActive(T model) + { + return model != null && (!OnlyActive || model.IsActive) ? model : default; + } } @@ -423,6 +446,11 @@ namespace zero.Core.Collections /// void ApplyScope(string scope); + /// + /// Include entities with IsActive=false for GET queries + /// + void WithInactive(bool include = true); + /// /// Get an entity by Id /// diff --git a/zero.Core/Collections/Integrations/IntegrationsCollection.cs b/zero.Core/Collections/Integrations/IntegrationsCollection.cs new file mode 100644 index 00000000..03716965 --- /dev/null +++ b/zero.Core/Collections/Integrations/IntegrationsCollection.cs @@ -0,0 +1,297 @@ +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.Entities; +using zero.Core.Extensions; +using zero.Core.Integrations; +using zero.Core.Options; + +namespace zero.Core.Collections +{ + public class IntegrationsCollection : CollectionBase, IIntegrationsCollection + { + /// + public IReadOnlyCollection RegisteredTypes { get; private set; } + + protected IZeroOptions Options { get; private set; } + + protected ILogger Logger { get; private set; } + + + public IntegrationsCollection(IZeroContext context, IZeroOptions options, ILogger logger, ICollectionInterceptorHandler interceptorHandler = null) : base(context, interceptorHandler) + { + Options = options; + RegisteredTypes = Options.Integrations.GetAllItems(); + Logger = logger; + } + + + /// + public IIntegration GetEmpty(string alias) + { + IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase)); + + if (type == null) + { + return null; + } + + try + { + IIntegration model = Activator.CreateInstance(type.ContentType) as IIntegration; + model.TypeAlias = type.Alias; + return model; + } + catch + { + Logger.LogWarning("Could not create integration with type {alias}", alias); + } + + 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 Get(string alias = null) where T : IIntegration, 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)); + + 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(); + } + + + /// + public async Task Any(string tag) + { + return (await GetByTag(tag)).Any(x => x.IsActive); + } + + + /// + public override async Task> GetByQuery(ListQuery query) + { + List result = new(); + List models = await Session.Query().ToListAsync(); + + foreach (IntegrationType type in RegisteredTypes) + { + IIntegration model = models.FirstOrDefault(x => x.TypeAlias == type.Alias); + + if (model != null) + { + model.Name = type.Name; + result.Add(model); + } + } + + return result.ToQueriedList(query); + } + + + /// + public async Task> GetTypesWithStatus() + { + List result = new(); + List models = await Session.Query().ToListAsync(); + + foreach (IntegrationType type in RegisteredTypes) + { + IIntegration model = models.FirstOrDefault(x => x.TypeAlias == type.Alias); + + result.Add(new() + { + Type = type, + Id = model?.Id, + IsActive = model?.IsActive ?? false, + IsConfigured = model != null + }); + } + + return result; + } + + + /// + public override async Task> Save(IIntegration model) + { + if (model == null) + { + return EntityResult.Fail("@integration.errors.notfound"); + } + + IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.Alias.Equals(model.TypeAlias, StringComparison.InvariantCultureIgnoreCase)); + + 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(); + + if (!existingId.IsNullOrEmpty() && existingId != model.Id) + { + return EntityResult.Fail("@integration.errors.multiplenotallowed"); + } + + model.Alias = type.Alias; + model.Name = null; + + EntityResult result = await base.Save(model); + + if (result.IsSuccess) + { + result.Model.Name = type.Name; + } + + return result; + } + + + /// + public async Task> Activate(string alias) + { + IIntegration model = await GetByAlias(alias); + if (model != null) + { + model.IsActive = true; + } + return await Save(model); + } + + + /// + public async Task> Deactivate(string alias) + { + IIntegration model = await GetByAlias(alias); + if (model != null) + { + model.IsActive = false; + } + return await Save(model); + } + + + /// + public async Task> Delete(string alias) + { + return await base.Delete(await GetByAlias(alias)); + } + + + /// + /// Get integration data from database + /// + protected async Task Load(IntegrationType type) where T : IIntegration, new() + { + if (type == null) + { + return default; + } + + T entity = await Session.Query().FirstOrDefaultAsync(x => x.TypeAlias == type.Alias); + + if (entity != null) + { + entity.Name = type.Name; + entity.TypeAlias = type.Alias; + } + + return entity; + } + } + + + public interface IIntegrationsCollection + { + /// + /// Get all registered integration types + /// + IReadOnlyCollection RegisteredTypes { get; } + + /// + /// Include entities with IsActive=false for GET queries + /// + void WithInactive(bool include = true); + + /// + /// Get new integration model for the specified integration type alias + /// + IIntegration GetEmpty(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 : IIntegration, new(); + + /// + /// Get all integrations by a certain tag + /// + Task> GetByTag(string tag); + + /// + /// Check if any integrations of certain tag are activated + /// + Task Any(string tag); + + /// + /// Get all integrations with the specified query + /// + Task> GetByQuery(ListQuery query); + + /// + /// Get all integration types with their configuration status + /// + Task> GetTypesWithStatus(); + + /// + /// Saves an integration + /// + Task> Save(IIntegration model); + + /// + /// Activates a configured integration + /// + Task> Activate(string alias); + + /// + /// Disables a configured integration + /// + Task> Deactivate(string alias); + + /// + /// Deletes configuration of an integration and disables it + /// + Task> Delete(string alias); + } +} diff --git a/zero.Core/Integrations/IntegrationService.cs b/zero.Core/Integrations/IntegrationService.cs index fa1f0857..9e10d28f 100644 --- a/zero.Core/Integrations/IntegrationService.cs +++ b/zero.Core/Integrations/IntegrationService.cs @@ -1,109 +1,14 @@ -using Raven.Client.Documents; -using Raven.Client.Documents.Linq; -using Raven.Client.Documents.Session; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using zero.Core.Database; +using Microsoft.Extensions.Logging; +using zero.Core.Collections; using zero.Core.Options; namespace zero.Core.Integrations { - public class IntegrationService : IIntegrationService + public class IntegrationService : IntegrationsCollection, IIntegrationService { - /// - public IReadOnlyCollection RegisteredTypes { get; private set; } - - protected IZeroOptions Options { get; private set; } - - protected IZeroStore Store { get; private set; } - - - public IntegrationService(IZeroOptions options, IZeroStore store) - { - Options = options; - Store = store; - RegisteredTypes = options.Integrations.GetAllItems(); - } - - - /// - public async Task Get() where T : IIntegration, new() - { - IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.ContentType == typeof(T)); - return await GetIntegration(type); - } - - - /// - public async Task Get(string alias) where T : IIntegration, new() - { - IntegrationType type = RegisteredTypes.FirstOrDefault(x => x.ContentType == typeof(T) && x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase)); - return await GetIntegration(type); - } - - - /// - public async Task Any(string tag) - { - IEnumerable types = RegisteredTypes.Where(x => x.Tags.Contains(tag, StringComparer.InvariantCultureIgnoreCase)); - - if (!types.Any()) - { - return false; - } - - //if (types.Any(x => x.IsAutoActivated)) - //{ - // return true; - //} - - string[] aliases = types.Select(x => x.Alias).ToArray(); - - using IAsyncDocumentSession session = Store.OpenAsyncSession(); - return await session.Query().AnyAsync(x => x.TypeAlias.In(aliases) && x.IsActive); - } - - - /// - /// Get integration data from database - /// - async Task GetIntegration(IntegrationType type) where T : IIntegration, new() - { - if (type == null) - { - return default; - } - - using IAsyncDocumentSession session = Store.OpenAsyncSession(); - T integration = await session.Query().FirstOrDefaultAsync(x => x.TypeAlias == type.Alias && x.IsActive); - - if (integration == null)// && type.IsAutoActivated) - { - return new T(); - } - - return integration; - } + public IntegrationService(IZeroOptions options, IZeroContext context, ILogger logger, ICollectionInterceptorHandler interceptorHandler = null) : base(context, options, logger, interceptorHandler) { } } - public interface IIntegrationService - { - /// - /// Get an integration by type - /// - Task Get() where T : IIntegration, new(); - - /// - /// Get an integration by type and alias - /// - Task Get(string alias) where T : IIntegration, new(); - - /// - /// Checks if any integrations for a certain tag are activated - /// - Task Any(string tag); - } + public interface IIntegrationService : IIntegrationsCollection { } } diff --git a/zero.Core/Integrations/IntegrationTypeWithStatus.cs b/zero.Core/Integrations/IntegrationTypeWithStatus.cs new file mode 100644 index 00000000..ebb6cc70 --- /dev/null +++ b/zero.Core/Integrations/IntegrationTypeWithStatus.cs @@ -0,0 +1,13 @@ +namespace zero.Core.Integrations +{ + public class IntegrationTypeWithStatus + { + public IntegrationType Type { get; set; } + + public bool IsActive { get; set; } + + public bool IsConfigured { get; set; } + + public string Id { get; set; } + } +} diff --git a/zero.Core/Integrations/IntegrationsCollection.cs b/zero.Core/Integrations/IntegrationsCollection.cs deleted file mode 100644 index f8fc62c3..00000000 --- a/zero.Core/Integrations/IntegrationsCollection.cs +++ /dev/null @@ -1,96 +0,0 @@ -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.Collections; -using zero.Core.Options; - -namespace zero.Core.Integrations -{ - public class IntegrationsCollection : CollectionConfigBase, IIntegrationsCollection - { - public IntegrationsCollection(IZeroContext context, ICollectionInterceptorHandler interceptorHandler) : base(context, interceptorHandler) { } - - - protected override IEnumerable GetDefinedTypes() => Context.Options.Integrations.GetAllItems(); - - - /// - public async Task Any(string tag) - { - IEnumerable types = GetDefinedTypes().Cast().Where(x => x.Tags.Contains(tag, StringComparer.InvariantCultureIgnoreCase)); - - if (!types.Any()) - { - return false; - } - - //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); - } - - - ///// - //public async Task Get() where T : class, IIntegrationModel - //{ - // Type type = typeof(T); - // IIntegration integration = RegisteredTypes.FirstOrDefault(x => x.ModelType == type); - - // if (integration == null) - // { - // return default; - // } - - // await Preload(); - // return Items.FirstOrDefault(x => x.IntegrationAlias == integration.Alias) as T; - //} - - - ///// - //public async Task GetByAlias(string alias) - //{ - // IIntegration integration = RegisteredTypes.FirstOrDefault(x => x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase)); - - // if (integration == null) - // { - // return default; - // } - - // await Preload(); - // return Items.FirstOrDefault(x => x.IntegrationAlias == integration.Alias); - //} - - - ///// - //public async Task GetById(string id) where T : class, IIntegrationModel - //{ - // return await GetById(id) as T; - //} - } - - - public interface IIntegrationsCollection : ICollectionBase - { - ///// - ///// Get settings for a specific integration - ///// - //Task Get() where T : class, IIntegrationModel; - - ///// - ///// Get settings for a specific integration - ///// - //Task GetByAlias(string alias); - - ///// - ///// Get settings for a specific integration - ///// - //Task GetById(string id) where T : class, IIntegrationModel; - } -} diff --git a/zero.Web.UI/App/api/integrations.js b/zero.Web.UI/App/api/integrations.js index 348d1873..ec78a2bd 100644 --- a/zero.Web.UI/App/api/integrations.js +++ b/zero.Web.UI/App/api/integrations.js @@ -1,12 +1,13 @@ -import { get, post, del } from '../helpers/request.ts'; +import { get, post, del } from 'zero/helpers/request.ts'; const base = 'integrations/'; export default { - getAll: async () => await get(base + 'getAll'), - getEmptySettings: async alias => await get(base + 'getEmptySettings', { params: { alias } }), - getSettingsById: async id => await get(base + 'getSettingsById', { params: { id } }), - getSettingsByAlias: async alias => await get(base + 'getSettingsByAlias', { params: { alias } }), - save: async model => await post(base + 'save', model), - delete: async id => await del(base + 'delete', { params: { id } }) + getEmpty: async alias => await get(base + 'getEmpty', { params: { alias } }), + getTypes: async () => await get(base + 'getTypes'), + getByAlias: async (alias, config) => await get(base + 'getByAlias', { ...config, params: { alias } }), + getByQuery: async (query, config) => await get(base + 'getByQuery', { ...config, params: { query } }), + save: async (model, config) => await post(base + 'save', model, { ...config }), + saveActiveState: async (model, config) => await post(base + 'saveActiveState', model, { ...config }), + delete: async alias => await del(base + 'delete', { params: { alias } }) }; \ No newline at end of file diff --git a/zero.Web.UI/App/components/pickers/iconPicker/overlay.vue b/zero.Web.UI/App/components/pickers/iconPicker/overlay.vue index d92faccf..be4563a8 100644 --- a/zero.Web.UI/App/components/pickers/iconPicker/overlay.vue +++ b/zero.Web.UI/App/components/pickers/iconPicker/overlay.vue @@ -152,7 +152,7 @@ &.is-active { background: var(--color-primary); - color: var(--color-primary-fg); + color: var(--color-primary-text); box-shadow: var(--shadow-short); } diff --git a/zero.Web.UI/App/editor/fields/password.vue b/zero.Web.UI/App/editor/fields/password.vue new file mode 100644 index 00000000..47d1029c --- /dev/null +++ b/zero.Web.UI/App/editor/fields/password.vue @@ -0,0 +1,27 @@ + + + + \ No newline at end of file diff --git a/zero.Web.UI/App/pages/settings/integration.vue b/zero.Web.UI/App/pages/settings/integration.vue index 04cf8bf4..c44ac8a3 100644 --- a/zero.Web.UI/App/pages/settings/integration.vue +++ b/zero.Web.UI/App/pages/settings/integration.vue @@ -9,7 +9,9 @@ @@ -56,17 +58,24 @@ onLoad(form) { - form.load(this.config.isCreate ? IntegrationsApi.getEmptySettings(this.config.alias) : IntegrationsApi.getSettingsByAlias(this.config.alias)).then(response => + form.load(this.config.isCreate ? IntegrationsApi.getEmpty(this.config.alias) : IntegrationsApi.getByAlias(this.config.alias)).then(response => { this.disabled = !response.meta.canEdit; this.meta = response.meta; this.model = response.entity; - this.editor = this.model.integrationAlias ? 'integration.' + this.model.integrationAlias : null; + this.editor = this.model.typeAlias ? 'integration.' + this.model.typeAlias : null; this.loading = false; }); }, + saveAndActivate(e) + { + this.model.isActive = true; + this.onSubmit(this.$refs.form); + }, + + onSubmit(form) { form.handle(IntegrationsApi.save(this.model)).then(res => @@ -81,14 +90,14 @@ Overlay.confirmDelete().then(opts => { opts.state('loading'); - IntegrationsApi.delete(this.model.id).then(response => + IntegrationsApi.delete(this.config.alias).then(response => { if (response.success) { opts.state('success'); opts.hide(); Notification.success('@deleteoverlay.success', '@deleteoverlay.success_text'); - this.config.close(); + this.config.confirm(response); } else { diff --git a/zero.Web.UI/App/pages/settings/integrations-item.vue b/zero.Web.UI/App/pages/settings/integrations-item.vue index 975de1b7..03af1b42 100644 --- a/zero.Web.UI/App/pages/settings/integrations-item.vue +++ b/zero.Web.UI/App/pages/settings/integrations-item.vue @@ -1,19 +1,27 @@  @@ -27,17 +35,17 @@ model: { type: Object, default: () => { } - }, - activated: { - type: Boolean, - default: false } }, + data: () => ({ + active: false + }), + computed: { hasColor() { - return !!this.model.color && this.activated; + return !!this.model.type.color && this.model.isConfigured; } }, @@ -48,13 +56,13 @@ return Overlay.open({ component: IntegrationOverlay, display: 'editor', - model: this.model, - isCreate: !this.activated, - alias: this.model.alias, + model: this.model.type, + isCreate: !this.model.isConfigured, + alias: this.model.type.alias, width: 960 }).then(value => { - console.log(value); + this.$emit('change', value); }); } } @@ -67,31 +75,34 @@ color: var(--color-text); font-size: var(--font-size); display: grid; - grid-template-columns: auto 1fr auto; - gap: 20px; - align-items: center; + grid-template-columns: auto 1fr; + gap: var(--padding); + align-items: flex-start; + } + + .integrations-item .ui-button.type-block + { + justify-content: center; + margin-top: 5px; } .integrations-item + .integrations-item { margin-top: var(--padding-m); + border-top: 1px solid var(--color-line); + padding-top: var(--padding-m); } .integrations-item-icon { - width: 100px; + display: inline-block; + width: 120px; height: 80px; - line-height: 79px !important; + line-height: 79px !important; font-size: 22px; text-align: center; - background: var(--color-box); - border-radius: var(--radius); - box-shadow: var(--shadow-short); - } - - .integrations-item.is-active .integrations-item-icon - { - opacity: 1; + background: var(--color-box-nested); + border-radius: var(--radius); } .integrations-item-icon.has-color @@ -104,7 +115,15 @@ { line-height: 1.3; color: var(--color-text-dim); - margin: 0; + margin: 0.5em 0 0; + max-width: 820px; + } + + .integrations-item-text i + { + color: var(--color-primary); + margin-left: 0.5em; + font-size: 1.1em; } .integrations-item-text strong @@ -114,4 +133,9 @@ color: var(--color-text); font-size: var(--font-size); } + + .integrations-item-toggle + { + margin-top: var(--padding); + } \ No newline at end of file diff --git a/zero.Web.UI/App/pages/settings/integrations.vue b/zero.Web.UI/App/pages/settings/integrations.vue index abfadec6..59f7be7d 100644 --- a/zero.Web.UI/App/pages/settings/integrations.vue +++ b/zero.Web.UI/App/pages/settings/integrations.vue @@ -1,13 +1,9 @@  @@ -16,23 +12,50 @@ diff --git a/zero.Web.UI/app/core/editor-field.ts b/zero.Web.UI/app/core/editor-field.ts index 8eda5877..f3ace563 100644 --- a/zero.Web.UI/app/core/editor-field.ts +++ b/zero.Web.UI/app/core/editor-field.ts @@ -1,5 +1,6 @@  import Text from '../editor/fields/text.vue'; +import Password from '../editor/fields/password.vue'; import Currency from '../editor/fields/currency.vue'; import Number from '../editor/fields/number.vue'; import Rte from '../editor/fields/rte.vue'; @@ -168,6 +169,18 @@ class EditorField } + /** + * Render a password input field + * @param {number} [maxLength] - Maximum length of the input + * @param {string} [placeholder] - Placeholder text (can be a translation) + * @returns {EditorField} + */ + password(maxLength, placeholder) + { + return this._setComponent(Password, { maxLength, placeholder }); + } + + /** * Render a currency input field * @param {string} [placeholder] - Placeholder text (can be a translation) diff --git a/zero.Web/Controllers/IntegrationsController.cs b/zero.Web/Controllers/IntegrationsController.cs index b7c8856d..fc709413 100644 --- a/zero.Web/Controllers/IntegrationsController.cs +++ b/zero.Web/Controllers/IntegrationsController.cs @@ -1,71 +1,45 @@ using Microsoft.AspNetCore.Mvc; -using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using zero.Core.Collections; using zero.Core.Entities; using zero.Core.Integrations; +using zero.Web.Models; namespace zero.Web.Controllers { - //[ZeroAuthorize(Permissions.Sections.Spaces, PermissionsValue.True)] + //[ZeroAuthorize(Permissions.Sections.PREFIX + "commerce", PermissionsValue.Read)] public class IntegrationsController : BackofficeController { - IIntegrationsCollection Integrations; - IIntegrationService Types; + IIntegrationsCollection Collection; - public IntegrationsController(IIntegrationsCollection integrations, IIntegrationService types) + public IntegrationsController(IIntegrationsCollection collection) { - Integrations = integrations; - Types = types; + Collection = collection; + Collection.WithInactive(); } - public async Task GetAll() - { - return Ok(new - { - //available = await Types.GetAvailable(), - //activated = await Types.GetActivated() - }); - } + public EditModel GetEmpty([FromQuery] string alias) => Edit(Collection.GetEmpty(alias)); - public IActionResult GetEmptySettings([FromQuery] string alias) - { - //IIntegration integration = Types.GetByAlias(alias); - //IIntegrationModel model = integration != null ? Activator.CreateInstance(integration.ModelType) as IIntegrationModel : null; - - //if (model != null) - //{ - // model.IntegrationAlias = integration.Alias; - //} - - return Ok(Edit(default(IIntegration))); - } + public async Task> GetByAlias([FromQuery] string alias) => Edit(await Collection.GetByAlias(alias)); - public async Task GetSettingsByAlias([FromQuery] string alias) - { - //IIntegrationModel content = await Integrations.GetByAlias(alias); - return Ok(Edit(default(IIntegration))); - } + public async Task> GetByQuery([FromQuery] ListQuery query) => await Collection.GetByQuery(query); - public async Task GetSettingsById([FromQuery] string id) - { - //IIntegrationModel content = await Integrations.GetById(id); - return Ok(Edit(default(IIntegration))); - } + public async Task> GetTypes() => await Collection.GetTypesWithStatus(); - public async Task Save([FromBody] IIntegration model) - { - return Ok(); - //return Ok(await Integrations.Save(model)); - } + [HttpPost] + public async Task> Save([FromBody] IIntegration model) => await Collection.Save(model); - public async Task Delete([FromQuery] string id) - { - return Ok(await Integrations.DeleteById(id)); - } + [HttpPost] + public async Task> SaveActiveState([FromBody] Integration model) => model.IsActive ? await Collection.Activate(model.Alias) : await Collection.Deactivate(model.Alias); + + [HttpDelete] + public async Task> Delete([FromQuery] string alias) => await Collection.Delete(alias); } -} +} \ No newline at end of file diff --git a/zero.Web/Defaults/ZeroBackofficePlugin.cs b/zero.Web/Defaults/ZeroBackofficePlugin.cs index cdee1385..783c89a4 100644 --- a/zero.Web/Defaults/ZeroBackofficePlugin.cs +++ b/zero.Web/Defaults/ZeroBackofficePlugin.cs @@ -120,8 +120,8 @@ namespace zero.Web.Defaults services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddTransient(); + services.AddTransient(); services.AddScoped(); services.AddScoped(); diff --git a/zero.Web/Resources/Localization/zero.en-us.json b/zero.Web/Resources/Localization/zero.en-us.json index e813388d..0f549939 100644 --- a/zero.Web/Resources/Localization/zero.en-us.json +++ b/zero.Web/Resources/Localization/zero.en-us.json @@ -462,6 +462,12 @@ "list": "Integrations", "name": "Integration", "activeWarning": "This integration is not activated and will therefore not run under any circumstances.", + "errors": { + "typenotfound": "The integration type does not exist", + "multiplenotallowed": "Can not create multiple integrations per type", + "notfound": "Could not find a matching integration", + "couldnotupdatestate": "Not updated" + }, "fields": { } },