diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs index 537ff3331..0230002fd 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs @@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents } else { - return contentsPublished.FindContentAsync(app, schema, id); + return contentsDraft.FindContentAsync(app, schema, id); } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs index 1758b1e98..b77d09195 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs @@ -197,7 +197,7 @@ namespace Squidex.Domain.Apps.Entities.Contents public void ConfirmChanges(ChangeContentStatus command) { - RaiseEvent(SimpleMapper.Map(command, new ContentChangesConfirmed())); + RaiseEvent(SimpleMapper.Map(command, new ContentChangesPublished())); } public void DiscardChanges(DiscardChanges command) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentHistoryEventsCreator.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentHistoryEventsCreator.cs index 848f70394..5778497e1 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentHistoryEventsCreator.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentHistoryEventsCreator.cs @@ -31,6 +31,9 @@ namespace Squidex.Domain.Apps.Entities.Contents AddEventMessage( "discarded pending changes of {[Schema]} content."); + AddEventMessage( + "published changes of {[Schema]} content."); + AddEventMessage( "proposed update for {[Schema]} content."); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs index 11795ca8f..3ce6941ca 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs @@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Contents var isFrontendClient = IsFrontendClient(user); var isVersioned = version > EtagVersion.Empty; - var parsedStatus = isFrontendClient ? new[] { Status.Published } : null; + var parsedStatus = isFrontendClient ? null : new[] { Status.Published }; var content = isVersioned ? @@ -156,7 +156,10 @@ namespace Squidex.Domain.Apps.Entities.Contents result.Data = result.Data.ToApiModel(schema.SchemaDef, app.LanguagesConfig, isFrontendClient, isTypeChecking); } - result.DataDraft = result.DataDraft.ToApiModel(schema.SchemaDef, app.LanguagesConfig, isFrontendClient, isTypeChecking); + if (result.DataDraft != null) + { + result.DataDraft = result.DataDraft.ToApiModel(schema.SchemaDef, app.LanguagesConfig, isFrontendClient, isTypeChecking); + } yield return result; } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerGrain.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerGrain.cs index 090fec75d..17511bab6 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerGrain.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Threading; using System.Threading.Tasks; using NodaTime; using Orleans; @@ -23,6 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents private readonly Lazy contentRepository; private readonly Lazy commandBus; private readonly IClock clock; + private TaskScheduler scheduler; public ContentSchedulerGrain( Lazy contentRepository, @@ -40,6 +42,8 @@ namespace Squidex.Domain.Apps.Entities.Contents public override Task OnActivateAsync() { + scheduler = TaskScheduler.Current; + DelayDeactivation(TimeSpan.FromDays(1)); RegisterOrUpdateReminder("Default", TimeSpan.Zero, TimeSpan.FromMinutes(10)); @@ -59,9 +63,12 @@ namespace Squidex.Domain.Apps.Entities.Contents return contentRepository.Value.QueryScheduledWithoutDataAsync(now, content => { - var command = new ChangeContentStatus { ContentId = content.Id, Status = content.ScheduledTo.Value, Actor = content.ScheduledBy }; + return Dispatch(() => + { + var command = new ChangeContentStatus { ContentId = content.Id, Status = content.ScheduledTo.Value, Actor = content.ScheduledBy }; - return commandBus.Value.PublishAsync(command); + return commandBus.Value.PublishAsync(command); + }); }); } @@ -69,5 +76,10 @@ namespace Squidex.Domain.Apps.Entities.Contents { return TaskHelper.Done; } + + private Task Dispatch(Func task) + { + return Task.Factory.StartNew(() => task(), CancellationToken.None, TaskCreationOptions.None, scheduler ?? TaskScheduler.Default).Unwrap(); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs index ceb88ba75..9ec3a653e 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs @@ -59,8 +59,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.State protected void On(ContentUpdated @event) { - Data = @event.Data; DataDraft = @event.Data; + + if (Data != null) + { + Data = @event.Data; + } } protected void On(ContentUpdateProposed @event) @@ -77,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.State IsPending = false; } - protected void On(ContentChangesConfirmed @event) + protected void On(ContentChangesPublished @event) { Data = DataDraft; @@ -102,8 +106,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.State if (@event.Status == Status.Published) { Data = DataDraft; - - IsPending = false; } } diff --git a/src/Squidex.Domain.Apps.Entities/SquidexCommand.cs b/src/Squidex.Domain.Apps.Entities/SquidexCommand.cs index a2c642e16..b9e15f41e 100644 --- a/src/Squidex.Domain.Apps.Entities/SquidexCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/SquidexCommand.cs @@ -17,6 +17,6 @@ namespace Squidex.Domain.Apps.Entities public ClaimsPrincipal User { get; set; } - public long ExpectedVersion { get; set; } + public long ExpectedVersion { get; set; } = EtagVersion.Any; } } diff --git a/src/Squidex.Domain.Apps.Events/Contents/ContentChangesConfirmed.cs b/src/Squidex.Domain.Apps.Events/Contents/ContentChangesPublished.cs similarity index 81% rename from src/Squidex.Domain.Apps.Events/Contents/ContentChangesConfirmed.cs rename to src/Squidex.Domain.Apps.Events/Contents/ContentChangesPublished.cs index 6e5554cb9..2235161a4 100644 --- a/src/Squidex.Domain.Apps.Events/Contents/ContentChangesConfirmed.cs +++ b/src/Squidex.Domain.Apps.Events/Contents/ContentChangesPublished.cs @@ -9,8 +9,8 @@ using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Events.Contents { - [EventType(nameof(ContentChangesConfirmed))] - public sealed class ContentChangesConfirmed : ContentEvent + [EventType(nameof(ContentChangesPublished))] + public sealed class ContentChangesPublished : ContentEvent { } } diff --git a/src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs index 8ee2b62da..082058de8 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/ETagCommandMiddleware.cs @@ -27,6 +27,8 @@ namespace Squidex.Pipeline.CommandMiddlewares { if (httpContextAccessor.HttpContext == null) { + await next(); + return; } diff --git a/src/Squidex/app/features/content/declarations.ts b/src/Squidex/app/features/content/declarations.ts index f8183d4c3..7fdcbae6e 100644 --- a/src/Squidex/app/features/content/declarations.ts +++ b/src/Squidex/app/features/content/declarations.ts @@ -16,4 +16,5 @@ export * from './shared/assets-editor.component'; export * from './shared/content-item.component'; export * from './shared/content-status.component'; export * from './shared/contents-selector.component'; +export * from './shared/due-time-selector.component'; export * from './shared/references-editor.component'; \ No newline at end of file diff --git a/src/Squidex/app/features/content/module.ts b/src/Squidex/app/features/content/module.ts index 2d2f67020..e7064c595 100644 --- a/src/Squidex/app/features/content/module.ts +++ b/src/Squidex/app/features/content/module.ts @@ -28,6 +28,7 @@ import { ContentsPageComponent, ContentsSelectorComponent, ContentStatusComponent, + DueTimeSelectorComponent, ReferencesEditorComponent, SchemasPageComponent, SearchFormComponent @@ -93,6 +94,7 @@ const routes: Routes = [ ContentStatusComponent, ContentsPageComponent, ContentsSelectorComponent, + DueTimeSelectorComponent, ReferencesEditorComponent, SchemasPageComponent, SearchFormComponent diff --git a/src/Squidex/app/features/content/pages/content/content-page.component.html b/src/Squidex/app/features/content/pages/content/content-page.component.html index 45133754a..9d5918c8d 100644 --- a/src/Squidex/app/features/content/pages/content/content-page.component.html +++ b/src/Squidex/app/features/content/pages/content/content-page.component.html @@ -31,17 +31,56 @@ - - - - + - @@ -86,4 +125,6 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/src/Squidex/app/features/content/pages/content/content-page.component.scss b/src/Squidex/app/features/content/pages/content/content-page.component.scss index 0237b8c43..fbb752506 100644 --- a/src/Squidex/app/features/content/pages/content/content-page.component.scss +++ b/src/Squidex/app/features/content/pages/content/content-page.component.scss @@ -1,26 +1,2 @@ @import '_vars'; -@import '_mixins'; - -.content-status { - & { - color: $color-text; - cursor: default !important; - } - - &:hover { - background: transparent; - } -} - -.discard-changes { - & { - color: $color-theme-blue !important; - cursor: pointer; - font-size: .9rem; - font-weight: normal; - } - - &:hover { - text-decoration: underline !important; - } -} \ No newline at end of file +@import '_mixins'; \ No newline at end of file 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 52271a6bf..837ea7264 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 @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; @@ -19,18 +19,25 @@ import { ContentsState, DialogService, EditContentForm, + fadeAnimation, ImmutableArray, LanguagesState, MessageBus, + ModalView, SchemaDetailsDto, SchemasState, Version } from '@app/shared'; +import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component'; + @Component({ selector: 'sqx-content-page', styleUrls: ['./content-page.component.scss'], - templateUrl: './content-page.component.html' + templateUrl: './content-page.component.html', + animations: [ + fadeAnimation + ] }) export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, OnInit { private languagesSubscription: Subscription; @@ -44,9 +51,14 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, public contentVersion: Version | null; public contentForm: EditContentForm; + public dropdown = new ModalView(false, true); + public language: AppLanguageDto; public languages: ImmutableArray; + @ViewChild('dueTimeSelector') + public dueTimeSelector: DueTimeSelectorComponent; + constructor( public readonly appsState: AppsState, private readonly contentsState: ContentsState, @@ -162,6 +174,43 @@ export class ContentPageComponent implements CanComponentDeactivate, OnDestroy, this.contentForm.loadData(data, this.content && this.content.status === 'Archived'); } + public publishChanges() { + this.contentsState.publishChanges(this.content).onErrorResumeNext().subscribe(); + } + + public discardChanges() { + this.contentsState.discardChanges(this.content).onErrorResumeNext().subscribe(); + } + + public publish() { + this.changeContentItems('Publish', 'Published'); + } + + public unpublish() { + this.changeContentItems('Unpublish', 'Draft'); + } + + public archive() { + this.changeContentItems('Archive', 'Archived'); + } + + public restore() { + this.changeContentItems('Restore', 'Draft'); + } + + public delete() { + this.contentsState.deleteMany([this.content]).onErrorResumeNext() + .subscribe(() => { + this.back(); + }); + } + + private changeContentItems(action: string, status: string) { + this.dueTimeSelector.selectDueTime(action) + .switchMap(d => this.contentsState.changeStatus(this.content, action, status, d)).onErrorResumeNext() + .subscribe(); + } + private loadVersion(version: Version) { if (this.content) { this.contentsState.loadVersion(this.content, version) 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 b9175ae0a..04ebda48b 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 @@ -118,33 +118,4 @@ - - - - {{dueTimeAction}} content item(s) - - - -
- - -
- -
- - -
- - -
- - - - - -
-
+ \ No newline at end of file diff --git a/src/Squidex/app/features/content/pages/contents/contents-page.component.ts b/src/Squidex/app/features/content/pages/contents/contents-page.component.ts index 4ac8e29f9..624e4b025 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-page.component.ts +++ b/src/Squidex/app/features/content/pages/contents/contents-page.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Subscription } from 'rxjs'; import { @@ -20,6 +20,8 @@ import { SchemasState } from '@app/shared'; +import { DueTimeSelectorComponent } from './../../shared/due-time-selector.component'; + @Component({ selector: 'sqx-contents-page', styleUrls: ['./contents-page.component.scss'], @@ -34,12 +36,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit { public searchModal = new ModalView(); - public dueTimeDialog = new ModalView(); - public dueTime: string | null = ''; - public dueTimeFunction: Function | null; - public dueTimeAction: string | null = ''; - public dueTimeMode = 'Immediately'; - public selectedItems: { [id: string]: boolean; } = {}; public selectionCount = 0; @@ -51,6 +47,9 @@ export class ContentsPageComponent implements OnDestroy, OnInit { public isAllSelected = false; + @ViewChild('dueTimeSelector') + public dueTimeSelector: DueTimeSelectorComponent; + constructor( public readonly appsState: AppsState, public readonly contentsState: ContentsState, @@ -131,26 +130,24 @@ export class ContentsPageComponent implements OnDestroy, OnInit { return; } - this.dueTimeFunction = () => { - this.resetSelection(); - - this.contentsState.changeStatus(contents, action, this.dueTime).onErrorResumeNext().subscribe(); - }; - - this.dueTimeAction = action; - this.dueTimeDialog.show(); + this.dueTimeSelector.selectDueTime(action) + .do(() => { + this.resetSelection(); + }) + .switchMap(d => this.contentsState.changeManyStatus(contents, action, d)).onErrorResumeNext() + .subscribe(); } public deleteSelected() { this.resetSelection(); - this.contentsState.delete(this.select()).onErrorResumeNext().subscribe(); + this.contentsState.deleteMany(this.select()).onErrorResumeNext().subscribe(); } public delete(content: ContentDto) { this.resetSelection(); - this.contentsState.delete([content]).onErrorResumeNext().subscribe(); + this.contentsState.deleteMany([content]).onErrorResumeNext().subscribe(); } public goArchive(isArchive: boolean) { @@ -203,21 +200,6 @@ export class ContentsPageComponent implements OnDestroy, OnInit { this.updateSelectionSummary(); } - public confirmStatusChange() { - this.dueTimeFunction!(); - this.dueTimeFunction = null; - this.dueTimeMode = 'Immediately'; - this.dueTimeDialog.hide(); - this.dueTime = null; - } - - public cancelStatusChange() { - this.dueTimeMode = 'Immediately'; - this.dueTimeDialog.hide(); - this.dueTimeFunction = null; - this.dueTime = null; - } - public trackByContent(content: ContentDto): string { return content.id; } diff --git a/src/Squidex/app/features/content/shared/content-item.component.html b/src/Squidex/app/features/content/shared/content-item.component.html index 956a06d21..5e9b8360a 100644 --- a/src/Squidex/app/features/content/shared/content-item.component.html +++ b/src/Squidex/app/features/content/shared/content-item.component.html @@ -99,6 +99,9 @@ Restore + + + Will be set to '{{scheduledTo}}' at {{scheduledAt | sqxFullDateTime}} -{{displayStatus}} \ No newline at end of file +{{displayStatus}} \ No newline at end of file diff --git a/src/Squidex/app/features/content/shared/content-status.component.scss b/src/Squidex/app/features/content/shared/content-status.component.scss index 333a1444b..36696d04b 100644 --- a/src/Squidex/app/features/content/shared/content-status.component.scss +++ b/src/Squidex/app/features/content/shared/content-status.component.scss @@ -22,6 +22,10 @@ color: $color-dark-black; } + &-label { + color: $color-text; + } + &-tooltip { @include border-radius; background: $color-tooltip; diff --git a/src/Squidex/app/features/content/shared/due-time-selector.component.html b/src/Squidex/app/features/content/shared/due-time-selector.component.html new file mode 100644 index 000000000..abc199f37 --- /dev/null +++ b/src/Squidex/app/features/content/shared/due-time-selector.component.html @@ -0,0 +1,30 @@ + + + + {{dueTimeAction}} content item(s) + + + +
+ + +
+ +
+ + +
+ + +
+ + + + + +
+
diff --git a/src/Squidex/app/features/content/shared/due-time-selector.component.scss b/src/Squidex/app/features/content/shared/due-time-selector.component.scss new file mode 100644 index 000000000..fbb752506 --- /dev/null +++ b/src/Squidex/app/features/content/shared/due-time-selector.component.scss @@ -0,0 +1,2 @@ +@import '_vars'; +@import '_mixins'; \ No newline at end of file diff --git a/src/Squidex/app/features/content/shared/due-time-selector.component.ts b/src/Squidex/app/features/content/shared/due-time-selector.component.ts new file mode 100644 index 000000000..2f904fba4 --- /dev/null +++ b/src/Squidex/app/features/content/shared/due-time-selector.component.ts @@ -0,0 +1,52 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { Component } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; + +import { fadeAnimation, ModalView } from '@app/shared'; + +@Component({ + selector: 'sqx-due-time-selector', + styleUrls: ['./due-time-selector.component.scss'], + templateUrl: './due-time-selector.component.html', + animations: [ + fadeAnimation + ] +}) +export class DueTimeSelectorComponent { + public dueTimeDialog = new ModalView(); + public dueTime: string | null = ''; + public dueTimeFunction: Subject; + public dueTimeAction: string | null = ''; + public dueTimeMode = 'Immediately'; + + public selectDueTime(action: string): Observable { + this.dueTimeAction = action; + this.dueTimeFunction = new Subject(); + this.dueTimeDialog.show(); + + return this.dueTimeFunction; + } + + public confirmStatusChange() { + const result = this.dueTimeMode === 'Immediately' ? null : this.dueTime; + + this.dueTimeFunction.next(result); + this.dueTimeFunction.complete(); + + this.cancelStatusChange(); + } + + public cancelStatusChange() { + this.dueTimeMode = 'Immediately'; + this.dueTimeDialog.hide(); + this.dueTimeFunction = null!; + this.dueTime = null; + } +} + diff --git a/src/Squidex/app/features/schemas/pages/schema/field.component.html b/src/Squidex/app/features/schemas/pages/schema/field.component.html index fe5f7c15e..4e63f7f9d 100644 --- a/src/Squidex/app/features/schemas/pages/schema/field.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/field.component.html @@ -39,12 +39,18 @@
Show in API + + + Lock and prevent changes + + + { .notify(this.dialogs); } - public changeStatus(contents: ContentDto[], action: string, dueTime: string | null): Observable { + public changeManyStatus(contents: ContentDto[], action: string, dueTime: string | null): Observable { return Observable.forkJoin( contents.map(c => this.contentsService.changeContentStatus(this.appName, this.schemaName, c.id, action, dueTime, c.version) @@ -271,7 +271,7 @@ export abstract class ContentsStateBase extends State { .switchMap(() => this.loadInternal()); } - public delete(contents: ContentDto[]): Observable { + public deleteMany(contents: ContentDto[]): Observable { return Observable.forkJoin( contents.map(c => this.contentsService.deleteContent(this.appName, this.schemaName, c.id, c.version) @@ -288,10 +288,20 @@ export abstract class ContentsStateBase extends State { .switchMap(() => this.loadInternal()); } + public changeStatus(content: ContentDto, action: string, status: string, dueTime: string | null, now?: DateTime): Observable { + return this.contentsService.changeContentStatus(this.appName, this.schemaName, content.id, action, dueTime, content.version) + .do(dto => { + this.dialogs.notifyInfo('Content updated successfully.'); + + this.replaceContent(changeStatus(content, status, dueTime, this.user, dto.version, now)); + }) + .notify(this.dialogs); + } + public update(content: ContentDto, request: any, now?: DateTime): Observable { return this.contentsService.putContent(this.appName, this.schemaName, content.id, request, false, content.version) .do(dto => { - this.dialogs.notifyInfo('Contents updated successfully.'); + this.dialogs.notifyInfo('Content updated successfully.'); this.replaceContent(updateData(content, dto.payload, this.user, dto.version, now)); }) @@ -308,12 +318,22 @@ export abstract class ContentsStateBase extends State { .notify(this.dialogs); } - public revertProposal(content: ContentDto, now?: DateTime): Observable { + public publishChanges(content: ContentDto, now?: DateTime): Observable { + return this.contentsService.changeContentStatus(this.appName, this.schemaName, content.id, 'Publish', null, content.version) + .do(dto => { + this.dialogs.notifyInfo('Content updated successfully.'); + + this.replaceContent(confirmChanges(content, this.user, dto.version, now)); + }) + .notify(this.dialogs); + } + + public discardChanges(content: ContentDto, now?: DateTime): Observable { return this.contentsService.discardChanges(this.appName, this.schemaName, content.id, content.version) .do(dto => { this.dialogs.notifyInfo('Content updated successfully.'); - this.replaceContent(revertDataDraft(content, this.user, dto.version, now)); + this.replaceContent(discardChanges(content, this.user, dto.version, now)); }) .notify(this.dialogs); } @@ -411,6 +431,20 @@ export class ManualContentsState extends ContentsStateBase { } } +const changeStatus = (content: ContentDto, status: string, dueTime: string | null, user: string, version: Version, now?: DateTime) => + new ContentDto( + content.id, + dueTime ? content.status : status, + content.createdBy, user, + content.created, now || DateTime.now(), + dueTime ? status : null, + dueTime ? user : null, + dueTime ? DateTime.parseISO_UTC(dueTime) : null, + content.isPending, + content.data, + content.dataDraft, + version); + const updateData = (content: ContentDto, data: any, user: string, version: Version, now?: DateTime) => new ContentDto( content.id, @@ -439,7 +473,21 @@ const updateDataDraft = (content: ContentDto, data: any, user: string, version: data, version); -const revertDataDraft = (content: ContentDto, user: string, version: Version, now?: DateTime) => +const confirmChanges = (content: ContentDto, user: string, version: Version, now?: DateTime) => + new ContentDto( + content.id, + content.status, + content.createdBy, user, + content.created, now || DateTime.now(), + content.scheduledTo, + content.scheduledBy, + content.scheduledAt, + false, + content.dataDraft, + content.dataDraft, + version); + +const discardChanges = (content: ContentDto, user: string, version: Version, now?: DateTime) => new ContentDto( content.id, content.status,