Browse Source

Elastic search action.

pull/254/head
Sebastian Stehle 9 years ago
parent
commit
7aba54c7de
  1. 31
      src/Squidex.Domain.Apps.Core.Model/Rules/Actions/ElasticSearchAction.cs
  2. 2
      src/Squidex.Domain.Apps.Core.Model/Rules/IRuleActionVisitor.cs
  3. 20
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AlgoliaActionHandler.cs
  4. 181
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/ElasticSearchActionHandler.cs
  5. 4
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs
  6. 4
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs
  7. 1
      src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
  8. 22
      src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleActionValidator.cs
  9. 8
      src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs
  10. 53
      src/Squidex/Areas/Api/Controllers/Rules/Models/Actions/ElasticSearchActionDto.cs
  11. 5
      src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleActionDtoFactory.cs
  12. 3
      src/Squidex/Config/Domain/ReadServices.cs
  13. 1
      src/Squidex/Squidex.csproj
  14. 1
      src/Squidex/app/features/rules/declarations.ts
  15. 2
      src/Squidex/app/features/rules/module.ts
  16. 2
      src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.html
  17. 18
      src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.ts
  18. 2
      src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.html
  19. 14
      src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.ts
  20. 73
      src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.html
  21. 2
      src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.scss
  22. 70
      src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.ts
  23. 2
      src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.html
  24. 12
      src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.ts
  25. 6
      src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.ts
  26. 6
      src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.ts
  27. 6
      src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html
  28. 2
      src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.html
  29. 4
      src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.scss
  30. 3
      src/Squidex/app/shared/services/rules.service.ts
  31. 4
      src/Squidex/app/theme/_bootstrap.scss
  32. 5
      src/Squidex/app/theme/_rules.scss
  33. 2
      src/Squidex/app/theme/icomoon/demo-files/demo.css
  34. 848
      src/Squidex/app/theme/icomoon/demo.html
  35. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.eot
  36. 1
      src/Squidex/app/theme/icomoon/fonts/icomoon.svg
  37. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.ttf
  38. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.woff
  39. 1938
      src/Squidex/app/theme/icomoon/selection.json
  40. 163
      src/Squidex/app/theme/icomoon/style.css

31
src/Squidex.Domain.Apps.Core.Model/Rules/Actions/ElasticSearchAction.cs

@ -0,0 +1,31 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.Actions
{
[TypeName(nameof(ElasticSearchAction))]
public sealed class ElasticSearchAction : RuleAction
{
public Uri Host { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string IndexName { get; set; }
public string IndexType { get; set; }
public override T Accept<T>(IRuleActionVisitor<T> visitor)
{
return visitor.Visit(this);
}
}
}

2
src/Squidex.Domain.Apps.Core.Model/Rules/IRuleActionVisitor.cs

@ -15,6 +15,8 @@ namespace Squidex.Domain.Apps.Core.Rules
T Visit(AzureQueueAction action);
T Visit(ElasticSearchAction action);
T Visit(FastlyAction action);
T Visit(SlackAction action);

20
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AlgoliaActionHandler.cs

@ -22,7 +22,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
public sealed class AlgoliaActionHandler : RuleActionHandler<AlgoliaAction>
{
private const string SchemaNamePlaceholder = "$SCHEMA_NAME";
private readonly ClientPool<(string AppId, string ApiKey, string IndexName), Index> clients;
private readonly RuleEventFormatter formatter;
@ -62,6 +61,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
case ContentCreated created:
{
ruleDescription = $"Add entry to Algolia index: {action.IndexName}";
ruleData["Content"] = new JObject(
new JProperty("id", contentEvent.ContentId),
new JProperty("created", timestamp),
@ -76,6 +76,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
case ContentUpdated updated:
{
ruleDescription = $"Update entry in Algolia index: {action.IndexName}";
ruleData["Content"] = new JObject(
new JProperty("lastModified", timestamp),
new JProperty("lastModifiedBy", updated.Actor.ToString()),
@ -86,7 +87,10 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
case ContentStatusChanged statusChanged:
{
ruleDescription = $"Update entry in Algolia index: {action.IndexName}";
ruleData["Content"] = new JObject(
new JProperty("lastModified", timestamp),
new JProperty("lastModifiedBy", statusChanged.Actor.ToString()),
new JProperty("status", statusChanged.Status.ToString()));
break;
}
@ -94,7 +98,9 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
case ContentDeleted deleted:
{
ruleDescription = $"Delete entry from Index: {action.IndexName}";
ruleData["Content"] = new JObject();
ruleData["Operation"] = "Delete";
break;
}
}
@ -128,16 +134,16 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
content["objectID"] = contentId;
var resonse = await index.PartialUpdateObjectAsync(content);
var response = await index.PartialUpdateObjectAsync(content);
return (resonse.ToString(Formatting.Indented), null);
return (response.ToString(Formatting.Indented), null);
}
case "Delete":
{
var resonse = await index.DeleteObjectAsync(contentId);
var response = await index.DeleteObjectAsync(contentId);
return (resonse.ToString(Formatting.Indented), null);
return (response.ToString(Formatting.Indented), null);
}
default:
@ -148,10 +154,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
return (ex.Message, ex);
}
catch (Exception ex)
{
return (null, ex);
}
}
}
}

181
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/ElasticSearchActionHandler.cs

@ -0,0 +1,181 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Elasticsearch.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Actions;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
public sealed class ElasticSearchActionHandler : RuleActionHandler<ElasticSearchAction>
{
private readonly ClientPool<(Uri Host, string Username, string Password), ElasticLowLevelClient> clients;
private readonly RuleEventFormatter formatter;
public ElasticSearchActionHandler(RuleEventFormatter formatter)
{
Guard.NotNull(formatter, nameof(formatter));
this.formatter = formatter;
clients = new ClientPool<(Uri Host, string Username, string Password), ElasticLowLevelClient>(key =>
{
var config = new ConnectionConfiguration(key.Host);
if (!string.IsNullOrEmpty(key.Username) && !string.IsNullOrWhiteSpace(key.Password))
{
config = config.BasicAuthentication(key.Username, key.Password);
}
return new ElasticLowLevelClient(config);
});
}
protected override (string Description, RuleJobData Data) CreateJob(Envelope<AppEvent> @event, string eventName, ElasticSearchAction action)
{
var ruleDescription = string.Empty;
var ruleData = new RuleJobData
{
["Host"] = action.Host,
["Username"] = action.Username,
["Password"] = action.Password
};
if (@event.Payload is ContentEvent contentEvent)
{
ruleData["ContentId"] = contentEvent.ContentId.ToString();
ruleData["IndexName"] = formatter.FormatString(action.IndexName, @event);
ruleData["IndexType"] = formatter.FormatString(action.IndexType, @event);
var timestamp = @event.Headers.Timestamp().ToString();
switch (@event.Payload)
{
case ContentCreated created:
{
ruleDescription = $"Add entry to Algolia index: {action.IndexName}";
ruleData["Operation"] = "Create";
ruleData["Content"] = new JObject(
new JProperty("id", contentEvent.ContentId),
new JProperty("created", timestamp),
new JProperty("createdBy", created.Actor.ToString()),
new JProperty("lastModified", timestamp),
new JProperty("lastModifiedBy", created.Actor.ToString()),
new JProperty("status", Status.Draft.ToString()),
new JProperty("data", formatter.ToRouteData(created.Data)));
break;
}
case ContentUpdated updated:
{
ruleDescription = $"Update entry in Algolia index: {action.IndexName}";
ruleData["Operation"] = "Update";
ruleData["Content"] = new JObject(
new JProperty("lastModified", timestamp),
new JProperty("lastModifiedBy", updated.Actor.ToString()),
new JProperty("data", formatter.ToRouteData(updated.Data)));
break;
}
case ContentStatusChanged statusChanged:
{
ruleDescription = $"Update entry in Algolia index: {action.IndexName}";
ruleData["Operation"] = "Update";
ruleData["Content"] = new JObject(
new JProperty("lastModified", timestamp),
new JProperty("lastModifiedBy", statusChanged.Actor.ToString()),
new JProperty("status", statusChanged.Status.ToString()));
break;
}
case ContentDeleted deleted:
{
ruleDescription = $"Delete entry from Index: {action.IndexName}";
ruleData["Operation"] = "Delete";
ruleData["Content"] = new JObject();
break;
}
}
}
return (ruleDescription, ruleData);
}
public override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(RuleJobData job)
{
if (!job.TryGetValue("Operation", out var operationToken))
{
return (null, new InvalidOperationException("The action cannot handle this event."));
}
var host = new Uri(job["Host"].Value<string>(), UriKind.Absolute);
var username = job["Username"].Value<string>();
var password = job["Password"].Value<string>();
var client = clients.GetClient((host, username, password));
var indexName = job["IndexName"].Value<string>();
var indexType = job["IndexType"].Value<string>();
var operation = operationToken.Value<string>();
var content = job["Content"].Value<JObject>();
var contentId = job["ContentId"].Value<string>();
try
{
switch (operation)
{
case "Create":
{
var doc = content.ToString();
var response = await client.IndexAsync<StringResponse>(indexName, indexType, contentId, doc);
return (response.Body, response.OriginalException);
}
case "Update":
{
var doc = new JObject(new JProperty("doc", content)).ToString();
var response = await client.UpdateAsync<StringResponse>(indexName, indexType, contentId, doc);
return (response.Body, response.OriginalException);
}
case "Delete":
{
var response = await client.DeleteAsync<StringResponse>(indexName, indexType, contentId);
return (response.Body, response.OriginalException);
}
default:
return (null, null);
}
}
catch (ElasticsearchClientException ex)
{
return (ex.Message, ex);
}
}
}
}

4
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs

@ -77,9 +77,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
}
else
{
var requestDump = ex.ToString();
return (requestDump, ex);
throw;
}
}
}

4
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs

@ -75,9 +75,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
}
else
{
var requestDump = ex.ToString();
return (requestDump, ex);
throw;
}
}
}

1
src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj

@ -14,6 +14,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Algolia.Search" Version="4.2.1" />
<PackageReference Include="Elasticsearch.Net" Version="6.0.1" />
<PackageReference Include="Jint" Version="2.11.58" />
<PackageReference Include="Microsoft.OData.Core" Version="7.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />

22
src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleActionValidator.cs

@ -68,6 +68,28 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
return Task.FromResult<IEnumerable<ValidationError>>(errors);
}
public Task<IEnumerable<ValidationError>> Visit(ElasticSearchAction action)
{
var errors = new List<ValidationError>();
if (action.Host == null || !action.Host.IsAbsoluteUri)
{
errors.Add(new ValidationError("Host is required and must be an absolute URL.", nameof(action.Host)));
}
if (string.IsNullOrWhiteSpace(action.IndexType))
{
errors.Add(new ValidationError("TypeName is required.", nameof(action.IndexType)));
}
if (string.IsNullOrWhiteSpace(action.IndexName))
{
errors.Add(new ValidationError("IndexName is required.", nameof(action.IndexName)));
}
return Task.FromResult<IEnumerable<ValidationError>>(errors);
}
public Task<IEnumerable<ValidationError>> Visit(FastlyAction action)
{
var errors = new List<ValidationError>();

8
src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs

@ -39,16 +39,16 @@ namespace Squidex.Domain.Apps.Entities.Rules
switch (command)
{
case CreateRule createRule:
return CreateAsync(createRule, c =>
return CreateAsync(createRule, async c =>
{
GuardRule.CanCreate(c, appProvider);
await GuardRule.CanCreate(c, appProvider);
Create(c);
});
case UpdateRule updateRule:
return UpdateAsync(updateRule, c =>
return UpdateAsync(updateRule, async c =>
{
GuardRule.CanUpdate(c, Snapshot.AppId.Id, appProvider);
await GuardRule.CanUpdate(c, Snapshot.AppId.Id, appProvider);
Update(c);
});

53
src/Squidex/Areas/Api/Controllers/Rules/Models/Actions/ElasticSearchActionDto.cs

@ -0,0 +1,53 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Actions;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Rules.Models.Actions
{
[JsonSchema("ElasticSearch")]
public sealed class ElasticSearchActionDto : RuleActionDto
{
/// <summary>
/// The host to the elastic search instance.
/// </summary>
[Required]
public Uri Host { get; set; }
/// <summary>
/// The name of the index.
/// </summary>
[Required]
public string IndexName { get; set; }
/// <summary>
/// The name of the index type.
/// </summary>
[Required]
public string IndexType { get; set; }
/// <summary>
/// The optional username for authentication.
/// </summary>
public string Username { get; set; }
/// <summary>
/// The optional password for authentication.
/// </summary>
public string Password { get; set; }
public override RuleAction ToAction()
{
return SimpleMapper.Map(this, new ElasticSearchAction());
}
}
}

5
src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleActionDtoFactory.cs

@ -35,6 +35,11 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Converters
return SimpleMapper.Map(action, new AzureQueueActionDto());
}
public RuleActionDto Visit(ElasticSearchAction action)
{
return SimpleMapper.Map(action, new ElasticSearchActionDto());
}
public RuleActionDto Visit(FastlyAction action)
{
return SimpleMapper.Map(action, new FastlyActionDto());

3
src/Squidex/Config/Domain/ReadServices.cs

@ -106,6 +106,9 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<AzureQueueActionHandler>()
.As<IRuleActionHandler>();
services.AddSingletonAs<ElasticSearchActionHandler>()
.As<IRuleActionHandler>();
services.AddSingletonAs<FastlyActionHandler>()
.As<IRuleActionHandler>();

1
src/Squidex/Squidex.csproj

@ -50,6 +50,7 @@
<ItemGroup>
<PackageReference Include="Algolia.Search" Version="4.2.1" />
<PackageReference Include="Ben.BlockingDetector" Version="0.0.2" />
<PackageReference Include="Elasticsearch.Net" Version="6.0.1" />
<PackageReference Include="EventStore.ClientAPI.NetCore" Version="4.0.2-rc" />
<PackageReference Include="IdentityServer4" Version="2.1.1" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.3.0" />

1
src/Squidex/app/features/rules/declarations.ts

@ -7,6 +7,7 @@
export * from './pages/rules/actions/algolia-action.component';
export * from './pages/rules/actions/azure-queue-action.component';
export * from './pages/rules/actions/elastic-search-action.component';
export * from './pages/rules/actions/fastly-action.component';
export * from './pages/rules/actions/slack-action.component';
export * from './pages/rules/actions/webhook-action.component';

2
src/Squidex/app/features/rules/module.ts

@ -19,6 +19,7 @@ import {
AssetChangedTriggerComponent,
AzureQueueActionComponent,
ContentChangedTriggerComponent,
ElasticSearchActionComponent,
FastlyActionComponent,
RuleEventsPageComponent,
RulesPageComponent,
@ -58,6 +59,7 @@ const routes: Routes = [
AssetChangedTriggerComponent,
AzureQueueActionComponent,
ContentChangedTriggerComponent,
ElasticSearchActionComponent,
FastlyActionComponent,
RuleEventsPageComponent,
RulesPageComponent,

2
src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.html

@ -5,7 +5,7 @@
<label class="col col-3 col-form-label" for="appId">App ID</label>
<div class="col col-9">
<sqx-control-errors for="text" [submitted]="actionFormSubmitted"></sqx-control-errors>
<sqx-control-errors for="appId" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="appId" formControlName="appId" />

18
src/Squidex/app/features/rules/pages/rules/actions/algolia-action.component.ts

@ -24,17 +24,17 @@ export class AlgoliaActionComponent implements OnInit {
public actionForm =
this.formBuilder.group({
appId: ['',
[
Validators.required
]],
[
Validators.required
]],
apiKey: ['',
[
Validators.required
]],
[
Validators.required
]],
indexName: ['$SCHEMA_NAME',
[
Validators.required
]]
[
Validators.required
]]
});
constructor(

2
src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.html

@ -5,7 +5,7 @@
<label class="col col-3 col-form-label" for="connectionString">Connection String</label>
<div class="col col-9">
<sqx-control-errors for="text" [submitted]="actionFormSubmitted"></sqx-control-errors>
<sqx-control-errors for="connectionString" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="connectionString" formControlName="connectionString" />

14
src/Squidex/app/features/rules/pages/rules/actions/azure-queue-action.component.ts

@ -26,14 +26,14 @@ export class AzureQueueActionComponent implements OnInit {
public actionForm =
this.formBuilder.group({
connectionString: ['',
[
Validators.required
]],
[
Validators.required
]],
queue: ['squidex',
[
Validators.required,
ValidatorsEx.pattern('[a-z][a-z0-9]{2,}(\-[a-z0-9]+)*', 'Name must be a valid azure queue name.')
]]
[
Validators.required,
ValidatorsEx.pattern('[a-z][a-z0-9]{2,}(\-[a-z0-9]+)*', 'Name must be a valid azure queue name.')
]]
});
constructor(

73
src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.html

@ -0,0 +1,73 @@
<h3 class="rule-title">Populate index in ElasticSearch with content</h3>
<form [formGroup]="actionForm" class="form-horizontal" (ngSubmit)="save()">
<div class="form-group row">
<label class="col col-3 col-form-label" for="host">Host</label>
<div class="col col-9">
<sqx-control-errors for="host" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="host" formControlName="host" placeholder="http://localhost:9200" />
<small class="form-text text-muted">
The url to your elastic search instance.
</small>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label" for="username">Username</label>
<div class="col col-9">
<sqx-control-errors for="username" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="username" formControlName="username" />
<small class="form-text text-muted">
The username for authentication. Highly recommended.
</small>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label" for="username">Password</label>
<div class="col col-9">
<sqx-control-errors for="password" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="password" formControlName="password" />
<small class="form-text text-muted">
The password for authentication. Highly recommended.
</small>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label" for="sharedSecret">Index Name</label>
<div class="col col-9">
<sqx-control-errors for="indexName" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="indexName" formControlName="indexName" />
<small class="form-text text-muted">
The name of the index. You can use advanced formatting (read help section).
</small>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-label" for="sharedSecret">Type Name</label>
<div class="col col-9">
<sqx-control-errors for="indexType" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="indexType" formControlName="indexType" />
<small class="form-text text-muted">
The name of the type. You can use advanced formatting (read help section).
</small>
</div>
</div>
</form>

2
src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.scss

@ -0,0 +1,2 @@
@import '_vars';
@import '_mixins';

70
src/Squidex/app/features/rules/pages/rules/actions/elastic-search-action.component.ts

@ -0,0 +1,70 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormBuilder, 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;
@Output()
public actionChanged = new EventEmitter<object>();
public actionFormSubmitted = false;
public actionForm =
this.formBuilder.group({
host: ['',
[
Validators.required
]],
indexName: ['$APP_NAME',
[
Validators.required
]],
indexType: ['$SCHEMA_NAME',
[
// Validators.required
]],
username: '',
password: ''
});
constructor(
private readonly formBuilder: FormBuilder
) {
}
public ngOnInit() {
this.action = Object.assign({}, {
host: '',
indexName: '$APP_NAME',
indexType: '$SCHEMA_NAME',
username: '',
password: ''
}, this.action || {});
this.actionFormSubmitted = false;
this.actionForm.reset();
this.actionForm.setValue(this.action);
}
public save() {
this.actionFormSubmitted = true;
if (this.actionForm.valid) {
const action = this.actionForm.value;
this.actionChanged.emit(action);
}
}
}

2
src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.html

@ -5,7 +5,7 @@
<label class="col col-3 col-form-label" for="serviceId">Service ID</label>
<div class="col col-9">
<sqx-control-errors for="text" [submitted]="actionFormSubmitted"></sqx-control-errors>
<sqx-control-errors for="serviceId" [submitted]="actionFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="serviceId" formControlName="serviceId" />

12
src/Squidex/app/features/rules/pages/rules/actions/fastly-action.component.ts

@ -24,13 +24,13 @@ export class FastlyActionComponent implements OnInit {
public actionForm =
this.formBuilder.group({
serviceId: ['',
[
Validators.required
]],
[
Validators.required
]],
apiKey: ['',
[
Validators.required
]]
[
Validators.required
]]
});
constructor(

6
src/Squidex/app/features/rules/pages/rules/actions/slack-action.component.ts

@ -24,9 +24,9 @@ export class SlackActionComponent implements OnInit {
public actionForm =
this.formBuilder.group({
webhookUrl: ['',
[
Validators.required
]],
[
Validators.required
]],
text: ['']
});

6
src/Squidex/app/features/rules/pages/rules/actions/webhook-action.component.ts

@ -24,9 +24,9 @@ export class WebhookActionComponent implements OnInit {
public actionForm =
this.formBuilder.group({
url: ['',
[
Validators.required
]],
[
Validators.required
]],
sharedSecret: ['']
});

6
src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html

@ -80,6 +80,12 @@
(actionChanged)="selectAction($event)">
</sqx-azure-queue-action>
</div>
<div *ngSwitchCase="'ElasticSearch'">
<sqx-elastic-search-action #actionControl
[action]="action"
(actionChanged)="selectAction($event)">
</sqx-elastic-search-action>
</div>
<div *ngSwitchCase="'Fastly'">
<sqx-fastly-action #actionControl
[action]="action"

2
src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.html

@ -60,7 +60,7 @@
</tr>
</table>
<div class="section" *ngIf="schemasToAdd.length > 0">
<div class="section" *ngIf="!handleAll && schemasToAdd.length > 0">
<form class="form-inline" (ngSubmit)="addSchema()">
<div class="form-group mr-1">
<select class="form-control schemas-control" [(ngModel)]="schemaToAdd" name="schema">

4
src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.scss

@ -7,6 +7,10 @@
padding-bottom: 0;
}
.form-check {
margin-top: 1rem;
}
.rotated-label {
@include rotate(-45deg);
}

3
src/Squidex/app/shared/services/rules.service.ts

@ -36,6 +36,9 @@ export const ruleActions: any = {
'AzureQueue': {
name: 'Send to Azure Queue'
},
'ElasticSearch': {
name: 'Populate ElasticSearch Index'
},
'Fastly': {
name: 'Purge fastly Cache'
},

4
src/Squidex/app/theme/_bootstrap.scss

@ -51,6 +51,10 @@
.alert-warning,
.alert-info,
.alert-danger {
& {
border: 0;
}
.alert-link {
color: $color-dark-foreground;
font-size: inherit;

5
src/Squidex/app/theme/_rules.scss

@ -5,6 +5,7 @@ $trigger-asset-changed: #3389ff;
$trigger-content-changed: #3389ff;
$action-webhook: #4bb958;
$action-elasticsearch: #1e5470;
$action-algolia: #0d9bf9;
$action-slack: #5c3a58;
$action-azure: #55b3ff;
@ -72,6 +73,10 @@ $action-fastly: #e23335;
@include build-element($action-algolia);
}
.rule-element-ElasticSearch {
@include build-element($action-elasticsearch);
}
.rule-element-Fastly {
@include build-element($action-fastly);
}

2
src/Squidex/app/theme/icomoon/demo-files/demo.css

@ -153,7 +153,7 @@ p {
font-size: 32px;
}
.fs3 {
font-size: 28px;
font-size: 32px;
}
.fs4 {
font-size: 32px;

848
src/Squidex/app/theme/icomoon/demo.html

File diff suppressed because it is too large

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.eot

Binary file not shown.

1
src/Squidex/app/theme/icomoon/fonts/icomoon.svg

@ -89,6 +89,7 @@
<glyph unicode="&#xe94f;" glyph-name="control-Slug" d="M512 960c-15.36 0-25.6-10.24-25.6-25.6s10.24-25.6 25.6-25.6h128v-870.4h-128c-15.36 0-25.6-10.24-25.6-25.6s10.24-25.6 25.6-25.6h307.2c15.36 0 25.6 10.24 25.6 25.6s-10.24 25.6-25.6 25.6h-128v870.4h128c15.36 0 25.6 10.24 25.6 25.6s-10.24 25.6-25.6 25.6h-307.2zM51.2 755.2c-28.16 0-51.2-23.040-51.2-51.2v-460.8c0-28.16 23.040-51.2 51.2-51.2h537.6v51.2h-512c-15.36 0-25.6 10.24-25.6 25.6v409.6c0 15.36 10.24 25.6 25.6 25.6h512v51.2h-537.6zM742.4 755.2v-51.2h204.8c15.36 0 25.6-10.24 25.6-25.6v-409.6c0-15.36-10.24-25.6-25.6-25.6h-204.8v-51.2h230.4c28.16 0 51.2 23.040 51.2 51.2v460.8c0 28.16-23.040 51.2-51.2 51.2h-230.4zM386.56 353.28c0-12.8-7.68-23.040-20.48-25.6-28.16-10.24-58.88-15.36-92.16-15.36-35.84 0-66.56 10.24-84.48 25.6s-25.6 38.4-25.6 66.56 10.24 51.2 25.6 66.56c17.92 17.92 46.080 23.040 84.48 23.040h69.12v38.4c0 35.84-25.6 53.76-64 53.76-23.040 0-46.080-7.68-69.12-20.48-2.56-2.56-5.12-2.56-10.24-2.56-10.24 0-20.48 7.68-20.48 20.48 0 7.68 2.56 12.8 10.24 17.92 30.72 20.48 61.44 25.6 92.16 25.6 56.32 0 104.96-30.72 104.96-92.16v-181.76zM345.6 458.24h-69.12c-61.44 0-69.12-28.16-69.12-53.76s7.68-56.32 69.12-56.32c23.040 0 46.080 2.56 69.12 10.24v99.84z" />
<glyph unicode="&#xe950;" glyph-name="clock" d="M658.744 210.744l-210.744 210.746v282.51h128v-229.49l173.256-173.254zM512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 64c-212.078 0-384 171.922-384 384s171.922 384 384 384c212.078 0 384-171.922 384-384s-171.922-384-384-384z" />
<glyph unicode="&#xe951;" glyph-name="circle" d="M1024 448c0-282.77-229.23-512-512-512s-512 229.23-512 512c0 282.77 229.23 512 512 512s512-229.23 512-512z" />
<glyph unicode="&#xe952;" glyph-name="action-ElasticSearch" horiz-adv-x="1071" d="M491.055 313.184h-338.784c-12.301 39.564-19.283 81.787-19.283 125.673s6.649 86.109 19.283 125.673h557.548c69.153 0 125.008-56.519 125.008-126.005 0-69.153-55.855-125.34-125.008-125.34h-218.764zM475.096 271.293h-306.868c32.582-74.473 86.442-137.974 153.6-182.192v0c66.161-43.553 145.288-69.153 230.4-69.153 145.288 0 273.288 74.14 348.426 186.514-38.566 39.896-92.758 64.831-152.603 64.831h-272.956zM748.052 606.421c59.844 0 114.036 24.935 152.603 64.831-75.138 112.374-203.138 186.514-348.758 186.514-85.112 0-164.239-25.6-230.4-69.153v0c-67.158-44.551-121.018-107.719-153.6-182.192h580.156z" />
<glyph unicode="&#xe9ca;" glyph-name="earth" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-0.002c-62.958 0-122.872 13.012-177.23 36.452l233.148 262.29c5.206 5.858 8.082 13.422 8.082 21.26v96c0 17.674-14.326 32-32 32-112.99 0-232.204 117.462-233.374 118.626-6 6.002-14.14 9.374-22.626 9.374h-128c-17.672 0-32-14.328-32-32v-192c0-12.122 6.848-23.202 17.69-28.622l110.31-55.156v-187.886c-116.052 80.956-192 215.432-192 367.664 0 68.714 15.49 133.806 43.138 192h116.862c8.488 0 16.626 3.372 22.628 9.372l128 128c6 6.002 9.372 14.14 9.372 22.628v77.412c40.562 12.074 83.518 18.588 128 18.588 70.406 0 137.004-16.26 196.282-45.2-4.144-3.502-8.176-7.164-12.046-11.036-36.266-36.264-56.236-84.478-56.236-135.764s19.97-99.5 56.236-135.764c36.434-36.432 85.218-56.264 135.634-56.26 3.166 0 6.342 0.080 9.518 0.236 13.814-51.802 38.752-186.656-8.404-372.334-0.444-1.744-0.696-3.488-0.842-5.224-81.324-83.080-194.7-134.656-320.142-134.656z" />
<glyph unicode="&#xf00a;" glyph-name="grid" d="M292.571 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857z" />
<glyph unicode="&#xf0c9;" glyph-name="list" horiz-adv-x="878" d="M877.714 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 475.428v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 768v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" />

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.ttf

Binary file not shown.

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.woff

Binary file not shown.

1938
src/Squidex/app/theme/icomoon/selection.json

File diff suppressed because it is too large

163
src/Squidex/app/theme/icomoon/style.css

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?vpgvr2');
src: url('fonts/icomoon.eot?vpgvr2#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?vpgvr2') format('truetype'),
url('fonts/icomoon.woff?vpgvr2') format('woff'),
url('fonts/icomoon.svg?vpgvr2#icomoon') format('svg');
src: url('fonts/icomoon.eot?asl9vj');
src: url('fonts/icomoon.eot?asl9vj#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?asl9vj') format('truetype'),
url('fonts/icomoon.woff?asl9vj') format('woff'),
url('fonts/icomoon.svg?asl9vj#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
@ -24,6 +24,84 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-action-ElasticSearch:before {
content: "\e952";
}
.icon-action-Slack:before {
content: "\e94d";
}
.icon-orleans:before {
content: "\e94b";
}
.icon-document-lock:before {
content: "\e949";
}
.icon-document-unpublish:before {
content: "\e93f";
}
.icon-angle-down:before {
content: "\e900";
}
.icon-angle-left:before {
content: "\e901";
}
.icon-angle-right:before {
content: "\e931";
}
.icon-angle-up:before {
content: "\e903";
}
.icon-api:before {
content: "\e945";
}
.icon-assets:before {
content: "\e948";
}
.icon-bug:before {
content: "\e93d";
}
.icon-caret-down:before {
content: "\e92c";
}
.icon-caret-left:before {
content: "\e92a";
}
.icon-caret-right:before {
content: "\e929";
}
.icon-caret-up:before {
content: "\e92b";
}
.icon-contents:before {
content: "\e946";
}
.icon-trigger-ContentChanged:before {
content: "\e946";
}
.icon-control-Date:before {
content: "\e936";
}
.icon-control-DateTime:before {
content: "\e937";
}
.icon-control-Markdown:before {
content: "\e938";
}
.icon-grid:before {
content: "\f00a";
}
.icon-list:before {
content: "\f0c9";
}
.icon-user-o:before {
content: "\e932";
}
.icon-rules:before {
content: "\e947";
}
.icon-action-Webhook:before {
content: "\e947";
}
.icon-circle:before {
content: "\e951";
}
@ -226,81 +304,6 @@
.icon-unlocked:before {
content: "\e933";
}
.icon-action-Slack:before {
content: "\e94d";
}
.icon-orleans:before {
content: "\e94b";
}
.icon-document-lock:before {
content: "\e949";
}
.icon-document-unpublish:before {
content: "\e93f";
}
.icon-angle-down:before {
content: "\e900";
}
.icon-angle-left:before {
content: "\e901";
}
.icon-angle-right:before {
content: "\e931";
}
.icon-angle-up:before {
content: "\e903";
}
.icon-api:before {
content: "\e945";
}
.icon-assets:before {
content: "\e948";
}
.icon-bug:before {
content: "\e93d";
}
.icon-caret-down:before {
content: "\e92c";
}
.icon-caret-left:before {
content: "\e92a";
}
.icon-caret-right:before {
content: "\e929";
}
.icon-caret-up:before {
content: "\e92b";
}
.icon-contents:before {
content: "\e946";
}
.icon-trigger-ContentChanged:before {
content: "\e946";
}
.icon-control-Date:before {
content: "\e936";
}
.icon-control-DateTime:before {
content: "\e937";
}
.icon-control-Markdown:before {
content: "\e938";
}
.icon-grid:before {
content: "\f00a";
}
.icon-list:before {
content: "\f0c9";
}
.icon-user-o:before {
content: "\e932";
}
.icon-rules:before {
content: "\e947";
}
.icon-action-Webhook:before {
content: "\e947";
}
.icon-browser:before {
content: "\e935";
}

Loading…
Cancel
Save