diff --git a/src/Squidex.Domain.Apps.Core.Model/Contents/StatusFlow.cs b/src/Squidex.Domain.Apps.Core.Model/Contents/StatusFlow.cs index 005b2d4b3..7add93c24 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Contents/StatusFlow.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Contents/StatusFlow.cs @@ -28,5 +28,10 @@ namespace Squidex.Domain.Apps.Core.Contents { return Flow.TryGetValue(status, out var state) && state.Contains(toStatus); } + + public static IEnumerable Next(Status status) + { + return Flow.TryGetValue(status, out var result) ? result : Enumerable.Empty(); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs index 5adf76236..104e0fa1d 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs @@ -81,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.Contents Create(c); - return EntityCreatedResult.Create(c.Data, Version); + return await GetRawStateAsync(); }); case UpdateContent updateContent: @@ -101,7 +101,7 @@ namespace Squidex.Domain.Apps.Entities.Contents }); case ChangeContentStatus changeContentStatus: - return UpdateAsync(changeContentStatus, async c => + return UpdateReturnAsync(changeContentStatus, async c => { try { @@ -157,6 +157,8 @@ namespace Squidex.Domain.Apps.Entities.Contents throw; } } + + return await GetRawStateAsync(); }); case DeleteContent deleteContent: @@ -172,11 +174,13 @@ namespace Squidex.Domain.Apps.Entities.Contents }); case DiscardChanges discardChanges: - return UpdateAsync(discardChanges, c => + return UpdateReturnAsync(discardChanges, async c => { GuardContent.CanDiscardChanges(Snapshot.IsPending, c); DiscardChanges(c); + + return await GetRawStateAsync(); }); default: @@ -220,7 +224,7 @@ namespace Squidex.Domain.Apps.Entities.Contents } } - return new ContentDataChangedResult(newData, Version); + return Snapshot; } public void Create(CreateContent command) @@ -305,6 +309,11 @@ namespace Squidex.Domain.Apps.Entities.Contents return operationContext; } + public Task GetRawStateAsync() + { + return Task.FromResult(Snapshot); + } + public Task> GetStateAsync(long version = EtagVersion.Any) { return J.AsTask(GetSnapshot(version)); diff --git a/src/Squidex.Shared/Permissions.cs b/src/Squidex.Shared/Permissions.cs index 98cb299c0..7118a0604 100644 --- a/src/Squidex.Shared/Permissions.cs +++ b/src/Squidex.Shared/Permissions.cs @@ -115,11 +115,8 @@ namespace Squidex.Shared public const string AppContentsRead = "squidex.apps.{app}.contents.{name}.read"; public const string AppContentsCreate = "squidex.apps.{app}.contents.{name}.create"; public const string AppContentsUpdate = "squidex.apps.{app}.contents.{name}.update"; + public const string AppContentsStatus = "squidex.apps.{app}.contents.{name}.status.{status}"; public const string AppContentsDiscard = "squidex.apps.{app}.contents.{name}.discard"; - public const string AppContentsArchive = "squidex.apps.{app}.contents.{name}.archive"; - public const string AppContentsRestore = "squidex.apps.{app}.contents.{name}.restore"; - public const string AppContentsPublish = "squidex.apps.{app}.contents.{name}.publish"; - public const string AppContentsUnpublish = "squidex.apps.{app}.contents.{name}.unpublish"; public const string AppContentsDelete = "squidex.apps.{app}.contents.{name}.delete"; public const string AppApi = "squidex.apps.{app}.api"; diff --git a/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs b/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs index 66264dcb4..2a893b573 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs @@ -10,8 +10,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; -using NodaTime; -using NodaTime.Text; using Squidex.Areas.Api.Controllers.Contents.Models; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities; @@ -127,7 +125,7 @@ namespace Squidex.Areas.Api.Controllers.Contents var result = await contentQuery.QueryAsync(context, Q.Empty.WithIds(ids).Ids); - var response = ContentsDto.FromContents(result, context, this, app); + var response = ContentsDto.FromContents(result, context, this, app, null); if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys) { @@ -163,9 +161,9 @@ namespace Squidex.Areas.Api.Controllers.Contents var result = await contentQuery.QueryAsync(context, name, Q.Empty.WithIds(ids).WithODataQuery(Request.QueryString.ToString())); - var response = ContentsDto.FromContents(result, context, this, app); + var response = ContentsDto.FromContents(result, context, this, app, name); - if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys) + if (ShouldProvideSurrogateKeys(response)) { Response.Headers["Surrogate-Key"] = response.ToSurrogateKeys(); } @@ -197,7 +195,7 @@ namespace Squidex.Areas.Api.Controllers.Contents var context = Context(); var content = await contentQuery.FindContentAsync(context, name, id); - var response = ContentDto.FromContent(content, context, this, app); + var response = ContentDto.FromContent(content, context, this, app, name); if (controllerOptions.Value.EnableSurrogateKeys) { @@ -233,7 +231,7 @@ namespace Squidex.Areas.Api.Controllers.Contents var context = Context(); var content = await contentQuery.FindContentAsync(context, name, id, version); - var response = ContentDto.FromContent(content, context, this, app); + var response = ContentDto.FromContent(content, context, this, app, name); if (controllerOptions.Value.EnableSurrogateKeys) { @@ -268,17 +266,14 @@ namespace Squidex.Areas.Api.Controllers.Contents { await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name); - if (publish && !this.HasPermission(Permissions.AppContentsPublish, app, name)) + if (publish && !this.HasPermission(Helper.StatusPermission(app, name, Status.Published))) { - return new StatusCodeResult(123); + return new ForbidResult(); } var command = new CreateContent { ContentId = Guid.NewGuid(), Data = request.ToCleaned(), Publish = publish }; - var context = await CommandBus.PublishAsync(command); - - var result = context.Result>(); - var response = ContentDto.FromCommand(command, result); + var response = await InvokeCommandAsync(app, name, command); return CreatedAtAction(nameof(GetContent), new { id = command.ContentId }, response); } @@ -308,10 +303,8 @@ namespace Squidex.Areas.Api.Controllers.Contents await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name); var command = new UpdateContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft }; - var context = await CommandBus.PublishAsync(command); - var result = context.Result(); - var response = result.Data; + var response = await InvokeCommandAsync(app, name, command); return Ok(response); } @@ -341,10 +334,8 @@ namespace Squidex.Areas.Api.Controllers.Contents await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name); var command = new PatchContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft }; - var context = await CommandBus.PublishAsync(command); - var result = context.Result(); - var response = result.Data; + var response = await InvokeCommandAsync(app, name, command); return Ok(response); } @@ -355,118 +346,33 @@ namespace Squidex.Areas.Api.Controllers.Contents /// The name of the app. /// The name of the schema. /// The id of the content item to publish. - /// The date and time when the content should be published. + /// The status request. /// /// 204 => Content published. /// 404 => Content, schema or app not found. - /// 400 => Content was already published. + /// 400 => Request is not valid. /// /// /// You can read the generated documentation for your app at /api/content/{appName}/docs. /// [HttpPut] - [Route("content/{app}/{name}/{id}/publish/")] - [ApiPermission(Permissions.AppContentsPublish)] - [ApiCosts(1)] - public async Task PublishContent(string app, string name, Guid id, string dueTime = null) - { - await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name); - - var command = CreateCommand(id, Status.Published, dueTime); - - await CommandBus.PublishAsync(command); - - return NoContent(); - } - - /// - /// Unpublish a content item. - /// - /// The name of the app. - /// The name of the schema. - /// The id of the content item to unpublish. - /// The date and time when the content should be unpublished. - /// - /// 204 => Content unpublished. - /// 404 => Content, schema or app not found. - /// 400 => Content was not published. - /// - /// - /// You can read the generated documentation for your app at /api/content/{appName}/docs. - /// - [HttpPut] - [Route("content/{app}/{name}/{id}/unpublish/")] - [ApiPermission(Permissions.AppContentsUnpublish)] - [ApiCosts(1)] - public async Task UnpublishContent(string app, string name, Guid id, string dueTime = null) - { - await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name); - - var command = CreateCommand(id, Status.Draft, dueTime); - - await CommandBus.PublishAsync(command); - - return NoContent(); - } - - /// - /// Archive a content item. - /// - /// The name of the app. - /// The name of the schema. - /// The id of the content item to archive. - /// The date and time when the content should be archived. - /// - /// 204 => Content archived. - /// 404 => Content, schema or app not found. - /// 400 => Content was already archived. - /// - /// - /// You can read the generated documentation for your app at /api/content/{appName}/docs. - /// - [HttpPut] - [Route("content/{app}/{name}/{id}/archive/")] - [ApiPermission(Permissions.AppContentsArchive)] + [Route("content/{app}/{name}/{id}/status/")] + [ApiPermission] [ApiCosts(1)] - public async Task ArchiveContent(string app, string name, Guid id, string dueTime = null) + public async Task PutContentStatus(string app, string name, Guid id, ChangeStatusDto request) { await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name); - var command = CreateCommand(id, Status.Archived, dueTime); - - await CommandBus.PublishAsync(command); - - return NoContent(); - } - - /// - /// Restore a content item. - /// - /// The name of the app. - /// The name of the schema. - /// The id of the content item to restore. - /// The date and time when the content should be restored. - /// - /// 204 => Content restored. - /// 404 => Content, schema or app not found. - /// 400 => Content was not archived. - /// - /// - /// You can read the generated documentation for your app at /api/content/{appName}/docs. - /// - [HttpPut] - [Route("content/{app}/{name}/{id}/restore/")] - [ApiPermission(Permissions.AppContentsRestore)] - [ApiCosts(1)] - public async Task RestoreContent(string app, string name, Guid id, string dueTime = null) - { - await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name); + if (!this.HasPermission(Helper.StatusPermission(app, name, Status.Published))) + { + return new ForbidResult(); + } - var command = CreateCommand(id, Status.Draft, dueTime); + var command = request.ToCommand(id); - await CommandBus.PublishAsync(command); + var response = await InvokeCommandAsync(app, name, command); - return NoContent(); + return Ok(response); } /// @@ -521,26 +427,19 @@ namespace Squidex.Areas.Api.Controllers.Contents var command = new DeleteContent { ContentId = id }; - await CommandBus.PublishAsync(command); + var response = await InvokeCommandAsync(app, name, command); - return NoContent(); + return Ok(response); } - private static ChangeContentStatus CreateCommand(Guid id, Status status, string dueTime) + private async Task InvokeCommandAsync(string app, string schema, ICommand command) { - Instant? dt = null; - - if (!string.IsNullOrWhiteSpace(dueTime)) - { - var parseResult = InstantPattern.General.Parse(dueTime); + var context = await CommandBus.PublishAsync(command); - if (parseResult.Success) - { - dt = parseResult.Value; - } - } + var result = context.Result(); + var response = ContentDto.FromContent(result, null, this, app, schema); - return new ChangeContentStatus { Status = status, ContentId = id, DueTime = dt }; + return response; } private QueryContext Context() @@ -551,5 +450,10 @@ namespace Squidex.Areas.Api.Controllers.Contents .WithLanguages(Request.Headers["X-Languages"]) .WithUnpublished(Request.Headers.ContainsKey("X-Unpublished")); } + + private bool ShouldProvideSurrogateKeys(ContentsDto response) + { + return controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys; + } } } diff --git a/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs b/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs index b9703b584..4b7cd1997 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs +++ b/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs @@ -178,8 +178,6 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator operation.Summary = $"Publish a {schemaName} content."; operation.AddResponse("204", $"{schemaName} content published."); - - AddSecurity(operation, Permissions.AppContentsPublish); }); } @@ -191,8 +189,6 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator operation.Summary = $"Unpublish a {schemaName} content."; operation.AddResponse("204", $"{schemaName} content unpublished."); - - AddSecurity(operation, Permissions.AppContentsUnpublish); }); } @@ -217,8 +213,6 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator operation.Summary = $"Restore a {schemaName} content."; operation.AddResponse("204", $"{schemaName} content restored."); - - AddSecurity(operation, Permissions.AppContentsRestore); }); } diff --git a/src/Squidex/Areas/Api/Controllers/Contents/Helper.cs b/src/Squidex/Areas/Api/Controllers/Contents/Helper.cs new file mode 100644 index 000000000..e65648c21 --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Contents/Helper.cs @@ -0,0 +1,23 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure.Security; +using Squidex.Shared; + +namespace Squidex.Areas.Api.Controllers.Contents +{ + public static class Helper + { + public static Permission StatusPermission(string app, string schema, Status status) + { + var id = Permissions.AppContentsStatus.Replace("{status}", status.ToString()); + + return Permissions.ForApp(id, app, schema); + } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Contents/Models/ChangeStatusDto.cs b/src/Squidex/Areas/Api/Controllers/Contents/Models/ChangeStatusDto.cs new file mode 100644 index 000000000..7a89789c9 --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Contents/Models/ChangeStatusDto.cs @@ -0,0 +1,34 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.ComponentModel.DataAnnotations; +using NodaTime; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Entities.Contents.Commands; + +namespace Squidex.Areas.Api.Controllers.Contents.Models +{ + public sealed class ChangeStatusDto + { + /// + /// The new status. + /// + [Required] + public Status Status { get; set; } + + /// + /// The due time. + /// + public Instant? DueTime { get; set; } + + public ChangeContentStatus ToCommand(Guid id) + { + return new ChangeContentStatus { ContentId = id, Status = Status, DueTime = DueTime }; + } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs b/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs index 81b605a7d..55b02caeb 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs @@ -12,10 +12,9 @@ using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Contents; -using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Infrastructure; -using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Reflection; +using Squidex.Shared; using Squidex.Web; namespace Squidex.Areas.Api.Controllers.Contents.Models @@ -80,30 +79,11 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models /// public long Version { get; set; } - public static ContentDto FromCommand(CreateContent command, EntityCreatedResult result) - { - var now = SystemClock.Instance.GetCurrentInstant(); - - var response = new ContentDto - { - Id = command.ContentId, - Data = result.IdOrValue, - Version = result.Version, - Created = now, - CreatedBy = command.Actor, - LastModified = now, - LastModifiedBy = command.Actor, - Status = command.Publish ? Status.Published : Status.Draft - }; - - return response; - } - - public static ContentDto FromContent(IContentEntity content, QueryContext context, ApiController controller, string app) + public static ContentDto FromContent(IContentEntity content, QueryContext context, ApiController controller, string app, string schema) { var response = SimpleMapper.Map(content, new ContentDto()); - if (context.Flatten) + if (context?.Flatten == true) { response.Data = content.Data?.ToFlatten(); response.DataDraft = content.DataDraft?.ToFlatten(); @@ -119,11 +99,58 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models response.ScheduleJob = SimpleMapper.Map(content.ScheduleJob, new ScheduleJobDto()); } - return response.CreateLinks(controller, app); + return response.CreateLinks(controller, app, schema); } - private ContentDto CreateLinks(ApiController controller, object app) + private ContentDto CreateLinks(ApiController controller, string app, string schema) { + var values = new { app, name = schema, id = Id }; + + AddSelfLink(controller.Url(x => nameof(x.GetContent), values)); + + if (Version > 0) + { + var versioned = new { app, name = schema, id = Id, version = Version - 1 }; + + AddGetLink("prev", controller.Url(x => nameof(x.GetContentVersion), versioned)); + } + + if (IsPending) + { + if (controller.HasPermission(Permissions.AppContentsDiscard, app, schema)) + { + AddPutLink("changes/discard", controller.Url(x => nameof(x.DiscardChanges), values)); + } + + if (controller.HasPermission(Helper.StatusPermission(app, schema, Status.Published))) + { + AddPutLink("changes/publish", controller.Url(x => nameof(x.PutContentStatus), values)); + } + } + + if (controller.HasPermission(Permissions.AppContentsUpdate, app, schema)) + { + AddPutLink("update", controller.Url(x => nameof(x.PutContent), values)); + + if (Status == Status.Published) + { + AddPutLink("update/change", controller.Url(x => nameof(x.PutContent), values) + "?asDraft"); + } + } + + if (controller.HasPermission(Permissions.AppContentsDelete, app, schema)) + { + AddPutLink("delete", controller.Url(x => nameof(x.DeleteContent), values)); + } + + foreach (var next in StatusFlow.Next(Status)) + { + if (controller.HasPermission(Helper.StatusPermission(app, schema, next))) + { + AddPutLink($"status/{next}", controller.Url(x => nameof(x.PutContentStatus), values)); + } + } + return this; } } diff --git a/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs b/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs index edcb6caa8..a3a79ade2 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs @@ -7,9 +7,11 @@ using System.Collections.Generic; using System.Linq; +using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Infrastructure; +using Squidex.Shared; using Squidex.Web; namespace Squidex.Areas.Api.Controllers.Contents.Models @@ -36,30 +38,47 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models return Items.ToSurrogateKeys(); } - public static ContentsDto FromContents(IList contents, QueryContext context, ApiController controller, string app) + public static ContentsDto FromContents(IList contents, QueryContext context, ApiController controller, string app, string schema) { var result = new ContentsDto { Total = contents.Count, - Items = contents.Select(x => ContentDto.FromContent(x, context, controller, app)).ToArray() + Items = contents.Select(x => ContentDto.FromContent(x, context, controller, app, schema)).ToArray() }; - return result.CreateLinks(controller, app); + return result.CreateLinks(controller, app, schema); } - public static ContentsDto FromContents(IResultList contents, QueryContext context, ApiController controller, string app) + public static ContentsDto FromContents(IResultList contents, QueryContext context, ApiController controller, string app, string schema) { var result = new ContentsDto { Total = contents.Total, - Items = contents.Select(x => ContentDto.FromContent(x, context, controller, app)).ToArray() + Items = contents.Select(x => ContentDto.FromContent(x, context, controller, app, schema)).ToArray() }; - return result.CreateLinks(controller, app); + return result.CreateLinks(controller, app, schema); } - private ContentsDto CreateLinks(ApiController controller, string app) + private ContentsDto CreateLinks(ApiController controller, string app, string schema) { + if (schema != null) + { + var values = new { app, name = schema }; + + AddSelfLink(controller.Url(x => nameof(x.GetContents), values)); + + if (controller.HasPermission(Permissions.AppContentsCreate, app, schema)) + { + AddPostLink("create", controller.Url(x => nameof(x.PostContent), values)); + + if (controller.HasPermission(Helper.StatusPermission(app, schema, Status.Published))) + { + AddPostLink("create/publish", controller.Url(x => nameof(x.PostContent), values) + "?publish=true"); + } + } + } + return this; } } 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 97b47dbc5..7438f1245 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 @@ -7,24 +7,21 @@ - + New Content - - Edit Content - - - Show Content - + + Content + - - @@ -48,35 +45,25 @@ - - - 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 228595a75..b16c3914b 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 @@ -24,6 +24,7 @@ import { EditContentForm, fadeAnimation, FieldDto, + hasAnyLink, ImmutableArray, LanguagesState, MessageBus, @@ -64,7 +65,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD constructor(apiUrl: ApiUrlConfig, authService: AuthService, public readonly appsState: AppsState, - private readonly contentsState: ContentsState, + public readonly contentsState: ContentsState, private readonly dialogs: DialogService, private readonly languagesState: LanguagesState, private readonly messageBus: MessageBus, @@ -174,29 +175,13 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD } private loadContent(data: any) { - this.contentForm.loadContent(data, this.content && this.content.status === 'Archived'); + this.contentForm.loadContent(data, this.content && !hasAnyLink(this.content, 'update')); } public discardChanges() { this.contentsState.discardChanges(this.content); } - 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]).pipe(onErrorResumeNext()) .subscribe(() => { @@ -210,9 +195,9 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD .subscribe(); } - private changeContentItems(action: string, status: string) { - this.dueTimeSelector.selectDueTime(action).pipe( - switchMap(d => this.contentsState.changeStatus(this.content, action, status, d)), onErrorResumeNext()) + public changeStatus(status: string) { + this.dueTimeSelector.selectDueTime(status).pipe( + switchMap(d => this.contentsState.changeStatus(this.content, status, d)), onErrorResumeNext()) .subscribe(); } 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 5cf558066..78723f4af 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 @@ -36,7 +36,7 @@
-
+
@@ -80,25 +80,13 @@
- {{selectionCount}} items selected:   + {{selectionCount}} items selected   - - - - - - - -