diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs index bc3e716b3..5a8a252bd 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs @@ -8,14 +8,11 @@ using System; using System.Collections.Generic; using System.Net.Http; -using System.Text; using System.Threading.Tasks; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.Actions; using Squidex.Domain.Apps.Events; -using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Http; diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetEntity.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetEntity.cs index 468bd2110..2f2729858 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetEntity.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetEntity.cs @@ -20,9 +20,16 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets IAssetEntity, IUpdateableEntityWithVersion, IUpdateableEntityWithCreatedBy, - IUpdateableEntityWithLastModifiedBy, - IUpdateableEntityWithAppRef + IUpdateableEntityWithLastModifiedBy { + [BsonRequired] + [BsonElement] + public Guid AppIdId { get; set; } + + [BsonRequired] + [BsonElement] + public NamedId AppId { get; set; } + [BsonRequired] [BsonElement] public string MimeType { get; set; } @@ -55,10 +62,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets [BsonElement] public int? PixelHeight { get; set; } - [BsonRequired] - [BsonElement] - public Guid AppId { get; set; } - [BsonRequired] [BsonElement] public RefToken CreatedBy { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs index 3e1cf0c22..a4056d0a7 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs @@ -36,6 +36,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets var entity = SimpleMapper.Map(value, new MongoAssetEntity()); entity.Version = newVersion; + entity.AppIdId = value.AppId.Id; await Collection.ReplaceOneAsync(x => x.Id == key && x.Version == oldVersion, entity, Upsert); } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs index b43fc8f48..af3a75764 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs @@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors { var filters = new List> { - Filter.Eq(x => x.AppId, appId), + Filter.Eq(x => x.AppIdId, appId), Filter.Eq(x => x.IsDeleted, false) }; diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs index 68d92be78..f35cb2286 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs @@ -35,12 +35,12 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents [BsonRequired] [BsonElement("ai")] [BsonRepresentation(BsonType.String)] - public Guid AppId { get; set; } + public Guid AppIdId { get; set; } [BsonRequired] [BsonElement("si")] [BsonRepresentation(BsonType.String)] - public Guid SchemaId { get; set; } + public Guid SchemaIdId { get; set; } [BsonRequired] [BsonElement("rf")] @@ -62,6 +62,26 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents [BsonJson] public IdContentData DataByIds { get; set; } + [BsonRequired] + [BsonElement("ai2")] + public NamedId AppId { get; set; } + + [BsonRequired] + [BsonElement("si2")] + public NamedId SchemaId { get; set; } + + [BsonIgnoreIfNull] + [BsonElement("sdt")] + public Status? ScheduledTo { get; set; } + + [BsonIgnoreIfNull] + [BsonElement("sda")] + public Instant? ScheduledAt { get; set; } + + [BsonIgnoreIfNull] + [BsonElement("sdb")] + public RefToken ScheduledBy { get; set; } + [BsonRequired] [BsonElement("ct")] public Instant Created { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs index a8ad1abcc..1d4cc508f 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.OData.UriParser; using MongoDB.Driver; +using NodaTime; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Contents; @@ -51,6 +52,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { await collection.Indexes.TryDropOneAsync("si_1_st_1_dl_1_dt_text"); + await archiveCollection.Indexes.CreateOneAsync( + Index + .Ascending(x => x.ScheduledTo)); + await archiveCollection.Indexes.CreateOneAsync( Index .Ascending(x => x.Id) @@ -59,13 +64,13 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents await collection.Indexes.CreateOneAsync( Index .Text(x => x.DataText) - .Ascending(x => x.SchemaId) + .Ascending(x => x.SchemaIdId) .Ascending(x => x.Status) .Ascending(x => x.IsDeleted)); await collection.Indexes.CreateOneAsync( Index - .Ascending(x => x.SchemaId) + .Ascending(x => x.SchemaIdId) .Ascending(x => x.Id) .Ascending(x => x.IsDeleted) .Ascending(x => x.Status)); @@ -121,7 +126,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet ids) { - var find = Collection.Find(x => x.SchemaId == schema.Id && ids.Contains(x.Id) && x.IsDeleted == false && status.Contains(x.Status)); + var find = Collection.Find(x => x.SchemaIdId == schema.Id && ids.Contains(x.Id) && x.IsDeleted == false && status.Contains(x.Status)); var contentItems = find.ToListAsync(); var contentCount = find.CountAsync(); @@ -139,7 +144,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task> QueryNotFoundAsync(Guid appId, Guid schemaId, IList ids) { var contentEntities = - await Collection.Find(x => x.SchemaId == schemaId && ids.Contains(x.Id) && x.IsDeleted == false).Only(x => x.Id) + await Collection.Find(x => x.SchemaIdId == schemaId && ids.Contains(x.Id) && x.IsDeleted == false).Only(x => x.Id) .ToListAsync(); return ids.Except(contentEntities.Select(x => Guid.Parse(x["id"].AsString))).ToList(); @@ -159,7 +164,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id) { var contentEntity = - await Collection.Find(x => x.SchemaId == schema.Id && x.Id == id && x.IsDeleted == false) + await Collection.Find(x => x.SchemaIdId == schema.Id && x.Id == id && x.IsDeleted == false) .FirstOrDefaultAsync(); contentEntity?.ParseData(schema.SchemaDef); @@ -167,6 +172,15 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents return contentEntity; } + public Task QueryScheduledWithoutDataAsync(Instant now, Func callback) + { + return Collection.Find(x => x.ScheduledAt < now && x.IsDeleted == false) + .ForEachAsync(c => + { + callback(c); + }); + } + public override async Task ClearAsync() { await Database.DropCollectionAsync("States_Contents_Archive"); diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs index c220e416e..54e236e4a 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs @@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents if (contentEntity != null) { - var schema = await GetSchemaAsync(contentEntity.AppId, contentEntity.SchemaId); + var schema = await GetSchemaAsync(contentEntity.AppIdId, contentEntity.SchemaIdId); contentEntity?.ParseData(schema.SchemaDef); @@ -40,12 +40,12 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task WriteAsync(Guid key, ContentState value, long oldVersion, long newVersion) { - if (value.SchemaId == Guid.Empty) + if (value.SchemaId.Id == Guid.Empty) { return; } - var schema = await GetSchemaAsync(value.AppId, value.SchemaId); + var schema = await GetSchemaAsync(value.AppId.Id, value.SchemaId.Id); var idData = value.Data?.ToIdModel(schema.SchemaDef, true); @@ -53,6 +53,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents var document = SimpleMapper.Map(value, new MongoContentEntity { + AppIdId = value.AppId.Id, + SchemaIdId = value.SchemaId.Id, IsDeleted = value.IsDeleted, DocumentId = key.ToString(), DataText = idData?.ToFullText(), @@ -92,7 +94,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents private async Task GetSchemaAsync(Guid appId, Guid schemaId) { - var schema = await appProvider.GetSchemaAsync(appId, schemaId); + var schema = await appProvider.GetSchemaAsync(appId, schemaId, true); if (schema == null) { diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs index c4ca769b6..cdfaff9b8 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs @@ -80,7 +80,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors { var filters = new List> { - Filter.Eq(x => x.SchemaId, schemaId), + Filter.Eq(x => x.SchemaIdId, schemaId), Filter.In(x => x.Status, status), Filter.Eq(x => x.IsDeleted, false) }; diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventEntity.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventEntity.cs index 610d38977..5e9d9d50d 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventEntity.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventEntity.cs @@ -16,11 +16,9 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.History { public sealed class MongoHistoryEventEntity : MongoEntity, IEntity, - IEntityWithAppRef, IUpdateableEntity, IUpdateableEntityWithVersion, - IUpdateableEntityWithCreatedBy, - IUpdateableEntityWithAppRef + IUpdateableEntityWithCreatedBy { [BsonElement] [BsonRequired] diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleRepository_SnapshotStore.cs index 408a3aaba..cd8a2ee02 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleRepository_SnapshotStore.cs @@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules { return Collection.UpsertVersionedAsync(key, oldVersion, newVersion, u => u .Set(x => x.State, value) - .Set(x => x.AppId, value.AppId) + .Set(x => x.AppId, value.AppId.Id) .Set(x => x.IsDeleted, value.IsDeleted)); } } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs index aadd8cbcb..a23899a7a 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository_SnapshotStore.cs @@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas { return Collection.UpsertVersionedAsync(key, oldVersion, newVersion, u => u .Set(x => x.State, value) - .Set(x => x.AppId, value.AppId) + .Set(x => x.AppId, value.AppId.Id) .Set(x => x.Name, value.Name) .Set(x => x.IsDeleted, value.IsDeleted)); } diff --git a/src/Squidex.Domain.Apps.Entities/AppProvider.cs b/src/Squidex.Domain.Apps.Entities/AppProvider.cs index ace83d66c..b2e028a12 100644 --- a/src/Squidex.Domain.Apps.Entities/AppProvider.cs +++ b/src/Squidex.Domain.Apps.Entities/AppProvider.cs @@ -88,11 +88,11 @@ namespace Squidex.Domain.Apps.Entities return (await stateFactory.GetSingleAsync(schemaId)).Snapshot; } - public async Task GetSchemaAsync(Guid appId, Guid id) + public async Task GetSchemaAsync(Guid appId, Guid id, bool allowDeleted = false) { var schema = await stateFactory.GetSingleAsync(id); - if (!IsFound(schema) || schema.Snapshot.IsDeleted || schema.Snapshot.AppId != appId) + if (!IsFound(schema) || (schema.Snapshot.IsDeleted && !allowDeleted) || schema.Snapshot.AppId.Id != appId) { return null; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs index 02bb242bd..829c1388c 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Collections.Generic; using System.Threading.Tasks; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Guards; @@ -180,7 +179,7 @@ namespace Squidex.Domain.Apps.Entities.Apps } else { - var result = await appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, command.AppId.Id, a.Snapshot.Name, command.PlanId); + var result = await appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, a.Snapshot.Id, a.Snapshot.Name, command.PlanId); if (result is PlanChangedResult) { diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddLanguage.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddLanguage.cs index b2619a6f6..3cfec0965 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddLanguage.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddLanguage.cs @@ -9,7 +9,7 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class AddLanguage : AppAggregateCommand + public sealed class AddLanguage : AppCommand { public Language Language { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddPattern.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddPattern.cs index 5442536c1..30873adbb 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddPattern.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AddPattern.cs @@ -9,7 +9,7 @@ using System; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class AddPattern : AppAggregateCommand + public sealed class AddPattern : AppCommand { public Guid PatternId { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/SchemaAggregateCommand.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AppCommand.cs similarity index 71% rename from src/Squidex.Domain.Apps.Entities/SchemaAggregateCommand.cs rename to src/Squidex.Domain.Apps.Entities/Apps/Commands/AppCommand.cs index d3d73dd62..a391da077 100644 --- a/src/Squidex.Domain.Apps.Entities/SchemaAggregateCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AppCommand.cs @@ -8,13 +8,15 @@ using System; using Squidex.Infrastructure.Commands; -namespace Squidex.Domain.Apps.Entities +namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public abstract class SchemaAggregateCommand : SchemaCommand, IAggregateCommand + public abstract class AppCommand : SquidexCommand, IAggregateCommand { + public Guid AppId { get; set; } + Guid IAggregateCommand.AggregateId { - get { return SchemaId.Id; } + get { return AppId; } } } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs index 13a60ddda..b54518a66 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Core.Apps; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class AssignContributor : AppAggregateCommand + public sealed class AssignContributor : AppCommand { public string ContributorId { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AttachClient.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AttachClient.cs index 4239c1e47..0fd8c6c4d 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/AttachClient.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/AttachClient.cs @@ -9,10 +9,15 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class AttachClient : AppAggregateCommand + public sealed class AttachClient : AppCommand { public string Id { get; set; } - public string Secret { get; } = RandomHash.New(); + public string Secret { get; set; } + + public AttachClient() + { + Secret = RandomHash.New(); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/ChangePlan.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/ChangePlan.cs index b5af896bf..a323b8b10 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/ChangePlan.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/ChangePlan.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class ChangePlan : AppAggregateCommand + public sealed class ChangePlan : AppCommand { public bool FromCallback { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/CreateApp.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/CreateApp.cs index 5e8050247..d4dc2528b 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/CreateApp.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/CreateApp.cs @@ -10,19 +10,12 @@ using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class CreateApp : SquidexCommand, IAggregateCommand + public sealed class CreateApp : AppCommand, IAggregateCommand { - public Guid AppId { get; set; } - public string Name { get; set; } public string Template { get; set; } - Guid IAggregateCommand.AggregateId - { - get { return AppId; } - } - public CreateApp() { AppId = Guid.NewGuid(); diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/DeletePattern.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/DeletePattern.cs index 5db33b435..199bff83c 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/DeletePattern.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/DeletePattern.cs @@ -9,7 +9,7 @@ using System; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class DeletePattern : AppAggregateCommand + public sealed class DeletePattern : AppCommand { public Guid PatternId { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveContributor.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveContributor.cs index 6f707811f..a4e27d426 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveContributor.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveContributor.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class RemoveContributor : AppAggregateCommand + public sealed class RemoveContributor : AppCommand { public string ContributorId { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveLanguage.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveLanguage.cs index c863e7b85..602c35756 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveLanguage.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveLanguage.cs @@ -9,7 +9,7 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class RemoveLanguage : AppAggregateCommand + public sealed class RemoveLanguage : AppCommand { public Language Language { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/RevokeClient.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/RevokeClient.cs index 623da3058..9361891ba 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/RevokeClient.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/RevokeClient.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class RevokeClient : AppAggregateCommand + public sealed class RevokeClient : AppCommand { public string Id { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs index 856de1f4a..6002bba30 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Core.Apps; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class UpdateClient : AppAggregateCommand + public sealed class UpdateClient : AppCommand { public string Id { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateLanguage.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateLanguage.cs index c0f442a25..52e40e5d0 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateLanguage.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateLanguage.cs @@ -10,7 +10,7 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class UpdateLanguage : AppAggregateCommand + public sealed class UpdateLanguage : AppCommand { public Language Language { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdatePattern.cs b/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdatePattern.cs index 9ae7b510e..415856189 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdatePattern.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdatePattern.cs @@ -9,7 +9,7 @@ using System; namespace Squidex.Domain.Apps.Entities.Apps.Commands { - public sealed class UpdatePattern : AppAggregateCommand + public sealed class UpdatePattern : AppCommand { public Guid PatternId { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs index 6eeac4e55..adea21e4c 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs @@ -36,17 +36,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates { var appId = new NamedId(createApp.AppId, createApp.Name); - Task publishAsync(AppCommand command) - { - command.AppId = appId; - - return context.CommandBus.PublishAsync(command); - } - return Task.WhenAll( - CreatePagesAsync(publishAsync, appId), - CreatePostsAsync(publishAsync, appId), - CreateClientAsync(publishAsync, appId)); + CreatePagesAsync(context.CommandBus, appId), + CreatePostsAsync(context.CommandBus, appId), + CreateClientAsync(context.CommandBus, appId)); } return TaskHelper.Done; @@ -57,16 +50,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates return string.Equals(createApp.Template, TemplateName, StringComparison.OrdinalIgnoreCase); } - private static async Task CreateClientAsync(Func publishAsync, NamedId appId) + private static async Task CreateClientAsync(ICommandBus bus, NamedId appId) { - await publishAsync(new AttachClient { Id = "sample-client" }); + await bus.PublishAsync(new AttachClient { Id = "sample-client" }); } - private async Task CreatePostsAsync(Func publishAsync, NamedId appId) + private async Task CreatePostsAsync(ICommandBus bus, NamedId appId) { - var postsId = await CreatePostsSchema(publishAsync); + var postsId = await CreatePostsSchema(bus, appId); - await publishAsync(new CreateContent + await bus.PublishAsync(new CreateContent { SchemaId = postsId, Data = @@ -81,11 +74,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates }); } - private async Task CreatePagesAsync(Func publishAsync, NamedId appId) + private async Task CreatePagesAsync(ICommandBus bus, NamedId appId) { - var pagesId = await CreatePagesSchema(publishAsync); + var pagesId = await CreatePagesSchema(bus, appId); - await publishAsync(new CreateContent + await bus.PublishAsync(new CreateContent { SchemaId = pagesId, Data = @@ -100,7 +93,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates }); } - private async Task> CreatePostsSchema(Func publishAsync) + private async Task> CreatePostsSchema(ICommandBus bus, NamedId appId) { var command = new CreateSchema { @@ -111,58 +104,59 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates Label = "Posts" }, Fields = new List + { + new CreateSchemaField { - new CreateSchemaField + Name = "title", + Partitioning = Partitioning.Invariant.Key, + Properties = new StringFieldProperties { - Name = "title", - Partitioning = Partitioning.Invariant.Key, - Properties = new StringFieldProperties - { - Editor = StringFieldEditor.Input, - IsRequired = true, - IsListField = true, - MaxLength = 100, - MinLength = 0, - Label = "Title" - } - }, - new CreateSchemaField + Editor = StringFieldEditor.Input, + IsRequired = true, + IsListField = true, + MaxLength = 100, + MinLength = 0, + Label = "Title" + } + }, + new CreateSchemaField + { + Name = "slug", + Partitioning = Partitioning.Invariant.Key, + Properties = new StringFieldProperties { - Name = "slug", - Partitioning = Partitioning.Invariant.Key, - Properties = new StringFieldProperties - { - Editor = StringFieldEditor.Slug, - IsRequired = false, - IsListField = true, - MaxLength = 100, - MinLength = 0, - Label = "Slug (Autogenerated)" - }, - IsDisabled = true + Editor = StringFieldEditor.Slug, + IsRequired = false, + IsListField = true, + MaxLength = 100, + MinLength = 0, + Label = "Slug (Autogenerated)" }, - new CreateSchemaField + IsDisabled = true + }, + new CreateSchemaField + { + Name = "text", + Partitioning = Partitioning.Invariant.Key, + Properties = new StringFieldProperties { - Name = "text", - Partitioning = Partitioning.Invariant.Key, - Properties = new StringFieldProperties - { - Editor = StringFieldEditor.RichText, - IsRequired = true, - IsListField = false, - Label = "Text" - } + Editor = StringFieldEditor.RichText, + IsRequired = true, + IsListField = false, + Label = "Text" } } + }, + AppId = appId }; - await publishAsync(command); + await bus.PublishAsync(command); var schemaId = new NamedId(command.SchemaId, command.Name); - await publishAsync(new ConfigureScripts + await bus.PublishAsync(new ConfigureScripts { - SchemaId = schemaId, + SchemaId = schemaId.Id, ScriptCreate = SlugScript, ScriptUpdate = SlugScript }); @@ -170,7 +164,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates return schemaId; } - private async Task> CreatePagesSchema(Func publishAsync) + private async Task> CreatePagesSchema(ICommandBus bus, NamedId appId) { var command = new CreateSchema { @@ -180,58 +174,59 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates Label = "Pages" }, Fields = new List + { + new CreateSchemaField { - new CreateSchemaField + Name = "title", + Partitioning = Partitioning.Invariant.Key, + Properties = new StringFieldProperties { - Name = "title", - Partitioning = Partitioning.Invariant.Key, - Properties = new StringFieldProperties - { - Editor = StringFieldEditor.Input, - IsRequired = true, - IsListField = true, - MaxLength = 100, - MinLength = 0, - Label = "Title" - } - }, - new CreateSchemaField + Editor = StringFieldEditor.Input, + IsRequired = true, + IsListField = true, + MaxLength = 100, + MinLength = 0, + Label = "Title" + } + }, + new CreateSchemaField + { + Name = "slug", + Partitioning = Partitioning.Invariant.Key, + Properties = new StringFieldProperties { - Name = "slug", - Partitioning = Partitioning.Invariant.Key, - Properties = new StringFieldProperties - { - Editor = StringFieldEditor.Slug, - IsRequired = false, - IsListField = true, - MaxLength = 100, - MinLength = 0, - Label = "Slug (Autogenerated)" - }, - IsDisabled = true + Editor = StringFieldEditor.Slug, + IsRequired = false, + IsListField = true, + MaxLength = 100, + MinLength = 0, + Label = "Slug (Autogenerated)" }, - new CreateSchemaField + IsDisabled = true + }, + new CreateSchemaField + { + Name = "text", + Partitioning = Partitioning.Invariant.Key, + Properties = new StringFieldProperties { - Name = "text", - Partitioning = Partitioning.Invariant.Key, - Properties = new StringFieldProperties - { - Editor = StringFieldEditor.RichText, - IsRequired = true, - IsListField = false, - Label = "Text" - } + Editor = StringFieldEditor.RichText, + IsRequired = true, + IsListField = false, + Label = "Text" } } + }, + AppId = appId }; - await publishAsync(command); + await bus.PublishAsync(command); var schemaId = new NamedId(command.SchemaId, command.Name); - await publishAsync(new ConfigureScripts + await bus.PublishAsync(new ConfigureScripts { - SchemaId = schemaId, + SchemaId = schemaId.Id, ScriptCreate = SlugScript, ScriptUpdate = SlugScript }); diff --git a/src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs b/src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs index 103cd9981..cd8e74dd4 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs @@ -7,6 +7,7 @@ using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.State; +using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Assets; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; @@ -74,6 +75,16 @@ namespace Squidex.Domain.Apps.Entities.Assets return this; } + private void RaiseEvent(AppEvent @event) + { + if (@event.AppId == null) + { + @event.AppId = Snapshot.AppId; + } + + RaiseEvent(Envelope.Create(@event)); + } + private void VerifyNotCreated() { if (!string.IsNullOrWhiteSpace(Snapshot.FileName)) diff --git a/src/Squidex.Domain.Apps.Entities/Assets/Commands/AssetAggregateCommand.cs b/src/Squidex.Domain.Apps.Entities/Assets/Commands/AssetCommand.cs similarity index 88% rename from src/Squidex.Domain.Apps.Entities/Assets/Commands/AssetAggregateCommand.cs rename to src/Squidex.Domain.Apps.Entities/Assets/Commands/AssetCommand.cs index efdb41e00..4898243dd 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/Commands/AssetAggregateCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/Commands/AssetCommand.cs @@ -10,7 +10,7 @@ using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities.Assets.Commands { - public abstract class AssetAggregateCommand : AppCommand, IAggregateCommand + public abstract class AssetCommand : SquidexCommand, IAggregateCommand { public Guid AssetId { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/Commands/CreateAsset.cs b/src/Squidex.Domain.Apps.Entities/Assets/Commands/CreateAsset.cs index f7696e14b..f421d9ed8 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/Commands/CreateAsset.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/Commands/CreateAsset.cs @@ -6,12 +6,15 @@ // ========================================================================== using System; +using Squidex.Infrastructure; using Squidex.Infrastructure.Assets; namespace Squidex.Domain.Apps.Entities.Assets.Commands { - public sealed class CreateAsset : AssetAggregateCommand + public sealed class CreateAsset : AssetCommand, IAppCommand { + public NamedId AppId { get; set; } + public AssetFile File { get; set; } public ImageInfo ImageInfo { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/Commands/DeleteAsset.cs b/src/Squidex.Domain.Apps.Entities/Assets/Commands/DeleteAsset.cs index 351333962..4848be209 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/Commands/DeleteAsset.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/Commands/DeleteAsset.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Commands { - public sealed class DeleteAsset : AssetAggregateCommand + public sealed class DeleteAsset : AssetCommand { } } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/Commands/RenameAsset.cs b/src/Squidex.Domain.Apps.Entities/Assets/Commands/RenameAsset.cs index 3dc784b88..65cba7f35 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/Commands/RenameAsset.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/Commands/RenameAsset.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Commands { - public sealed class RenameAsset : AssetAggregateCommand + public sealed class RenameAsset : AssetCommand { public string FileName { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/Commands/UpdateAsset.cs b/src/Squidex.Domain.Apps.Entities/Assets/Commands/UpdateAsset.cs index 8c419ca71..1bb193419 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/Commands/UpdateAsset.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/Commands/UpdateAsset.cs @@ -9,7 +9,7 @@ using Squidex.Infrastructure.Assets; namespace Squidex.Domain.Apps.Entities.Assets.Commands { - public sealed class UpdateAsset : AssetAggregateCommand + public sealed class UpdateAsset : AssetCommand { public AssetFile File { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs b/src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs index c95179f66..c61c52cc0 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs @@ -5,18 +5,21 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using Squidex.Domain.Apps.Core.ValidateContent; +using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Assets { public interface IAssetEntity : IEntity, - IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion, IAssetInfo { + NamedId AppId { get; } + string MimeType { get; } long FileVersion { get; } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs index 8a2f65796..3ed7714a7 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs @@ -10,6 +10,7 @@ using Newtonsoft.Json; using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Assets; +using Squidex.Infrastructure; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Reflection; @@ -18,11 +19,10 @@ namespace Squidex.Domain.Apps.Entities.Assets.State { public class AssetState : DomainObjectState, IAssetEntity, - IAssetInfo, - IUpdateableEntityWithAppRef + IAssetInfo { [JsonProperty] - public Guid AppId { get; set; } + public NamedId AppId { get; set; } [JsonProperty] public string FileName { get; set; } @@ -61,6 +61,8 @@ namespace Squidex.Domain.Apps.Entities.Assets.State SimpleMapper.Map(@event, this); TotalSize += @event.FileSize; + + AppId = @event.AppId; } protected void On(AssetUpdated @event) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Commands/ChangeContentStatus.cs b/src/Squidex.Domain.Apps.Entities/Contents/Commands/ChangeContentStatus.cs index 9e8de0bd2..e855a9aff 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Commands/ChangeContentStatus.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Commands/ChangeContentStatus.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================= +using NodaTime; using Squidex.Domain.Apps.Core.Contents; namespace Squidex.Domain.Apps.Entities.Contents.Commands @@ -12,5 +13,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Commands public sealed class ChangeContentStatus : ContentCommand { public Status Status { get; set; } + + public Instant? DueTime { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentCommand.cs b/src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentCommand.cs index 3629bdfdc..8e15d2a7b 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentCommand.cs @@ -10,7 +10,7 @@ using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities.Contents.Commands { - public abstract class ContentCommand : SchemaCommand, IAggregateCommand + public abstract class ContentCommand : SquidexCommand, IAggregateCommand { public Guid ContentId { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Commands/CreateContent.cs b/src/Squidex.Domain.Apps.Entities/Contents/Commands/CreateContent.cs index 71e0a11e7..2eec65f73 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Commands/CreateContent.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Commands/CreateContent.cs @@ -6,11 +6,16 @@ // ========================================================================== using System; +using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Contents.Commands { - public sealed class CreateContent : ContentDataCommand + public sealed class CreateContent : ContentDataCommand, ISchemaCommand, IAppCommand { + public NamedId AppId { get; set; } + + public NamedId SchemaId { get; set; } + public bool Publish { get; set; } public CreateContent() diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs index 6b10ba6df..7674f1512 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs @@ -109,9 +109,12 @@ namespace Squidex.Domain.Apps.Entities.Contents { GuardContent.CanChangeContentStatus(content.Snapshot.Status, command); - var operationContext = await CreateContext(command, content, () => "Failed to patch content."); + if (!command.DueTime.HasValue) + { + var operationContext = await CreateContext(command, content, () => "Failed to patch content."); - await operationContext.ExecuteScriptAsync(x => x.ScriptChange, command.Status); + await operationContext.ExecuteScriptAsync(x => x.ScriptChange, command.Status); + } content.ChangeStatus(command); }); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs index 0b46b12d7..3cda651fb 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs @@ -8,6 +8,7 @@ using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.State; +using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Contents; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; @@ -45,7 +46,14 @@ namespace Squidex.Domain.Apps.Entities.Contents { VerifyCreatedAndNotDeleted(); - RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged())); + if (command.DueTime.HasValue) + { + RaiseEvent(SimpleMapper.Map(command, new ContentStatusScheduled { DueTime = command.DueTime.Value })); + } + else + { + RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged())); + } return this; } @@ -80,6 +88,21 @@ namespace Squidex.Domain.Apps.Entities.Contents return this; } + private void RaiseEvent(SchemaEvent @event) + { + if (@event.AppId == null) + { + @event.AppId = Snapshot.AppId; + } + + if (@event.SchemaId == null) + { + @event.SchemaId = Snapshot.SchemaId; + } + + RaiseEvent(Envelope.Create(@event)); + } + private void VerifyNotCreated() { if (Snapshot.Data != null) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs index 9c0bc7af7..27e1c1895 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs @@ -18,7 +18,9 @@ namespace Squidex.Domain.Apps.Entities.Contents { public Guid Id { get; set; } - public Guid AppId { get; set; } + public NamedId AppId { get; set; } + + public NamedId SchemaId { get; set; } public long Version { get; set; } @@ -26,14 +28,20 @@ namespace Squidex.Domain.Apps.Entities.Contents public Instant LastModified { get; set; } + public Status Status { get; set; } + + public Status? ScheduledTo { get; set; } + + public Instant? ScheduledAt { get; set; } + + public RefToken ScheduledBy { get; set; } + public RefToken CreatedBy { get; set; } public RefToken LastModifiedBy { get; set; } public NamedContentData Data { get; set; } - public Status Status { get; set; } - public static ContentEntity Create(CreateContent command, EntityCreatedResult result) { var now = SystemClock.Instance.GetCurrentInstant(); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs index c6a8c72b3..8660af246 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs @@ -42,7 +42,16 @@ namespace Squidex.Domain.Apps.Entities.Contents IScriptEngine scriptEngine, Func message) { - var (appEntity, schemaEntity) = await appProvider.GetAppWithSchemaAsync(command.AppId.Id, command.SchemaId.Id); + var a = content.Snapshot.AppId; + var s = content.Snapshot.SchemaId; + + if (command is CreateContent createContent) + { + a = a ?? createContent.AppId; + s = s ?? createContent.SchemaId; + } + + var (appEntity, schemaEntity) = await appProvider.GetAppWithSchemaAsync(a.Id, s.Id); var context = new ContentOperationContext { @@ -75,17 +84,15 @@ namespace Squidex.Domain.Apps.Entities.Contents { var errors = new List(); - var appId = command.AppId.Id; - var ctx = new ValidationContext( (contentIds, schemaId) => { - return QueryContentsAsync(appId, schemaId, contentIds); + return QueryContentsAsync(content.Snapshot.AppId.Id, schemaId, contentIds); }, assetIds => { - return QueryAssetsAsync(appId, assetIds); + return QueryAssetsAsync(content.Snapshot.AppId.Id, assetIds); }); if (partial) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentScheduler.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentScheduler.cs new file mode 100644 index 000000000..23d3c05a9 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentScheduler.cs @@ -0,0 +1,57 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using NodaTime; +using Squidex.Domain.Apps.Entities.Contents.Commands; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.Timers; + +namespace Squidex.Domain.Apps.Entities.Contents +{ + public sealed class ContentScheduler : IRunnable + { + private readonly CompletionTimer timer; + private readonly IContentRepository contentRepository; + private readonly ICommandBus commandBus; + private readonly IClock clock; + + public ContentScheduler( + IContentRepository contentRepository, + ICommandBus commandBus, + IClock clock) + { + Guard.NotNull(contentRepository, nameof(contentRepository)); + Guard.NotNull(commandBus, nameof(commandBus)); + Guard.NotNull(clock, nameof(clock)); + + this.contentRepository = contentRepository; + this.commandBus = commandBus; + this.clock = clock; + + timer = new CompletionTimer(5000, x => PublishAsync()); + } + + public void Run() + { + } + + private Task PublishAsync() + { + var now = clock.GetCurrentInstant(); + + return contentRepository.QueryScheduledWithoutDataAsync(now, content => + { + var command = new ChangeContentStatus { ContentId = content.Id, Status = content.ScheduledTo.Value, Actor = content.ScheduledBy }; + + return commandBus.PublishAsync(command); + }); + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppMutationsGraphType.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppMutationsGraphType.cs index 22c30512c..58ec5d018 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppMutationsGraphType.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppMutationsGraphType.cs @@ -38,13 +38,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types var inputType = new ContentDataGraphInputType(model, schema); AddContentCreate(schemaId, schemaType, schemaName, inputType, contentDataType, contentType); - AddContentUpdate(schemaId, schemaType, schemaName, inputType, resultType); - AddContentPatch(schemaId, schemaType, schemaName, inputType, resultType); - AddContentPublish(schemaId, schemaType, schemaName); - AddContentUnpublish(schemaId, schemaType, schemaName); - AddContentArchive(schemaId, schemaType, schemaName); - AddContentRestore(schemaId, schemaType, schemaName); - AddContentDelete(schemaId, schemaType, schemaName); + AddContentUpdate(schemaType, schemaName, inputType, resultType); + AddContentPatch(schemaType, schemaName, inputType, resultType); + AddContentPublish(schemaType, schemaName); + AddContentUnpublish(schemaType, schemaName); + AddContentArchive(schemaType, schemaName); + AddContentRestore(schemaType, schemaName); + AddContentDelete(schemaType, schemaName); } Description = "The app mutations."; @@ -86,7 +86,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types var contentData = GetContentData(c); - var command = new CreateContent { SchemaId = schemaId, ContentId = Guid.NewGuid(), Data = contentData, Publish = argPublish }; + var command = new CreateContent { SchemaId = schemaId, Data = contentData, Publish = argPublish }; var commandContext = await publish(command); var result = commandContext.Result>(); @@ -98,7 +98,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types }); } - private void AddContentUpdate(NamedId schemaId, string schemaType, string schemaName, ContentDataGraphInputType inputType, IComplexGraphType resultType) + private void AddContentUpdate(string schemaType, string schemaName, ContentDataGraphInputType inputType, IComplexGraphType resultType) { AddField(new FieldType { @@ -133,7 +133,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types var contentId = c.GetArgument("id"); var contentData = GetContentData(c); - var command = new UpdateContent { SchemaId = schemaId, ContentId = contentId, Data = contentData }; + var command = new UpdateContent { ContentId = contentId, Data = contentData }; var commandContext = await publish(command); var result = commandContext.Result(); @@ -144,7 +144,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types }); } - private void AddContentPatch(NamedId schemaId, string schemaType, string schemaName, ContentDataGraphInputType inputType, IComplexGraphType resultType) + private void AddContentPatch(string schemaType, string schemaName, ContentDataGraphInputType inputType, IComplexGraphType resultType) { AddField(new FieldType { @@ -179,7 +179,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types var contentId = c.GetArgument("id"); var contentData = GetContentData(c); - var command = new PatchContent { SchemaId = schemaId, ContentId = contentId, Data = contentData }; + var command = new PatchContent { ContentId = contentId, Data = contentData }; var commandContext = await publish(command); var result = commandContext.Result(); @@ -190,7 +190,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types }); } - private void AddContentPublish(NamedId schemaId, string schemaType, string schemaName) + private void AddContentPublish(string schemaType, string schemaName) { AddField(new FieldType { @@ -201,7 +201,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { var contentId = c.GetArgument("id"); - var command = new ChangeContentStatus { SchemaId = schemaId, ContentId = contentId, Status = Status.Published }; + var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Published }; return publish(command); }), @@ -209,7 +209,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types }); } - private void AddContentUnpublish(NamedId schemaId, string schemaType, string schemaName) + private void AddContentUnpublish(string schemaType, string schemaName) { AddField(new FieldType { @@ -220,7 +220,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { var contentId = c.GetArgument("id"); - var command = new ChangeContentStatus { SchemaId = schemaId, ContentId = contentId, Status = Status.Draft }; + var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Draft }; return publish(command); }), @@ -228,7 +228,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types }); } - private void AddContentArchive(NamedId schemaId, string schemaType, string schemaName) + private void AddContentArchive(string schemaType, string schemaName) { AddField(new FieldType { @@ -239,7 +239,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { var contentId = c.GetArgument("id"); - var command = new ChangeContentStatus { SchemaId = schemaId, ContentId = contentId, Status = Status.Archived }; + var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Archived }; return publish(command); }), @@ -247,7 +247,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types }); } - private void AddContentRestore(NamedId schemaId, string schemaType, string schemaName) + private void AddContentRestore(string schemaType, string schemaName) { AddField(new FieldType { @@ -258,7 +258,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { var contentId = c.GetArgument("id"); - var command = new ChangeContentStatus { SchemaId = schemaId, ContentId = contentId, Status = Status.Draft }; + var command = new ChangeContentStatus { ContentId = contentId, Status = Status.Draft }; return publish(command); }), @@ -266,7 +266,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types }); } - private void AddContentDelete(NamedId schemaId, string schemaType, string schemaName) + private void AddContentDelete(string schemaType, string schemaName) { AddField(new FieldType { @@ -277,7 +277,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { var contentId = c.GetArgument("id"); - var command = new DeleteContent { SchemaId = schemaId, ContentId = contentId }; + var command = new DeleteContent { ContentId = contentId }; return publish(command); }), diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs b/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs index 1ae0abb79..dfd5d8b68 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs @@ -5,6 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; +using NodaTime; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Infrastructure; @@ -62,6 +64,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards { error(new ValidationError($"Content cannot be changed from status {status} to {command.Status}.", nameof(command.Status))); } + + if (command.DueTime.HasValue && command.DueTime.Value < SystemClock.Instance.GetCurrentInstant()) + { + error(new ValidationError("DueTime must be in the future.", nameof(command.DueTime))); + } }); } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/IContentEntity.cs b/src/Squidex.Domain.Apps.Entities/Contents/IContentEntity.cs index 4e9573116..11a33154c 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/IContentEntity.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/IContentEntity.cs @@ -6,19 +6,31 @@ // ========================================================================== // ========================================================================== +using System; +using NodaTime; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Contents { public interface IContentEntity : IEntity, - IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion { + NamedId AppId { get; } + + NamedId SchemaId { get; } + Status Status { get; } + Status? ScheduledTo { get; } + + Instant? ScheduledAt { get; } + + RefToken ScheduledBy { get; } + NamedContentData Data { get; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs b/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs index 509fc42ca..b9aba61be 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.OData.UriParser; +using NodaTime; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Schemas; @@ -27,5 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Repositories Task FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id); Task FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id, long version); + + Task QueryScheduledWithoutDataAsync(Instant now, Func callback); } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs index cdc9884f1..489a03832 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs @@ -7,38 +7,50 @@ using System; using Newtonsoft.Json; +using NodaTime; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Contents; +using Squidex.Infrastructure; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Entities.Contents.State { public class ContentState : DomainObjectState, - IContentEntity, - IUpdateableEntityWithAppRef + IContentEntity { [JsonProperty] - public NamedContentData Data { get; set; } + public NamedId AppId { get; set; } [JsonProperty] - public Guid AppId { get; set; } + public NamedId SchemaId { get; set; } [JsonProperty] - public Guid SchemaId { get; set; } + public NamedContentData Data { get; set; } [JsonProperty] public Status Status { get; set; } + [JsonProperty] + public Status? ScheduledTo { get; set; } + + [JsonProperty] + public Instant? ScheduledAt { get; set; } + + [JsonProperty] + public RefToken ScheduledBy { get; set; } + [JsonProperty] public bool IsDeleted { get; set; } protected void On(ContentCreated @event) { - SchemaId = @event.SchemaId.Id; + SchemaId = @event.SchemaId; Data = @event.Data; + + AppId = @event.AppId; } protected void On(ContentUpdated @event) @@ -46,9 +58,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.State Data = @event.Data; } + protected void On(ContentStatusScheduled @event) + { + ScheduledAt = @event.DueTime; + ScheduledBy = @event.Actor; + ScheduledTo = @event.Status; + } + protected void On(ContentStatusChanged @event) { Status = @event.Status; + + ScheduledAt = null; + ScheduledBy = null; + ScheduledTo = null; } protected void On(ContentDeleted @event) diff --git a/src/Squidex.Domain.Apps.Entities/EntityMapper.cs b/src/Squidex.Domain.Apps.Entities/EntityMapper.cs index 1dd17d847..f990ae781 100644 --- a/src/Squidex.Domain.Apps.Entities/EntityMapper.cs +++ b/src/Squidex.Domain.Apps.Entities/EntityMapper.cs @@ -17,7 +17,6 @@ namespace Squidex.Domain.Apps.Entities public static T Update(this T entity, SquidexEvent @event, EnvelopeHeaders headers, Action updater = null) where T : IEntity { SetId(entity, headers); - SetAppId(entity, @event); SetCreated(entity, headers); SetCreatedBy(entity, @event); SetLastModified(entity, headers); @@ -76,13 +75,5 @@ namespace Squidex.Domain.Apps.Entities withModifiedBy.LastModifiedBy = @event.Actor; } } - - private static void SetAppId(IEntity entity, SquidexEvent @event) - { - if (entity is IUpdateableEntityWithAppRef appEntity && @event is AppEvent appEvent) - { - appEntity.AppId = appEvent.AppId.Id; - } - } } } diff --git a/src/Squidex.Domain.Apps.Entities/AppCommand.cs b/src/Squidex.Domain.Apps.Entities/IAppCommand.cs similarity index 70% rename from src/Squidex.Domain.Apps.Entities/AppCommand.cs rename to src/Squidex.Domain.Apps.Entities/IAppCommand.cs index ba5b6a4e4..6a7bcf31b 100644 --- a/src/Squidex.Domain.Apps.Entities/AppCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/IAppCommand.cs @@ -1,17 +1,18 @@ // ========================================================================== // Squidex Headless CMS // ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) +// Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System; using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities { - public abstract class AppCommand : SquidexCommand + public interface IAppCommand : ICommand { - public NamedId AppId { get; set; } + NamedId AppId { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/IAppProvider.cs b/src/Squidex.Domain.Apps.Entities/IAppProvider.cs index c41da76c9..e246b3cb7 100644 --- a/src/Squidex.Domain.Apps.Entities/IAppProvider.cs +++ b/src/Squidex.Domain.Apps.Entities/IAppProvider.cs @@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities Task GetAppAsync(string appName); - Task GetSchemaAsync(Guid appId, Guid id); + Task GetSchemaAsync(Guid appId, Guid id, bool allowDeleted = false); Task GetSchemaAsync(Guid appId, string name); diff --git a/src/Squidex.Domain.Apps.Entities/SchemaCommand.cs b/src/Squidex.Domain.Apps.Entities/ISchemaCommand.cs similarity index 69% rename from src/Squidex.Domain.Apps.Entities/SchemaCommand.cs rename to src/Squidex.Domain.Apps.Entities/ISchemaCommand.cs index 62db6405d..bd75842d8 100644 --- a/src/Squidex.Domain.Apps.Entities/SchemaCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/ISchemaCommand.cs @@ -1,17 +1,18 @@ // ========================================================================== // Squidex Headless CMS // ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) +// Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System; using Squidex.Infrastructure; +using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities { - public abstract class SchemaCommand : AppCommand + public interface ISchemaCommand : ICommand { - public NamedId SchemaId { get; set; } + NamedId SchemaId { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/Commands/CreateRule.cs b/src/Squidex.Domain.Apps.Entities/Rules/Commands/CreateRule.cs index 667f95dd7..07b49c12b 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/Commands/CreateRule.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/Commands/CreateRule.cs @@ -6,11 +6,14 @@ // ========================================================================== using System; +using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Rules.Commands { - public sealed class CreateRule : RuleEditCommand + public sealed class CreateRule : RuleEditCommand, IAppCommand { + public NamedId AppId { get; set; } + public CreateRule() { RuleId = Guid.NewGuid(); diff --git a/src/Squidex.Domain.Apps.Entities/Rules/Commands/DeleteRule.cs b/src/Squidex.Domain.Apps.Entities/Rules/Commands/DeleteRule.cs index 055730bd9..d895ed5b4 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/Commands/DeleteRule.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/Commands/DeleteRule.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Commands { - public sealed class DeleteRule : RuleAggregateCommand + public sealed class DeleteRule : RuleCommand { } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/Commands/DisableRule.cs b/src/Squidex.Domain.Apps.Entities/Rules/Commands/DisableRule.cs index 0718a7b12..de40cf95a 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/Commands/DisableRule.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/Commands/DisableRule.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Commands { - public sealed class DisableRule : RuleAggregateCommand + public sealed class DisableRule : RuleCommand { } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/Commands/EnableRule.cs b/src/Squidex.Domain.Apps.Entities/Rules/Commands/EnableRule.cs index b35f97d86..62cd528f0 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/Commands/EnableRule.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/Commands/EnableRule.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Commands { - public sealed class EnableRule : RuleAggregateCommand + public sealed class EnableRule : RuleCommand { } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleAggregateCommand.cs b/src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleCommand.cs similarity index 88% rename from src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleAggregateCommand.cs rename to src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleCommand.cs index 41a81ea3e..7d8690c46 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleAggregateCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleCommand.cs @@ -10,7 +10,7 @@ using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities.Rules.Commands { - public abstract class RuleAggregateCommand : AppCommand, IAggregateCommand + public abstract class RuleCommand : SquidexCommand, IAggregateCommand { public Guid RuleId { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleEditCommand.cs b/src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleEditCommand.cs index ba8d77981..e461ff8ac 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleEditCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/Commands/RuleEditCommand.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Core.Rules; namespace Squidex.Domain.Apps.Entities.Rules.Commands { - public abstract class RuleEditCommand : RuleAggregateCommand + public abstract class RuleEditCommand : RuleCommand { public RuleTrigger Trigger { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs b/src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs index 249553206..77f1298df 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Threading.Tasks; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Entities.Rules.Commands; @@ -44,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards }); } - public static Task CanUpdate(UpdateRule command, IAppProvider appProvider) + public static Task CanUpdate(UpdateRule command, Guid appId, IAppProvider appProvider) { Guard.NotNull(command, nameof(command)); @@ -57,7 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards if (command.Trigger != null) { - var errors = await RuleTriggerValidator.ValidateAsync(command.AppId.Id, command.Trigger, appProvider); + var errors = await RuleTriggerValidator.ValidateAsync(appId, command.Trigger, appProvider); errors.Foreach(error); } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/IRuleEntity.cs b/src/Squidex.Domain.Apps.Entities/Rules/IRuleEntity.cs index 2caaa814c..ef69c574f 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/IRuleEntity.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/IRuleEntity.cs @@ -5,17 +5,20 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using Squidex.Domain.Apps.Core.Rules; +using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Rules { public interface IRuleEntity : IEntity, - IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion { + NamedId AppId { get; set; } + Rule RuleDef { get; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/RuleCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Rules/RuleCommandMiddleware.cs index c0ee1cf5b..8a6e43441 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/RuleCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/RuleCommandMiddleware.cs @@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Entities.Rules { return handler.UpdateSyncedAsync(context, async r => { - await GuardRule.CanUpdate(command, appProvider); + await GuardRule.CanUpdate(command, r.Snapshot.AppId.Id, appProvider); r.Update(command); }); diff --git a/src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs b/src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs index bc05ac8bd..03d3dd802 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs @@ -7,6 +7,7 @@ using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.State; +using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Rules; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; @@ -52,6 +53,16 @@ namespace Squidex.Domain.Apps.Entities.Rules RaiseEvent(SimpleMapper.Map(command, new RuleDeleted())); } + private void RaiseEvent(AppEvent @event) + { + if (@event.AppId == null) + { + @event.AppId = Snapshot.AppId; + } + + RaiseEvent(Envelope.Create(@event)); + } + private void VerifyNotCreated() { if (Snapshot.RuleDef != null) diff --git a/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs b/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs index 7b6dd9602..fa87078da 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs @@ -10,18 +10,17 @@ using Newtonsoft.Json; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Rules; +using Squidex.Infrastructure; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Entities.Rules.State { public class RuleState : DomainObjectState, - IRuleEntity, - IEntityWithAppRef, - IUpdateableEntityWithAppRef + IRuleEntity { [JsonProperty] - public Guid AppId { get; set; } + public NamedId AppId { get; set; } [JsonProperty] public Rule RuleDef { get; set; } @@ -32,6 +31,8 @@ namespace Squidex.Domain.Apps.Entities.Rules.State protected void On(RuleCreated @event) { RuleDef = new Rule(@event.Trigger, @event.Action); + + AppId = @event.AppId; } protected void On(RuleUpdated @event) diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/AddField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/AddField.cs index e14d082e8..8856821d6 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/AddField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/AddField.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class AddField : SchemaAggregateCommand + public sealed class AddField : SchemaCommand { public string Name { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigureScripts.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigureScripts.cs index f4fea680a..d850076a6 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigureScripts.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigureScripts.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class ConfigureScripts : SchemaAggregateCommand + public sealed class ConfigureScripts : SchemaCommand { public string ScriptQuery { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs index afbd735f5..923499e79 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs @@ -7,14 +7,16 @@ using System; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure; using SchemaFields = System.Collections.Generic.List; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class CreateSchema : AppCommand, IAggregateCommand + public sealed class CreateSchema : SchemaCommand, IAppCommand { - public Guid SchemaId { get; set; } + public NamedId AppId { get; set; } + + public string Name { get; set; } public SchemaFields Fields { get; set; } @@ -22,13 +24,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands public bool Publish { get; set; } - public string Name { get; set; } - - Guid IAggregateCommand.AggregateId - { - get { return SchemaId; } - } - public CreateSchema() { SchemaId = Guid.NewGuid(); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/DeleteSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/DeleteSchema.cs index d4c3d9bfb..d3b79c454 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/DeleteSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/DeleteSchema.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class DeleteSchema : SchemaAggregateCommand + public sealed class DeleteSchema : SchemaCommand { } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldCommand.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldCommand.cs index 9ddbd0301..5ad93ddf1 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldCommand.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public class FieldCommand : SchemaAggregateCommand + public class FieldCommand : SchemaCommand { public long FieldId { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/PublishSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/PublishSchema.cs index 8bb789b72..c8d68314d 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/PublishSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/PublishSchema.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class PublishSchema : SchemaAggregateCommand + public sealed class PublishSchema : SchemaCommand { } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ReorderFields.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ReorderFields.cs index 068b41162..9afe0346c 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ReorderFields.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ReorderFields.cs @@ -9,7 +9,7 @@ using System.Collections.Generic; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class ReorderFields : SchemaAggregateCommand + public sealed class ReorderFields : SchemaCommand { public List FieldIds { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/AppAggregateCommand.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/SchemaCommand.cs similarity index 77% rename from src/Squidex.Domain.Apps.Entities/AppAggregateCommand.cs rename to src/Squidex.Domain.Apps.Entities/Schemas/Commands/SchemaCommand.cs index a4143215c..49bba3620 100644 --- a/src/Squidex.Domain.Apps.Entities/AppAggregateCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/SchemaCommand.cs @@ -10,11 +10,13 @@ using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities { - public class AppAggregateCommand : AppCommand, IAggregateCommand + public abstract class SchemaCommand : SquidexCommand, IAggregateCommand { + public Guid SchemaId { get; set; } + Guid IAggregateCommand.AggregateId { - get { return AppId.Id; } + get { return SchemaId; } } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UnpublishSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UnpublishSchema.cs index c8c2b722d..31d5c284a 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UnpublishSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UnpublishSchema.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class UnpublishSchema : SchemaAggregateCommand + public sealed class UnpublishSchema : SchemaCommand { } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpdateSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpdateSchema.cs index 329cbc400..579f55bb7 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpdateSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpdateSchema.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class UpdateSchema : SchemaAggregateCommand + public sealed class UpdateSchema : SchemaCommand { public SchemaProperties Properties { get; set; } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs b/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs index da1fd2045..8c341e76e 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs @@ -5,17 +5,20 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Schemas { public interface ISchemaEntity : IEntity, - IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion { + NamedId AppId { get; } + string Name { get; } bool IsPublished { get; } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs index cc081ee28..04c904853 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.State; +using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; @@ -190,6 +191,21 @@ namespace Squidex.Domain.Apps.Entities.Schemas RaiseEvent(@event); } + private void RaiseEvent(SchemaEvent @event) + { + if (@event.SchemaId == null) + { + @event.SchemaId = new NamedId(Snapshot.Id, Snapshot.Name); + } + + if (@event.AppId == null) + { + @event.AppId = Snapshot.AppId; + } + + RaiseEvent(Envelope.Create(@event)); + } + private void VerifyNotCreated() { if (Snapshot.SchemaDef != null) diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs index 5d06deb3b..bc148430b 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs @@ -11,6 +11,7 @@ using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Schemas; +using Squidex.Infrastructure; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Reflection; @@ -18,16 +19,13 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Entities.Schemas.State { public class SchemaState : DomainObjectState, - ISchemaEntity, - IUpdateableEntityWithAppRef, - IUpdateableEntityWithCreatedBy, - IUpdateableEntityWithLastModifiedBy + ISchemaEntity { [JsonProperty] - public string Name { get; set; } + public NamedId AppId { get; set; } [JsonProperty] - public Guid AppId { get; set; } + public string Name { get; set; } [JsonProperty] public int TotalFields { get; set; } = 0; @@ -108,6 +106,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State } SchemaDef = schema; + + AppId = @event.AppId; } protected void On(FieldAdded @event, FieldRegistry registry) diff --git a/src/Squidex.Domain.Apps.Entities/IUpdateableEntityWithAppRef.cs b/src/Squidex.Domain.Apps.Events/Contents/ContentStatusScheduled.cs similarity index 52% rename from src/Squidex.Domain.Apps.Entities/IUpdateableEntityWithAppRef.cs rename to src/Squidex.Domain.Apps.Events/Contents/ContentStatusScheduled.cs index c8ca9da7e..e0d0a5bac 100644 --- a/src/Squidex.Domain.Apps.Entities/IUpdateableEntityWithAppRef.cs +++ b/src/Squidex.Domain.Apps.Events/Contents/ContentStatusScheduled.cs @@ -5,12 +5,17 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; +using NodaTime; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure.EventSourcing; -namespace Squidex.Domain.Apps.Entities +namespace Squidex.Domain.Apps.Events.Contents { - public interface IUpdateableEntityWithAppRef + [EventType(nameof(ContentStatusScheduled))] + public sealed class ContentStatusScheduled : ContentEvent { - Guid AppId { get; set; } + public Status Status { get; set; } + + public Instant DueTime { get; set; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs b/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs index 9fb56d378..6cc59d1b7 100644 --- a/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs @@ -10,6 +10,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using NodaTime; +using NodaTime.Text; using NSwag.Annotations; using Squidex.Areas.Api.Controllers.Contents.Models; using Squidex.Domain.Apps.Core.Contents; @@ -213,11 +215,11 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPut] [Route("content/{app}/{name}/{id}/publish/")] [ApiCosts(1)] - public async Task PublishContent(string name, Guid id) + public async Task PublishContent(string name, Guid id, string dueTime = null) { await contentQuery.FindSchemaAsync(App, name); - var command = new ChangeContentStatus { Status = Status.Published, ContentId = id }; + var command = CreateCommand(id, Status.Published, dueTime); await CommandBus.PublishAsync(command); @@ -228,11 +230,11 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPut] [Route("content/{app}/{name}/{id}/unpublish/")] [ApiCosts(1)] - public async Task UnpublishContent(string name, Guid id) + public async Task UnpublishContent(string name, Guid id, string dueTime = null) { await contentQuery.FindSchemaAsync(App, name); - var command = new ChangeContentStatus { Status = Status.Draft, ContentId = id }; + var command = CreateCommand(id, Status.Draft, dueTime); await CommandBus.PublishAsync(command); @@ -243,11 +245,11 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPut] [Route("content/{app}/{name}/{id}/archive/")] [ApiCosts(1)] - public async Task ArchiveContent(string name, Guid id) + public async Task ArchiveContent(string name, Guid id, string dueTime = null) { await contentQuery.FindSchemaAsync(App, name); - var command = new ChangeContentStatus { Status = Status.Archived, ContentId = id }; + var command = CreateCommand(id, Status.Archived, dueTime); await CommandBus.PublishAsync(command); @@ -258,11 +260,11 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPut] [Route("content/{app}/{name}/{id}/restore/")] [ApiCosts(1)] - public async Task RestoreContent(string name, Guid id) + public async Task RestoreContent(string name, Guid id, string dueTime = null) { await contentQuery.FindSchemaAsync(App, name); - var command = new ChangeContentStatus { Status = Status.Draft, ContentId = id }; + var command = CreateCommand(id, Status.Draft, dueTime); await CommandBus.PublishAsync(command); @@ -283,5 +285,22 @@ namespace Squidex.Areas.Api.Controllers.Contents return NoContent(); } + + private static ChangeContentStatus CreateCommand(Guid id, Status status, string dueTime) + { + Instant? dt = null; + + if (!string.IsNullOrWhiteSpace(dueTime)) + { + var parseResult = InstantPattern.General.Parse(dueTime); + + if (parseResult.Success) + { + dt = parseResult.Value; + } + } + + return new ChangeContentStatus { Status = status, ContentId = id, DueTime = dt }; + } } } diff --git a/src/Squidex/Areas/Api/Controllers/Content/Models/ContentDto.cs b/src/Squidex/Areas/Api/Controllers/Content/Models/ContentDto.cs index eee19d42f..2be029606 100644 --- a/src/Squidex/Areas/Api/Controllers/Content/Models/ContentDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Content/Models/ContentDto.cs @@ -40,6 +40,21 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models [Required] public object Data { get; set; } + /// + /// The scheduled status. + /// + public Status? ScheduledTo { get; set; } + + /// + /// The scheduled date. + /// + public Instant? ScheduledAt { get; set; } + + /// + /// The user that has scheduled the content. + /// + public RefToken ScheduledBy { get; set; } + /// /// The date and time when the content item has been created. /// diff --git a/src/Squidex/Config/Authentication/MicrosoftHandler.cs b/src/Squidex/Config/Authentication/MicrosoftHandler.cs index c308814af..168995ad9 100644 --- a/src/Squidex/Config/Authentication/MicrosoftHandler.cs +++ b/src/Squidex/Config/Authentication/MicrosoftHandler.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.OAuth; using Squidex.Shared.Identity; diff --git a/src/Squidex/Config/Domain/ReadServices.cs b/src/Squidex/Config/Domain/ReadServices.cs index cd3961628..16f925fff 100644 --- a/src/Squidex/Config/Domain/ReadServices.cs +++ b/src/Squidex/Config/Domain/ReadServices.cs @@ -46,6 +46,8 @@ namespace Squidex.Config.Domain .As(); services.AddSingletonAs() .As(); + services.AddSingletonAs() + .As(); } var exposeSourceUrl = config.GetOptionalValue("assetStore:exposeSourceUrl", true); diff --git a/src/Squidex/Config/Domain/SerializationServices.cs b/src/Squidex/Config/Domain/SerializationServices.cs index c8f3cbb12..1b8f75a26 100644 --- a/src/Squidex/Config/Domain/SerializationServices.cs +++ b/src/Squidex/Config/Domain/SerializationServices.cs @@ -28,10 +28,10 @@ namespace Squidex.Config.Domain { private static readonly TypeNameRegistry TypeNameRegistry = new TypeNameRegistry() - .MapUnmapped(typeof(Migration01_FromCqrs).Assembly) .MapUnmapped(typeof(SquidexCoreModel).Assembly) .MapUnmapped(typeof(SquidexEvents).Assembly) - .MapUnmapped(typeof(SquidexInfrastructure).Assembly); + .MapUnmapped(typeof(SquidexInfrastructure).Assembly) + .MapUnmapped(typeof(SquidexMigrations).Assembly); private static readonly FieldRegistry FieldRegistry = new FieldRegistry(TypeNameRegistry); diff --git a/src/Squidex/Config/Domain/WriteServices.cs b/src/Squidex/Config/Domain/WriteServices.cs index 7c890b235..2a59c47fa 100644 --- a/src/Squidex/Config/Domain/WriteServices.cs +++ b/src/Squidex/Config/Domain/WriteServices.cs @@ -79,6 +79,9 @@ namespace Squidex.Config.Domain services.AddTransientAs() .As(); + services.AddTransientAs() + .As(); + services.AddTransientAs() .AsSelf(); diff --git a/src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs index 25cbebe80..8ee2b62da 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs @@ -25,6 +25,11 @@ namespace Squidex.Pipeline.CommandMiddlewares public async Task HandleAsync(CommandContext context, Func next) { + if (httpContextAccessor.HttpContext == null) + { + return; + } + var headers = httpContextAccessor.HttpContext.Request.Headers; var headerMatch = headers["If-Match"].ToString(); diff --git a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddleware.cs index ce1b72ced..f8e16d354 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddleware.cs @@ -27,6 +27,11 @@ namespace Squidex.Pipeline.CommandMiddlewares public Task HandleAsync(CommandContext context, Func next) { + if (httpContextAccessor.HttpContext == null) + { + return next(); + } + if (context.Command is SquidexCommand squidexCommand) { if (squidexCommand.Actor == null) diff --git a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs index 27d93b073..1a063c723 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs @@ -25,7 +25,12 @@ namespace Squidex.Pipeline.CommandMiddlewares public Task HandleAsync(CommandContext context, Func next) { - if (context.Command is AppCommand appCommand && appCommand.AppId == null) + if (httpContextAccessor.HttpContext == null) + { + return next(); + } + + if (context.Command is IAppCommand appCommand && appCommand.AppId == null) { var appFeature = httpContextAccessor.HttpContext.Features.Get(); diff --git a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs index 15a20857f..495131855 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs @@ -29,8 +29,35 @@ namespace Squidex.Pipeline.CommandMiddlewares public async Task HandleAsync(CommandContext context, Func next) { - if (context.Command is SchemaCommand schemaCommand && schemaCommand.SchemaId == null) + if (actionContextAccessor.ActionContext == null) { + await next(); + } + + if (context.Command is ISchemaCommand schemaCommand && schemaCommand.SchemaId == null) + { + NamedId appId = null; + + if (context.Command is IAppCommand appCommand) + { + appId = appCommand.AppId; + } + + if (appId == null) + { + var appFeature = actionContextAccessor.ActionContext.HttpContext.Features.Get(); + + if (appFeature != null && appFeature.App != null) + { + appId = new NamedId(appFeature.App.Id, appFeature.App.Name); + } + } + + if (appId == null) + { + return; + } + var routeValues = actionContextAccessor.ActionContext.RouteData.Values; if (routeValues.ContainsKey("name")) @@ -41,11 +68,11 @@ namespace Squidex.Pipeline.CommandMiddlewares if (Guid.TryParse(schemaName, out var id)) { - schema = await appProvider.GetSchemaAsync(schemaCommand.AppId.Id, id); + schema = await appProvider.GetSchemaAsync(appId.Id, id); } else { - schema = await appProvider.GetSchemaAsync(schemaCommand.AppId.Id, schemaName); + schema = await appProvider.GetSchemaAsync(appId.Id, schemaName); } if (schema == null) diff --git a/src/Squidex/app/features/content/pages/content/content-page.component.ts b/src/Squidex/app/features/content/pages/content/content-page.component.ts index 0b6467631..9ba93a9e9 100644 --- a/src/Squidex/app/features/content/pages/content/content-page.component.ts +++ b/src/Squidex/app/features/content/pages/content/content-page.component.ts @@ -12,9 +12,8 @@ import { Observable, Subscription } from 'rxjs'; import { ContentCreated, - ContentPublished, ContentRemoved, - ContentUnpublished, + ContentStatusChanged, ContentUpdated, ContentVersionSelected } from './../messages'; @@ -39,8 +38,7 @@ import { ] }) export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, OnInit { - private contentPublishedSubscription: Subscription; - private contentUnpublishedSubscription: Subscription; + private contentStatusChangedSubscription: Subscription; private contentDeletedSubscription: Subscription; private contentVersionSelectedSubscription: Subscription; @@ -63,8 +61,7 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, public ngOnDestroy() { this.contentVersionSelectedSubscription.unsubscribe(); - this.contentUnpublishedSubscription.unsubscribe(); - this.contentPublishedSubscription.unsubscribe(); + this.contentStatusChangedSubscription.unsubscribe(); this.contentDeletedSubscription.unsubscribe(); } @@ -75,27 +72,25 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, this.loadVersion(message.version); }); - this.contentPublishedSubscription = - this.ctx.bus.of(ContentPublished) - .subscribe(message => { - if (this.content && message.content.id === this.content.id) { - this.content = this.content.publish(message.content.lastModifiedBy, message.content.version, message.content.lastModified); - } - }); - - this.contentUnpublishedSubscription = - this.ctx.bus.of(ContentUnpublished) + this.contentDeletedSubscription = + this.ctx.bus.of(ContentRemoved) .subscribe(message => { if (this.content && message.content.id === this.content.id) { - this.content = this.content.unpublish(message.content.lastModifiedBy, message.content.version, message.content.lastModified); + this.router.navigate(['../'], { relativeTo: this.ctx.route }); } }); - this.contentDeletedSubscription = - this.ctx.bus.of(ContentRemoved) + this.contentStatusChangedSubscription = + this.ctx.bus.of(ContentStatusChanged) .subscribe(message => { if (this.content && message.content.id === this.content.id) { - this.router.navigate(['../'], { relativeTo: this.ctx.route }); + this.content = + this.content.changeStatus( + message.content.scheduledTo || message.content.status, + message.content.scheduledAt, + message.content.lastModifiedBy, + message.content.version, + message.content.lastModified); } }); diff --git a/src/Squidex/app/features/content/pages/contents/contents-page.component.html b/src/Squidex/app/features/content/pages/contents/contents-page.component.html index 9589b5b6b..f00ddc40a 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-page.component.html +++ b/src/Squidex/app/features/content/pages/contents/contents-page.component.html @@ -28,7 +28,7 @@ Search for content using full text search over all fields and languages! -