Browse Source

Temp

pull/382/head
Sebastian Stehle 7 years ago
parent
commit
f42a47d9ae
  1. 8
      src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs
  2. 33
      src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs
  3. 4
      src/Squidex.Shared/Permissions.cs
  4. 2
      src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  5. 4
      src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs
  6. 23
      src/Squidex/Areas/Api/Controllers/Contents/Helper.cs
  7. 21
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs
  8. 2
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs
  9. 80
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs

8
src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs

@ -50,6 +50,14 @@ namespace Squidex.Domain.Apps.Core.Contents
return new Workflows(With(Guid.Empty, workflow));
}
[Pure]
public Workflows Set(Guid id, Workflow workflow)
{
Guard.NotNull(workflow, nameof(workflow));
return new Workflows(With(id, workflow));
}
[Pure]
public Workflows Update(Guid id, Workflow workflow)
{

33
src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs

@ -34,21 +34,21 @@ namespace Squidex.Domain.Apps.Entities.Contents
public async Task<StatusInfo[]> GetAllAsync(ISchemaEntity schema)
{
var workflow = await GetWorkflowAsync(schema.AppId.Id);
var workflow = await GetWorkflowAsync(schema.AppId.Id, schema.Id);
return workflow.Steps.Select(x => new StatusInfo(x.Key, GetColor(x.Value))).ToArray();
}
public async Task<bool> CanMoveToAsync(IContentEntity content, Status next, ClaimsPrincipal user)
{
var workflow = await GetWorkflowAsync(content.AppId.Id);
var workflow = await GetWorkflowAsync(content.AppId.Id, content.SchemaId.Id);
return workflow.TryGetTransition(content.Status, next, out var transition) && CanUse(transition, content, user);
}
public async Task<bool> CanUpdateAsync(IContentEntity content)
{
var workflow = await GetWorkflowAsync(content.AppId.Id);
var workflow = await GetWorkflowAsync(content.AppId.Id, content.SchemaId.Id);
if (workflow.TryGetStep(content.Status, out var step))
{
@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
public async Task<StatusInfo> GetInfoAsync(IContentEntity content)
{
var workflow = await GetWorkflowAsync(content.AppId.Id);
var workflow = await GetWorkflowAsync(content.AppId.Id, content.SchemaId.Id);
if (workflow.TryGetStep(content.Status, out var step))
{
@ -72,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
public async Task<StatusInfo> GetInitialStatusAsync(ISchemaEntity schema)
{
var workflow = await GetWorkflowAsync(schema.AppId.Id);
var workflow = await GetWorkflowAsync(schema.AppId.Id, schema.Id);
var (status, step) = workflow.GetInitialStep();
@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
var result = new List<StatusInfo>();
var workflow = await GetWorkflowAsync(content.AppId.Id);
var workflow = await GetWorkflowAsync(content.AppId.Id, content.SchemaId.Id);
foreach (var (to, step, transition) in workflow.GetTransitions(content.Status))
{
@ -114,11 +114,28 @@ namespace Squidex.Domain.Apps.Entities.Contents
return true;
}
private async Task<Workflow> GetWorkflowAsync(Guid appId)
private async Task<Workflow> GetWorkflowAsync(Guid appId, Guid schemaId)
{
Workflow result = null;
var app = await appProvider.GetAppAsync(appId);
return app?.Workflows.GetFirst();
if (app != null)
{
result = app.Workflows.Values.FirstOrDefault(x => x.SchemaIds.Contains(schemaId));
if (result == null)
{
result = app.Workflows.Values.FirstOrDefault(x => x.SchemaIds.Count == 0);
}
}
if (result == null)
{
result = Workflow.Default;
}
return result;
}
private static string GetColor(WorkflowStep step)

4
src/Squidex.Shared/Permissions.cs

@ -121,8 +121,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 AppContentsDraftDiscard = "squidex.apps.{app}.contents.{name}.draft.discard";
public const string AppContentsDraftPublish = "squidex.apps.{app}.contents.{name}.draft.publish";
public const string AppContentsDelete = "squidex.apps.{app}.contents.{name}.delete";
public const string AppApi = "squidex.apps.{app}.api";

2
src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -396,7 +396,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[HttpPut]
[Route("content/{app}/{name}/{id}/discard/")]
[ProducesResponseType(typeof(ContentsDto), 200)]
[ApiPermission(Permissions.AppContentsDiscard)]
[ApiPermission(Permissions.AppContentsDraftDiscard)]
[ApiCosts(1)]
public async Task<IActionResult> DiscardDraft(string app, string name, Guid id)
{

4
src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs

@ -194,7 +194,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator
operation.AddResponse("204", $"{schemaName} content status changed.", contentSchema);
operation.AddResponse("400", "Content data valid.");
AddSecurity(operation, Permissions.AppContentsStatus);
AddSecurity(operation, Permissions.AppContentsMove);
});
}
@ -209,7 +209,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator
operation.AddResponse("400", "No pending draft.");
operation.AddResponse("200", $"{schemaName} content status changed.", contentSchema);
AddSecurity(operation, Permissions.AppContentsDiscard);
AddSecurity(operation, Permissions.AppContentsDraftDiscard);
});
}

23
src/Squidex/Areas/Api/Controllers/Contents/Helper.cs

@ -1,23 +0,0 @@
// ==========================================================================
// 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.Name);
return Permissions.ForApp(id, app, schema);
}
}
}

21
src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs

@ -122,12 +122,12 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
if (IsPending)
{
if (controller.HasPermission(Permissions.AppContentsDiscard, app, schema))
if (controller.HasPermission(Permissions.AppContentsDraftDiscard, app, schema))
{
AddPutLink("draft/discard", controller.Url<ContentsController>(x => nameof(x.DiscardDraft), values));
}
if (controller.HasPermission(Helper.StatusPermission(app, schema, Status.Published)))
if (controller.HasPermission(Permissions.AppContentsDraftPublish, app, schema))
{
AddPutLink("draft/publish", controller.Url<ContentsController>(x => nameof(x.PutContentStatus), values));
}
@ -146,24 +146,21 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
}
AddPatchLink("patch", controller.Url<ContentsController>(x => nameof(x.PatchContent), values));
}
if (controller.HasPermission(Permissions.AppContentsDelete, app, schema))
{
AddDeleteLink("delete", controller.Url<ContentsController>(x => nameof(x.DeleteContent), values));
}
if (content.Nexts != null)
{
foreach (var next in content.Nexts)
if (content.Nexts != null)
{
if (controller.HasPermission(Helper.StatusPermission(app, schema, next.Status)))
foreach (var next in content.Nexts)
{
AddPutLink($"status/{next.Status}", controller.Url<ContentsController>(x => nameof(x.PutContentStatus), values), next.Color);
}
}
}
if (controller.HasPermission(Permissions.AppContentsDelete, app, schema))
{
AddDeleteLink("delete", controller.Url<ContentsController>(x => nameof(x.DeleteContent), values));
}
return this;
}
}

2
src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs

@ -80,7 +80,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
{
AddPostLink("create", controller.Url<ContentsController>(x => nameof(x.PostContent), values));
if (controller.HasPermission(Helper.StatusPermission(app, schema, Status.Published)))
if (controller.HasPermission(Permissions.AppContentsCreatePublished, app, schema))
{
AddPostLink("create/publish", controller.Url<ContentsController>(x => nameof(x.PostContent), values) + "?publish=true");
}

80
tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs

@ -23,6 +23,8 @@ namespace Squidex.Domain.Apps.Entities.Contents
public class DynamicContentWorkflowTests
{
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app");
private readonly NamedId<Guid> schemaId = NamedId.Of(Guid.NewGuid(), "my-schema");
private readonly NamedId<Guid> simpleSchemaId = NamedId.Of(Guid.NewGuid(), "my-simple-schema");
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
private readonly IAppEntity appEntity = A.Fake<IAppEntity>();
private readonly DynamicContentWorkflow sut;
@ -56,13 +58,38 @@ namespace Squidex.Domain.Apps.Entities.Contents
StatusColors.Published)
});
private readonly Workflow simpleWorkflow;
public DynamicContentWorkflowTests()
{
simpleWorkflow = new Workflow(
Status.Draft,
new Dictionary<Status, WorkflowStep>
{
[Status.Draft] =
new WorkflowStep(
new Dictionary<Status, WorkflowTransition>
{
[Status.Published] = new WorkflowTransition()
},
StatusColors.Draft),
[Status.Published] =
new WorkflowStep(
new Dictionary<Status, WorkflowTransition>
{
[Status.Draft] = new WorkflowTransition()
},
StatusColors.Published)
},
new List<Guid> { simpleSchemaId.Id });
var workflows = Workflows.Empty.Set(workflow).Set(Guid.NewGuid(), simpleWorkflow);
A.CallTo(() => appProvider.GetAppAsync(appId.Id))
.Returns(appEntity);
A.CallTo(() => appEntity.Workflows)
.Returns(Workflows.Empty.Set(workflow));
.Returns(workflows);
sut = new DynamicContentWorkflow(new JintScriptEngine(), appProvider);
}
@ -229,24 +256,67 @@ namespace Squidex.Domain.Apps.Entities.Contents
result.Should().BeEquivalentTo(expected);
}
private ISchemaEntity CreateSchema()
[Fact]
public async Task Should_return_all_statuses_for_simple_schema_workflow()
{
var expected = new[]
{
new StatusInfo(Status.Draft, StatusColors.Draft),
new StatusInfo(Status.Published, StatusColors.Published)
};
var result = await sut.GetAllAsync(CreateSchema(true));
result.Should().BeEquivalentTo(expected);
}
[Fact]
public async Task Should_return_all_statuses_for_default_workflow_when_no_workflow_configured()
{
A.CallTo(() => appEntity.Workflows).Returns(Workflows.Empty);
var expected = new[]
{
new StatusInfo(Status.Archived, StatusColors.Archived),
new StatusInfo(Status.Draft, StatusColors.Draft),
new StatusInfo(Status.Published, StatusColors.Published)
};
var result = await sut.GetAllAsync(CreateSchema(true));
result.Should().BeEquivalentTo(expected);
}
private ISchemaEntity CreateSchema(bool simple = false)
{
var schema = A.Fake<ISchemaEntity>();
A.CallTo(() => schema.AppId).Returns(appId);
A.CallTo(() => schema.Id).Returns(simple ? simpleSchemaId.Id : schemaId.Id);
return schema;
}
private IContentEntity CreateContent(Status status, int value)
private IContentEntity CreateContent(Status status, int value, bool simple = false)
{
var data =
var content = new ContentEntity { AppId = appId, Status = status };
if (simple)
{
content.SchemaId = simpleSchemaId;
}
else
{
content.SchemaId = schemaId;
}
content.DataDraft =
new NamedContentData()
.AddField("field",
new ContentFieldData()
.AddValue("iv", value));
return new ContentEntity { AppId = appId, Status = status, DataDraft = data };
return content;
}
private ClaimsPrincipal User(string role)

Loading…
Cancel
Save