Browse Source

Asset changed trigger.

pull/241/head
Sebastian Stehle 8 years ago
parent
commit
300d1d0786
  1. 2
      src/Squidex.Domain.Apps.Core.Model/Rules/IRuleTriggerVisitor.cs
  2. 28
      src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/AssetChangedTrigger.cs
  3. 26
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AlgoliaActionHandler.cs
  4. 23
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AzureQueueActionHandler.cs
  5. 3
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs
  6. 6
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs
  7. 39
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/ClientPool.cs
  8. 25
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/HttpClientPool.cs
  9. 31
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/AssetChangedTriggerHandler.cs
  10. 5
      src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleTriggerValidator.cs
  11. 5
      src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleTriggerDtoFactory.cs
  12. 1
      src/Squidex/Areas/Api/Controllers/Rules/Models/RuleTriggerDto.cs
  13. 43
      src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/AssetChangedTriggerDto.cs
  14. 8
      src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerSchemaDto.cs
  15. 3
      src/Squidex/Config/Domain/ReadServices.cs
  16. 1
      src/Squidex/app/features/rules/declarations.ts
  17. 2
      src/Squidex/app/features/rules/module.ts
  18. 6
      src/Squidex/app/features/rules/pages/rules/rule-wizard.component.html
  19. 37
      src/Squidex/app/features/rules/pages/rules/triggers/asset-changed-trigger.component.html
  20. 2
      src/Squidex/app/features/rules/pages/rules/triggers/asset-changed-trigger.component.scss
  21. 59
      src/Squidex/app/features/rules/pages/rules/triggers/asset-changed-trigger.component.ts
  22. 20
      src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.html
  23. 4
      src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.scss
  24. 1
      src/Squidex/app/shared/services/rules.service.ts
  25. 5
      src/Squidex/app/theme/_rules.scss
  26. 16
      src/Squidex/app/theme/icomoon/demo.html
  27. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.eot
  28. 2
      src/Squidex/app/theme/icomoon/fonts/icomoon.svg
  29. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.ttf
  30. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.woff
  31. 2
      src/Squidex/app/theme/icomoon/selection.json
  32. 13
      src/Squidex/app/theme/icomoon/style.css
  33. 60
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/AssetChangedTriggerTests.cs

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

@ -11,6 +11,8 @@ namespace Squidex.Domain.Apps.Core.Rules
{
public interface IRuleTriggerVisitor<out T>
{
T Visit(AssetChangedTrigger trigger);
T Visit(ContentChangedTrigger trigger);
}
}

28
src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/AssetChangedTrigger.cs

@ -0,0 +1,28 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.Triggers
{
[TypeName(nameof(AssetChangedTrigger))]
public sealed class AssetChangedTrigger : RuleTrigger
{
public bool SendCreate { get; set; }
public bool SendUpdate { get; set; }
public bool SendRename { get; set; }
public bool SendDelete { get; set; }
public override T Accept<T>(IRuleTriggerVisitor<T> visitor)
{
return visitor.Visit(this);
}
}
}

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

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Algolia.Search;
using Newtonsoft.Json;
@ -23,7 +22,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
public sealed class AlgoliaActionHandler : RuleActionHandler<AlgoliaAction>
{
private const string SchemaNamePlaceholder = "$SCHEMA_NAME";
private readonly ConcurrentDictionary<(string AppId, string ApiKey, string IndexName), Index> clients = new ConcurrentDictionary<(string AppId, string ApiKey, string IndexName), Index>();
private readonly ClientPool<(string AppId, string ApiKey, string IndexName), Index> clients;
private readonly RuleEventFormatter formatter;
public AlgoliaActionHandler(RuleEventFormatter formatter)
@ -31,6 +30,13 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
Guard.NotNull(formatter, nameof(formatter));
this.formatter = formatter;
clients = new ClientPool<(string AppId, string ApiKey, string IndexName), Index>(key =>
{
var client = new AlgoliaClient(key.AppId, key.ApiKey);
return client.InitIndex(key.IndexName);
});
}
protected override (string Description, RuleJobData Data) CreateJob(Envelope<AppEvent> @event, string eventName, AlgoliaAction action)
@ -102,16 +108,16 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
public override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(RuleJobData job)
{
if (job["Operation"] == null)
{
return (null, new InvalidOperationException("The action cannot handle this event."));
}
var appId = job["AppId"].Value<string>();
var apiKey = job["ApiKey"].Value<string>();
var indexName = job["IndexName"].Value<string>();
var index = clients.GetOrAdd((appId, apiKey, indexName), s =>
{
var client = new AlgoliaClient(appId, apiKey);
return client.InitIndex(indexName);
});
var index = clients.GetClient((appId, apiKey, indexName));
var operation = job["Operation"].Value<string>();
var content = job["Content"].Value<JObject>();
@ -138,9 +144,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
}
default:
{
return ("Nothing to do!", null);
}
return (null, null);
}
}
catch (AlgoliaException ex)

23
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AzureQueueActionHandler.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Queue;
@ -22,7 +21,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
public sealed class AzureQueueActionHandler : RuleActionHandler<AzureQueueAction>
{
private readonly ConcurrentDictionary<(string ConnectionString, string QueueName), CloudQueue> queues = new ConcurrentDictionary<(string ConnectionString, string QueueName), CloudQueue>();
private readonly ClientPool<(string ConnectionString, string QueueName), CloudQueue> clients;
private readonly RuleEventFormatter formatter;
public AzureQueueActionHandler(RuleEventFormatter formatter)
@ -30,6 +29,16 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
Guard.NotNull(formatter, nameof(formatter));
this.formatter = formatter;
clients = new ClientPool<(string ConnectionString, string QueueName), CloudQueue>(key =>
{
var storageAccount = CloudStorageAccount.Parse(key.ConnectionString);
var queueClient = storageAccount.CreateCloudQueueClient();
var queueRef = queueClient.GetQueueReference(key.QueueName);
return queueRef;
});
}
protected override (string Description, RuleJobData Data) CreateJob(Envelope<AppEvent> @event, string eventName, AzureQueueAction action)
@ -52,15 +61,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
var queueConnectionString = job["QueueConnectionString"].Value<string>();
var queueName = job["QueueName"].Value<string>();
var queue = queues.GetOrAdd((queueConnectionString, queueName), s =>
{
var storageAccount = CloudStorageAccount.Parse(queueConnectionString);
var queueClient = storageAccount.CreateCloudQueueClient();
var queueRef = queueClient.GetQueueReference(queueName);
return queueRef;
});
var queue = clients.GetClient((queueConnectionString, queueName));
var messageBody = job["MessageBody"].ToString(Formatting.Indented);

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

@ -23,7 +23,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
public sealed class SlackActionHandler : RuleActionHandler<SlackAction>
{
private static readonly HttpClient Client = new HttpClient { Timeout = TimeSpan.FromSeconds(2) };
private readonly RuleEventFormatter formatter;
public SlackActionHandler(RuleEventFormatter formatter)
@ -61,7 +60,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
try
{
response = await Client.SendAsync(requestMsg);
response = await HttpClientPool.GetHttpClient().SendAsync(requestMsg);
var responseString = await response.Content.ReadAsStringAsync();
var requestDump = DumpFormatter.BuildDump(requestMsg, response, requestBody, responseString, TimeSpan.Zero, false);

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

@ -23,7 +23,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
public sealed class WebhookActionHandler : RuleActionHandler<WebhookAction>
{
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2);
private readonly RuleEventFormatter formatter;
public WebhookActionHandler(RuleEventFormatter formatter)
@ -59,16 +58,13 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions
try
{
using (var client = new HttpClient { Timeout = Timeout })
{
response = await client.SendAsync(requestMsg);
response = await HttpClientPool.GetHttpClient().SendAsync(requestMsg);
var responseString = await response.Content.ReadAsStringAsync();
var requestDump = DumpFormatter.BuildDump(requestMsg, response, requestBody, responseString, TimeSpan.Zero, false);
return (requestDump, null);
}
}
catch (Exception ex)
{
if (requestMsg != null)

39
src/Squidex.Domain.Apps.Core.Operations/HandleRules/ClientPool.cs

@ -0,0 +1,39 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
#pragma warning disable RECS0108 // Warns about static fields in generic types
namespace Squidex.Domain.Apps.Core.HandleRules
{
internal sealed class ClientPool<TKey, TClient>
{
private static readonly TimeSpan TTL = TimeSpan.FromMinutes(30);
private readonly MemoryCache memoryCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
private readonly Func<TKey, TClient> factory;
public ClientPool(Func<TKey, TClient> factory)
{
this.factory = factory;
}
public TClient GetClient(TKey key)
{
if (!memoryCache.TryGetValue<TClient>(key, out var client))
{
client = factory(key);
memoryCache.Set(key, client, TTL);
}
return client;
}
}
}

25
src/Squidex.Domain.Apps.Core.Operations/HandleRules/HttpClientPool.cs

@ -0,0 +1,25 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Net.Http;
namespace Squidex.Domain.Apps.Core.HandleRules
{
public static class HttpClientPool
{
private static readonly ClientPool<string, HttpClient> Pool = new ClientPool<string, HttpClient>(key =>
{
return new HttpClient { Timeout = TimeSpan.FromSeconds(2) };
});
public static HttpClient GetHttpClient()
{
return Pool.GetClient(string.Empty);
}
}
}

31
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/AssetChangedTriggerHandler.cs

@ -0,0 +1,31 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
{
public sealed class AssetChangedTriggerHandler : RuleTriggerHandler<AssetChangedTrigger>
{
protected override bool Triggers(Envelope<AppEvent> @event, AssetChangedTrigger trigger)
{
return @event.Payload is AssetEvent assetEvent && MatchsType(trigger, assetEvent);
}
private static bool MatchsType(AssetChangedTrigger trigger, AssetEvent @event)
{
return
(trigger.SendCreate && @event is AssetCreated) ||
(trigger.SendUpdate && @event is AssetUpdated) ||
(trigger.SendDelete && @event is AssetDeleted) ||
(trigger.SendRename && @event is AssetRenamed);
}
}
}

5
src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleTriggerValidator.cs

@ -35,6 +35,11 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
return action.Accept(visitor);
}
public Task<IEnumerable<ValidationError>> Visit(AssetChangedTrigger trigger)
{
return Task.FromResult(Enumerable.Empty<ValidationError>());
}
public async Task<IEnumerable<ValidationError>> Visit(ContentChangedTrigger trigger)
{
if (trigger.Schemas != null)

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

@ -26,6 +26,11 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Converters
return properties.Accept(Instance);
}
public RuleTriggerDto Visit(AssetChangedTrigger trigger)
{
return SimpleMapper.Map(trigger, new AssetChangedTriggerDto());
}
public RuleTriggerDto Visit(ContentChangedTrigger trigger)
{
return new ContentChangedTriggerDto

1
src/Squidex/Areas/Api/Controllers/Rules/Models/RuleTriggerDto.cs

@ -13,6 +13,7 @@ using Squidex.Domain.Apps.Core.Rules;
namespace Squidex.Areas.Api.Controllers.Rules.Models
{
[JsonConverter(typeof(JsonInheritanceConverter), "triggerType")]
[KnownType(typeof(AssetChangedTriggerDto))]
[KnownType(typeof(ContentChangedTriggerDto))]
public abstract class RuleTriggerDto
{

43
src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/AssetChangedTriggerDto.cs

@ -0,0 +1,43 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
{
[JsonSchema("AssetChanged")]
public sealed class AssetChangedTriggerDto : RuleTriggerDto
{
/// <summary>
/// Determines whether to handle the event when an asset is created.
/// </summary>
public bool SendCreate { get; set; }
/// <summary>
/// Determines whether to handle the event when an asset is updated.
/// </summary>
public bool SendUpdate { get; set; }
/// <summary>
/// Determines whether to handle the event when an asset is renamed.
/// </summary>
public bool SendRename { get; set; }
/// <summary>
/// Determines whether to handle the event when an asset is deleted.
/// </summary>
public bool SendDelete { get; set; }
public override RuleTrigger ToTrigger()
{
return SimpleMapper.Map(this, new AssetChangedTrigger());
}
}
}

8
src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerSchemaDto.cs

@ -17,22 +17,22 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
public Guid SchemaId { get; set; }
/// <summary>
/// True, when to send a message for created events.
/// Determines whether to handle the event when a content is created.
/// </summary>
public bool SendCreate { get; set; }
/// <summary>
/// True, when to send a message for updated events.
/// Determines whether to handle the event when a content is updated.
/// </summary>
public bool SendUpdate { get; set; }
/// <summary>
/// True, when to send a message for deleted events.
/// Determines whether to handle the event when a content is deleted.
/// </summary>
public bool SendDelete { get; set; }
/// <summary>
/// True, when to send a message for published events.
/// Determines whether to handle the event when a content is published.
/// </summary>
public bool SendPublish { get; set; }
}

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

@ -92,6 +92,9 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<RuleEventFormatter>()
.AsSelf();
services.AddSingletonAs<AssetChangedTriggerHandler>()
.As<IRuleTriggerHandler>();
services.AddSingletonAs<ContentChangedTriggerHandler>()
.As<IRuleTriggerHandler>();

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

@ -9,6 +9,7 @@ export * from './pages/rules/actions/algolia-action.component';
export * from './pages/rules/actions/azure-queue-action.component';
export * from './pages/rules/actions/slack-action.component';
export * from './pages/rules/actions/webhook-action.component';
export * from './pages/rules/triggers/asset-changed-trigger.component';
export * from './pages/rules/triggers/content-changed-trigger.component';
export * from './pages/rules/rule-wizard.component';
export * from './pages/rules/rules-page.component';

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

@ -16,6 +16,7 @@ import {
import {
AlgoliaActionComponent,
AssetChangedTriggerComponent,
AzureQueueActionComponent,
ContentChangedTriggerComponent,
RuleEventsPageComponent,
@ -53,6 +54,7 @@ const routes: Routes = [
],
declarations: [
AlgoliaActionComponent,
AssetChangedTriggerComponent,
AzureQueueActionComponent,
ContentChangedTriggerComponent,
RuleEventsPageComponent,

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

@ -39,6 +39,12 @@
<div *ngIf="step === 2 && schemas" class="modal-form">
<div [ngSwitch]="triggerType">
<div *ngSwitchCase="'AssetChanged'">
<sqx-asset-changed-trigger #triggerControl
[trigger]="trigger"
(triggerChanged)="selectTrigger($event)">
</sqx-asset-changed-trigger>
</div>
<div *ngSwitchCase="'ContentChanged'">
<sqx-content-changed-trigger #triggerControl
[schemas]="schemas"

37
src/Squidex/app/features/rules/pages/rules/triggers/asset-changed-trigger.component.html

@ -0,0 +1,37 @@
<h3>Trigger rule when asset has been...</h3>
<form [formGroup]="triggerForm" class="form-horizontal" (ngSubmit)="save()">
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sendCreate" formControlName="sendCreate">
<label class="form-check-label" for="sendCreate">
created
</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sendUpdate" formControlName="sendUpdate">
<label class="form-check-label" for="sendUpdate">
updated
</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sendRename" formControlName="sendRename">
<label class="form-check-label" for="sendRename">
renamed
</label>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sendDelete" formControlName="sendDelete">
<label class="form-check-label" for="sendDelete">
deleted
</label>
</div>
</div>
</form>

2
src/Squidex/app/features/rules/pages/rules/triggers/asset-changed-trigger.component.scss

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

59
src/Squidex/app/features/rules/pages/rules/triggers/asset-changed-trigger.component.ts

@ -0,0 +1,59 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormBuilder } from '@angular/forms';
@Component({
selector: 'sqx-asset-changed-trigger',
styleUrls: ['./asset-changed-trigger.component.scss'],
templateUrl: './asset-changed-trigger.component.html'
})
export class AssetChangedTriggerComponent implements OnInit {
@Input()
public trigger: any;
@Output()
public triggerChanged = new EventEmitter<object>();
public triggerFormSubmitted = false;
public triggerForm =
this.formBuilder.group({
sendCreate: false,
sendUpdate: false,
sendRename: false,
sendDelete: false
});
constructor(
private readonly formBuilder: FormBuilder
) {
}
public ngOnInit() {
this.trigger = Object.assign({}, {
sendCreate: false,
sendUpdate: false,
sendRename: false,
sendDelete: false
}, this.trigger || {});
this.triggerFormSubmitted = false;
this.triggerForm.reset();
this.triggerForm.setValue(this.trigger);
}
public save() {
this.triggerFormSubmitted = true;
if (this.triggerForm.valid) {
const trigger = this.triggerForm.value;
this.triggerChanged.emit(trigger);
}
}
}

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

@ -13,20 +13,20 @@
<th>
Schema
</th>
<th class="text-center" title="All">
All
<th class="text-center">
<div class="rotated-label">All</div>
</th>
<th class="text-center" title="Created">
C
<th class="text-center">
<div class="rotated-label">Created</div>
</th>
<th class="text-center" title="Updated">
U
<th class="text-center">
<div class="rotated-label">Updated</div>
</th>
<th class="text-center" title="Deleted">
D
<th class="text-center">
<div class="rotated-label">Deleted</div>
</th>
<th class="text-center" title="Published">
P
<th class="text-center">
<div class="rotated-label">Published</div>
</th>
<th></th>
</tr>

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

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

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

@ -21,6 +21,7 @@ import {
} from 'framework';
export const ruleTriggers: any = {
'AssetChanged': 'Asset changed',
'ContentChanged': 'Content changed'
};

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

@ -1,6 +1,7 @@
@import '_mixins';
@import '_vars';
$trigger-asset-changed: #3389ff;
$trigger-content-changed: #3389ff;
$action-webhook: #4bb958;
@ -48,6 +49,10 @@ $action-azure: #55b3ff;
}
}
.rule-element-AssetChanged {
@include build-element($trigger-asset-changed);
}
.rule-element-ContentChanged {
@include build-element($trigger-content-changed);
}

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

@ -605,6 +605,22 @@
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-trigger-AssetChanged">
</span>
<span class="mls"> icon-trigger-AssetChanged</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e91d" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe91d;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-more">

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

Binary file not shown.

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

@ -36,7 +36,7 @@
<glyph unicode="&#xe91a;" glyph-name="type-Json, json" d="M179.2 704c0 28.262 22.938 51.2 51.2 51.2h25.6c14.157 0 25.6 11.443 25.6 25.6 0 14.131-11.443 25.6-25.6 25.6h-25.6c-56.55 0-102.4-45.85-102.4-102.4v-179.2c0-28.262-22.938-51.2-51.2-51.2h-25.6c-14.157 0-25.6-11.469-25.6-25.6 0-14.157 11.443-25.6 25.6-25.6h25.6c28.262 0 51.2-22.938 51.2-51.2v-179.2c0-56.55 45.85-102.4 102.4-102.4h25.6c14.157 0 25.6 11.443 25.6 25.6s-11.443 25.6-25.6 25.6h-25.6c-28.262 0-51.2 22.938-51.2 51.2v179.2c0 30.746-13.85 58.061-35.328 76.8 21.478 18.765 35.328 46.029 35.328 76.8v179.2zM972.8 473.6h-25.6c-28.262 0-51.2 22.938-51.2 51.2v179.2c0 56.55-45.85 102.4-102.4 102.4h-25.6c-14.157 0-25.6-11.469-25.6-25.6 0-14.157 11.443-25.6 25.6-25.6h25.6c28.262 0 51.2-22.938 51.2-51.2v-179.2c0-30.771 13.85-58.035 35.328-76.8-21.478-18.739-35.328-46.054-35.328-76.8v-179.2c0-28.262-22.938-51.2-51.2-51.2h-25.6c-14.157 0-25.6-11.443-25.6-25.6s11.443-25.6 25.6-25.6h25.6c56.55 0 102.4 45.85 102.4 102.4v179.2c0 28.262 22.938 51.2 51.2 51.2h25.6c14.157 0 25.6 11.443 25.6 25.6 0 14.131-11.443 25.6-25.6 25.6zM512 627.2c-14.157 0-25.6-11.469-25.6-25.6 0-14.157 11.443-25.6 25.6-25.6s25.6 11.443 25.6 25.6c0 14.131-11.443 25.6-25.6 25.6zM512 524.8c-14.157 0-25.6-11.469-25.6-25.6v-204.8c0-14.157 11.443-25.6 25.6-25.6s25.6 11.443 25.6 25.6v204.8c0 14.131-11.443 25.6-25.6 25.6z" />
<glyph unicode="&#xe91b;" glyph-name="location, control-Map, type-Geolocation" d="M512 960c-169.421 0-307.2-137.779-307.2-307.2 0-78.643 15.258-164.915 45.261-256.41 23.859-72.55 56.986-148.582 98.56-226.099 70.707-131.635 140.339-220.774 143.309-224.512 4.813-6.195 12.288-9.779 20.070-9.779 7.834 0 15.258 3.584 20.122 9.779 2.97 3.686 72.602 92.826 143.309 224.512 41.574 77.517 74.701 153.549 98.56 226.099 29.952 91.494 45.21 177.766 45.21 256.41 0 169.421-137.83 307.2-307.2 307.2zM630.682 195.328c-46.234-86.374-92.979-154.982-118.682-190.822-25.6 35.635-72.038 103.885-118.221 189.952-62.874 117.146-137.779 291.738-137.779 458.342 0 141.158 114.842 256 256 256s256-114.842 256-256c0-166.298-74.65-340.582-137.318-457.472zM512 806.4c-84.685 0-153.6-68.915-153.6-153.6s68.915-153.6 153.6-153.6 153.6 68.915 153.6 153.6-68.915 153.6-153.6 153.6zM512 550.4c-56.525 0-102.4 45.875-102.4 102.4 0 56.474 45.875 102.4 102.4 102.4 56.474 0 102.4-45.926 102.4-102.4 0-56.525-45.926-102.4-102.4-102.4z" />
<glyph unicode="&#xe91c;" glyph-name="logo" d="M512.273 876.218c-0.141-0.056-182.959-84.073-229.418-256.782-4.481-16.584 32.696-9.296 31.036-27.527-2.034-22.136-44.668-31.201-39.109-94.764 5.659-64.734 60.321-130.141 68.527-169.673v-27.655c-0.497-8.54-4.566-31.715-18.018-43.036-7.378-6.19-17.322-8.421-30.436-6.782-18.205 2.275-25.449 14.468-28.345 24.309-4.753 16.218-0.322 35.123 10.345 44 10.724 8.924 12.17 24.842 3.236 35.564-8.934 10.712-24.858 12.161-35.582 3.218-25.995-21.64-36.887-61.52-26.491-97 9.815-33.392 36.197-55.884 70.6-60.182 4.903-0.609 9.566-0.909 14-0.909 26.623 0 44.661 10.175 55.582 19.455 32.866 27.97 35.449 74.593 35.636 79.818 0.009 0.309 0.018 0.618 0.018 0.927v21.218h0.109v1.418c0 12.351 10.008 22.364 22.382 22.364 11.944 0 21.609-9.346 22.273-21.109v-202.491c-0.206-2.912-2.536-29.892-17.891-42.945-7.368-6.274-17.384-8.53-30.545-6.873-18.214 2.275-25.476 14.468-28.364 24.291-4.762 16.228-0.322 35.151 10.345 44.018 10.724 8.933 12.188 24.833 3.255 35.564-8.924 10.694-24.876 12.161-35.6 3.218-26.013-21.631-36.887-61.52-26.491-97 9.796-33.392 36.197-55.893 70.6-60.2 4.903-0.609 9.566-0.891 14-0.891 26.623 0 44.671 10.156 55.564 19.436 32.875 27.97 35.458 74.611 35.636 79.836 0.019 0.328 0.018 0.609 0.018 0.909v225.636l0.127 0.055v1c0 12.595 10.219 22.8 22.836 22.8 12.349 0 22.333-9.824 22.727-22.073v-227.418c0-0.309 0-0.591 0.018-0.909 0.187-5.216 2.779-51.866 35.655-79.836 10.912-9.28 28.959-19.436 55.582-19.436 4.443 0 9.088 0.282 13.982 0.891 34.394 4.307 60.804 26.818 70.6 60.2 10.405 35.48-0.487 75.36-26.491 97-10.743 8.943-26.676 7.466-35.6-3.218-8.934-10.74-7.488-26.63 3.236-35.564 10.668-8.868 15.135-27.79 10.364-44.018-2.878-9.823-10.159-22.015-28.364-24.291-13.105-1.648-23.050 0.592-30.418 6.782-13.508 11.358-17.558 34.657-18.036 43v201.818c0.297 12.093 10.14 21.818 22.327 21.818 12.374 0 22.4-10.003 22.4-22.364v-1.418h0.073v-21.218c0-0.318 0-0.628 0.018-0.927 0.178-5.216 2.779-51.848 35.655-79.818 10.912-9.28 28.941-19.455 55.564-19.455 4.434 0 9.107 0.292 14 0.891 34.394 4.298 60.786 26.818 70.582 60.2 10.405 35.48-0.487 75.351-26.491 97-10.743 8.933-26.667 7.476-35.582-3.236-8.943-10.722-7.488-26.622 3.236-35.545 10.668-8.877 15.117-27.8 10.345-44.018-2.878-9.842-10.159-22.025-28.364-24.291-13.086-1.648-23.050 0.583-30.418 6.764-13.508 11.368-17.549 34.675-18.018 43v21.018c5.305 54.103 63.095 107.777 69.091 176.364 5.531 63.563-37.121 72.627-39.145 94.764-1.669 18.232 35.498 10.944 31.036 27.527-46.468 172.709-229.269 256.726-229.4 256.782z" />
<glyph unicode="&#xe91d;" glyph-name="media, type-Assets" d="M947.2 960h-870.4c-42.342 0-76.8-34.458-76.8-76.8v-870.4c0-42.342 34.458-76.8 76.8-76.8h870.4c42.342 0 76.8 34.458 76.8 76.8v870.4c0 42.342-34.458 76.8-76.8 76.8zM972.8 12.8c0-14.157-11.443-25.6-25.6-25.6h-870.4c-14.131 0-25.6 11.443-25.6 25.6v870.4c0 14.131 11.469 25.6 25.6 25.6h870.4c14.157 0 25.6-11.469 25.6-25.6v-870.4zM665.6 499.2c56.448 0 102.4 45.926 102.4 102.4s-45.952 102.4-102.4 102.4c-56.448 0-102.4-45.926-102.4-102.4s45.952-102.4 102.4-102.4zM665.6 652.8c28.211 0 51.2-22.989 51.2-51.2s-22.989-51.2-51.2-51.2c-28.211 0-51.2 22.989-51.2 51.2s22.989 51.2 51.2 51.2zM896 857.6h-768c-14.131 0-25.6-11.469-25.6-25.6v-614.4c0-14.157 11.469-25.6 25.6-25.6h768c14.157 0 25.6 11.443 25.6 25.6v614.4c0 14.131-11.443 25.6-25.6 25.6zM153.6 243.2v118.246l164.301 184.858c4.198 4.787 9.728 7.373 15.462 7.475 5.734 0.051 11.29-2.458 15.642-7.040l283.238-303.539h-478.643zM870.4 243.2h-168.090l-315.853 338.432c-14.285 15.334-33.331 23.603-53.709 23.347-20.326-0.256-39.219-9.011-53.094-24.627l-126.054-141.798v367.846h716.8v-563.2z" />
<glyph unicode="&#xe91d;" glyph-name="media, type-Assets, trigger-AssetChanged" d="M947.2 960h-870.4c-42.342 0-76.8-34.458-76.8-76.8v-870.4c0-42.342 34.458-76.8 76.8-76.8h870.4c42.342 0 76.8 34.458 76.8 76.8v870.4c0 42.342-34.458 76.8-76.8 76.8zM972.8 12.8c0-14.157-11.443-25.6-25.6-25.6h-870.4c-14.131 0-25.6 11.443-25.6 25.6v870.4c0 14.131 11.469 25.6 25.6 25.6h870.4c14.157 0 25.6-11.469 25.6-25.6v-870.4zM665.6 499.2c56.448 0 102.4 45.926 102.4 102.4s-45.952 102.4-102.4 102.4c-56.448 0-102.4-45.926-102.4-102.4s45.952-102.4 102.4-102.4zM665.6 652.8c28.211 0 51.2-22.989 51.2-51.2s-22.989-51.2-51.2-51.2c-28.211 0-51.2 22.989-51.2 51.2s22.989 51.2 51.2 51.2zM896 857.6h-768c-14.131 0-25.6-11.469-25.6-25.6v-614.4c0-14.157 11.469-25.6 25.6-25.6h768c14.157 0 25.6 11.443 25.6 25.6v614.4c0 14.131-11.443 25.6-25.6 25.6zM153.6 243.2v118.246l164.301 184.858c4.198 4.787 9.728 7.373 15.462 7.475 5.734 0.051 11.29-2.458 15.642-7.040l283.238-303.539h-478.643zM870.4 243.2h-168.090l-315.853 338.432c-14.285 15.334-33.331 23.603-53.709 23.347-20.326-0.256-39.219-9.011-53.094-24.627l-126.054-141.798v367.846h716.8v-563.2z" />
<glyph unicode="&#xe91e;" glyph-name="more, dots" d="M128 576c-70.656 0-128-57.344-128-128s57.344-128 128-128c70.656 0 128 57.344 128 128s-57.344 128-128 128zM512 576c-70.656 0-128-57.344-128-128s57.344-128 128-128c70.656 0 128 57.344 128 128s-57.344 128-128 128zM896 576c-70.656 0-128-57.344-128-128s57.344-128 128-128c70.656 0 128 57.344 128 128s-57.344 128-128 128z" />
<glyph unicode="&#xe91f;" glyph-name="pencil" d="M877.12 648.896l-66.304-66.368-228.224 228.224 66.368 66.368c25.216 25.152 66.048 25.152 91.264 0l136.896-137.024c25.216-25.216 25.216-65.984 0-91.2zM760.896 532.608l-386.176-386.112c-25.216-25.28-66.048-25.28-91.264 0l-136.96 136.896c-25.216 25.28-25.216 66.112 0 91.264l386.24 386.24 228.16-228.288zM64 64v191.872l191.936-191.872h-191.936z" />
<glyph unicode="&#xe920;" glyph-name="reference" d="M892.083 828.083c-73.523 73.498-193.152 73.498-266.65 0l-157.184-157.107c-9.958-10.035-9.958-26.214 0-36.275 10.061-9.984 26.24-9.984 36.25 0l157.133 157.107c53.504 53.555 140.672 53.555 194.176 0 53.581-53.504 53.581-140.672 0-194.176l-186.138-186.163c-53.53-53.581-140.672-53.581-194.176 0-10.086 10.010-26.24 10.010-36.275 0-10.035-10.086-10.035-26.189 0-36.25 36.787-36.736 84.992-55.117 133.325-55.117s96.589 18.432 133.376 55.117l186.163 186.214c73.498 73.472 73.498 193.152 0 266.65zM519.45 261.274l-157.082-157.082c-53.504-53.555-140.672-53.555-194.176 0-53.581 53.504-53.581 140.672 0 194.176l186.138 186.163c53.53 53.581 140.672 53.581 194.176 0 10.086-9.984 26.189-9.984 36.275 0 10.035 10.086 10.035 26.214 0 36.25-73.549 73.498-193.203 73.498-266.701 0l-186.163-186.163c-73.498-73.574-73.498-193.203 0-266.701 36.787-36.71 85.043-55.117 133.325-55.117 48.333 0 96.538 18.406 133.325 55.117l157.133 157.133c10.010 10.010 10.010 26.189 0 36.224-10.010 9.984-26.189 9.984-36.25 0z" />

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 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.

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

@ -899,7 +899,7 @@
"properties": {
"order": 30,
"id": 11,
"name": "media, type-Assets",
"name": "media, type-Assets, trigger-AssetChanged",
"prevSize": 32,
"code": 59677
},

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

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?dojoae');
src: url('fonts/icomoon.eot?dojoae#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?dojoae') format('truetype'),
url('fonts/icomoon.woff?dojoae') format('woff'),
url('fonts/icomoon.svg?dojoae#icomoon') format('svg');
src: url('fonts/icomoon.eot?uoafl5');
src: url('fonts/icomoon.eot?uoafl5#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?uoafl5') format('truetype'),
url('fonts/icomoon.woff?uoafl5') format('woff'),
url('fonts/icomoon.svg?uoafl5#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
@ -136,6 +136,9 @@
.icon-type-Assets:before {
content: "\e91d";
}
.icon-trigger-AssetChanged:before {
content: "\e91d";
}
.icon-more:before {
content: "\e91e";
}

60
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/AssetChangedTriggerTests.cs

@ -0,0 +1,60 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.Triggers;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure.EventSourcing;
using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers
{
public sealed class AssetChangedTriggerTests
{
private readonly IRuleTriggerHandler sut = new AssetChangedTriggerHandler();
public static IEnumerable<object[]> TestData
{
get
{
return new[]
{
new object[] { 0, 1, 1, 1, 1, new RuleCreated() },
new object[] { 1, 1, 0, 0, 0, new AssetCreated() },
new object[] { 0, 0, 0, 0, 0, new AssetCreated() },
new object[] { 1, 0, 1, 0, 0, new AssetUpdated() },
new object[] { 0, 0, 0, 0, 0, new AssetUpdated() },
new object[] { 1, 0, 0, 1, 0, new AssetRenamed() },
new object[] { 0, 0, 0, 0, 0, new AssetRenamed() },
new object[] { 1, 0, 0, 0, 1, new AssetDeleted() },
new object[] { 0, 0, 0, 0, 0, new AssetDeleted() }
};
}
}
[Theory]
[MemberData(nameof(TestData))]
public void Should_return_result_depending_on_event(int expected, int sendCreate, int sendUpdate, int sendRename, int sendDelete, AppEvent @event)
{
var trigger = new AssetChangedTrigger
{
SendCreate = sendCreate == 1,
SendUpdate = sendUpdate == 1,
SendRename = sendRename == 1,
SendDelete = sendDelete == 1
};
var result = sut.Triggers(new Envelope<AppEvent>(@event), trigger);
Assert.Equal(expected == 1, result);
}
}
}
Loading…
Cancel
Save