diff --git a/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs b/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs index 44f0ce8c2..00b4a74db 100644 --- a/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs +++ b/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs @@ -42,12 +42,10 @@ namespace Squidex.Extensions.Actions.ElasticSearch [Display(Name = "Username", Description = "The optional username.")] [DataType(DataType.Text)] - [Formattable] public string Username { get; set; } [Display(Name = "Password", Description = "The optional password.")] - [DataType(DataType.Password)] - [Formattable] + [DataType(DataType.Text)] public string Password { get; set; } } } diff --git a/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs b/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs index e85c19f32..47e1dddc7 100644 --- a/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs +++ b/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs @@ -26,7 +26,6 @@ namespace Squidex.Extensions.Actions.Slack [Required] [Display(Name = "Webhook Url", Description = "The slack webhook url.")] [DataType(DataType.Text)] - [Formattable] public Uri WebhookUrl { get; set; } [Required] diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionDefinition.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionDefinition.cs index 595f6d56b..97a9110d1 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionDefinition.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionDefinition.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Collections.Generic; namespace Squidex.Domain.Apps.Core.HandleRules { @@ -22,5 +23,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules public string Display { get; set; } public string Description { get; set; } + + public List Properties { get; } = new List(); } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionProperty.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionProperty.cs new file mode 100644 index 000000000..1611e76ea --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionProperty.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Domain.Apps.Core.HandleRules +{ + public sealed class RuleActionProperty + { + public RuleActionPropertyEditor Editor { get; set; } + + public string Name { get; set; } + + public string Display { get; set; } + + public string Description { get; set; } + + public bool IsFormattable { get; set; } + + public bool IsRequired { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionPropertyEditor.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionPropertyEditor.cs new file mode 100644 index 000000000..469e01a35 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionPropertyEditor.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Domain.Apps.Core.HandleRules +{ + public enum RuleActionPropertyEditor + { + Checkbox, + Email, + Number, + Password, + Text, + TextArea, + Url + } +} diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionRegistry.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionRegistry.cs index a17a58872..c1eebfb1e 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionRegistry.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionRegistry.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using Squidex.Domain.Apps.Core.Rules; @@ -17,7 +18,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules public static class RuleActionRegistry { private const string ActionSuffix = "Action"; - private const string ActionSuffixV2 = "Action"; + private const string ActionSuffixV2 = "ActionV2"; private static readonly HashSet ActionHandlerTypes = new HashSet(); private static readonly Dictionary ActionTypes = new Dictionary(); @@ -70,7 +71,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules var name = GetActionName(actionType); - ActionTypes[name] = + var definition = new RuleActionDefinition { Type = actionType, @@ -81,9 +82,81 @@ namespace Squidex.Domain.Apps.Core.HandleRules ReadMore = metadata.ReadMore }; + foreach (var property in actionType.GetProperties()) + { + if (property.CanRead && property.CanWrite) + { + var actionProperty = new RuleActionProperty { Name = property.Name.ToCamelCase(), Display = property.Name }; + + var display = property.GetCustomAttribute(); + + if (!string.IsNullOrWhiteSpace(display?.Name)) + { + actionProperty.Display = display.Name; + } + + if (!string.IsNullOrWhiteSpace(display?.Description)) + { + actionProperty.Description = display.Description; + } + + var type = property.PropertyType; + + if ((property.GetCustomAttribute() != null || (type.IsValueType && !IsNullable(type))) && type != typeof(bool) && type != typeof(bool?)) + { + actionProperty.IsRequired = true; + } + + if (property.GetCustomAttribute() != null) + { + actionProperty.IsFormattable = true; + } + + var dataType = property.GetCustomAttribute()?.DataType; + + if (type == typeof(bool) || type == typeof(bool?)) + { + actionProperty.Editor = RuleActionPropertyEditor.Checkbox; + } + else if (type == typeof(int) || type == typeof(int?)) + { + actionProperty.Editor = RuleActionPropertyEditor.Number; + } + else if (dataType == DataType.Url) + { + actionProperty.Editor = RuleActionPropertyEditor.Url; + } + else if (dataType == DataType.Password) + { + actionProperty.Editor = RuleActionPropertyEditor.Password; + } + else if (dataType == DataType.EmailAddress) + { + actionProperty.Editor = RuleActionPropertyEditor.Email; + } + else if (dataType == DataType.MultilineText) + { + actionProperty.Editor = RuleActionPropertyEditor.TextArea; + } + else + { + actionProperty.Editor = RuleActionPropertyEditor.Text; + } + + definition.Properties.Add(actionProperty); + } + } + + ActionTypes[name] = definition; + ActionHandlerTypes.Add(actionType.GetCustomAttribute().HandlerType); } + private static bool IsNullable(Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + public static TypeNameRegistry MapRuleActions(this TypeNameRegistry typeNameRegistry) { foreach (var actionType in ActionTypes.Values) diff --git a/src/Squidex.Infrastructure/Plugins/IPlugin.cs b/src/Squidex.Infrastructure/Plugins/IPlugin.cs index 72ed11047..2c8fc4674 100644 --- a/src/Squidex.Infrastructure/Plugins/IPlugin.cs +++ b/src/Squidex.Infrastructure/Plugins/IPlugin.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionConverter.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionConverter.cs index fe5666dad..b36243b4f 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionConverter.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionConverter.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.Linq; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules; -using Squidex.Extensions.Actions; namespace Squidex.Areas.Api.Controllers.Rules.Models { diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs index 2ea9995b8..b2abd5b91 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs @@ -13,7 +13,6 @@ using NSwag.SwaggerGeneration.Processors; using NSwag.SwaggerGeneration.Processors.Contexts; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules; -using Squidex.Extensions.Actions; namespace Squidex.Areas.Api.Controllers.Rules.Models { diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleElementDto.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleElementDto.cs index e419be526..910e87c38 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleElementDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleElementDto.cs @@ -6,6 +6,9 @@ // ========================================================================== using System.ComponentModel.DataAnnotations; +using System.Linq; +using Squidex.Domain.Apps.Core.HandleRules; +using Squidex.Infrastructure.Reflection; namespace Squidex.Areas.Api.Controllers.Rules.Models { @@ -37,5 +40,20 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models /// The optional link to the product that is integrated. /// public string ReadMore { get; set; } + + /// + /// The properties. + /// + [Required] + public RuleElementPropertyDto[] Properties { get; set; } + + public static RuleElementDto FromDefinition(RuleActionDefinition definition) + { + var result = SimpleMapper.Map(definition, new RuleElementDto()); + + result.Properties = definition.Properties.Select(x => SimpleMapper.Map(x, new RuleElementPropertyDto())).ToArray(); + + return result; + } } } diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleElementPropertyDto.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleElementPropertyDto.cs new file mode 100644 index 000000000..81170e36f --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleElementPropertyDto.cs @@ -0,0 +1,48 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; +using Squidex.Domain.Apps.Core.HandleRules; + +namespace Squidex.Areas.Api.Controllers.Rules.Models +{ + public sealed class RuleElementPropertyDto + { + /// + /// The html editor. + /// + [Required] + public RuleActionPropertyEditor Editor { get; set; } + + /// + /// The name of the editor. + /// + [Required] + public string Name { get; set; } + + /// + /// The label to use. + /// + [Required] + public string Display { get; set; } + + /// + /// The optional description. + /// + public string Description { get; set; } + + /// + /// Indicates if the property is formattable. + /// + public bool IsFormattable { get; set; } + + /// + /// Indicates if the property is required. + /// + public bool IsRequired { get; set; } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs b/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs index 752b4e041..d3d7edd1b 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs @@ -17,10 +17,8 @@ using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.Repositories; -using Squidex.Extensions.Actions; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Infrastructure.Reflection; using Squidex.Pipeline; using Squidex.Shared; @@ -58,7 +56,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [ApiCosts(0)] public IActionResult GetActions() { - var response = RuleActionRegistry.Actions.ToDictionary(x => x.Key, x => SimpleMapper.Map(x.Value, new RuleElementDto())); + var response = RuleActionRegistry.Actions.ToDictionary(x => x.Key, x => RuleElementDto.FromDefinition(x.Value)); Response.Headers[HeaderNames.ETag] = RuleActionsEtag; diff --git a/src/Squidex/Config/Domain/RuleServices.cs b/src/Squidex/Config/Domain/RuleServices.cs index 5bdc3f45d..685bc2077 100644 --- a/src/Squidex/Config/Domain/RuleServices.cs +++ b/src/Squidex/Config/Domain/RuleServices.cs @@ -12,7 +12,6 @@ using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Rules; using Squidex.Domain.Apps.Entities.Rules.UsageTracking; using Squidex.Domain.Apps.Entities.Schemas; -using Squidex.Extensions.Actions; using Squidex.Infrastructure.DependencyInjection; using Squidex.Infrastructure.EventSourcing; diff --git a/src/Squidex/app/features/rules/declarations.ts b/src/Squidex/app/features/rules/declarations.ts index 6b65eccfb..e8d6e49d0 100644 --- a/src/Squidex/app/features/rules/declarations.ts +++ b/src/Squidex/app/features/rules/declarations.ts @@ -5,17 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -export * from './pages/rules/actions/algolia-action.component'; -export * from './pages/rules/actions/azure-queue-action.component'; -export * from './pages/rules/actions/discourse-action.component'; -export * from './pages/rules/actions/elastic-search-action.component'; -export * from './pages/rules/actions/email-action.component'; -export * from './pages/rules/actions/fastly-action.component'; -export * from './pages/rules/actions/medium-action.component'; -export * from './pages/rules/actions/prerender-action.component'; -export * from './pages/rules/actions/slack-action.component'; -export * from './pages/rules/actions/tweet-action.component'; -export * from './pages/rules/actions/webhook-action.component'; +export * from './pages/rules/actions/generic-action.component'; export * from './pages/rules/triggers/asset-changed-trigger.component'; export * from './pages/rules/triggers/content-changed-trigger.component'; diff --git a/src/Squidex/app/features/rules/module.ts b/src/Squidex/app/features/rules/module.ts index 95551399c..7ee3218b3 100644 --- a/src/Squidex/app/features/rules/module.ts +++ b/src/Squidex/app/features/rules/module.ts @@ -15,26 +15,16 @@ import { } from '@app/shared'; import { - AlgoliaActionComponent, AssetChangedTriggerComponent, - AzureQueueActionComponent, ContentChangedTriggerComponent, - DiscourseActionComponent, - ElasticSearchActionComponent, - EmailActionComponent, - FastlyActionComponent, - MediumActionComponent, - PrerenderActionComponent, + GenericActionComponent, RuleElementComponent, RuleEventBadgeClassPipe, RuleEventsPageComponent, RulesPageComponent, RuleWizardComponent, SchemaChangedTriggerComponent, - SlackActionComponent, - TweetActionComponent, - UsageTriggerComponent, - WebhookActionComponent + UsageTriggerComponent } from './declarations'; const routes: Routes = [ @@ -64,26 +54,16 @@ const routes: Routes = [ RouterModule.forChild(routes) ], declarations: [ - AlgoliaActionComponent, AssetChangedTriggerComponent, - AzureQueueActionComponent, ContentChangedTriggerComponent, - DiscourseActionComponent, - EmailActionComponent, - ElasticSearchActionComponent, - FastlyActionComponent, - MediumActionComponent, - PrerenderActionComponent, + GenericActionComponent, RuleElementComponent, RuleEventBadgeClassPipe, RuleEventsPageComponent, RulesPageComponent, RuleWizardComponent, SchemaChangedTriggerComponent, - SlackActionComponent, - TweetActionComponent, - UsageTriggerComponent, - WebhookActionComponent + UsageTriggerComponent ] }) export class SqxFeatureRulesModule { } \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.html deleted file mode 100644 index 10e8f129e..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.html +++ /dev/null @@ -1,43 +0,0 @@ -
-
- - -
- - - - - - The ID to you algolia application. - -
-
- -
- - -
- - - - - - The API Key to access you algolia app. - -
-
- -
- - -
- - - - - - The name of the index. You can use advanced formatting (read help section). - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.scss deleted file mode 100644 index fbb752506..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import '_vars'; -@import '_mixins'; \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.ts deleted file mode 100644 index 0ac2c1641..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -@Component({ - selector: 'sqx-algolia-action', - styleUrls: ['./algolia-action.component.scss'], - templateUrl: './algolia-action.component.html' -}) -export class AlgoliaActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('appId', - new FormControl(this.action.appId || '', [ - Validators.required - ])); - - this.actionForm.setControl('apiKey', - new FormControl(this.action.apiKey || '', [ - Validators.required - ])); - - this.actionForm.setControl('indexName', - new FormControl(this.action.indexName || '$SCHEMA_NAME', [ - Validators.required - ])); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.html deleted file mode 100644 index ffae8dd67..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
- - -
- - - - - - The connection string to the storage account. - -
-
- -
- - -
- - - - - - The name of the queue. - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.scss deleted file mode 100644 index fbb752506..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import '_vars'; -@import '_mixins'; \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.ts deleted file mode 100644 index 8df30f90e..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -import { ValidatorsEx } from '@app/shared'; - -@Component({ - selector: 'sqx-azure-queue-action', - styleUrls: ['./azure-queue-action.component.scss'], - templateUrl: './azure-queue-action.component.html' -}) -export class AzureQueueActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('connectionString', - new FormControl(this.action.connectionString || '', [ - Validators.required - ])); - - this.actionForm.setControl('queue', - new FormControl(this.action.queue || 'squidex', [ - Validators.required, - ValidatorsEx.pattern('[a-z][a-z0-9]{2,}(\-[a-z0-9]+)*', 'Name must be a valid azure queue name.') - ])); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/discourse-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/discourse-action.component.html deleted file mode 100644 index e22fd9080..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/discourse-action.component.html +++ /dev/null @@ -1,99 +0,0 @@ -
-
- - -
- - - - - - The url to your discourse server. - -
-
- -
- - -
- - - - - - The api key to authenticate to your discourse server. - -
-
- -
- - -
- - - - - - The api username to authenticate to your discourse server. - -
-
- -
- - -
- - - - - - The text for your topic or post. Read the help section for information about advanced formatting. - -
-
- -
- - -
- - - - - - The optional title, when you want to create a topic. Read the help section for information about advanced formatting. - -
-
- -
- - -
- - - - - - The topic id when you want to create a post. - -
-
- -
- - -
- - - - - - The category id when you create a topic. - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/discourse-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/discourse-action.component.scss deleted file mode 100644 index 756609665..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/discourse-action.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import '_vars'; -@import '_mixins'; - -textarea { - height: 150px; -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/discourse-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/discourse-action.component.ts deleted file mode 100644 index a2e25ac55..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/discourse-action.component.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -@Component({ - selector: 'sqx-discourse-action', - styleUrls: ['./discourse-action.component.scss'], - templateUrl: './discourse-action.component.html' -}) -export class DiscourseActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('url', - new FormControl(this.action.url || '', [ - Validators.required - ])); - - this.actionForm.setControl('apiKey', - new FormControl(this.action.apiKey || '', [ - Validators.required - ])); - - this.actionForm.setControl('apiUsername', - new FormControl(this.action.apiUsername || '', [ - Validators.required - ])); - - this.actionForm.setControl('text', - new FormControl(this.action.text || '', [ - Validators.required - ])); - - this.actionForm.setControl('title', - new FormControl(this.action.title)); - - this.actionForm.setControl('topic', - new FormControl(this.action.topic)); - - this.actionForm.setControl('category', - new FormControl(this.action.category)); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.html deleted file mode 100644 index fee16364c..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.html +++ /dev/null @@ -1,71 +0,0 @@ -
-
- - -
- - - - - - The url to your elastic search instance. - -
-
- -
- - -
- - - - - - The username for authentication. Highly recommended. - -
-
- -
- - -
- - - - - - The password for authentication. Highly recommended. - -
-
- -
- - -
- - - - - - The name of the index. You can use advanced formatting (read help section). - -
-
- -
- - -
- - - - - - The name of the type. You can use advanced formatting (read help section). - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.scss deleted file mode 100644 index fbb752506..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import '_vars'; -@import '_mixins'; \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.ts deleted file mode 100644 index 8e6e0c8f2..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -@Component({ - selector: 'sqx-elastic-search-action', - styleUrls: ['./elastic-search-action.component.scss'], - templateUrl: './elastic-search-action.component.html' -}) -export class ElasticSearchActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('host', - new FormControl(this.action.host || '', [ - Validators.required - ])); - - this.actionForm.setControl('indexName', - new FormControl(this.action.indexName || '$APP_NAME', [ - Validators.required - ])); - - this.actionForm.setControl('indexType', - new FormControl(this.action.indexType || '$SCHEMA_NAME')); - - this.actionForm.setControl('username', - new FormControl(this.action.username)); - - this.actionForm.setControl('password', - new FormControl(this.action.password)); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/email-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/email-action.component.html deleted file mode 100644 index c083bb1cc..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/email-action.component.html +++ /dev/null @@ -1,128 +0,0 @@ -
-
- - -
- - - - - - The IP address or host to the SMTP server. - -
-
- -
- - -
- - - - - - The port to the SMTP server. - -
-
- -
-
-
- - -
- - Specify whether the SMTP client uses Secure Sockets Layer (SSL) to encrypt the connection. - -
-
- -
- - -
- - - - - - The username for the SMTP server. - -
-
- -
- - -
- - - - - - The password for the SMTP server. - -
-
- -
- - -
- - - - - - The email sending address. Read the help section for information about advanced formatting. - -
-
- -
- - -
- - - - - - The email message will be sent to. Read the help section for information about advanced formatting. - -
-
- -
- - -
- - - - - - The subject line for this email message. Read the help section for information about advanced formatting. - -
-
- - -
- - -
- - - - - - The message body. Read the help section for information about advanced formatting. - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/email-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/email-action.component.scss deleted file mode 100644 index 51468cba5..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/email-action.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import '_vars'; -@import '_mixins'; - -textarea { - height: 250px; -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/email-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/email-action.component.ts deleted file mode 100644 index 31761bef8..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/email-action.component.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -@Component({ - selector: 'sqx-email-action', - styleUrls: ['./email-action.component.scss'], - templateUrl: './email-action.component.html' -}) -export class EmailActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('serverHost', - new FormControl(this.action.serverHost || 'smtp.gmail.com', [ - Validators.required - ])); - - this.actionForm.setControl('serverPort', - new FormControl(this.action.serverPort || 465, [ - Validators.required - ])); - - this.actionForm.setControl('serverUseSsl', - new FormControl(this.action.serverUseSsl || true)); - - this.actionForm.setControl('serverUsername', - new FormControl(this.action.serverUsername || '', [ - Validators.required - ])); - - this.actionForm.setControl('serverPassword', - new FormControl(this.action.serverPassword || '', [ - Validators.required - ])); - - this.actionForm.setControl('messageFrom', - new FormControl(this.action.messageFrom || '', [ - Validators.required - ])); - - this.actionForm.setControl('messageTo', - new FormControl(this.action.messageTo || '', [ - Validators.required - ])); - - this.actionForm.setControl('messageSubject', - new FormControl(this.action.messageSubject || '', [ - Validators.required - ])); - - this.actionForm.setControl('messageBody', - new FormControl(this.action.messageBody || '', [ - Validators.required - ])); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.html deleted file mode 100644 index b089b622c..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
- - -
- - - - - - The service ID of the fastly account. - -
-
- -
- - -
- - - - - - The API key for the fastly account. - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.scss deleted file mode 100644 index fbb752506..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import '_vars'; -@import '_mixins'; \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.ts deleted file mode 100644 index 192216769..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -@Component({ - selector: 'sqx-fastly-action', - styleUrls: ['./fastly-action.component.scss'], - templateUrl: './fastly-action.component.html' -}) -export class FastlyActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('serviceId', - new FormControl(this.action.serviceId || '', [ - Validators.required - ])); - - this.actionForm.setControl('apiKey', - new FormControl(this.action.apiKey || '', [ - Validators.required - ])); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/generic-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/generic-action.component.html new file mode 100644 index 000000000..5c0438347 --- /dev/null +++ b/src/Squidex/app/features/rules/pages/rules/actions/generic-action.component.html @@ -0,0 +1,34 @@ +
+
+ + +
+ + +
+
+ +
+
+
+ + +
+
+
+ +
+
+ + + {{property.description}} + + + You can use advanced formatting: Documentation + + +
+
+
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/generic-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/generic-action.component.scss new file mode 100644 index 000000000..6f7a94e21 --- /dev/null +++ b/src/Squidex/app/features/rules/pages/rules/actions/generic-action.component.scss @@ -0,0 +1,6 @@ +@import '_vars'; +@import '_mixins'; + +.form-check { + padding-top: .5rem; +} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/generic-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/generic-action.component.ts new file mode 100644 index 000000000..06992b17e --- /dev/null +++ b/src/Squidex/app/features/rules/pages/rules/actions/generic-action.component.ts @@ -0,0 +1,44 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; + +import { RuleElementDto } from '@app/shared'; + +@Component({ + selector: 'sqx-generic-action', + styleUrls: ['./generic-action.component.scss'], + templateUrl: './generic-action.component.html' +}) +export class GenericActionComponent implements OnInit { + @Input() + public definition: RuleElementDto; + + @Input() + public action: any; + + @Input() + public actionForm: FormGroup; + + @Input() + public actionFormSubmitted = false; + + public ngOnInit() { + for (let property of this.definition.properties) { + const validators = []; + + if (property.isRequired) { + validators.push(Validators.required); + } + + const control = new FormControl(this.action[property.name] || '', validators); + + this.actionForm.setControl(property.name, control); + } + } +} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.html deleted file mode 100644 index d92a5c7e8..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.html +++ /dev/null @@ -1,96 +0,0 @@ -
-
- - -
- - - - - - The self issued access token. Can be created under https://medium.com/me/settings. - -
-
- -
- - -
- - - - - - The title of the post. Note that this title is used for SEO and when rendering the post as a listing. - -
-
- -
- - -
- - - - - - The body of the post, in a valid, semantic, HTML fragment, or Markdown. - -
-
- -
-
-
- - -
-
-
- -
- - -
- - - - - - The original home of this content, if it was originally published elsewhere. - -
-
- -
- - -
- - - - - - Optional comma-separated list of tags. - -
-
- -
- - -
- - - - - - Optional publication id. Go to https://medium.com/[PUBLICATION]?format=json to fetch the id. - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.scss deleted file mode 100644 index 756609665..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import '_vars'; -@import '_mixins'; - -textarea { - height: 150px; -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.ts deleted file mode 100644 index a14f0841c..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/medium-action.component.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -@Component({ - selector: 'sqx-medium-action', - styleUrls: ['./medium-action.component.scss'], - templateUrl: './medium-action.component.html' -}) -export class MediumActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('accessToken', - new FormControl(this.action.accessToken || '', [ - Validators.required - ])); - - this.actionForm.setControl('title', - new FormControl(this.action.title || '', [ - Validators.required - ])); - - this.actionForm.setControl('content', - new FormControl(this.action.content || '', [ - Validators.required - ])); - - this.actionForm.setControl('canonicalUrl', - new FormControl(this.action.canonicalUrl || '')); - - this.actionForm.setControl('tags', - new FormControl(this.action.tags || '')); - - this.actionForm.setControl('publicationId', - new FormControl(this.action.publicationId || '')); - - this.actionForm.setControl('isHtml', - new FormControl(this.action.isHtml || false)); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/prerender-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/prerender-action.component.html deleted file mode 100644 index 63b5a2319..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/prerender-action.component.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
- - -
- - - - - - The prerender token from your account. - -
-
- -
- - -
- - - - - - The url to recache. Read the help section for information about advanced formatting. - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/prerender-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/prerender-action.component.scss deleted file mode 100644 index fbb752506..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/prerender-action.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import '_vars'; -@import '_mixins'; \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/prerender-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/prerender-action.component.ts deleted file mode 100644 index adf25fb6d..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/prerender-action.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -@Component({ - selector: 'sqx-prerender-action', - styleUrls: ['./prerender-action.component.scss'], - templateUrl: './prerender-action.component.html' -}) -export class PrerenderActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('token', - new FormControl(this.action.token || '', [ - Validators.required - ])); - - this.actionForm.setControl('url', - new FormControl(this.action.url || '', [ - Validators.required - ])); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.html deleted file mode 100644 index 0f21696a0..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
- - -
- - - - - - The url to the incoming slack webhook. - -
-
- -
- - -
- - - - - - The text to send to slack. Read the help section for information about advanced formatting. - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.scss deleted file mode 100644 index 756609665..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import '_vars'; -@import '_mixins'; - -textarea { - height: 150px; -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.ts deleted file mode 100644 index 9a425591b..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -@Component({ - selector: 'sqx-slack-action', - styleUrls: ['./slack-action.component.scss'], - templateUrl: './slack-action.component.html' -}) -export class SlackActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('webhookUrl', - new FormControl(this.action.webhookUrl || '', [ - Validators.required - ])); - - this.actionForm.setControl('text', - new FormControl(this.action.text || '', [ - Validators.required - ])); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/tweet-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/tweet-action.component.html deleted file mode 100644 index 341d20043..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/tweet-action.component.html +++ /dev/null @@ -1,55 +0,0 @@ -
-
-
- - - - - - - - - - - -
-
- -
- - -
- - - -
-
- -
- - -
- - - -
-
- -
- - -
- - - - - - The text to tweet. Read the help section for information about advanced formatting. - -
-
- \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/tweet-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/tweet-action.component.scss deleted file mode 100644 index 756609665..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/tweet-action.component.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import '_vars'; -@import '_mixins'; - -textarea { - height: 150px; -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/tweet-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/tweet-action.component.ts deleted file mode 100644 index 84b8b802c..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/tweet-action.component.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { HttpClient } from '@angular/common/http'; -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -import { DialogService } from '@app/shared'; - -@Component({ - selector: 'sqx-tweet-action', - styleUrls: ['./tweet-action.component.scss'], - templateUrl: './tweet-action.component.html' -}) -export class TweetActionComponent implements OnInit { - private request: any; - - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public isAuthenticating = false; - public isRedirected = false; - - public pinCode: string; - - constructor( - private readonly dialogs: DialogService, - private readonly httpClient: HttpClient - ) { - } - - public ngOnInit() { - this.actionForm.setControl('accessToken', - new FormControl(this.action.accessToken || '', [ - Validators.required - ])); - - this.actionForm.setControl('accessSecret', - new FormControl(this.action.accessSecret || '', [ - Validators.required - ])); - - this.actionForm.setControl('text', - new FormControl(this.action.text || '', [ - Validators.required, - Validators.maxLength(280) - ])); - } - - public auth() { - this.isAuthenticating = true; - - this.httpClient.get('api/rules/twitter/auth') - .subscribe((response: any) => { - this.request = { - requestToken: response.requestToken, - requestTokenSecret: response.requestTokenSecret - }; - - this.isAuthenticating = false; - this.isRedirected = true; - - window.open(response.authorizeUri, '_blank'); - }, () => { - this.dialogs.notifyError('Failed to authenticate with twitter.'); - - this.isAuthenticating = false; - this.isRedirected = false; - }); - } - - public complete() { - this.request.pinCode = this.pinCode; - - this.httpClient.post('api/rules/twitter/token', this.request) - .subscribe((response: any) => { - this.actionForm.get('accessToken')!.setValue(response.accessToken); - this.actionForm.get('accessSecret')!.setValue(response.accessTokenSecret); - - this.isRedirected = false; - }, () => { - this.dialogs.notifyError('Failed to request access token.'); - - this.isAuthenticating = false; - this.isRedirected = false; - }); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.html b/src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.html deleted file mode 100644 index b8d4bb1d8..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.html +++ /dev/null @@ -1,29 +0,0 @@ -
-
- - -
- - - - - - The url where the events will be sent to. - -
-
- -
- - -
- - - - - - The shared secret will be used to add a header X-Signature=Base64(Sha256(RequestBody + Secret)) - -
-
-
\ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.scss b/src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.scss deleted file mode 100644 index fbb752506..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import '_vars'; -@import '_mixins'; \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.ts b/src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.ts deleted file mode 100644 index 767ed1442..000000000 --- a/src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Squidex Headless CMS - * - * @license - * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. - */ - -import { Component, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; - -@Component({ - selector: 'sqx-webhook-action', - styleUrls: ['./webhook-action.component.scss'], - templateUrl: './webhook-action.component.html' -}) -export class WebhookActionComponent implements OnInit { - @Input() - public action: any; - - @Input() - public actionForm: FormGroup; - - @Input() - public actionFormSubmitted = false; - - public ngOnInit() { - this.actionForm.setControl('url', - new FormControl(this.action.url || '', [ - Validators.required - ])); - - this.actionForm.setControl('sharedSecret', - new FormControl(this.action.sharedSecret || '')); - } -} \ No newline at end of file diff --git a/src/Squidex/app/features/rules/pages/rules/rule-element.component.html b/src/Squidex/app/features/rules/pages/rules/rule-element.component.html index 977302dab..695bdea88 100644 --- a/src/Squidex/app/features/rules/pages/rules/rule-element.component.html +++ b/src/Squidex/app/features/rules/pages/rules/rule-element.component.html @@ -1,16 +1,14 @@ -
-
+
+
-
-
- {{element.display}} -
+
+ {{element.display}}
diff --git a/src/Squidex/app/features/rules/pages/rules/rule-element.component.scss b/src/Squidex/app/features/rules/pages/rules/rule-element.component.scss index b22df1cd7..aab579f44 100644 --- a/src/Squidex/app/features/rules/pages/rules/rule-element.component.scss +++ b/src/Squidex/app/features/rules/pages/rules/rule-element.component.scss @@ -5,9 +5,12 @@ & { @include transition(background-color .4s ease); cursor: pointer; + height: 3rem; + position: relative; } &-text { + @include absolute(0, 0, 0, 3rem); @include truncate; color: $color-dark-foreground; line-height: 3rem; @@ -17,16 +20,14 @@ } &-icon { + @include absolute(0, auto, 0, 0); + color: $color-dark-foreground; line-height: 3.2rem; font-size: 1.2rem; font-weight: normal; padding: 0 .8rem; } - .col { - height: 3rem; - } - .icon { font-size: 20px; } diff --git a/src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html b/src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html index 16f1fca8e..9c5b40f42 100644 --- a/src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html +++ b/src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html @@ -79,85 +79,12 @@

{{ruleActions[actionType].display}}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
diff --git a/src/Squidex/app/shared/services/rules.service.spec.ts b/src/Squidex/app/shared/services/rules.service.spec.ts index c5d6fe99b..da576b6f0 100644 --- a/src/Squidex/app/shared/services/rules.service.spec.ts +++ b/src/Squidex/app/shared/services/rules.service.spec.ts @@ -22,6 +22,7 @@ import { UpdateRuleDto, Version } from './../'; +import { RuleElementPropertyDto } from './rules.service'; describe('RulesService', () => { const now = DateTime.now(); @@ -65,20 +66,43 @@ describe('RulesService', () => { description: 'description2', iconColor: '#222', iconImage: '', - readMore: 'link2' + readMore: 'link2', + properties: [{ + name: 'property1', + editor: 'Editor1', + display: 'Display1', + description: 'Description1', + isRequired: true, + isFormattable: false + }, { + name: 'property2', + editor: 'Editor2', + display: 'Display2', + description: 'Description2', + isRequired: false, + isFormattable: true + }] }, 'action1': { display: 'display1', description: 'description1', iconColor: '#111', iconImage: '', - readMore: 'link1' + readMore: 'link1', + properties: [] } }); + const action1 = new RuleElementDto('display1', 'description1', '#111', '', null, 'link1', []); + + const action2 = new RuleElementDto('display2', 'description2', '#222', '', null, 'link2', [ + new RuleElementPropertyDto('property1', 'Editor1', 'Display1', 'Description1', false, true), + new RuleElementPropertyDto('property2', 'Editor2', 'Display2', 'Description2', true, false) + ]); + expect(actions!).toEqual({ - 'action1': new RuleElementDto('display1', 'description1', '#111', '', null, 'link1'), - 'action2': new RuleElementDto('display2', 'description2', '#222', '', null, 'link2') + 'action1': action1, + 'action2': action2 }); })); diff --git a/src/Squidex/app/shared/services/rules.service.ts b/src/Squidex/app/shared/services/rules.service.ts index cb2eae816..9ca980f73 100644 --- a/src/Squidex/app/shared/services/rules.service.ts +++ b/src/Squidex/app/shared/services/rules.service.ts @@ -54,7 +54,20 @@ export class RuleElementDto { public readonly iconColor: string, public readonly iconImage: string, public readonly iconCode: string | null, - public readonly readMore: string + public readonly readMore: string, + public readonly properties: RuleElementPropertyDto[] + ) { + } +} + +export class RuleElementPropertyDto { + constructor( + public readonly name: string, + public readonly editor: string, + public readonly display: string, + public readonly description: string, + public readonly isFormattable: boolean, + public readonly isRequired: boolean ) { } } @@ -147,7 +160,23 @@ export class RulesService { for (let key of Object.keys(items).sort()) { const value = items[key]; - result[key] = new RuleElementDto(value.display, value.description, value.iconColor, value.iconImage, null, value.readMore); + const properties = value.properties.map((property: any) => + new RuleElementPropertyDto( + property.name, + property.editor, + property.display, + property.description, + property.isFormattable, + property.isRequired + )); + + result[key] = new RuleElementDto( + value.display, + value.description, + value.iconColor, + value.iconImage, null, + value.readMore, + properties); } return result; diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistry.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistry.cs new file mode 100644 index 000000000..3cef0cc4d --- /dev/null +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistry.cs @@ -0,0 +1,166 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using FluentAssertions; +using Squidex.Domain.Apps.Core.HandleRules; +using Squidex.Domain.Apps.Core.Rules; +using Xunit; + +namespace Squidex.Domain.Apps.Core.Operations.HandleRules +{ + public class RuleElementRegistry + { + private abstract class MyRuleActionHandler : RuleActionHandler + { + protected MyRuleActionHandler(RuleEventFormatter formatter) + : base(formatter) + { + } + } + + [RuleActionHandler(typeof(MyRuleActionHandler))] + [RuleAction( + IconImage = "", + IconColor = "#1e5470", + Display = "Action display", + Description = "Action description.", + ReadMore = "https://www.readmore.com/")] + public sealed class MyRuleAction : RuleAction + { + [Required] + [Display(Name = "Url Name", Description = "Url Description")] + [DataType(DataType.Url)] + [Formattable] + public Uri Url { get; set; } + + [DataType(DataType.EmailAddress)] + public string Email { get; set; } + + [DataType(DataType.Text)] + public string Text { get; set; } + + [DataType(DataType.MultilineText)] + public string TextMultiline { get; set; } + + [DataType(DataType.Password)] + public string Password { get; set; } + + public bool Boolean { get; set; } + + public bool? BooleanOptional { get; set; } + + public int Number { get; set; } + + public int? NumberOptional { get; set; } + } + + [Fact] + public void Should_create_definition() + { + var expected = new RuleActionDefinition + { + Type = typeof(MyRuleAction), + IconImage = "", + IconColor = "#1e5470", + Display = "Action display", + Description = "Action description.", + ReadMore = "https://www.readmore.com/" + }; + + expected.Properties.Add(new RuleActionProperty + { + Name = "url", + Display = "Url Name", + Description = "Url Description", + Editor = RuleActionPropertyEditor.Url, + IsFormattable = true, + IsRequired = true + }); + + expected.Properties.Add(new RuleActionProperty + { + Name = "email", + Display = "Email", + Description = null, + Editor = RuleActionPropertyEditor.Email, + IsRequired = false + }); + + expected.Properties.Add(new RuleActionProperty + { + Name = "text", + Display = "Text", + Description = null, + Editor = RuleActionPropertyEditor.Text, + IsRequired = false + }); + + expected.Properties.Add(new RuleActionProperty + { + Name = "textMultiline", + Display = "TextMultiline", + Description = null, + Editor = RuleActionPropertyEditor.TextArea, + IsRequired = false + }); + + expected.Properties.Add(new RuleActionProperty + { + Name = "password", + Display = "Password", + Description = null, + Editor = RuleActionPropertyEditor.Password, + IsRequired = false + }); + + expected.Properties.Add(new RuleActionProperty + { + Name = "boolean", + Display = "Boolean", + Description = null, + Editor = RuleActionPropertyEditor.Checkbox, + IsRequired = false + }); + + expected.Properties.Add(new RuleActionProperty + { + Name = "booleanOptional", + Display = "BooleanOptional", + Description = null, + Editor = RuleActionPropertyEditor.Checkbox, + IsRequired = false + }); + + expected.Properties.Add(new RuleActionProperty + { + Name = "number", + Display = "Number", + Description = null, + Editor = RuleActionPropertyEditor.Number, + IsRequired = true + }); + + expected.Properties.Add(new RuleActionProperty + { + Name = "numberOptional", + Display = "NumberOptional", + Description = null, + Editor = RuleActionPropertyEditor.Number, + IsRequired = false + }); + + RuleActionRegistry.Add(); + + var currentDefinition = RuleActionRegistry.Actions.Values.First(); + + currentDefinition.Should().BeEquivalentTo(expected); + } + } +}