Browse Source

Scheduler improved.

pull/247/head
Sebastian Stehle 8 years ago
parent
commit
3e980ddd6f
  1. 16
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs
  2. 28
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  3. 6
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs
  4. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs
  5. 3
      src/Squidex.Domain.Apps.Entities/Contents/Commands/ChangeContentStatus.cs
  6. 16
      src/Squidex.Domain.Apps.Entities/Contents/Commands/PublishContentAt.cs
  7. 13
      src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs
  8. 14
      src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs
  9. 10
      src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs
  10. 9
      src/Squidex.Domain.Apps.Entities/Contents/ContentScheduler.cs
  11. 13
      src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs
  12. 6
      src/Squidex.Domain.Apps.Entities/Contents/IContentEntity.cs
  13. 2
      src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs
  14. 19
      src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs
  15. 9
      src/Squidex.Domain.Apps.Events/Contents/ContentStatusScheduled.cs
  16. 35
      src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs

16
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs

@ -35,12 +35,12 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
[BsonRequired] [BsonRequired]
[BsonElement("ai")] [BsonElement("ai")]
[BsonRepresentation(BsonType.String)] [BsonRepresentation(BsonType.String)]
public Guid IdxAppId { get; set; } public Guid AppIdId { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement("si")] [BsonElement("si")]
[BsonRepresentation(BsonType.String)] [BsonRepresentation(BsonType.String)]
public Guid IdxSchemaId { get; set; } public Guid SchemaIdId { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement("rf")] [BsonElement("rf")]
@ -71,12 +71,16 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
public NamedId<Guid> SchemaId { get; set; } public NamedId<Guid> SchemaId { get; set; }
[BsonIgnoreIfNull] [BsonIgnoreIfNull]
[BsonElement("pa")] [BsonElement("st")]
public Instant? PublishAt { get; set; } public Status? ScheduledTo { get; set; }
[BsonIgnoreIfNull]
[BsonElement("sa")]
public Instant? ScheduledAt { get; set; }
[BsonIgnoreIfNull] [BsonIgnoreIfNull]
[BsonElement("pb")] [BsonElement("sb")]
public RefToken PublishAtBy { get; set; } public RefToken ScheduledBy { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement("ct")] [BsonElement("ct")]

28
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs

@ -52,6 +52,10 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{ {
await collection.Indexes.TryDropOneAsync("si_1_st_1_dl_1_dt_text"); 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( await archiveCollection.Indexes.CreateOneAsync(
Index Index
.Ascending(x => x.Id) .Ascending(x => x.Id)
@ -60,13 +64,13 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
await collection.Indexes.CreateOneAsync( await collection.Indexes.CreateOneAsync(
Index Index
.Text(x => x.DataText) .Text(x => x.DataText)
.Ascending(x => x.IdxSchemaId) .Ascending(x => x.SchemaIdId)
.Ascending(x => x.Status) .Ascending(x => x.Status)
.Ascending(x => x.IsDeleted)); .Ascending(x => x.IsDeleted));
await collection.Indexes.CreateOneAsync( await collection.Indexes.CreateOneAsync(
Index Index
.Ascending(x => x.IdxSchemaId) .Ascending(x => x.SchemaIdId)
.Ascending(x => x.Id) .Ascending(x => x.Id)
.Ascending(x => x.IsDeleted) .Ascending(x => x.IsDeleted)
.Ascending(x => x.Status)); .Ascending(x => x.Status));
@ -122,7 +126,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet<Guid> ids) public async Task<IResultList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet<Guid> ids)
{ {
var find = Collection.Find(x => x.IdxSchemaId == 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 contentItems = find.ToListAsync();
var contentCount = find.CountAsync(); var contentCount = find.CountAsync();
@ -140,7 +144,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
public async Task<IReadOnlyList<Guid>> QueryNotFoundAsync(Guid appId, Guid schemaId, IList<Guid> ids) public async Task<IReadOnlyList<Guid>> QueryNotFoundAsync(Guid appId, Guid schemaId, IList<Guid> ids)
{ {
var contentEntities = var contentEntities =
await Collection.Find(x => x.IdxSchemaId == 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(); .ToListAsync();
return ids.Except(contentEntities.Select(x => Guid.Parse(x["id"].AsString))).ToList(); return ids.Except(contentEntities.Select(x => Guid.Parse(x["id"].AsString))).ToList();
@ -160,7 +164,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
public async Task<IContentEntity> FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id) public async Task<IContentEntity> FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id)
{ {
var contentEntity = var contentEntity =
await Collection.Find(x => x.IdxSchemaId == schema.Id && x.Id == id && x.IsDeleted == false) await Collection.Find(x => x.SchemaIdId == schema.Id && x.Id == id && x.IsDeleted == false)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
contentEntity?.ParseData(schema.SchemaDef); contentEntity?.ParseData(schema.SchemaDef);
@ -168,16 +172,20 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
return contentEntity; return contentEntity;
} }
public Task QueryScheduledWithoutDataAsync(Instant now, Func<IContentEntity, Task> callback)
{
return Collection.Find(x => x.ScheduledAt < now && x.IsDeleted == false)
.ForEachAsync(c =>
{
callback(c);
});
}
public override async Task ClearAsync() public override async Task ClearAsync()
{ {
await Database.DropCollectionAsync("States_Contents_Archive"); await Database.DropCollectionAsync("States_Contents_Archive");
await base.ClearAsync(); await base.ClearAsync();
} }
public Task QueryContentToPublishAsync(Instant now, Func<IContentEntity, Task> callback)
{
throw new NotSupportedException();
}
} }
} }

6
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs

@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
if (contentEntity != null) if (contentEntity != null)
{ {
var schema = await GetSchemaAsync(contentEntity.IdxAppId, contentEntity.IdxSchemaId); var schema = await GetSchemaAsync(contentEntity.AppIdId, contentEntity.SchemaIdId);
contentEntity?.ParseData(schema.SchemaDef); contentEntity?.ParseData(schema.SchemaDef);
@ -53,8 +53,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
var document = SimpleMapper.Map(value, new MongoContentEntity var document = SimpleMapper.Map(value, new MongoContentEntity
{ {
IdxAppId = value.AppId.Id, AppIdId = value.AppId.Id,
IdxSchemaId = value.SchemaId.Id, SchemaIdId = value.SchemaId.Id,
IsDeleted = value.IsDeleted, IsDeleted = value.IsDeleted,
DocumentId = key.ToString(), DocumentId = key.ToString(),
DataText = idData?.ToFullText(), DataText = idData?.ToFullText(),

2
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<FilterDefinition<MongoContentEntity>> var filters = new List<FilterDefinition<MongoContentEntity>>
{ {
Filter.Eq(x => x.IdxSchemaId, schemaId), Filter.Eq(x => x.SchemaIdId, schemaId),
Filter.In(x => x.Status, status), Filter.In(x => x.Status, status),
Filter.Eq(x => x.IsDeleted, false) Filter.Eq(x => x.IsDeleted, false)
}; };

3
src/Squidex.Domain.Apps.Entities/Contents/Commands/ChangeContentStatus.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================= // =========================================================================
using NodaTime;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Entities.Contents.Commands namespace Squidex.Domain.Apps.Entities.Contents.Commands
@ -12,5 +13,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Commands
public sealed class ChangeContentStatus : ContentCommand public sealed class ChangeContentStatus : ContentCommand
{ {
public Status Status { get; set; } public Status Status { get; set; }
public Instant? DueDate { get; set; }
} }
} }

16
src/Squidex.Domain.Apps.Entities/Contents/Commands/PublishContentAt.cs

@ -1,16 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Entities.Contents.Commands
{
public sealed class PublishContentAt : ContentDataCommand
{
public DateTimeOffset PublishAt { get; set; }
}
}

13
src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs

@ -109,9 +109,12 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
GuardContent.CanChangeContentStatus(content.Snapshot.Status, command); GuardContent.CanChangeContentStatus(content.Snapshot.Status, command);
if (!command.DueDate.HasValue)
{
var operationContext = await CreateContext(command, content, () => "Failed to patch content."); 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); content.ChangeStatus(command);
}); });
@ -131,16 +134,6 @@ namespace Squidex.Domain.Apps.Entities.Contents
}); });
} }
protected Task On(PublishContentAt command, CommandContext context)
{
return handler.UpdateAsync<ContentDomainObject>(context, content =>
{
GuardContent.CanPublishAt(command);
content.PublishAt(command);
});
}
public async Task HandleAsync(CommandContext context, Func<Task> next) public async Task HandleAsync(CommandContext context, Func<Task> next)
{ {
await this.DispatchActionAsync(context.Command, context); await this.DispatchActionAsync(context.Command, context);

14
src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs

@ -46,16 +46,14 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged())); if (command.DueDate.HasValue)
{
return this; RaiseEvent(SimpleMapper.Map(command, new ContentStatusScheduled()));
} }
else
public ContentDomainObject PublishAt(PublishContentAt command)
{ {
VerifyCreatedAndNotDeleted(); RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged()));
}
RaiseEvent(SimpleMapper.Map(command, new ContentPublishScheduled()));
return this; return this;
} }

10
src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs

@ -28,9 +28,13 @@ namespace Squidex.Domain.Apps.Entities.Contents
public Instant LastModified { get; set; } public Instant LastModified { get; set; }
public Instant? PublishAt { get; set; } public Status Status { get; set; }
public Status? ScheduledTo { get; set; }
public Instant? ScheduledAt { get; set; }
public RefToken PublishAtBy { get; set; } public RefToken ScheduledBy { get; set; }
public RefToken CreatedBy { get; set; } public RefToken CreatedBy { get; set; }
@ -38,8 +42,6 @@ namespace Squidex.Domain.Apps.Entities.Contents
public NamedContentData Data { get; set; } public NamedContentData Data { get; set; }
public Status Status { get; set; }
public static ContentEntity Create(CreateContent command, EntityCreatedResult<NamedContentData> result) public static ContentEntity Create(CreateContent command, EntityCreatedResult<NamedContentData> result)
{ {
var now = SystemClock.Instance.GetCurrentInstant(); var now = SystemClock.Instance.GetCurrentInstant();

9
src/Squidex.Domain.Apps.Entities/Contents/ContentPublisher.cs → src/Squidex.Domain.Apps.Entities/Contents/ContentScheduler.cs

@ -7,7 +7,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -16,14 +15,14 @@ using Squidex.Infrastructure.Timers;
namespace Squidex.Domain.Apps.Entities.Contents namespace Squidex.Domain.Apps.Entities.Contents
{ {
public sealed class ContentPublisher : IRunnable public sealed class ContentScheduler : IRunnable
{ {
private readonly CompletionTimer timer; private readonly CompletionTimer timer;
private readonly IContentRepository contentRepository; private readonly IContentRepository contentRepository;
private readonly ICommandBus commandBus; private readonly ICommandBus commandBus;
private readonly IClock clock; private readonly IClock clock;
public ContentPublisher( public ContentScheduler(
IContentRepository contentRepository, IContentRepository contentRepository,
ICommandBus commandBus, ICommandBus commandBus,
IClock clock) IClock clock)
@ -47,9 +46,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
{ {
var now = clock.GetCurrentInstant(); var now = clock.GetCurrentInstant();
return contentRepository.QueryContentToPublishAsync(now, content => return contentRepository.QueryScheduledWithoutDataAsync(now, content =>
{ {
var command = new ChangeContentStatus { ContentId = content.Id, Status = Status.Published, Actor = content.PublishAtBy }; var command = new ChangeContentStatus { ContentId = content.Id, Status = content.ScheduledTo.Value, Actor = content.ScheduledBy };
return commandBus.PublishAsync(command); return commandBus.PublishAsync(command);
}); });

13
src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs

@ -6,6 +6,7 @@
// ========================================================================== // ==========================================================================
using System; using System;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -63,18 +64,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
{ {
error(new ValidationError($"Content cannot be changed from status {status} to {command.Status}.", nameof(command.Status))); error(new ValidationError($"Content cannot be changed from status {status} to {command.Status}.", nameof(command.Status)));
} }
});
}
public static void CanPublishAt(PublishContentAt command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot schedule content tol publish.", error => if (command.DueDate.HasValue && command.DueDate.Value < SystemClock.Instance.GetCurrentInstant())
{
if (command.PublishAt < DateTime.UtcNow)
{ {
error(new ValidationError("Date must be in the future.", nameof(command.PublishAt))); error(new ValidationError("DueDate must be in the future.", nameof(command.DueDate)));
} }
}); });
} }

6
src/Squidex.Domain.Apps.Entities/Contents/IContentEntity.cs

@ -25,9 +25,11 @@ namespace Squidex.Domain.Apps.Entities.Contents
Status Status { get; } Status Status { get; }
Instant? PublishAt { get; } Status? ScheduledTo { get; }
RefToken PublishAtBy { get; } Instant? ScheduledAt { get; }
RefToken ScheduledBy { get; }
NamedContentData Data { get; } NamedContentData Data { get; }
} }

2
src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs

@ -29,6 +29,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.Repositories
Task<IContentEntity> FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id, long version); Task<IContentEntity> FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id, long version);
Task QueryContentToPublishAsync(Instant now, Func<IContentEntity, Task> callback); Task QueryScheduledWithoutDataAsync(Instant now, Func<IContentEntity, Task> callback);
} }
} }

19
src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs

@ -33,10 +33,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.State
public Status Status { get; set; } public Status Status { get; set; }
[JsonProperty] [JsonProperty]
public RefToken PublishAtBy { get; set; } public Status? ScheduledTo { get; set; }
[JsonProperty] [JsonProperty]
public Instant? PublishAt { get; set; } public Instant? ScheduledAt { get; set; }
[JsonProperty]
public RefToken ScheduledBy { get; set; }
[JsonProperty] [JsonProperty]
public bool IsDeleted { get; set; } public bool IsDeleted { get; set; }
@ -55,18 +58,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.State
Data = @event.Data; Data = @event.Data;
} }
protected void On(ContentPublishScheduled @event) protected void On(ContentStatusScheduled @event)
{ {
PublishAt = @event.PublishAt; ScheduledAt = @event.DueTime;
PublishAtBy = @event.Actor; ScheduledBy = @event.Actor;
ScheduledTo = @event.Status;
} }
protected void On(ContentStatusChanged @event) protected void On(ContentStatusChanged @event)
{ {
Status = @event.Status; Status = @event.Status;
PublishAt = null; ScheduledAt = null;
PublishAtBy = null; ScheduledBy = null;
ScheduledTo = null;
} }
protected void On(ContentDeleted @event) protected void On(ContentDeleted @event)

9
src/Squidex.Domain.Apps.Events/Contents/ContentPublishScheduled.cs → src/Squidex.Domain.Apps.Events/Contents/ContentStatusScheduled.cs

@ -6,13 +6,16 @@
// ========================================================================== // ==========================================================================
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events.Contents namespace Squidex.Domain.Apps.Events.Contents
{ {
[EventType(nameof(ContentPublishScheduled))] [EventType(nameof(ContentStatusScheduled))]
public sealed class ContentPublishScheduled : ContentEvent public sealed class ContentStatusScheduled : ContentEvent
{ {
public Instant PublishAt { get; set; } public Status Status { get; set; }
public Instant DueTime { get; set; }
} }
} }

35
src/Squidex/Areas/Api/Controllers/Content/ContentsController.cs

@ -10,6 +10,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using NodaTime;
using NodaTime.Text;
using NSwag.Annotations; using NSwag.Annotations;
using Squidex.Areas.Api.Controllers.Contents.Models; using Squidex.Areas.Api.Controllers.Contents.Models;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
@ -213,11 +215,11 @@ namespace Squidex.Areas.Api.Controllers.Contents
[HttpPut] [HttpPut]
[Route("content/{app}/{name}/{id}/publish/")] [Route("content/{app}/{name}/{id}/publish/")]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PublishContent(string name, Guid id) public async Task<IActionResult> PublishContent(string name, Guid id, string dueDate = null)
{ {
await contentQuery.FindSchemaAsync(App, name); await contentQuery.FindSchemaAsync(App, name);
var command = new ChangeContentStatus { Status = Status.Published, ContentId = id }; var command = CreateCommand(id, Status.Published, dueDate);
await CommandBus.PublishAsync(command); await CommandBus.PublishAsync(command);
@ -228,11 +230,11 @@ namespace Squidex.Areas.Api.Controllers.Contents
[HttpPut] [HttpPut]
[Route("content/{app}/{name}/{id}/unpublish/")] [Route("content/{app}/{name}/{id}/unpublish/")]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> UnpublishContent(string name, Guid id) public async Task<IActionResult> UnpublishContent(string name, Guid id, string dueDate = null)
{ {
await contentQuery.FindSchemaAsync(App, name); await contentQuery.FindSchemaAsync(App, name);
var command = new ChangeContentStatus { Status = Status.Draft, ContentId = id }; var command = CreateCommand(id, Status.Draft, dueDate);
await CommandBus.PublishAsync(command); await CommandBus.PublishAsync(command);
@ -243,11 +245,11 @@ namespace Squidex.Areas.Api.Controllers.Contents
[HttpPut] [HttpPut]
[Route("content/{app}/{name}/{id}/archive/")] [Route("content/{app}/{name}/{id}/archive/")]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> ArchiveContent(string name, Guid id) public async Task<IActionResult> ArchiveContent(string name, Guid id, string dueDate = null)
{ {
await contentQuery.FindSchemaAsync(App, name); await contentQuery.FindSchemaAsync(App, name);
var command = new ChangeContentStatus { Status = Status.Archived, ContentId = id }; var command = CreateCommand(id, Status.Archived, dueDate);
await CommandBus.PublishAsync(command); await CommandBus.PublishAsync(command);
@ -258,11 +260,11 @@ namespace Squidex.Areas.Api.Controllers.Contents
[HttpPut] [HttpPut]
[Route("content/{app}/{name}/{id}/restore/")] [Route("content/{app}/{name}/{id}/restore/")]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> RestoreContent(string name, Guid id) public async Task<IActionResult> RestoreContent(string name, Guid id, string dueDate = null)
{ {
await contentQuery.FindSchemaAsync(App, name); await contentQuery.FindSchemaAsync(App, name);
var command = new ChangeContentStatus { Status = Status.Draft, ContentId = id }; var command = CreateCommand(id, Status.Draft, dueDate);
await CommandBus.PublishAsync(command); await CommandBus.PublishAsync(command);
@ -283,5 +285,22 @@ namespace Squidex.Areas.Api.Controllers.Contents
return NoContent(); return NoContent();
} }
private static ChangeContentStatus CreateCommand(Guid id, Status status, string dueDate)
{
Instant? dt = null;
if (string.IsNullOrWhiteSpace(dueDate))
{
var parseResult = InstantPattern.General.Parse(dueDate);
if (!parseResult.Success)
{
dt = parseResult.Value;
}
}
return new ChangeContentStatus { Status = status, ContentId = id, DueDate = dt };
}
} }
} }

Loading…
Cancel
Save