diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchema.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchema.cs index aca1f16e5..cfcb516d1 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchema.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchema.cs @@ -22,5 +22,9 @@ namespace Squidex.Domain.Apps.Core.Rules.Triggers public bool SendPublish { get; set; } public bool SendUnpublish { get; set; } + + public bool SendArchived { get; set; } + + public bool SendRestore { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEventType.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEventType.cs index ccefbeaf2..45148a8e2 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEventType.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEventType.cs @@ -9,9 +9,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents { public enum EnrichedContentEventType { + Archived, Created, Deleted, Published, + Restored, Unpublished, Updated } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs index c0505f309..a65ccbcc7 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs @@ -17,7 +17,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Triggers { protected override bool Triggers(Envelope @event, ContentChangedTrigger trigger) { - if (trigger.HandleAll && @event.Payload is ContentEvent) + if (trigger.HandleAll && + @event.Payload is ContentEvent && + !(@event.Payload is ContentChangesPublished) && + !(@event.Payload is ContentChangesDiscarded) && + !(@event.Payload is ContentUpdateProposed)) { return true; } @@ -44,21 +48,33 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Triggers private static bool MatchsType(ContentChangedTriggerSchema schema, SchemaEvent @event) { return + IsArchived(schema, @event) || IsCreate(schema, @event) || - IsUpdate(schema, @event) || IsDelete(schema, @event) || IsPublished(schema, @event) || + IsRestored(schema, @event) || + IsUpdate(schema, @event) || IsUnpublished(schema, @event); } private static bool IsPublished(ContentChangedTriggerSchema schema, SchemaEvent @event) { - return schema.SendPublish && @event is ContentStatusChanged statusChanged && statusChanged.Status == Status.Published; + return schema.SendPublish && @event is ContentStatusChanged statusChanged && statusChanged.Change == StatusChange.Published; + } + + private static bool IsRestored(ContentChangedTriggerSchema schema, SchemaEvent @event) + { + return schema.SendRestore && @event is ContentStatusChanged statusChanged && statusChanged.Change == StatusChange.Restored; + } + + private static bool IsArchived(ContentChangedTriggerSchema schema, SchemaEvent @event) + { + return schema.SendArchived && @event is ContentStatusChanged statusChanged && statusChanged.Change == StatusChange.Archived; } private static bool IsUnpublished(ContentChangedTriggerSchema schema, SchemaEvent @event) { - return schema.SendUnpublish && @event is ContentStatusChanged statusChanged && statusChanged.Status != Status.Published; + return schema.SendUnpublish && @event is ContentStatusChanged statusChanged && statusChanged.Change == StatusChange.Unpublished; } private static bool IsCreate(ContentChangedTriggerSchema schema, SchemaEvent @event) diff --git a/src/Squidex.Domain.Apps.Entities/AppProvider.cs b/src/Squidex.Domain.Apps.Entities/AppProvider.cs index a00bda2d7..c8c821f49 100644 --- a/src/Squidex.Domain.Apps.Entities/AppProvider.cs +++ b/src/Squidex.Domain.Apps.Entities/AppProvider.cs @@ -31,6 +31,7 @@ namespace Squidex.Domain.Apps.Entities Guard.NotNull(localCache, nameof(localCache)); this.grainFactory = grainFactory; + this.localCache = localCache; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs index 6839d4b56..62961abc4 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs @@ -57,13 +57,6 @@ namespace Squidex.Domain.Apps.Entities.Apps this.initialPatterns = initialPatterns; } - public override Task OnActivateAsync() - { - CleanupOldSnapshots(); - - return base.OnActivateAsync(); - } - protected override Task ExecuteAsync(IAggregateCommand command) { VerifyNotArchived(); diff --git a/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs b/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs index 00fa7b95e..969d0852e 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs @@ -140,9 +140,9 @@ namespace Squidex.Domain.Apps.Entities.Assets ApplySnapshot(Snapshot.Apply(@event)); } - public Task> GetStateAsync(long version = EtagVersion.Empty) + public Task> GetStateAsync() { - return J.AsTask(GetSnapshot(version)); + return J.AsTask(Snapshot); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/IAssetGrain.cs b/src/Squidex.Domain.Apps.Entities/Assets/IAssetGrain.cs index 4018d7e3d..f76f5e48f 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/IAssetGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/IAssetGrain.cs @@ -6,7 +6,6 @@ // ========================================================================== using System.Threading.Tasks; -using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; @@ -14,6 +13,6 @@ namespace Squidex.Domain.Apps.Entities.Assets { public interface IAssetGrain : IDomainObjectGrain { - Task> GetStateAsync(long version = EtagVersion.Any); + Task> GetStateAsync(); } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs index e9439de49..9e2698cdb 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs @@ -117,9 +117,28 @@ namespace Squidex.Domain.Apps.Entities.Contents } else { - await ctx.ExecuteScriptAsync(x => x.ScriptChange, c.Status, c, Snapshot.Data); - - ChangeStatus(c); + var reason = StatusChange.Published; + + if (c.Status == Status.Published) + { + reason = StatusChange.Published; + } + else if (c.Status == Status.Archived) + { + reason = StatusChange.Archived; + } + else if (Snapshot.Status == Status.Published) + { + reason = StatusChange.Unpublished; + } + else + { + reason = StatusChange.Restored; + } + + await ctx.ExecuteScriptAsync(x => x.ScriptChange, reason, c, Snapshot.Data); + + ChangeStatus(c, reason); } } } @@ -206,7 +225,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (command.Publish) { - RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged { Status = Status.Published })); + RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged { Status = Status.Published, Change = StatusChange.Published })); } } @@ -245,9 +264,9 @@ namespace Squidex.Domain.Apps.Entities.Contents RaiseEvent(SimpleMapper.Map(command, new ContentStatusScheduled { DueTime = command.DueTime.Value })); } - public void ChangeStatus(ChangeContentStatus command) + public void ChangeStatus(ChangeContentStatus command, StatusChange reason) { - RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged())); + RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged { Change = reason })); } private void RaiseEvent(SchemaEvent @event) @@ -281,19 +300,16 @@ namespace Squidex.Domain.Apps.Entities.Contents private async Task CreateContext(Guid appId, Guid schemaId, Func message) { var operationContext = - await ContentOperationContext.CreateAsync(appId, schemaId, - appProvider, - assetRepository, - contentRepository, - scriptEngine, - message); + await ContentOperationContext.CreateAsync( + appId, schemaId, + appProvider, assetRepository, contentRepository, scriptEngine, message); return operationContext; } - public Task> GetStateAsync(long version = -2) + public Task> GetStateAsync() { - return J.AsTask(GetSnapshot(version)); + return J.AsTask(Snapshot); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs index 7016766ef..25ec318e2 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentVersionLoader.cs @@ -7,38 +7,53 @@ using System; using System.Threading.Tasks; -using Orleans; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.Contents.State; using Squidex.Infrastructure; using Squidex.Infrastructure.Log; +using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities.Contents { public sealed class ContentVersionLoader : IContentVersionLoader { - private readonly IGrainFactory grainFactory; + private readonly IStore store; + private readonly FieldRegistry registry; - public ContentVersionLoader(IGrainFactory grainFactory) + public ContentVersionLoader(IStore store, FieldRegistry registry) { - Guard.NotNull(grainFactory, nameof(grainFactory)); + Guard.NotNull(store, nameof(store)); + Guard.NotNull(registry, nameof(registry)); - this.grainFactory = grainFactory; + this.store = store; + + this.registry = registry; } public async Task LoadAsync(Guid id, long version) { using (Profiler.TraceMethod()) { - var grain = grainFactory.GetGrain(id); + var content = new ContentState(); + + var persistence = store.WithEventSourcing(id, e => + { + if (content.Version < version) + { + content = content.Apply(e); + content.Version++; + } + }); - var content = await grain.GetStateAsync(version); + await persistence.ReadAsync(); - if (content.Value == null || content.Value.Version != version) + if (content.Version != version) { throw new DomainObjectNotFoundException(id.ToString(), typeof(IContentEntity)); } - return content.Value; + return content; } } } -} +} \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Entities/Contents/IContentGrain.cs b/src/Squidex.Domain.Apps.Entities/Contents/IContentGrain.cs index 429a27746..24eddbcc5 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/IContentGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/IContentGrain.cs @@ -6,7 +6,6 @@ // ========================================================================== using System.Threading.Tasks; -using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; @@ -14,6 +13,6 @@ namespace Squidex.Domain.Apps.Entities.Contents { public interface IContentGrain : IDomainObjectGrain { - Task> GetStateAsync(long version = EtagVersion.Any); + Task> GetStateAsync(); } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs index f9028fcbf..aa3746811 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs @@ -32,10 +32,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.State public NamedContentData DataDraft { get; set; } [JsonProperty] - public Status Status { get; set; } + public ScheduleJob ScheduleJob { get; set; } [JsonProperty] - public ScheduleJob ScheduleJob { get; set; } + public Status Status { get; set; } [JsonProperty] public bool IsPending { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/EventEnricher.cs b/src/Squidex.Domain.Apps.Entities/Rules/EventEnricher.cs index f4f449bce..d3072ca78 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/EventEnricher.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/EventEnricher.cs @@ -8,7 +8,6 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; -using NodaTime; using Orleans; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.HandleRules; @@ -31,19 +30,16 @@ namespace Squidex.Domain.Apps.Entities.Rules private readonly IGrainFactory grainFactory; private readonly IMemoryCache cache; private readonly IUserResolver userResolver; - private readonly IClock clock; - public EventEnricher(IGrainFactory grainFactory, IMemoryCache cache, IUserResolver userResolver, IClock clock) + public EventEnricher(IGrainFactory grainFactory, IMemoryCache cache, IUserResolver userResolver) { Guard.NotNull(grainFactory, nameof(grainFactory)); Guard.NotNull(cache, nameof(cache)); - Guard.NotNull(clock, nameof(clock)); Guard.NotNull(userResolver, nameof(userResolver)); - this.userResolver = userResolver; - this.cache = cache; - this.clock = clock; this.grainFactory = grainFactory; + this.cache = cache; + this.userResolver = userResolver; } public async Task EnrichAsync(Envelope @event) @@ -80,9 +76,10 @@ namespace Squidex.Domain.Apps.Entities.Rules var asset = (await grainFactory .GetGrain(assetEvent.AssetId) - .GetStateAsync(@event.Headers.EventStreamNumber())).Value; + .GetStateAsync()).Value; SimpleMapper.Map(asset, result); + SimpleMapper.Map(assetEvent, result); switch (assetEvent) { @@ -108,12 +105,14 @@ namespace Squidex.Domain.Apps.Entities.Rules var content = (await grainFactory .GetGrain(contentEvent.ContentId) - .GetStateAsync(@event.Headers.EventStreamNumber())).Value; + .GetStateAsync()).Value; SimpleMapper.Map(content, result); result.Data = content.Data ?? content.DataDraft; + SimpleMapper.Map(contentEvent, result); + switch (contentEvent) { case ContentCreated _: @@ -122,17 +121,25 @@ namespace Squidex.Domain.Apps.Entities.Rules case ContentDeleted _: result.Type = EnrichedContentEventType.Deleted; break; + case ContentChangesPublished _: case ContentUpdated _: result.Type = EnrichedContentEventType.Updated; break; case ContentStatusChanged contentStatusChanged: - if (contentStatusChanged.Status == Status.Published) - { - result.Type = EnrichedContentEventType.Published; - } - else + switch (contentStatusChanged.Change) { - result.Type = EnrichedContentEventType.Unpublished; + case StatusChange.Published: + result.Type = EnrichedContentEventType.Published; + break; + case StatusChange.Unpublished: + result.Type = EnrichedContentEventType.Unpublished; + break; + case StatusChange.Archived: + result.Type = EnrichedContentEventType.Archived; + break; + case StatusChange.Restored: + result.Type = EnrichedContentEventType.Restored; + break; } break; @@ -143,10 +150,7 @@ namespace Squidex.Domain.Apps.Entities.Rules private async Task EnrichDefaultAsync(EnrichedEvent result, Envelope @event) { - result.Timestamp = - @event.Headers.Contains(CommonHeaders.Timestamp) ? - @event.Headers.Timestamp() : - clock.GetCurrentInstant(); + result.Timestamp = @event.Headers.Timestamp(); if (@event.Payload is SquidexEvent squidexEvent) { diff --git a/src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs b/src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs index 0ea48b9f2..81e475e4d 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs @@ -34,13 +34,6 @@ namespace Squidex.Domain.Apps.Entities.Rules this.appProvider = appProvider; } - public override Task OnActivateAsync() - { - CleanupOldSnapshots(); - - return base.OnActivateAsync(); - } - protected override Task ExecuteAsync(IAggregateCommand command) { VerifyNotDeleted(); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs index aed31f013..0939f5332 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs @@ -40,13 +40,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas this.registry = registry; } - public override Task OnActivateAsync() - { - CleanupOldSnapshots(); - - return base.OnActivateAsync(); - } - protected override Task ExecuteAsync(IAggregateCommand command) { VerifyNotDeleted(); diff --git a/src/Squidex.Domain.Apps.Events/Contents/ContentStatusChanged.cs b/src/Squidex.Domain.Apps.Events/Contents/ContentStatusChanged.cs index 2c97f5902..81b95e5fb 100644 --- a/src/Squidex.Domain.Apps.Events/Contents/ContentStatusChanged.cs +++ b/src/Squidex.Domain.Apps.Events/Contents/ContentStatusChanged.cs @@ -13,6 +13,8 @@ namespace Squidex.Domain.Apps.Events.Contents [EventType(nameof(ContentStatusChanged))] public sealed class ContentStatusChanged : ContentEvent { + public StatusChange? Change { get; set; } + public Status Status { get; set; } } } diff --git a/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs b/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs index fcea5d4cc..3036d25dc 100644 --- a/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs +++ b/src/Squidex.Infrastructure/Commands/DomainObjectGrain.cs @@ -21,9 +21,8 @@ namespace Squidex.Infrastructure.Commands private readonly List> uncomittedEvents = new List>(); private readonly IStore store; private readonly ISemanticLog log; - private readonly List snapshots = new List { new T { Version = EtagVersion.Empty } }; - private bool cleanup; private Guid id; + private T snapshot = new T { Version = EtagVersion.Empty }; private IPersistence persistence; public Guid Id @@ -33,17 +32,17 @@ namespace Squidex.Infrastructure.Commands public long Version { - get { return snapshots.Count - 2; } + get { return snapshot.Version; } } public long NewVersion { - get { return Version; } + get { return snapshot.Version + uncomittedEvents.Count; } } public T Snapshot { - get { return snapshots[snapshots.Count - 1]; } + get { return snapshot; } } protected DomainObjectGrain(IStore store, ISemanticLog log) @@ -56,31 +55,6 @@ namespace Squidex.Infrastructure.Commands this.log = log; } - public void CleanupOldSnapshots() - { - cleanup = true; - } - - public T GetSnapshot(long version) - { - if (version == EtagVersion.Any) - { - return Snapshot; - } - - if (version == EtagVersion.Empty) - { - return snapshots[0]; - } - - if (version >= 0 && version < snapshots.Count - 1) - { - return snapshots[(int)version + 1]; - } - - return default(T); - } - public override async Task OnActivateAsync(Guid key) { using (log.MeasureInformation(w => w @@ -122,11 +96,9 @@ namespace Squidex.Infrastructure.Commands uncomittedEvents.Clear(); } - public virtual void ApplySnapshot(T snapshot) + public virtual void ApplySnapshot(T newSnapshot) { - snapshot.Version = snapshots.Count - 1; - - snapshots.Add(snapshot); + snapshot = newSnapshot; } public virtual void ApplyEvent(Envelope @event) @@ -200,8 +172,7 @@ namespace Squidex.Infrastructure.Commands throw new DomainException("Object has already been created."); } - var size = snapshots.Count; - + var previousSnapshot = snapshot; try { var result = await handler(command); @@ -210,8 +181,10 @@ namespace Squidex.Infrastructure.Commands if (events.Length > 0) { + snapshot.Version = NewVersion; + await persistence.WriteEventsAsync(events); - await persistence.WriteSnapshotAsync(Snapshot); + await persistence.WriteSnapshotAsync(snapshot); } if (result == null) @@ -230,23 +203,12 @@ namespace Squidex.Infrastructure.Commands } catch { - while (snapshots.Count > size) - { - snapshots.RemoveAt(snapshots.Count - 1); - } + snapshot = previousSnapshot; throw; } finally { - if (cleanup) - { - for (var i = 0; i < snapshots.Count - 1; i++) - { - snapshots[i] = default(T); - } - } - uncomittedEvents.Clear(); } } @@ -260,4 +222,4 @@ namespace Squidex.Infrastructure.Commands protected abstract Task ExecuteAsync(IAggregateCommand command); } -} +} \ No newline at end of file diff --git a/src/Squidex.Infrastructure/ValidationException.cs b/src/Squidex.Infrastructure/ValidationException.cs index 163904187..764e45ab7 100644 --- a/src/Squidex.Infrastructure/ValidationException.cs +++ b/src/Squidex.Infrastructure/ValidationException.cs @@ -21,7 +21,7 @@ namespace Squidex.Infrastructure public IReadOnlyList Errors { - get { return errors; } + get { return errors ?? FallbackErrors; } } public string Summary { get; } diff --git a/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerSchemaDto.cs b/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerSchemaDto.cs index 14e17c511..2a16e9639 100644 --- a/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerSchemaDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerSchemaDto.cs @@ -40,5 +40,15 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers /// Determines whether to handle the event when a content is unpublished. /// public bool SendUnpublish { get; set; } + + /// + /// Determines whether to handle the event when a content is archived. + /// + public bool SendArchived { get; set; } + + /// + /// Determines whether to handle the event when a content is restored. + /// + public bool SendRestore { get; set; } } } diff --git a/src/Squidex/Config/Domain/RuleServices.cs b/src/Squidex/Config/Domain/RuleServices.cs index d80b0e5c7..3a9ae24d1 100644 --- a/src/Squidex/Config/Domain/RuleServices.cs +++ b/src/Squidex/Config/Domain/RuleServices.cs @@ -18,6 +18,9 @@ namespace Squidex.Config.Domain { public static void AddMyRuleServices(this IServiceCollection services) { + services.AddSingletonAs() + .As(); + services.AddSingletonAs() .As(); diff --git a/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs b/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs index 3dfd01123..dc5375d28 100644 --- a/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs +++ b/src/Squidex/Pipeline/ApiExceptionFilterAttribute.cs @@ -54,7 +54,7 @@ namespace Squidex.Pipeline private static IActionResult OnValidationException(ValidationException ex) { - return ErrorResult(400, new ErrorDto { Message = ex.Summary, Details = ex.Errors.Select(e => e.Message).ToArray() }); + return ErrorResult(400, new ErrorDto { Message = ex.Summary, Details = ex.Errors?.Select(e => e.Message).ToArray() }); } private static IActionResult ErrorResult(int statusCode, ErrorDto error) diff --git a/src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.html b/src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.html index 495888ae6..cbd5fe4d6 100644 --- a/src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.html +++ b/src/Squidex/app/features/rules/pages/rules/triggers/content-changed-trigger.component.html @@ -10,6 +10,9 @@ + + + @@ -31,6 +34,15 @@
Published
+ +
Unpublished
+ + +
Archived
+ + +
Restored
+ @@ -53,6 +65,15 @@ + + + + + + + + +