diff --git a/backend/src/Squidex.Web/ApiPermissionAttribute.cs b/backend/src/Squidex.Web/ApiPermissionAttribute.cs index c48fd41d8..557958579 100644 --- a/backend/src/Squidex.Web/ApiPermissionAttribute.cs +++ b/backend/src/Squidex.Web/ApiPermissionAttribute.cs @@ -15,7 +15,7 @@ using Squidex.Shared; namespace Squidex.Web { - public sealed class ApiPermissionAttribute : AuthorizeAttribute, IAsyncActionFilter, IAllowAnonymous + public class ApiPermissionAttribute : AuthorizeAttribute, IAsyncActionFilter { private readonly string[] permissionIds; diff --git a/backend/src/Squidex.Web/ApiPermissionOrAnonymousAttribute.cs b/backend/src/Squidex.Web/ApiPermissionOrAnonymousAttribute.cs new file mode 100644 index 000000000..9175d9aa9 --- /dev/null +++ b/backend/src/Squidex.Web/ApiPermissionOrAnonymousAttribute.cs @@ -0,0 +1,19 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.AspNetCore.Authorization; + +namespace Squidex.Web +{ + public class ApiPermissionOrAnonymousAttribute : ApiPermissionAttribute, IAllowAnonymous + { + public ApiPermissionOrAnonymousAttribute(params string[] ids) + : base(ids) + { + } + } +} diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs index d3efa2fcc..17070ecf8 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs @@ -42,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpGet] [Route("apps/{app}/clients/")] [ProducesResponseType(typeof(ClientsDto), 200)] - [ApiPermission(Permissions.AppClientsRead)] + [ApiPermissionOrAnonymous(Permissions.AppClientsRead)] [ApiCosts(0)] public IActionResult GetClients(string app) { @@ -73,7 +73,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPost] [Route("apps/{app}/clients/")] [ProducesResponseType(typeof(ClientsDto), 201)] - [ApiPermission(Permissions.AppClientsCreate)] + [ApiPermissionOrAnonymous(Permissions.AppClientsCreate)] [ApiCosts(1)] public async Task PostClient(string app, [FromBody] CreateClientDto request) { @@ -101,7 +101,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPut] [Route("apps/{app}/clients/{id}/")] [ProducesResponseType(typeof(ClientsDto), 200)] - [ApiPermission(Permissions.AppClientsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppClientsUpdate)] [ApiCosts(1)] public async Task PutClient(string app, string id, [FromBody] UpdateClientDto request) { @@ -127,7 +127,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpDelete] [Route("apps/{app}/clients/{id}/")] [ProducesResponseType(typeof(ClientsDto), 200)] - [ApiPermission(Permissions.AppClientsDelete)] + [ApiPermissionOrAnonymous(Permissions.AppClientsDelete)] [ApiCosts(1)] public async Task DeleteClient(string app, string id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs index f9699bee7..18a330b68 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs @@ -48,7 +48,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpGet] [Route("apps/{app}/contributors/")] [ProducesResponseType(typeof(ContributorsDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public IActionResult GetContributors(string app) { @@ -75,7 +75,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPost] [Route("apps/{app}/contributors/")] [ProducesResponseType(typeof(ContributorsDto), 201)] - [ApiPermission(Permissions.AppContributorsAssign)] + [ApiPermissionOrAnonymous(Permissions.AppContributorsAssign)] [ApiCosts(1)] public async Task PostContributor(string app, [FromBody] AssignContributorDto request) { @@ -98,7 +98,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpDelete] [Route("apps/{app}/contributors/{id}/")] [ProducesResponseType(typeof(ContributorsDto), 200)] - [ApiPermission(Permissions.AppContributorsRevoke)] + [ApiPermissionOrAnonymous(Permissions.AppContributorsRevoke)] [ApiCosts(1)] public async Task DeleteContributor(string app, string id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs index 2ce73190e..4595058b8 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs @@ -42,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpGet] [Route("apps/{app}/languages/")] [ProducesResponseType(typeof(AppLanguagesDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public IActionResult GetLanguages(string app) { @@ -69,7 +69,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPost] [Route("apps/{app}/languages/")] [ProducesResponseType(typeof(AppLanguagesDto), 201)] - [ApiPermission(Permissions.AppLanguagesCreate)] + [ApiPermissionOrAnonymous(Permissions.AppLanguagesCreate)] [ApiCosts(1)] public async Task PostLanguage(string app, [FromBody] AddLanguageDto request) { @@ -94,7 +94,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPut] [Route("apps/{app}/languages/{language}/")] [ProducesResponseType(typeof(AppLanguagesDto), 200)] - [ApiPermission(Permissions.AppLanguagesUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppLanguagesUpdate)] [ApiCosts(1)] public async Task PutLanguage(string app, string language, [FromBody] UpdateLanguageDto request) { @@ -118,7 +118,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpDelete] [Route("apps/{app}/languages/{language}/")] [ProducesResponseType(typeof(AppLanguagesDto), 200)] - [ApiPermission(Permissions.AppLanguagesDelete)] + [ApiPermissionOrAnonymous(Permissions.AppLanguagesDelete)] [ApiCosts(1)] public async Task DeleteLanguage(string app, string language) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs index da84d6945..94ee344d3 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs @@ -43,7 +43,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpGet] [Route("apps/{app}/patterns/")] [ProducesResponseType(typeof(PatternsDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public IActionResult GetPatterns(string app) { @@ -70,7 +70,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPost] [Route("apps/{app}/patterns/")] [ProducesResponseType(typeof(PatternsDto), 201)] - [ApiPermission(Permissions.AppPatternsCreate)] + [ApiPermissionOrAnonymous(Permissions.AppPatternsCreate)] [ApiCosts(1)] public async Task PostPattern(string app, [FromBody] UpdatePatternDto request) { @@ -95,7 +95,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPut] [Route("apps/{app}/patterns/{id}/")] [ProducesResponseType(typeof(PatternsDto), 200)] - [ApiPermission(Permissions.AppPatternsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppPatternsUpdate)] [ApiCosts(1)] public async Task PutPattern(string app, Guid id, [FromBody] UpdatePatternDto request) { @@ -121,7 +121,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpDelete] [Route("apps/{app}/patterns/{id}/")] [ProducesResponseType(typeof(PatternsDto), 200)] - [ApiPermission(Permissions.AppPatternsDelete)] + [ApiPermissionOrAnonymous(Permissions.AppPatternsDelete)] [ApiCosts(1)] public async Task DeletePattern(string app, Guid id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs index 62eafb071..53292cf69 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs @@ -43,7 +43,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpGet] [Route("apps/{app}/roles/")] [ProducesResponseType(typeof(RolesDto), 200)] - [ApiPermission(Permissions.AppRolesRead)] + [ApiPermissionOrAnonymous(Permissions.AppRolesRead)] [ApiCosts(0)] public IActionResult GetRoles(string app) { @@ -68,7 +68,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpGet] [Route("apps/{app}/roles/permissions")] [ProducesResponseType(typeof(string[]), 200)] - [ApiPermission(Permissions.AppRolesRead)] + [ApiPermissionOrAnonymous(Permissions.AppRolesRead)] [ApiCosts(0)] public IActionResult GetPermissions(string app) { @@ -95,7 +95,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPost] [Route("apps/{app}/roles/")] [ProducesResponseType(typeof(RolesDto), 201)] - [ApiPermission(Permissions.AppRolesCreate)] + [ApiPermissionOrAnonymous(Permissions.AppRolesCreate)] [ApiCosts(1)] public async Task PostRole(string app, [FromBody] AddRoleDto request) { @@ -120,7 +120,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPut] [Route("apps/{app}/roles/{roleName}/")] [ProducesResponseType(typeof(RolesDto), 200)] - [ApiPermission(Permissions.AppRolesUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppRolesUpdate)] [ApiCosts(1)] public async Task PutRole(string app, string roleName, [FromBody] UpdateRoleDto request) { @@ -144,7 +144,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpDelete] [Route("apps/{app}/roles/{roleName}/")] [ProducesResponseType(typeof(RolesDto), 200)] - [ApiPermission(Permissions.AppRolesDelete)] + [ApiPermissionOrAnonymous(Permissions.AppRolesDelete)] [ApiCosts(1)] public async Task DeleteRole(string app, string roleName) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppWorkflowsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppWorkflowsController.cs index 5a5099d4e..6dc5308b8 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppWorkflowsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppWorkflowsController.cs @@ -44,7 +44,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpGet] [Route("apps/{app}/workflows/")] [ProducesResponseType(typeof(WorkflowsDto), 200)] - [ApiPermission(Permissions.AppWorkflowsRead)] + [ApiPermissionOrAnonymous(Permissions.AppWorkflowsRead)] [ApiCosts(0)] public IActionResult GetWorkflows(string app) { @@ -71,7 +71,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPost] [Route("apps/{app}/workflows/")] [ProducesResponseType(typeof(WorkflowsDto), 200)] - [ApiPermission(Permissions.AppWorkflowsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppWorkflowsUpdate)] [ApiCosts(1)] public async Task PostWorkflow(string app, [FromBody] AddWorkflowDto request) { @@ -96,7 +96,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPut] [Route("apps/{app}/workflows/{id}")] [ProducesResponseType(typeof(WorkflowsDto), 200)] - [ApiPermission(Permissions.AppWorkflowsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppWorkflowsUpdate)] [ApiCosts(1)] public async Task PutWorkflow(string app, Guid id, [FromBody] UpdateWorkflowDto request) { @@ -119,7 +119,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpDelete] [Route("apps/{app}/workflows/{id}")] [ProducesResponseType(typeof(WorkflowsDto), 200)] - [ApiPermission(Permissions.AppWorkflowsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppWorkflowsUpdate)] [ApiCosts(1)] public async Task DeleteWorkflow(string app, Guid id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs index 32392e1df..45afd435c 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs @@ -152,7 +152,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPut] [Route("apps/{app}/")] [ProducesResponseType(typeof(AppDto), 200)] - [ApiPermission(Permissions.AppUpdateGeneral)] + [ApiPermissionOrAnonymous(Permissions.AppUpdateGeneral)] [ApiCosts(0)] public async Task UpdateApp(string app, [FromBody] UpdateAppDto request) { @@ -173,7 +173,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpPost] [Route("apps/{app}/image")] [ProducesResponseType(typeof(AppDto), 200)] - [ApiPermission(Permissions.AppUpdateImage)] + [ApiPermissionOrAnonymous(Permissions.AppUpdateImage)] [ApiCosts(0)] public async Task UploadImage(string app, IFormFile file) { @@ -264,7 +264,7 @@ namespace Squidex.Areas.Api.Controllers.Apps [HttpDelete] [Route("apps/{app}/image")] [ProducesResponseType(typeof(AppDto), 200)] - [ApiPermission(Permissions.AppUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppUpdate)] [ApiCosts(0)] public async Task DeleteImage(string app) { @@ -283,7 +283,7 @@ namespace Squidex.Areas.Api.Controllers.Apps /// [HttpDelete] [Route("apps/{app}/")] - [ApiPermission(Permissions.AppDelete)] + [ApiPermissionOrAnonymous(Permissions.AppDelete)] [ApiCosts(0)] public async Task DeleteApp(string app) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs index ba46c2964..585f402a9 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetFoldersController.cs @@ -48,7 +48,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [HttpGet] [Route("apps/{app}/assets/folders", Order = -1)] [ProducesResponseType(typeof(AssetsDto), 200)] - [ApiPermission(Permissions.AppAssetsRead)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsRead)] [ApiCosts(1)] public async Task GetAssetFolders(string app, [FromQuery] Guid parentId) { @@ -79,7 +79,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [Route("apps/{app}/assets/folders", Order = -1)] [ProducesResponseType(typeof(AssetDto), 201)] [AssetRequestSizeLimit] - [ApiPermission(Permissions.AppAssetsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsUpdate)] [ApiCosts(1)] public async Task PostAssetFolder(string app, [FromBody] CreateAssetFolderDto request) { @@ -105,7 +105,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [Route("apps/{app}/assets/folders/{id}/", Order = -1)] [ProducesResponseType(typeof(AssetDto), 200)] [AssetRequestSizeLimit] - [ApiPermission(Permissions.AppAssetsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsUpdate)] [ApiCosts(1)] public async Task PutAssetFolder(string app, Guid id, [FromBody] RenameAssetFolderDto request) { @@ -130,7 +130,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [Route("apps/{app}/assets/folders/{id}/parent", Order = -1)] [ProducesResponseType(typeof(AssetDto), 200)] [AssetRequestSizeLimit] - [ApiPermission(Permissions.AppAssetsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsUpdate)] [ApiCosts(1)] public async Task PutAssetFolderParent(string app, Guid id, [FromBody] MoveAssetItemDto request) { @@ -152,7 +152,7 @@ namespace Squidex.Areas.Api.Controllers.Assets /// [HttpDelete] [Route("apps/{app}/assets/folders/{id}/", Order = -1)] - [ApiPermission(Permissions.AppAssetsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsUpdate)] [ApiCosts(1)] public async Task DeleteAssetFolder(string app, Guid id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs index a6312f470..6f86b3d40 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs @@ -64,7 +64,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [HttpGet] [Route("apps/{app}/assets/tags")] [ProducesResponseType(typeof(Dictionary), 200)] - [ApiPermission(Permissions.AppAssetsRead)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsRead)] [ApiCosts(1)] public async Task GetTags(string app) { @@ -92,7 +92,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [HttpGet] [Route("apps/{app}/assets/")] [ProducesResponseType(typeof(AssetsDto), 200)] - [ApiPermission(Permissions.AppAssetsRead)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsRead)] [ApiCosts(1)] public async Task GetAssets(string app, [FromQuery] Guid? parentId, [FromQuery] string? ids = null, [FromQuery] string? q = null) { @@ -121,7 +121,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [HttpPost] [Route("apps/{app}/assets/query")] [ProducesResponseType(typeof(AssetsDto), 200)] - [ApiPermission(Permissions.AppAssetsRead)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsRead)] [ApiCosts(1)] public async Task GetAssetsPost(string app, [FromBody] QueryDto query) { @@ -147,7 +147,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [HttpGet] [Route("apps/{app}/assets/{id}/")] [ProducesResponseType(typeof(AssetDto), 200)] - [ApiPermission(Permissions.AppAssetsRead)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsRead)] [ApiCosts(1)] public async Task GetAsset(string app, Guid id) { @@ -184,7 +184,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [Route("apps/{app}/assets/")] [ProducesResponseType(typeof(AssetDto), 201)] [AssetRequestSizeLimit] - [ApiPermission(Permissions.AppAssetsCreate)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsCreate)] [ApiCosts(1)] public async Task PostAsset(string app, [FromQuery] Guid parentId, IFormFile file) { @@ -214,7 +214,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [HttpPut] [Route("apps/{app}/assets/{id}/content/")] [ProducesResponseType(typeof(AssetDto), 200)] - [ApiPermission(Permissions.AppAssetsUpload)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsUpload)] [ApiCosts(1)] public async Task PutAssetContent(string app, Guid id, IFormFile file) { @@ -242,7 +242,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [Route("apps/{app}/assets/{id}/")] [ProducesResponseType(typeof(AssetDto), 200)] [AssetRequestSizeLimit] - [ApiPermission(Permissions.AppAssetsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsUpdate)] [ApiCosts(1)] public async Task PutAsset(string app, Guid id, [FromBody] AnnotateAssetDto request) { @@ -267,7 +267,7 @@ namespace Squidex.Areas.Api.Controllers.Assets [Route("apps/{app}/assets/{id}/parent")] [ProducesResponseType(typeof(AssetDto), 200)] [AssetRequestSizeLimit] - [ApiPermission(Permissions.AppAssetsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsUpdate)] [ApiCosts(1)] public async Task PutAssetParent(string app, Guid id, [FromBody] MoveAssetItemDto request) { @@ -289,7 +289,7 @@ namespace Squidex.Areas.Api.Controllers.Assets /// [HttpDelete] [Route("apps/{app}/assets/{id}/")] - [ApiPermission(Permissions.AppAssetsDelete)] + [ApiPermissionOrAnonymous(Permissions.AppAssetsDelete)] [ApiCosts(1)] public async Task DeleteAsset(string app, Guid id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs index d73e03c19..faf4faf5b 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupsController.cs @@ -44,7 +44,7 @@ namespace Squidex.Areas.Api.Controllers.Backups [HttpGet] [Route("apps/{app}/backups/")] [ProducesResponseType(typeof(BackupJobsDto), 200)] - [ApiPermission(Permissions.AppBackupsRead)] + [ApiPermissionOrAnonymous(Permissions.AppBackupsRead)] [ApiCosts(0)] public async Task GetBackups(string app) { @@ -66,7 +66,7 @@ namespace Squidex.Areas.Api.Controllers.Backups [HttpPost] [Route("apps/{app}/backups/")] [ProducesResponseType(typeof(List), 200)] - [ApiPermission(Permissions.AppBackupsCreate)] + [ApiPermissionOrAnonymous(Permissions.AppBackupsCreate)] [ApiCosts(0)] public IActionResult PostBackup(string app) { @@ -87,7 +87,7 @@ namespace Squidex.Areas.Api.Controllers.Backups [HttpDelete] [Route("apps/{app}/backups/{id}")] [ProducesResponseType(typeof(List), 200)] - [ApiPermission(Permissions.AppBackupsDelete)] + [ApiPermissionOrAnonymous(Permissions.AppBackupsDelete)] [ApiCosts(0)] public async Task DeleteBackup(string app, Guid id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs index 792f038a5..b64d9e867 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs @@ -49,7 +49,7 @@ namespace Squidex.Areas.Api.Controllers.Comments [HttpGet] [Route("apps/{app}/comments/{commentsId}")] [ProducesResponseType(typeof(CommentsDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task GetComments(string app, string commentsId, [FromQuery] long version = EtagVersion.Any) { @@ -79,7 +79,7 @@ namespace Squidex.Areas.Api.Controllers.Comments [HttpPost] [Route("apps/{app}/comments/{commentsId}")] [ProducesResponseType(typeof(CommentDto), 201)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task PostComment(string app, string commentsId, [FromBody] UpsertCommentDto request) { @@ -106,7 +106,7 @@ namespace Squidex.Areas.Api.Controllers.Comments /// [HttpPut] [Route("apps/{app}/comments/{commentsId}/{commentId}")] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task PutComment(string app, string commentsId, Guid commentId, [FromBody] UpsertCommentDto request) { @@ -127,7 +127,7 @@ namespace Squidex.Areas.Api.Controllers.Comments /// [HttpDelete] [Route("apps/{app}/comments/{commentsId}/{commentId}")] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task DeleteComment(string app, string commentsId, Guid commentId) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs index 8d222b9d6..32daf8efb 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs @@ -268,7 +268,7 @@ namespace Squidex.Areas.Api.Controllers.Contents /// [HttpGet] [Route("content/{app}/{name}/{id}/{version}/")] - [ApiPermission(Permissions.AppContentsRead)] + [ApiPermissionOrAnonymous(Permissions.AppContentsRead)] [ApiCosts(1)] public async Task GetContentVersion(string app, string name, Guid id, int version) { @@ -297,7 +297,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPost] [Route("content/{app}/{name}/")] [ProducesResponseType(typeof(ContentsDto), 201)] - [ApiPermission(Permissions.AppContentsCreate)] + [ApiPermissionOrAnonymous(Permissions.AppContentsCreate)] [ApiCosts(1)] public async Task PostContent(string app, string name, [FromBody] NamedContentData request, [FromQuery] bool publish = false) { @@ -325,7 +325,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPost] [Route("content/{app}/{name}/import")] [ProducesResponseType(typeof(BulkResultDto[]), 200)] - [ApiPermission(Permissions.AppContentsCreate)] + [ApiPermissionOrAnonymous(Permissions.AppContentsCreate)] [ApiCosts(5)] public async Task PostContents(string app, string name, [FromBody] ImportContentsDto request) { @@ -356,7 +356,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPost] [Route("content/{app}/{name}/bulk")] [ProducesResponseType(typeof(BulkResultDto[]), 200)] - [ApiPermission(Permissions.AppContents)] + [ApiPermissionOrAnonymous(Permissions.AppContents)] [ApiCosts(5)] public async Task BulkContents(string app, string name, [FromBody] BulkUpdateDto request) { @@ -388,7 +388,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPut] [Route("content/{app}/{name}/{id}/")] [ProducesResponseType(typeof(ContentsDto), 200)] - [ApiPermission(Permissions.AppContentsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppContentsUpdate)] [ApiCosts(1)] public async Task PutContent(string app, string name, Guid id, [FromBody] NamedContentData request) { @@ -417,7 +417,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPatch] [Route("content/{app}/{name}/{id}/")] [ProducesResponseType(typeof(ContentsDto), 200)] - [ApiPermission(Permissions.AppContentsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppContentsUpdate)] [ApiCosts(1)] public async Task PatchContent(string app, string name, Guid id, [FromBody] NamedContentData request) { @@ -446,7 +446,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPut] [Route("content/{app}/{name}/{id}/status/")] [ProducesResponseType(typeof(ContentsDto), 200)] - [ApiPermission(Permissions.AppContentsUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppContentsUpdate)] [ApiCosts(1)] public async Task PutContentStatus(string app, string name, Guid id, ChangeStatusDto request) { @@ -473,7 +473,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpPost] [Route("content/{app}/{name}/{id}/draft/")] [ProducesResponseType(typeof(ContentsDto), 200)] - [ApiPermission(Permissions.AppContentsVersionCreate)] + [ApiPermissionOrAnonymous(Permissions.AppContentsVersionCreate)] [ApiCosts(1)] public async Task CreateDraft(string app, string name, Guid id) { @@ -500,7 +500,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [HttpDelete] [Route("content/{app}/{name}/{id}/draft/")] [ProducesResponseType(typeof(ContentsDto), 200)] - [ApiPermission(Permissions.AppContentsDelete)] + [ApiPermissionOrAnonymous(Permissions.AppContentsDelete)] [ApiCosts(1)] public async Task DeleteVersion(string app, string name, Guid id) { @@ -526,7 +526,7 @@ namespace Squidex.Areas.Api.Controllers.Contents /// [HttpDelete] [Route("content/{app}/{name}/{id}/")] - [ApiPermission(Permissions.AppContentsDelete)] + [ApiPermissionOrAnonymous(Permissions.AppContentsDelete)] [ApiCosts(1)] public async Task DeleteContent(string app, string name, Guid id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs b/backend/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs index dda051f83..a12a3f265 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs @@ -42,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.History [HttpGet] [Route("apps/{app}/history/")] [ProducesResponseType(typeof(HistoryEventDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0.1)] public async Task GetHistory(string app, string channel) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Ping/PingController.cs b/backend/src/Squidex/Areas/Api/Controllers/Ping/PingController.cs index 6771038f2..fa83c3818 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Ping/PingController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Ping/PingController.cs @@ -68,7 +68,7 @@ namespace Squidex.Areas.Api.Controllers.Ping /// [HttpGet] [Route("ping/{app}/")] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public IActionResult GetAppPing(string app) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs b/backend/src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs index 2194f56c7..b58a0de1d 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs @@ -45,7 +45,7 @@ namespace Squidex.Areas.Api.Controllers.Plans [HttpGet] [Route("apps/{app}/plans/")] [ProducesResponseType(typeof(AppPlansDto), 200)] - [ApiPermission(Permissions.AppPlansRead)] + [ApiPermissionOrAnonymous(Permissions.AppPlansRead)] [ApiCosts(0)] public IActionResult GetPlans(string app) { @@ -74,7 +74,7 @@ namespace Squidex.Areas.Api.Controllers.Plans [HttpPut] [Route("apps/{app}/plan/")] [ProducesResponseType(typeof(PlanChangedDto), 200)] - [ApiPermission(Permissions.AppPlansChange)] + [ApiPermissionOrAnonymous(Permissions.AppPlansChange)] [ApiCosts(0)] public async Task PutPlan(string app, [FromBody] ChangePlanDto request) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs index d34c674d0..e8b6f72f6 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs @@ -85,7 +85,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpGet] [Route("apps/{app}/rules/")] [ProducesResponseType(typeof(RulesDto), 200)] - [ApiPermission(Permissions.AppRulesRead)] + [ApiPermissionOrAnonymous(Permissions.AppRulesRead)] [ApiCosts(1)] public async Task GetRules(string app) { @@ -114,7 +114,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpPost] [Route("apps/{app}/rules/")] [ProducesResponseType(typeof(RuleDto), 201)] - [ApiPermission(Permissions.AppRulesCreate)] + [ApiPermissionOrAnonymous(Permissions.AppRulesCreate)] [ApiCosts(1)] public async Task PostRule(string app, [FromBody] CreateRuleDto request) { @@ -135,7 +135,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpDelete] [Route("apps/{app}/rules/run")] [ProducesResponseType(204)] - [ApiPermission(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiCosts(1)] public async Task DeleteRuleRun(string app) { @@ -158,7 +158,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpPut] [Route("apps/{app}/rules/{id}/")] [ProducesResponseType(typeof(RuleDto), 200)] - [ApiPermission(Permissions.AppRulesUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppRulesUpdate)] [ApiCosts(1)] public async Task PutRule(string app, Guid id, [FromBody] UpdateRuleDto request) { @@ -181,7 +181,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpPut] [Route("apps/{app}/rules/{id}/enable/")] [ProducesResponseType(typeof(RuleDto), 200)] - [ApiPermission(Permissions.AppRulesDisable)] + [ApiPermissionOrAnonymous(Permissions.AppRulesDisable)] [ApiCosts(1)] public async Task EnableRule(string app, Guid id) { @@ -204,7 +204,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpPut] [Route("apps/{app}/rules/{id}/disable/")] [ProducesResponseType(typeof(RuleDto), 200)] - [ApiPermission(Permissions.AppRulesDisable)] + [ApiPermissionOrAnonymous(Permissions.AppRulesDisable)] [ApiCosts(1)] public async Task DisableRule(string app, Guid id) { @@ -226,7 +226,7 @@ namespace Squidex.Areas.Api.Controllers.Rules /// [HttpPut] [Route("apps/{app}/rules/{id}/trigger/")] - [ApiPermission(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiCosts(1)] public async Task TriggerRule(string app, Guid id) { @@ -248,7 +248,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpPut] [Route("apps/{app}/rules/{id}/run")] [ProducesResponseType(204)] - [ApiPermission(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiCosts(1)] public async Task PutRuleRun(string app, Guid id) { @@ -268,7 +268,7 @@ namespace Squidex.Areas.Api.Controllers.Rules /// [HttpDelete] [Route("apps/{app}/rules/{id}/")] - [ApiPermission(Permissions.AppRulesDelete)] + [ApiPermissionOrAnonymous(Permissions.AppRulesDelete)] [ApiCosts(1)] public async Task DeleteRule(string app, Guid id) { @@ -291,7 +291,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpGet] [Route("apps/{app}/rules/events/")] [ProducesResponseType(typeof(RuleEventsDto), 200)] - [ApiPermission(Permissions.AppRulesRead)] + [ApiPermissionOrAnonymous(Permissions.AppRulesRead)] [ApiCosts(0)] public async Task GetEvents(string app, [FromQuery] Guid? ruleId = null, [FromQuery] int skip = 0, [FromQuery] int take = 20) { @@ -313,7 +313,7 @@ namespace Squidex.Areas.Api.Controllers.Rules /// [HttpPut] [Route("apps/{app}/rules/events/{id}/")] - [ApiPermission(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiCosts(0)] public async Task PutEvent(string app, Guid id) { @@ -340,7 +340,7 @@ namespace Squidex.Areas.Api.Controllers.Rules /// [HttpDelete] [Route("apps/{app}/rules/events/{id}/")] - [ApiPermission(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] [ApiCosts(0)] public async Task DeleteEvent(string app, Guid id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs index 458701778..b9ad5c1b5 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs @@ -42,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPost] [Route("apps/{app}/schemas/{name}/fields/")] [ProducesResponseType(typeof(SchemaDetailsDto), 201)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PostField(string app, string name, [FromBody] AddFieldDto request) { @@ -69,7 +69,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPost] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/")] [ProducesResponseType(typeof(SchemaDetailsDto), 201)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PostNestedField(string app, string name, long parentId, [FromBody] AddFieldDto request) { @@ -94,7 +94,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/ui/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutSchemaUIFields(string app, string name, [FromBody] ConfigureUIFieldsDto request) { @@ -119,7 +119,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/ordering/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutSchemaFieldOrdering(string app, string name, [FromBody] ReorderFieldsDto request) { @@ -145,7 +145,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/ordering/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutNestedFieldOrdering(string app, string name, long parentId, [FromBody] ReorderFieldsDto request) { @@ -171,7 +171,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutField(string app, string name, long id, [FromBody] UpdateFieldDto request) { @@ -198,7 +198,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutNestedField(string app, string name, long parentId, long id, [FromBody] UpdateFieldDto request) { @@ -225,7 +225,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/lock/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task LockField(string app, string name, long id) { @@ -253,7 +253,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/lock/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task LockNestedField(string app, string name, long parentId, long id) { @@ -280,7 +280,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/hide/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task HideField(string app, string name, long id) { @@ -308,7 +308,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/hide/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task HideNestedField(string app, string name, long parentId, long id) { @@ -335,7 +335,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/show/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task ShowField(string app, string name, long id) { @@ -363,7 +363,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/show/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task ShowNestedField(string app, string name, long parentId, long id) { @@ -390,7 +390,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/enable/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task EnableField(string app, string name, long id) { @@ -418,7 +418,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/enable/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task EnableNestedField(string app, string name, long parentId, long id) { @@ -445,7 +445,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/disable/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task DisableField(string app, string name, long id) { @@ -473,7 +473,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/disable/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task DisableNestedField(string app, string name, long parentId, long id) { @@ -498,7 +498,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpDelete] [Route("apps/{app}/schemas/{name}/fields/{id:long}/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task DeleteField(string app, string name, long id) { @@ -524,7 +524,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpDelete] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task DeleteNestedField(string app, string name, long parentId, long id) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs index 68a678d88..b331b1bfe 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs @@ -44,7 +44,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpGet] [Route("apps/{app}/schemas/")] [ProducesResponseType(typeof(SchemasDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task GetSchemas(string app) { @@ -72,7 +72,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpGet] [Route("apps/{app}/schemas/{name}/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task GetSchema(string app, string name) { @@ -115,7 +115,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPost] [Route("apps/{app}/schemas/")] [ProducesResponseType(typeof(SchemaDetailsDto), 201)] - [ApiPermission(Permissions.AppSchemasCreate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasCreate)] [ApiCosts(1)] public async Task PostSchema(string app, [FromBody] CreateSchemaDto request) { @@ -140,7 +140,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutSchema(string app, string name, [FromBody] UpdateSchemaDto request) { @@ -165,7 +165,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/sync")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutSchemaSync(string app, string name, [FromBody] SynchronizeSchemaDto request) { @@ -189,7 +189,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/category")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutCategory(string app, string name, [FromBody] ChangeCategoryDto request) { @@ -213,7 +213,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/preview-urls")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutPreviewUrls(string app, string name, [FromBody] ConfigurePreviewUrlsDto request) { @@ -238,7 +238,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/scripts/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasScripts)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasScripts)] [ApiCosts(1)] public async Task PutScripts(string app, string name, [FromBody] SchemaScriptsDto request) { @@ -261,7 +261,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/publish/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasPublish)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasPublish)] [ApiCosts(1)] public async Task PublishSchema(string app, string name) { @@ -284,7 +284,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [HttpPut] [Route("apps/{app}/schemas/{name}/unpublish/")] [ProducesResponseType(typeof(SchemaDetailsDto), 200)] - [ApiPermission(Permissions.AppSchemasPublish)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasPublish)] [ApiCosts(1)] public async Task UnpublishSchema(string app, string name) { @@ -306,7 +306,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpDelete] [Route("apps/{app}/schemas/{name}/")] - [ApiPermission(Permissions.AppSchemasDelete)] + [ApiPermissionOrAnonymous(Permissions.AppSchemasDelete)] [ApiCosts(1)] public async Task DeleteSchema(string app, string name) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Search/SearchController.cs b/backend/src/Squidex/Areas/Api/Controllers/Search/SearchController.cs index b2c38aa77..c4351a411 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Search/SearchController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Search/SearchController.cs @@ -42,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.Search [HttpGet] [Route("apps/{app}/search/")] [ProducesResponseType(typeof(SearchResultDto[]), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task GetSchemas(string app, [FromQuery] string? query = null) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs b/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs index 8f42efb86..13b744ccd 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs @@ -66,7 +66,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics [HttpGet] [Route("apps/{app}/usages/log/")] [ProducesResponseType(typeof(LogDownloadDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public IActionResult GetLog(string app) { @@ -93,7 +93,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics [HttpGet] [Route("apps/{app}/usages/calls/{fromDate}/{toDate}/")] [ProducesResponseType(typeof(CallsUsageDtoDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task GetUsages(string app, DateTime fromDate, DateTime toDate) { @@ -122,7 +122,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics [HttpGet] [Route("apps/{app}/usages/storage/today/")] [ProducesResponseType(typeof(CurrentStorageDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task GetCurrentStorageSize(string app) { @@ -149,7 +149,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics [HttpGet] [Route("apps/{app}/usages/storage/{fromDate}/{toDate}/")] [ProducesResponseType(typeof(StorageUsagePerDateDto[]), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task GetStorageSizes(string app, DateTime fromDate, DateTime toDate) { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs index 5fc5cd8e4..6aa691b33 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs @@ -40,7 +40,7 @@ namespace Squidex.Areas.Api.Controllers.Translations [HttpPost] [Route("apps/{app}/translations/")] [ProducesResponseType(typeof(TranslationDto), 200)] - [ApiPermission(Permissions.AppCommon)] + [ApiPermissionOrAnonymous(Permissions.AppCommon)] [ApiCosts(0)] public async Task GetLanguages(string app, [FromBody] TranslateDto request) { diff --git a/frontend/app-config/webpack.config.js b/frontend/app-config/webpack.config.js index e2e13750f..609ef0da8 100644 --- a/frontend/app-config/webpack.config.js +++ b/frontend/app-config/webpack.config.js @@ -227,6 +227,8 @@ module.exports = function (env) { { from: './node_modules/font-awesome/css/font-awesome.min.css', to: 'dependencies/font-awesome/css/font-awesome.min.css' }, { from: './node_modules/font-awesome/fonts', to: 'dependencies/font-awesome/fonts' }, + + { from: './node_modules/vis-network/standalone/umd/vis-network.min.js', to: 'dependencies/vis-network.min.js' }, ], }), ], diff --git a/frontend/app/features/settings/declarations.ts b/frontend/app/features/settings/declarations.ts index 039727896..fc9ad5074 100644 --- a/frontend/app/features/settings/declarations.ts +++ b/frontend/app/features/settings/declarations.ts @@ -27,8 +27,9 @@ export * from './pages/roles/role-add-form.component'; export * from './pages/roles/role.component'; export * from './pages/roles/roles-page.component'; export * from './pages/workflows/workflow-add-form.component'; +export * from './pages/workflows/workflow-diagram.component'; export * from './pages/workflows/workflow-step.component'; export * from './pages/workflows/workflow-transition.component'; -export * from './pages/workflows/workflow.component'; export * from './pages/workflows/workflows-page.component'; +export * from './pages/workflows/workflow.component'; export * from './settings-area.component'; \ No newline at end of file diff --git a/frontend/app/features/settings/module.ts b/frontend/app/features/settings/module.ts index 4e6709dec..9e7eed837 100644 --- a/frontend/app/features/settings/module.ts +++ b/frontend/app/features/settings/module.ts @@ -11,6 +11,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HelpComponent, HistoryComponent, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; import { BackupComponent, BackupsPageComponent, ClientAddFormComponent, ClientComponent, ClientConnectFormComponent, ClientsPageComponent, ContributorAddFormComponent, ContributorComponent, ContributorsPageComponent, ImportContributorsDialogComponent, LanguageAddFormComponent, LanguageComponent, LanguagesPageComponent, MorePageComponent, PatternComponent, PatternsPageComponent, PlanComponent, PlansPageComponent, RoleAddFormComponent, RoleComponent, RolesPageComponent, SettingsAreaComponent, WorkflowAddFormComponent, WorkflowComponent, WorkflowsPageComponent, WorkflowStepComponent, WorkflowTransitionComponent } from './declarations'; +import { WorkflowDiagramComponent } from './pages/workflows/workflow-diagram.component'; const routes: Routes = [ { @@ -195,6 +196,7 @@ const routes: Routes = [ SettingsAreaComponent, WorkflowAddFormComponent, WorkflowComponent, + WorkflowDiagramComponent, WorkflowsPageComponent, WorkflowStepComponent, WorkflowTransitionComponent diff --git a/frontend/app/features/settings/pages/workflows/workflow-diagram.component.html b/frontend/app/features/settings/pages/workflows/workflow-diagram.component.html new file mode 100644 index 000000000..341a2d4f9 --- /dev/null +++ b/frontend/app/features/settings/pages/workflows/workflow-diagram.component.html @@ -0,0 +1,5 @@ + + +
+
+
\ No newline at end of file diff --git a/frontend/app/features/settings/pages/workflows/workflow-diagram.component.scss b/frontend/app/features/settings/pages/workflows/workflow-diagram.component.scss new file mode 100644 index 000000000..51c331517 --- /dev/null +++ b/frontend/app/features/settings/pages/workflows/workflow-diagram.component.scss @@ -0,0 +1,3 @@ +div { + height: 500px; +} \ No newline at end of file diff --git a/frontend/app/features/settings/pages/workflows/workflow-diagram.component.ts b/frontend/app/features/settings/pages/workflows/workflow-diagram.component.ts new file mode 100644 index 000000000..b6bbd63b2 --- /dev/null +++ b/frontend/app/features/settings/pages/workflows/workflow-diagram.component.ts @@ -0,0 +1,151 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnDestroy, ViewChild } from '@angular/core'; +import { ResourceLoaderService, WorkflowDto } from '@app/shared'; + +declare var vis: any; + +@Component({ + selector: 'sqx-workflow-diagram', + styleUrls: ['./workflow-diagram.component.scss'], + templateUrl: './workflow-diagram.component.html' +}) +export class WorkflowDiagramComponent implements AfterViewInit, OnDestroy, OnChanges { + private network: any; + + @ViewChild('chartContainer', { static: false }) + public chartContainer: ElementRef; + + @Input() + public workflow: WorkflowDto; + + public isLoaded = false; + + constructor( + private readonly resourceLoader: ResourceLoaderService + ) { + } + + public ngOnDestroy() { + this.network?.destroy(); + } + + public ngOnChanges() { + this.updateNetwork(); + } + + public ngAfterViewInit() { + this.updateNetwork(); + } + + private updateNetwork() { + if (this.chartContainer?.nativeElement && this.workflow) { + this.resourceLoader.loadLocalScript('dependencies/vis-network.min.js') + .then(() => { + this.network?.destroy(); + + const nodes = new vis.DataSet(); + + for (const step of this.workflow.steps) { + let label = `${step.name}`; + + if (step.noUpdate) { + label += `\nPrevent updates`; + + if (step.noUpdateExpression) { + label += `\nwhen (${step.noUpdateExpression})`; + } + + if (step.noUpdateRoles && step.noUpdateRoles.length > 0) { + label += `\nfor ${step.noUpdateRoles.join(', ')}`; + } + } + + if (step.name === 'Published') { + label += `\nAvailable in the API`; + } + + const node: any = { id: step.name, label, color: step.color }; + + nodes.add(node); + } + + if (this.workflow.initial) { + nodes.add({ id: 0, color: '#000', label: 'Start', shape: 'dot', size: 3 }); + } + + const edges = new vis.DataSet(); + + for (const transition of this.workflow.transitions) { + let label = ''; + + if (transition.expression) { + label += `\nwhen (${transition.expression})`; + } + + if (transition.roles && transition.roles.length > 0) { + label += `\nfor ${transition.roles.join(', ')}`; + } + + const edge: any = { ...transition, label }; + + edges.add(edge); + } + + if (this.workflow.initial) { + edges.add({ from: 0, to: this.workflow.initial }); + } + + this.network = new vis.Network(this.chartContainer.nativeElement, { edges, nodes }, GRAPH_OPTIONS); + this.network.stabilize(); + this.network.fit(); + + this.isLoaded = true; + }); + } + } +} + +const GRAPH_OPTIONS = { + nodes: { + borderWidth: 2, + font: { + multi: true, align: 'left', + ital: { + size: 16 + }, + bold: { + size: 20 + }, + size: 16 + }, + shape: 'dot', + shadow: true + }, + edges: { + arrows: 'to', + font: { + multi: true, + ital: { + size: 16 + }, + size: 16 + }, + color: 'gray' + }, + layout: { + randomSeed: 2 + }, + physics: { + enabled: false, + repulsion: { + nodeDistance: 300 + }, + solver: 'repulsion' + } +}; \ No newline at end of file diff --git a/frontend/app/features/settings/pages/workflows/workflow.component.html b/frontend/app/features/settings/pages/workflows/workflow.component.html index 6c2a71048..7dcdc8dd7 100644 --- a/frontend/app/features/settings/pages/workflows/workflow.component.html +++ b/frontend/app/features/settings/pages/workflows/workflow.component.html @@ -32,6 +32,15 @@
+ +
@@ -39,57 +48,61 @@
- - -
- - -
- - - - Optional name for the workflow. - + + + +
+ + +
+ + + + Optional name for the workflow. + +
-
- -
- - -
- - - - - Restrict this workflow to specific schemas or keep it empty for all schemas. - + +
+ + +
+ + + + + Restrict this workflow to specific schemas or keep it empty for all schemas. + +
-
- - - + + + + + + - + + +
diff --git a/frontend/app/features/settings/pages/workflows/workflow.component.ts b/frontend/app/features/settings/pages/workflows/workflow.component.ts index f14dd157e..53a030edb 100644 --- a/frontend/app/features/settings/pages/workflows/workflow.component.ts +++ b/frontend/app/features/settings/pages/workflows/workflow.component.ts @@ -32,6 +32,8 @@ export class WorkflowComponent implements OnChanges { public isEditing = false; public isEditable = false; + public selectedTab = 0; + constructor( private readonly workflowsState: WorkflowsState ) { @@ -111,6 +113,10 @@ export class WorkflowComponent implements OnChanges { this.workflow = this.workflow.removeStep(step.name); } + public selectTab(tab: number) { + this.selectedTab = tab; + } + public trackByStep(index: number, step: WorkflowStep) { return step.name; } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 024061231..602cbacb4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -414,6 +414,14 @@ "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", "dev": true }, + "@egjs/hammerjs": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", + "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "requires": { + "@types/hammerjs": "^2.0.36" + } + }, "@ngtools/webpack": { "version": "9.0.6", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-9.0.6.tgz", @@ -534,6 +542,11 @@ "@types/node": "*" } }, + "@types/hammerjs": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", + "integrity": "sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==" + }, "@types/jasmine": { "version": "3.5.9", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.9.tgz", @@ -7366,6 +7379,11 @@ } } }, + "keycharm": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.3.1.tgz", + "integrity": "sha512-zn47Ti4FJT9zdF+YBBLWJsfKF/fYQHkrYlBeB5Ez5e2PjW7SoIxr43yehAne2HruulIoid4NKZZxO0dHBygCtQ==" + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -13402,6 +13420,21 @@ "extsprintf": "^1.2.0" } }, + "vis-data": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-6.6.1.tgz", + "integrity": "sha512-xmujDB2Dzf8T04rGFJ9OP4OA6zRVrz8R9hb0CVKryBrZRCljCga9JjSfgctA8S7wdZu7otDtUIwX4ZOgfV/57w==" + }, + "vis-network": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-7.10.0.tgz", + "integrity": "sha512-SYQ3y+cGqcFpPfaQbpGBXsaQjjbQq5xjt5dVztCD6x5ETQHlAYaFiYiWnVo0qoVI9lspfwoCfp4wOhW+rvId6Q==" + }, + "vis-util": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-4.3.2.tgz", + "integrity": "sha512-FIS75hhrzbX1qJwFVwVVm1q2/TEktJWjgWsV0T3E9AYC4PWyQCBKk2LgsSLi+O8NBi7gTe9D4K75MqdPTHrRnA==" + }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0947f5b34..72d4e8a13 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,6 +26,7 @@ "@angular/platform-browser-dynamic": "9.0.6", "@angular/platform-server": "9.0.6", "@angular/router": "9.0.6", + "@egjs/hammerjs": "^2.0.17", "ace-builds": "^1.4.11", "angular-gridster2": "^9.2.0", "angular-mentions": "1.1.4", @@ -39,6 +40,7 @@ "graphiql": "0.17.5", "graphql": "14.6.0", "image-focus": "1.1.0", + "keycharm": "^0.3.1", "marked": "0.8.0", "mersenne-twister": "1.1.0", "mousetrap": "1.6.5", @@ -53,6 +55,9 @@ "slugify": "1.4.0", "tinymce": "^5.2.0", "tslib": "1.11.1", + "vis-data": "^6.6.1", + "vis-network": "^7.10.0", + "vis-util": "^4.3.2", "zone.js": "0.10.3" }, "devDependencies": {