diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/DefaultContentWorkflow.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/DefaultContentWorkflow.cs index 7b0e5049e..a29dab387 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/DefaultContentWorkflow.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/DefaultContentWorkflow.cs @@ -52,6 +52,11 @@ namespace Squidex.Domain.Apps.Entities.Contents return Task.FromResult(Status.Draft); } + public Task CanPublishInitialAsync(ISchemaEntity schema, ClaimsPrincipal? user) + { + return Task.FromResult(true); + } + public Task CanMoveToAsync(ISchemaEntity schema, Status status, Status next, ContentData data, ClaimsPrincipal? user) { var result = Flow.TryGetValue(status, out var step) && step.Transitions.Any(x => x.Status == next); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs index 3ccc8dcaa..34eb3957e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs @@ -35,6 +35,13 @@ namespace Squidex.Domain.Apps.Entities.Contents return workflow.Steps.Select(x => new StatusInfo(x.Key, GetColor(x.Value))).ToArray(); } + public async Task CanPublishInitialAsync(ISchemaEntity schema, ClaimsPrincipal? user) + { + var workflow = await GetWorkflowAsync(schema.AppId.Id, schema.Id); + + return workflow.TryGetTransition(workflow.Initial, Status.Published, out var transition) && IsTrue(transition, null, user); + } + public async Task CanMoveToAsync(ISchemaEntity schema, Status status, Status next, ContentData data, ClaimsPrincipal? user) { var workflow = await GetWorkflowAsync(schema.AppId.Id, schema.Id); @@ -49,13 +56,6 @@ namespace Squidex.Domain.Apps.Entities.Contents return workflow.TryGetTransition(status, next, out var transition) && IsTrue(transition, content.Data, user); } - public async Task CanPublishOnCreateAsync(ISchemaEntity schema, ContentData data, ClaimsPrincipal? user) - { - var workflow = await GetWorkflowAsync(schema.AppId.Id, schema.Id); - - return workflow.TryGetTransition(workflow.Initial, Status.Published, out var transition) && IsTrue(transition, data, user); - } - public async Task CanUpdateAsync(IContentEntity content, Status status, ClaimsPrincipal? user) { var workflow = await GetWorkflowAsync(content.AppId.Id, content.SchemaId.Id); @@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Entities.Contents return result.ToArray(); } - private bool IsTrue(WorkflowCondition condition, ContentData data, ClaimsPrincipal? user) + private bool IsTrue(WorkflowCondition condition, ContentData? data, ClaimsPrincipal? user) { if (condition?.Roles != null && user != null) { @@ -116,7 +116,7 @@ namespace Squidex.Domain.Apps.Entities.Contents } } - if (!string.IsNullOrWhiteSpace(condition?.Expression)) + if (!string.IsNullOrWhiteSpace(condition?.Expression) && data != null) { var vars = new ScriptVars { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/IContentWorkflow.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/IContentWorkflow.cs index bd9adedce..2637d27cb 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/IContentWorkflow.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/IContentWorkflow.cs @@ -22,6 +22,8 @@ namespace Squidex.Domain.Apps.Entities.Contents Task CanUpdateAsync(IContentEntity content, Status status, ClaimsPrincipal? user); + Task CanPublishInitialAsync(ISchemaEntity schema, ClaimsPrincipal? user); + Task GetInfoAsync(IContentEntity content, Status status); Task GetNextAsync(IContentEntity content, Status status, ClaimsPrincipal? user); diff --git a/backend/src/Squidex.Web/Resources.cs b/backend/src/Squidex.Web/Resources.cs index 334deee6b..4a7b22521 100644 --- a/backend/src/Squidex.Web/Resources.cs +++ b/backend/src/Squidex.Web/Resources.cs @@ -30,6 +30,8 @@ namespace Squidex.Web public bool CanDeleteContentVersion(string schema) => IsAllowedForSchema(Permissions.AppContentsVersionDeleteOwn, schema); + public bool CanChangeStatus(string schema) => IsAllowedForSchema(Permissions.AppContentsChangeStatus, schema); + public bool CanCancelContentStatus(string schema) => IsAllowedForSchema(Permissions.AppContentsChangeStatusCancelOwn, schema); public bool CanUpdateContent(string schema) => IsAllowedForSchema(Permissions.AppContentsUpdateOwn, schema); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs index 61cf41f01..decb47ffd 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs @@ -173,7 +173,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models } } - if (content.NextStatuses != null && resources.CanUpdateContent(schema)) + if (content.NextStatuses != null && resources.CanChangeStatus(schema)) { foreach (var next in content.NextStatuses) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs index 181a24c59..8384f7159 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs @@ -47,7 +47,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models { await result.AssignStatusesAsync(workflow, schema); - result.CreateLinks(resources, schema.SchemaDef.Name); + await result.CreateLinksAsync(resources, workflow, schema); } return result; @@ -60,19 +60,22 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models Statuses = allStatuses.Select(StatusInfoDto.FromStatusInfo).ToArray(); } - private void CreateLinks(Resources resources, string schema) + private async Task CreateLinksAsync(Resources resources, IContentWorkflow workflow, ISchemaEntity schema) { - var values = new { app = resources.App, schema }; + var values = new { app = resources.App, schema = schema.SchemaDef.Name }; AddSelfLink(resources.Url(x => nameof(x.GetContents), values)); - if (resources.CanCreateContent(schema)) + if (resources.CanCreateContent(values.schema)) { AddPostLink("create", resources.Url(x => nameof(x.PostContent), values)); - var publishValues = new { values.app, values.schema, publish = true }; + if (resources.CanChangeStatus(values.schema) && await workflow.CanPublishInitialAsync(schema, resources.Context.User)) + { + var publishValues = new { values.app, values.schema, publish = true }; - AddPostLink("create/publish", resources.Url(x => nameof(x.PostContent), publishValues)); + AddPostLink("create/publish", resources.Url(x => nameof(x.PostContent), publishValues)); + } } } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs index 520c6fefd..ef2948a75 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs @@ -40,6 +40,14 @@ namespace Squidex.Domain.Apps.Entities.Contents Assert.Equal(Status.Draft, result); } + [Fact] + public async Task Should_allow_publish_on_create() + { + var result = await sut.CanPublishInitialAsync(null!, null); + + Assert.True(result); + } + [Fact] public async Task Should_allow_if_transition_is_valid() { diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs index 68ca6257a..42b8dc1e9 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs @@ -133,29 +133,15 @@ namespace Squidex.Domain.Apps.Entities.Contents [Fact] public async Task Should_allow_publish_on_create() { - var content = CreateContent(Status.Draft, 2); - - var result = await sut.CanPublishOnCreateAsync(Mocks.Schema(appId, schemaId), content.Data, Mocks.FrontendUser("Editor")); + var result = await sut.CanPublishInitialAsync(Mocks.Schema(appId, schemaId), Mocks.FrontendUser("Editor")); Assert.True(result); } - [Fact] - public async Task Should_not_allow_publish_on_create_if_data_is_invalid() - { - var content = CreateContent(Status.Draft, 4); - - var result = await sut.CanPublishOnCreateAsync(Mocks.Schema(appId, schemaId), content.Data, Mocks.FrontendUser("Editor")); - - Assert.False(result); - } - [Fact] public async Task Should_not_allow_publish_on_create_if_role_not_allowed() { - var content = CreateContent(Status.Draft, 2); - - var result = await sut.CanPublishOnCreateAsync(Mocks.Schema(appId, schemaId), content.Data, Mocks.FrontendUser("Developer")); + var result = await sut.CanPublishInitialAsync(Mocks.Schema(appId, schemaId), Mocks.FrontendUser("Developer")); Assert.False(result); } diff --git a/frontend/app/features/administration/pages/restore/restore-page.component.html b/frontend/app/features/administration/pages/restore/restore-page.component.html index 7e4f56b09..bb64478bb 100644 --- a/frontend/app/features/administration/pages/restore/restore-page.component.html +++ b/frontend/app/features/administration/pages/restore/restore-page.component.html @@ -47,9 +47,13 @@
+ +
+ +
diff --git a/frontend/app/features/content/pages/content/content-page.component.html b/frontend/app/features/content/pages/content/content-page.component.html index 34970c470..239a75bb6 100644 --- a/frontend/app/features/content/pages/content/content-page.component.html +++ b/frontend/app/features/content/pages/content/content-page.component.html @@ -91,11 +91,11 @@ - - diff --git a/frontend/app/framework/angular/forms/editors/dropdown.component.html b/frontend/app/framework/angular/forms/editors/dropdown.component.html index 88defcb08..5e3d2c315 100644 --- a/frontend/app/framework/angular/forms/editors/dropdown.component.html +++ b/frontend/app/framework/angular/forms/editors/dropdown.component.html @@ -5,9 +5,11 @@ autocapitalize="off">
- {{selectedItem}} - - +
+ {{selectedItem}} + + +
diff --git a/frontend/app/framework/angular/forms/editors/dropdown.component.scss b/frontend/app/framework/angular/forms/editors/dropdown.component.scss index 2480334fc..6d4a6c2e5 100644 --- a/frontend/app/framework/angular/forms/editors/dropdown.component.scss +++ b/frontend/app/framework/angular/forms/editors/dropdown.component.scss @@ -33,8 +33,11 @@ $color-input-disabled: #eef1f4; .control-dropdown-item { @include absolute(0, 1.75rem, 0, 0); + align-items: center; + display: flex; line-height: 1.2rem; overflow: hidden; + padding-top: 0; padding-bottom: 0; pointer-events: none; position: absolute;