Browse Source

Merge pull request #103 from Squidex/feature-regex-suggestions

Feature regex suggestions
pull/104/head
Sebastian Stehle 9 years ago
committed by GitHub
parent
commit
936a6b5f73
  1. 10
      src/Squidex.Shared/Identity/SquidexRoles.cs
  2. 2
      src/Squidex/Config/Constants.cs
  3. 19
      src/Squidex/Config/MyUIOptions.cs
  4. 49
      src/Squidex/Config/Swagger/ScopesProcessor.cs
  5. 5
      src/Squidex/Config/Swagger/SwaggerServices.cs
  6. 2
      src/Squidex/Controllers/Api/Apps/AppClientsController.cs
  7. 2
      src/Squidex/Controllers/Api/Apps/AppContributorsController.cs
  8. 2
      src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs
  9. 2
      src/Squidex/Controllers/Api/Apps/AppsController.cs
  10. 2
      src/Squidex/Controllers/Api/Apps/Models/AddAppLanguageDto.cs
  11. 2
      src/Squidex/Controllers/Api/Apps/Models/AppLanguageDto.cs
  12. 2
      src/Squidex/Controllers/Api/Apps/Models/ClientDto.cs
  13. 2
      src/Squidex/Controllers/Api/Apps/Models/ContributorsDto.cs
  14. 2
      src/Squidex/Controllers/Api/Apps/Models/UpdateAppClientDto.cs
  15. 2
      src/Squidex/Controllers/Api/Apps/Models/UpdateAppLanguageDto.cs
  16. 2
      src/Squidex/Controllers/Api/Assets/AssetContentController.cs
  17. 2
      src/Squidex/Controllers/Api/Assets/AssetsController.cs
  18. 2
      src/Squidex/Controllers/Api/Assets/Models/AssetUpdateDto.cs
  19. 2
      src/Squidex/Controllers/Api/EntityCreatedDto.cs
  20. 2
      src/Squidex/Controllers/Api/History/HistoryController.cs
  21. 2
      src/Squidex/Controllers/Api/History/Models/HistoryEventDto.cs
  22. 2
      src/Squidex/Controllers/Api/LanguageDto.cs
  23. 2
      src/Squidex/Controllers/Api/Languages/LanguagesController.cs
  24. 2
      src/Squidex/Controllers/Api/Ping/PingController.cs
  25. 2
      src/Squidex/Controllers/Api/Plans/AppPlansController.cs
  26. 2
      src/Squidex/Controllers/Api/Plans/Models/AppPlansDto.cs
  27. 2
      src/Squidex/Controllers/Api/Plans/Models/ChangePlanDto.cs
  28. 2
      src/Squidex/Controllers/Api/Plans/Models/PlanChangedDto.cs
  29. 2
      src/Squidex/Controllers/Api/Plans/Models/PlanDto.cs
  30. 2
      src/Squidex/Controllers/Api/Schemas/Models/ReorderFieldsDto.cs
  31. 2
      src/Squidex/Controllers/Api/Schemas/Models/SchemaDto.cs
  32. 2
      src/Squidex/Controllers/Api/Schemas/Models/SchemaPropertiesDto.cs
  33. 2
      src/Squidex/Controllers/Api/Schemas/Models/UpdateFieldDto.cs
  34. 2
      src/Squidex/Controllers/Api/Schemas/Models/UpdateSchemaDto.cs
  35. 2
      src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs
  36. 2
      src/Squidex/Controllers/Api/Schemas/SchemasController.cs
  37. 2
      src/Squidex/Controllers/Api/Statistics/Models/CallsUsageDto.cs
  38. 2
      src/Squidex/Controllers/Api/Statistics/Models/CurrentCallsDto.cs
  39. 2
      src/Squidex/Controllers/Api/Statistics/Models/CurrentStorageDto.cs
  40. 2
      src/Squidex/Controllers/Api/Statistics/Models/StorageUsageDto.cs
  41. 2
      src/Squidex/Controllers/Api/Statistics/UsagesController.cs
  42. 27
      src/Squidex/Controllers/Api/UI/Models/UIRegexSuggestionDto.cs
  43. 22
      src/Squidex/Controllers/Api/UI/Models/UISettingsDto.cs
  44. 57
      src/Squidex/Controllers/Api/UI/UIController.cs
  45. 2
      src/Squidex/Controllers/Api/Users/Models/UserCreatedDto.cs
  46. 2
      src/Squidex/Controllers/Api/Users/Models/UsersDto.cs
  47. 2
      src/Squidex/Controllers/Api/Users/UserManagementController.cs
  48. 2
      src/Squidex/Controllers/Api/Users/UsersController.cs
  49. 2
      src/Squidex/Controllers/Api/Webhooks/Models/CreateWebhookDto.cs
  50. 2
      src/Squidex/Controllers/Api/Webhooks/Models/UpdateWebhookDto.cs
  51. 2
      src/Squidex/Controllers/Api/Webhooks/Models/WebhookCreatedDto.cs
  52. 2
      src/Squidex/Controllers/Api/Webhooks/Models/WebhookDto.cs
  53. 2
      src/Squidex/Controllers/Api/Webhooks/Models/WebhookEventDto.cs
  54. 2
      src/Squidex/Controllers/Api/Webhooks/Models/WebhookEventsDto.cs
  55. 2
      src/Squidex/Controllers/Api/Webhooks/Models/WebhookSchemaDto.cs
  56. 2
      src/Squidex/Controllers/Api/Webhooks/WebhooksController.cs
  57. 2
      src/Squidex/Controllers/ContentApi/ContentSwaggerController.cs
  58. 2
      src/Squidex/Controllers/ContentApi/ContentsController.cs
  59. 56
      src/Squidex/Controllers/ContentApi/Generator/SchemaSwaggerGenerator.cs
  60. 26
      src/Squidex/Controllers/ContentApi/Generator/SchemasSwaggerGenerator.cs
  61. 2
      src/Squidex/Controllers/ContentApi/Models/ContentsDto.cs
  62. 2
      src/Squidex/Controllers/UI/Error/ErrorController.cs
  63. 2
      src/Squidex/Controllers/UI/Profile/PortalController.cs
  64. 2
      src/Squidex/Controllers/UI/Profile/ProfileController.cs
  65. 7
      src/Squidex/Pipeline/Swagger/SwaggerHelper.cs
  66. 2
      src/Squidex/Startup.cs
  67. 15
      src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.html
  68. 27
      src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.scss
  69. 45
      src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts
  70. 1
      src/Squidex/app/shared/declarations-base.ts
  71. 2
      src/Squidex/app/shared/module.ts
  72. 79
      src/Squidex/app/shared/services/ui.service.spec.ts
  73. 49
      src/Squidex/app/shared/services/ui.service.ts
  74. 16
      src/Squidex/appsettings.json

10
src/Squidex.Shared/Identity/SquidexRoles.cs

@ -10,14 +10,14 @@ namespace Squidex.Shared.Identity
{ {
public static class SquidexRoles public static class SquidexRoles
{ {
public static readonly string Administrator = "ADMINISTRATOR"; public static readonly string Administrator = "administrator";
public static readonly string AppOwner = "APP-OWNER"; public static readonly string AppOwner = "app:owner";
public static readonly string AppEditor = "APP-EDITOR"; public static readonly string AppEditor = "app:editor";
public static readonly string AppReader = "APP-READER"; public static readonly string AppReader = "app:reader";
public static readonly string AppDeveloper = "APP-DEVELOPER"; public static readonly string AppDeveloper = "app:dev";
} }
} }

2
src/Squidex/Config/Constants.cs

@ -10,7 +10,7 @@ namespace Squidex.Config
{ {
public static class Constants public static class Constants
{ {
public static readonly string SecurityDefinition = "oauth-client-auth"; public static readonly string SecurityDefinition = "squidex-oauth-auth";
public static readonly string ApiPrefix = "/api"; public static readonly string ApiPrefix = "/api";

19
src/Squidex/Config/MyUIOptions.cs

@ -0,0 +1,19 @@
// ==========================================================================
// MyUIOptions.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
// ReSharper disable CollectionNeverUpdated.Global
namespace Squidex.Config
{
public sealed class MyUIOptions
{
public Dictionary<string, string> RegexSuggestions { get; set; }
}
}

49
src/Squidex/Config/Swagger/ScopesProcessor.cs

@ -0,0 +1,49 @@
// ==========================================================================
// ScopesProcessor.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using NSwag;
using NSwag.SwaggerGeneration.Processors;
using NSwag.SwaggerGeneration.Processors.Contexts;
using Squidex.Infrastructure.Tasks;
// ReSharper disable InvertIf
namespace Squidex.Config.Swagger
{
public class ScopesProcessor : IOperationProcessor
{
public Task<bool> ProcessAsync(OperationProcessorContext context)
{
if (context.OperationDescription.Operation.Security == null)
{
context.OperationDescription.Operation.Security = new List<SwaggerSecurityRequirement>();
}
var authorizeAttributes =
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Union(
context.MethodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType<AuthorizeAttribute>()).ToArray();
if (authorizeAttributes.Any())
{
var scopes = authorizeAttributes.Where(a => a.Roles != null).SelectMany(a => a.Roles.Split(',')).Distinct().ToList();
context.OperationDescription.Operation.Security.Add(new SwaggerSecurityRequirement
{
{ Constants.SecurityDefinition, scopes }
});
}
return TaskHelper.True;
}
}
}

5
src/Squidex/Config/Swagger/SwaggerServices.cs

@ -42,10 +42,9 @@ namespace Squidex.Config.Swagger
private static SwaggerSettings ConfigureIdentity(this SwaggerSettings settings, MyUrlsOptions urlOptions) private static SwaggerSettings ConfigureIdentity(this SwaggerSettings settings, MyUrlsOptions urlOptions)
{ {
settings.DocumentProcessors.Add( settings.DocumentProcessors.Add(new SecurityDefinitionAppender(Constants.SecurityDefinition, SwaggerHelper.CreateOAuthSchema(urlOptions)));
new SecurityDefinitionAppender(Constants.SecurityDefinition, SwaggerHelper.CreateOAuthSchema(urlOptions)));
settings.OperationProcessors.Add(new OperationSecurityScopeProcessor(Constants.SecurityDefinition)); settings.OperationProcessors.Add(new ScopesProcessor());
return settings; return settings;
} }

2
src/Squidex/Controllers/Api/Apps/AppClientsController.cs

@ -26,7 +26,7 @@ namespace Squidex.Controllers.Api.Apps
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Apps")] [SwaggerTag("Apps")]
public class AppClientsController : ControllerBase public sealed class AppClientsController : ControllerBase
{ {
public AppClientsController(ICommandBus commandBus) public AppClientsController(ICommandBus commandBus)
: base(commandBus) : base(commandBus)

2
src/Squidex/Controllers/Api/Apps/AppContributorsController.cs

@ -27,7 +27,7 @@ namespace Squidex.Controllers.Api.Apps
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Apps")] [SwaggerTag("Apps")]
public class AppContributorsController : ControllerBase public sealed class AppContributorsController : ControllerBase
{ {
private readonly IAppPlansProvider appPlansProvider; private readonly IAppPlansProvider appPlansProvider;

2
src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs

@ -29,7 +29,7 @@ namespace Squidex.Controllers.Api.Apps
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Apps")] [SwaggerTag("Apps")]
public class AppLanguagesController : ControllerBase public sealed class AppLanguagesController : ControllerBase
{ {
public AppLanguagesController(ICommandBus commandBus) public AppLanguagesController(ICommandBus commandBus)
: base(commandBus) : base(commandBus)

2
src/Squidex/Controllers/Api/Apps/AppsController.cs

@ -28,7 +28,7 @@ namespace Squidex.Controllers.Api.Apps
[Authorize] [Authorize]
[ApiExceptionFilter] [ApiExceptionFilter]
[SwaggerTag("Apps")] [SwaggerTag("Apps")]
public class AppsController : ControllerBase public sealed class AppsController : ControllerBase
{ {
private readonly IAppRepository appRepository; private readonly IAppRepository appRepository;

2
src/Squidex/Controllers/Api/Apps/Models/AddAppLanguageDto.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Controllers.Api.Apps.Models namespace Squidex.Controllers.Api.Apps.Models
{ {
public class AddAppLanguageDto public sealed class AddAppLanguageDto
{ {
/// <summary> /// <summary>
/// The language to add. /// The language to add.

2
src/Squidex/Controllers/Api/Apps/Models/AppLanguageDto.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Controllers.Api.Apps.Models namespace Squidex.Controllers.Api.Apps.Models
{ {
public class AppLanguageDto public sealed class AppLanguageDto
{ {
/// <summary> /// <summary>
/// The iso code of the language. /// The iso code of the language.

2
src/Squidex/Controllers/Api/Apps/Models/ClientDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Apps.Models namespace Squidex.Controllers.Api.Apps.Models
{ {
public class ClientDto public sealed class ClientDto
{ {
/// <summary> /// <summary>
/// The client id. /// The client id.

2
src/Squidex/Controllers/Api/Apps/Models/ContributorsDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Apps.Models namespace Squidex.Controllers.Api.Apps.Models
{ {
public class ContributorsDto public sealed class ContributorsDto
{ {
/// <summary> /// <summary>
/// The contributors. /// The contributors.

2
src/Squidex/Controllers/Api/Apps/Models/UpdateAppClientDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Apps.Models namespace Squidex.Controllers.Api.Apps.Models
{ {
public class UpdateAppClientDto public sealed class UpdateAppClientDto
{ {
/// <summary> /// <summary>
/// The new display name of the client. /// The new display name of the client.

2
src/Squidex/Controllers/Api/Apps/Models/UpdateAppLanguageDto.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Controllers.Api.Apps.Models namespace Squidex.Controllers.Api.Apps.Models
{ {
public class UpdateAppLanguageDto public sealed class UpdateAppLanguageDto
{ {
/// <summary> /// <summary>
/// Set the value to true to make the language the master. /// Set the value to true to make the language the master.

2
src/Squidex/Controllers/Api/Assets/AssetContentController.cs

@ -26,7 +26,7 @@ namespace Squidex.Controllers.Api.Assets
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Assets")] [SwaggerTag("Assets")]
public class AssetContentController : ControllerBase public sealed class AssetContentController : ControllerBase
{ {
private readonly IAssetStore assetStorage; private readonly IAssetStore assetStorage;
private readonly IAssetRepository assetRepository; private readonly IAssetRepository assetRepository;

2
src/Squidex/Controllers/Api/Assets/AssetsController.cs

@ -36,7 +36,7 @@ namespace Squidex.Controllers.Api.Assets
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Assets")] [SwaggerTag("Assets")]
public class AssetsController : ControllerBase public sealed class AssetsController : ControllerBase
{ {
private readonly IAssetRepository assetRepository; private readonly IAssetRepository assetRepository;
private readonly IAssetStatsRepository assetStatsRepository; private readonly IAssetStatsRepository assetStatsRepository;

2
src/Squidex/Controllers/Api/Assets/Models/AssetUpdateDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Assets.Models namespace Squidex.Controllers.Api.Assets.Models
{ {
public class AssetUpdateDto public sealed class AssetUpdateDto
{ {
/// <summary> /// <summary>
/// The new name of the asset. /// The new name of the asset.

2
src/Squidex/Controllers/Api/EntityCreatedDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api namespace Squidex.Controllers.Api
{ {
public class EntityCreatedDto public sealed class EntityCreatedDto
{ {
/// <summary> /// <summary>
/// Id of the created entity. /// Id of the created entity.

2
src/Squidex/Controllers/Api/History/HistoryController.cs

@ -25,7 +25,7 @@ namespace Squidex.Controllers.Api.History
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("History")] [SwaggerTag("History")]
public class HistoryController : ControllerBase public sealed class HistoryController : ControllerBase
{ {
private readonly IHistoryEventRepository historyEventRepository; private readonly IHistoryEventRepository historyEventRepository;

2
src/Squidex/Controllers/Api/History/Models/HistoryEventDto.cs

@ -12,7 +12,7 @@ using NodaTime;
namespace Squidex.Controllers.Api.History.Models namespace Squidex.Controllers.Api.History.Models
{ {
public class HistoryEventDto public sealed class HistoryEventDto
{ {
/// <summary> /// <summary>
/// The message of the event. /// The message of the event.

2
src/Squidex/Controllers/Api/LanguageDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api namespace Squidex.Controllers.Api
{ {
public class LanguageDto public sealed class LanguageDto
{ {
/// <summary> /// <summary>
/// The iso code of the language. /// The iso code of the language.

2
src/Squidex/Controllers/Api/Languages/LanguagesController.cs

@ -22,7 +22,7 @@ namespace Squidex.Controllers.Api.Languages
[Authorize] [Authorize]
[ApiExceptionFilter] [ApiExceptionFilter]
[SwaggerTag("Languages")] [SwaggerTag("Languages")]
public class LanguagesController : Controller public sealed class LanguagesController : Controller
{ {
/// <summary> /// <summary>
/// Get supported languages. /// Get supported languages.

2
src/Squidex/Controllers/Api/Ping/PingController.cs

@ -19,7 +19,7 @@ namespace Squidex.Controllers.Api.Ping
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Ping")] [SwaggerTag("Ping")]
public class PingController : Controller public sealed class PingController : Controller
{ {
/// <summary> /// <summary>
/// Get ping status. /// Get ping status.

2
src/Squidex/Controllers/Api/Plans/AppPlansController.cs

@ -28,7 +28,7 @@ namespace Squidex.Controllers.Api.Plans
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Plans")] [SwaggerTag("Plans")]
public class AppPlansController : ControllerBase public sealed class AppPlansController : ControllerBase
{ {
private readonly IAppPlansProvider appPlansProvider; private readonly IAppPlansProvider appPlansProvider;
private readonly IAppPlanBillingManager appPlansBillingManager; private readonly IAppPlanBillingManager appPlansBillingManager;

2
src/Squidex/Controllers/Api/Plans/Models/AppPlansDto.cs

@ -10,7 +10,7 @@ using System.Collections.Generic;
namespace Squidex.Controllers.Api.Plans.Models namespace Squidex.Controllers.Api.Plans.Models
{ {
public class AppPlansDto public sealed class AppPlansDto
{ {
/// <summary> /// <summary>
/// The available plans. /// The available plans.

2
src/Squidex/Controllers/Api/Plans/Models/ChangePlanDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Plans.Models namespace Squidex.Controllers.Api.Plans.Models
{ {
public class ChangePlanDto public sealed class ChangePlanDto
{ {
/// <summary> /// <summary>
/// The new plan id. /// The new plan id.

2
src/Squidex/Controllers/Api/Plans/Models/PlanChangedDto.cs

@ -8,7 +8,7 @@
namespace Squidex.Controllers.Api.Plans.Models namespace Squidex.Controllers.Api.Plans.Models
{ {
public class PlanChangedDto public sealed class PlanChangedDto
{ {
/// <summary> /// <summary>
/// Optional redirect uri. /// Optional redirect uri.

2
src/Squidex/Controllers/Api/Plans/Models/PlanDto.cs

@ -8,7 +8,7 @@
namespace Squidex.Controllers.Api.Plans.Models namespace Squidex.Controllers.Api.Plans.Models
{ {
public class PlanDto public sealed class PlanDto
{ {
/// <summary> /// <summary>
/// The id of the plan. /// The id of the plan.

2
src/Squidex/Controllers/Api/Schemas/Models/ReorderFieldsDto.cs

@ -11,7 +11,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Schemas.Models namespace Squidex.Controllers.Api.Schemas.Models
{ {
public class ReorderFieldsDto public sealed class ReorderFieldsDto
{ {
/// <summary> /// <summary>
/// The field ids in the target order. /// The field ids in the target order.

2
src/Squidex/Controllers/Api/Schemas/Models/SchemaDto.cs

@ -13,7 +13,7 @@ using Squidex.Infrastructure;
namespace Squidex.Controllers.Api.Schemas.Models namespace Squidex.Controllers.Api.Schemas.Models
{ {
public class SchemaDto public sealed class SchemaDto
{ {
/// <summary> /// <summary>
/// The id of the schema. /// The id of the schema.

2
src/Squidex/Controllers/Api/Schemas/Models/SchemaPropertiesDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Schemas.Models namespace Squidex.Controllers.Api.Schemas.Models
{ {
public class SchemaPropertiesDto public sealed class SchemaPropertiesDto
{ {
/// <summary> /// <summary>
/// Optional label for the editor. /// Optional label for the editor.

2
src/Squidex/Controllers/Api/Schemas/Models/UpdateFieldDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Schemas.Models namespace Squidex.Controllers.Api.Schemas.Models
{ {
public class UpdateFieldDto public sealed class UpdateFieldDto
{ {
/// <summary> /// <summary>
/// The field properties. /// The field properties.

2
src/Squidex/Controllers/Api/Schemas/Models/UpdateSchemaDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Schemas.Models namespace Squidex.Controllers.Api.Schemas.Models
{ {
public class UpdateSchemaDto public sealed class UpdateSchemaDto
{ {
/// <summary> /// <summary>
/// Optional label for the editor. /// Optional label for the editor.

2
src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs

@ -23,7 +23,7 @@ namespace Squidex.Controllers.Api.Schemas
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Schemas")] [SwaggerTag("Schemas")]
public class SchemaFieldsController : ControllerBase public sealed class SchemaFieldsController : ControllerBase
{ {
public SchemaFieldsController(ICommandBus commandBus) public SchemaFieldsController(ICommandBus commandBus)
: base(commandBus) : base(commandBus)

2
src/Squidex/Controllers/Api/Schemas/SchemasController.cs

@ -30,7 +30,7 @@ namespace Squidex.Controllers.Api.Schemas
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Schemas")] [SwaggerTag("Schemas")]
public class SchemasController : ControllerBase public sealed class SchemasController : ControllerBase
{ {
private readonly ISchemaRepository schemaRepository; private readonly ISchemaRepository schemaRepository;

2
src/Squidex/Controllers/Api/Statistics/Models/CallsUsageDto.cs

@ -10,7 +10,7 @@ using System;
namespace Squidex.Controllers.Api.Statistics.Models namespace Squidex.Controllers.Api.Statistics.Models
{ {
public class CallsUsageDto public sealed class CallsUsageDto
{ {
/// <summary> /// <summary>
/// The date when the usage was tracked. /// The date when the usage was tracked.

2
src/Squidex/Controllers/Api/Statistics/Models/CurrentCallsDto.cs

@ -8,7 +8,7 @@
namespace Squidex.Controllers.Api.Statistics.Models namespace Squidex.Controllers.Api.Statistics.Models
{ {
public class CurrentCallsDto public sealed class CurrentCallsDto
{ {
/// <summary> /// <summary>
/// The number of calls. /// The number of calls.

2
src/Squidex/Controllers/Api/Statistics/Models/CurrentStorageDto.cs

@ -8,7 +8,7 @@
namespace Squidex.Controllers.Api.Statistics.Models namespace Squidex.Controllers.Api.Statistics.Models
{ {
public class CurrentStorageDto public sealed class CurrentStorageDto
{ {
/// <summary> /// <summary>
/// The size in bytes. /// The size in bytes.

2
src/Squidex/Controllers/Api/Statistics/Models/StorageUsageDto.cs

@ -10,7 +10,7 @@ using System;
namespace Squidex.Controllers.Api.Statistics.Models namespace Squidex.Controllers.Api.Statistics.Models
{ {
public class StorageUsageDto public sealed class StorageUsageDto
{ {
/// <summary> /// <summary>
/// The date when the usage was tracked. /// The date when the usage was tracked.

2
src/Squidex/Controllers/Api/Statistics/UsagesController.cs

@ -27,7 +27,7 @@ namespace Squidex.Controllers.Api.Statistics
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerTag("Statistics")] [SwaggerTag("Statistics")]
public class UsagesController : ControllerBase public sealed class UsagesController : ControllerBase
{ {
private readonly IUsageTracker usageTracker; private readonly IUsageTracker usageTracker;
private readonly IAppPlansProvider appPlanProvider; private readonly IAppPlansProvider appPlanProvider;

27
src/Squidex/Controllers/Api/UI/Models/UIRegexSuggestionDto.cs

@ -0,0 +1,27 @@
// ==========================================================================
// UIRegexSuggestionDto.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.UI.Models
{
public sealed class UIRegexSuggestionDto
{
/// <summary>
/// The name of the suggestion.
/// </summary>
[Required]
public string Name { get; set; }
/// <summary>
/// The regex pattern.
/// </summary>
[Required]
public string Pattern { get; set; }
}
}

22
src/Squidex/Controllers/Api/UI/Models/UISettingsDto.cs

@ -0,0 +1,22 @@
// ==========================================================================
// UISettingsDto.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.UI.Models
{
public sealed class UISettingsDto
{
/// <summary>
/// The regex suggestions.
/// </summary>
[Required]
public List<UIRegexSuggestionDto> RegexSuggestions { get; set; }
}
}

57
src/Squidex/Controllers/Api/UI/UIController.cs

@ -0,0 +1,57 @@
// ==========================================================================
// UIController.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using NSwag.Annotations;
using Squidex.Config;
using Squidex.Controllers.Api.UI.Models;
using Squidex.Pipeline;
namespace Squidex.Controllers.Api.UI
{
/// <summary>
/// Manages ui settings and configs.
/// </summary>
[ApiExceptionFilter]
[SwaggerTag("UI")]
public sealed class UIController : Controller
{
private readonly MyUIOptions uiOptions;
public UIController(IOptions<MyUIOptions> uiOptions)
{
this.uiOptions = uiOptions.Value;
}
/// <summary>
/// Get ui settings.
/// </summary>
[HttpGet]
[Route("ui/settings/")]
[ProducesResponseType(typeof(UISettingsDto), 200)]
[ApiCosts(0)]
public IActionResult GetSettings()
{
var dto = new UISettingsDto
{
RegexSuggestions =
uiOptions.RegexSuggestions?
.Where(x =>
!string.IsNullOrWhiteSpace(x.Key) &&
!string.IsNullOrWhiteSpace(x.Value))
.Select(x => new UIRegexSuggestionDto { Name = x.Key, Pattern = x.Value }).ToList()
?? new List<UIRegexSuggestionDto>()
};
return Ok(dto);
}
}
}

2
src/Squidex/Controllers/Api/Users/Models/UserCreatedDto.cs

@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Users.Models namespace Squidex.Controllers.Api.Users.Models
{ {
public class UserCreatedDto public sealed class UserCreatedDto
{ {
[Required] [Required]
public string Id { get; set; } public string Id { get; set; }

2
src/Squidex/Controllers/Api/Users/Models/UsersDto.cs

@ -8,7 +8,7 @@
namespace Squidex.Controllers.Api.Users.Models namespace Squidex.Controllers.Api.Users.Models
{ {
public class UsersDto public sealed class UsersDto
{ {
/// <summary> /// <summary>
/// The total number of users. /// The total number of users.

2
src/Squidex/Controllers/Api/Users/UserManagementController.cs

@ -25,7 +25,7 @@ namespace Squidex.Controllers.Api.Users
[MustBeAdministrator] [MustBeAdministrator]
[ApiExceptionFilter] [ApiExceptionFilter]
[SwaggerIgnore] [SwaggerIgnore]
public class UserManagementController : Controller public sealed class UserManagementController : Controller
{ {
private readonly UserManager<IUser> userManager; private readonly UserManager<IUser> userManager;
private readonly IUserFactory userFactory; private readonly IUserFactory userFactory;

2
src/Squidex/Controllers/Api/Users/UsersController.cs

@ -30,7 +30,7 @@ namespace Squidex.Controllers.Api.Users
/// </summary> /// </summary>
[ApiExceptionFilter] [ApiExceptionFilter]
[SwaggerTag("Users")] [SwaggerTag("Users")]
public class UsersController : Controller public sealed class UsersController : Controller
{ {
private static readonly byte[] AvatarBytes; private static readonly byte[] AvatarBytes;
private readonly UserManager<IUser> userManager; private readonly UserManager<IUser> userManager;

2
src/Squidex/Controllers/Api/Webhooks/Models/CreateWebhookDto.cs

@ -14,7 +14,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Webhooks.Models namespace Squidex.Controllers.Api.Webhooks.Models
{ {
public class CreateWebhookDto public sealed class CreateWebhookDto
{ {
/// <summary> /// <summary>
/// The url of the webhook. /// The url of the webhook.

2
src/Squidex/Controllers/Api/Webhooks/Models/UpdateWebhookDto.cs

@ -12,7 +12,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Webhooks.Models namespace Squidex.Controllers.Api.Webhooks.Models
{ {
public class UpdateWebhookDto public sealed class UpdateWebhookDto
{ {
/// <summary> /// <summary>
/// The url of the webhook. /// The url of the webhook.

2
src/Squidex/Controllers/Api/Webhooks/Models/WebhookCreatedDto.cs

@ -11,7 +11,7 @@ using System.ComponentModel.DataAnnotations;
namespace Squidex.Controllers.Api.Webhooks.Models namespace Squidex.Controllers.Api.Webhooks.Models
{ {
public class WebhookCreatedDto public sealed class WebhookCreatedDto
{ {
/// <summary> /// <summary>
/// The id of the webhook. /// The id of the webhook.

2
src/Squidex/Controllers/Api/Webhooks/Models/WebhookDto.cs

@ -14,7 +14,7 @@ using System.Collections.Generic;
namespace Squidex.Controllers.Api.Webhooks.Models namespace Squidex.Controllers.Api.Webhooks.Models
{ {
public class WebhookDto public sealed class WebhookDto
{ {
/// <summary> /// <summary>
/// The id of the webhook. /// The id of the webhook.

2
src/Squidex/Controllers/Api/Webhooks/Models/WebhookEventDto.cs

@ -13,7 +13,7 @@ using Squidex.Domain.Apps.Read.Webhooks;
namespace Squidex.Controllers.Api.Webhooks.Models namespace Squidex.Controllers.Api.Webhooks.Models
{ {
public class WebhookEventDto public sealed class WebhookEventDto
{ {
/// <summary> /// <summary>
/// The id of the event. /// The id of the event.

2
src/Squidex/Controllers/Api/Webhooks/Models/WebhookEventsDto.cs

@ -8,7 +8,7 @@
namespace Squidex.Controllers.Api.Webhooks.Models namespace Squidex.Controllers.Api.Webhooks.Models
{ {
public class WebhookEventsDto public sealed class WebhookEventsDto
{ {
/// <summary> /// <summary>
/// The total number of webhook events. /// The total number of webhook events.

2
src/Squidex/Controllers/Api/Webhooks/Models/WebhookSchemaDto.cs

@ -10,7 +10,7 @@ using System;
namespace Squidex.Controllers.Api.Webhooks.Models namespace Squidex.Controllers.Api.Webhooks.Models
{ {
public class WebhookSchemaDto public sealed class WebhookSchemaDto
{ {
/// <summary> /// <summary>
/// The id of the schema. /// The id of the schema.

2
src/Squidex/Controllers/Api/Webhooks/WebhooksController.cs

@ -29,7 +29,7 @@ namespace Squidex.Controllers.Api.Webhooks
[AppApi] [AppApi]
[SwaggerTag("Webhooks")] [SwaggerTag("Webhooks")]
[MustBeAppDeveloper] [MustBeAppDeveloper]
public class WebhooksController : ControllerBase public sealed class WebhooksController : ControllerBase
{ {
private readonly IWebhookRepository webhooksRepository; private readonly IWebhookRepository webhooksRepository;
private readonly IWebhookEventRepository webhookEventsRepository; private readonly IWebhookEventRepository webhookEventsRepository;

2
src/Squidex/Controllers/ContentApi/ContentSwaggerController.cs

@ -20,7 +20,7 @@ namespace Squidex.Controllers.ContentApi
{ {
[ApiExceptionFilter] [ApiExceptionFilter]
[SwaggerIgnore] [SwaggerIgnore]
public class ContentSwaggerController : Controller public sealed class ContentSwaggerController : Controller
{ {
private readonly ISchemaRepository schemaRepository; private readonly ISchemaRepository schemaRepository;
private readonly IAppProvider appProvider; private readonly IAppProvider appProvider;

2
src/Squidex/Controllers/ContentApi/ContentsController.cs

@ -32,7 +32,7 @@ namespace Squidex.Controllers.ContentApi
[ApiExceptionFilter] [ApiExceptionFilter]
[AppApi] [AppApi]
[SwaggerIgnore] [SwaggerIgnore]
public class ContentsController : ControllerBase public sealed class ContentsController : ControllerBase
{ {
private readonly ISchemaProvider schemas; private readonly ISchemaProvider schemas;
private readonly IContentRepository contentRepository; private readonly IContentRepository contentRepository;

56
src/Squidex/Controllers/ContentApi/Generator/SchemaSwaggerGenerator.cs

@ -11,11 +11,13 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using NJsonSchema; using NJsonSchema;
using NSwag; using NSwag;
using Squidex.Config;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Contents.JsonSchema; using Squidex.Domain.Apps.Read.Contents.JsonSchema;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Pipeline.Swagger; using Squidex.Pipeline.Swagger;
using Squidex.Shared.Identity;
// ReSharper disable InvertIf // ReSharper disable InvertIf
@ -23,8 +25,10 @@ namespace Squidex.Controllers.ContentApi.Generator
{ {
public sealed class SchemaSwaggerGenerator public sealed class SchemaSwaggerGenerator
{ {
private static readonly string schemaQueryDescription; private static readonly string SchemaQueryDescription;
private static readonly string schemaBodyDescription; private static readonly string SchemaBodyDescription;
private static readonly List<SwaggerSecurityRequirement> EditorSecurity;
private static readonly List<SwaggerSecurityRequirement> ReaderSecurity;
private readonly ContentSchemaBuilder schemaBuilder = new ContentSchemaBuilder(); private readonly ContentSchemaBuilder schemaBuilder = new ContentSchemaBuilder();
private readonly SwaggerDocument document; private readonly SwaggerDocument document;
private readonly JsonSchema4 contentSchema; private readonly JsonSchema4 contentSchema;
@ -36,8 +40,28 @@ namespace Squidex.Controllers.ContentApi.Generator
static SchemaSwaggerGenerator() static SchemaSwaggerGenerator()
{ {
schemaBodyDescription = SwaggerHelper.LoadDocs("schemabody"); SchemaBodyDescription = SwaggerHelper.LoadDocs("schemabody");
schemaQueryDescription = SwaggerHelper.LoadDocs("schemaquery"); SchemaQueryDescription = SwaggerHelper.LoadDocs("schemaquery");
ReaderSecurity = new List<SwaggerSecurityRequirement>
{
new SwaggerSecurityRequirement
{
{
Constants.SecurityDefinition, new[] { SquidexRoles.AppReader }
}
}
};
EditorSecurity = new List<SwaggerSecurityRequirement>
{
new SwaggerSecurityRequirement
{
{
Constants.SecurityDefinition, new[] { SquidexRoles.AppEditor }
}
}
};
} }
public SchemaSwaggerGenerator(SwaggerDocument document, string path, Schema schema, public SchemaSwaggerGenerator(SwaggerDocument document, string path, Schema schema,
@ -89,7 +113,7 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.OperationId = $"Query{schemaKey}Contents"; operation.OperationId = $"Query{schemaKey}Contents";
operation.Summary = $"Queries {schemaName} contents."; operation.Summary = $"Queries {schemaName} contents.";
operation.Description = schemaQueryDescription; operation.Description = SchemaQueryDescription;
operation.AddQueryParameter("$top", JsonObjectType.Number, "Optional number of contents to take."); operation.AddQueryParameter("$top", JsonObjectType.Number, "Optional number of contents to take.");
operation.AddQueryParameter("$skip", JsonObjectType.Number, "Optional number of contents to skip."); operation.AddQueryParameter("$skip", JsonObjectType.Number, "Optional number of contents to skip.");
@ -98,6 +122,8 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.AddQueryParameter("orderby", JsonObjectType.String, "Optional OData order definition."); operation.AddQueryParameter("orderby", JsonObjectType.String, "Optional OData order definition.");
operation.AddResponse("200", $"{schemaName} content retrieved.", CreateContentsSchema(schemaName, contentSchema)); operation.AddResponse("200", $"{schemaName} content retrieved.", CreateContentsSchema(schemaName, contentSchema));
operation.Security = ReaderSecurity;
}); });
} }
@ -109,6 +135,8 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.Summary = $"Get a {schemaName} content."; operation.Summary = $"Get a {schemaName} content.";
operation.AddResponse("200", $"{schemaName} content found.", contentSchema); operation.AddResponse("200", $"{schemaName} content found.", contentSchema);
operation.Security = ReaderSecurity;
}); });
} }
@ -119,10 +147,12 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.OperationId = $"Create{schemaKey}Content"; operation.OperationId = $"Create{schemaKey}Content";
operation.Summary = $"Create a {schemaName} content."; operation.Summary = $"Create a {schemaName} content.";
operation.AddBodyParameter("data", dataSchema, schemaBodyDescription); operation.AddBodyParameter("data", dataSchema, SchemaBodyDescription);
operation.AddQueryParameter("publish", JsonObjectType.Boolean, "Set to true to autopublish content."); operation.AddQueryParameter("publish", JsonObjectType.Boolean, "Set to true to autopublish content.");
operation.AddResponse("201", $"{schemaName} created.", contentSchema); operation.AddResponse("201", $"{schemaName} created.", contentSchema);
operation.Security = EditorSecurity;
}); });
} }
@ -133,9 +163,11 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.OperationId = $"Update{schemaKey}Content"; operation.OperationId = $"Update{schemaKey}Content";
operation.Summary = $"Update a {schemaName} content."; operation.Summary = $"Update a {schemaName} content.";
operation.AddBodyParameter("data", dataSchema, schemaBodyDescription); operation.AddBodyParameter("data", dataSchema, SchemaBodyDescription);
operation.AddResponse("204", $"{schemaName} element updated."); operation.AddResponse("204", $"{schemaName} element updated.");
operation.Security = EditorSecurity;
}); });
} }
@ -146,9 +178,11 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.OperationId = $"Path{schemaKey}Content"; operation.OperationId = $"Path{schemaKey}Content";
operation.Summary = $"Patchs a {schemaName} content."; operation.Summary = $"Patchs a {schemaName} content.";
operation.AddBodyParameter("data", contentSchema, schemaBodyDescription); operation.AddBodyParameter("data", contentSchema, SchemaBodyDescription);
operation.AddResponse("204", $"{schemaName} element updated."); operation.AddResponse("204", $"{schemaName} element updated.");
operation.Security = EditorSecurity;
}); });
} }
@ -160,6 +194,8 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.Summary = $"Publish a {schemaName} content."; operation.Summary = $"Publish a {schemaName} content.";
operation.AddResponse("204", $"{schemaName} element published."); operation.AddResponse("204", $"{schemaName} element published.");
operation.Security = EditorSecurity;
}); });
} }
@ -171,6 +207,8 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.Summary = $"Unpublish a {schemaName} content."; operation.Summary = $"Unpublish a {schemaName} content.";
operation.AddResponse("204", $"{schemaName} element unpublished."); operation.AddResponse("204", $"{schemaName} element unpublished.");
operation.Security = EditorSecurity;
}); });
} }
@ -182,6 +220,8 @@ namespace Squidex.Controllers.ContentApi.Generator
operation.Summary = $"Delete a {schemaName} content."; operation.Summary = $"Delete a {schemaName} content.";
operation.AddResponse("204", $"{schemaName} content deleted."); operation.AddResponse("204", $"{schemaName} content deleted.");
operation.Security = EditorSecurity;
}); });
} }

26
src/Squidex/Controllers/ContentApi/Generator/SchemasSwaggerGenerator.cs

@ -20,7 +20,6 @@ using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Pipeline.Swagger; using Squidex.Pipeline.Swagger;
using Squidex.Shared.Identity;
// ReSharper disable InvertIf // ReSharper disable InvertIf
// ReSharper disable SuggestBaseTypeForParameter // ReSharper disable SuggestBaseTypeForParameter
@ -55,37 +54,12 @@ namespace Squidex.Controllers.ContentApi.Generator
swaggerGenerator = new SwaggerGenerator(schemaGenerator, settings, schemaResolver); swaggerGenerator = new SwaggerGenerator(schemaGenerator, settings, schemaResolver);
GenerateSchemasOperations(schemas, app); GenerateSchemasOperations(schemas, app);
GenerateSecurityRequirements();
await GenerateDefaultErrorsAsync(); await GenerateDefaultErrorsAsync();
return document; return document;
} }
private void GenerateSecurityRequirements()
{
var securityRequirements = new List<SwaggerSecurityRequirement>
{
new SwaggerSecurityRequirement
{
{
Constants.SecurityDefinition,
new List<string>
{
SquidexRoles.AppOwner,
SquidexRoles.AppDeveloper,
SquidexRoles.AppEditor
}
}
}
};
foreach (var operation in document.Paths.Values.SelectMany(x => x.Values))
{
operation.Security = securityRequirements;
}
}
private void GenerateSchemasOperations(IEnumerable<ISchemaEntity> schemas, IAppEntity app) private void GenerateSchemasOperations(IEnumerable<ISchemaEntity> schemas, IAppEntity app)
{ {
var appBasePath = $"/content/{app.Name}"; var appBasePath = $"/content/{app.Name}";

2
src/Squidex/Controllers/ContentApi/Models/ContentsDto.cs

@ -8,7 +8,7 @@
namespace Squidex.Controllers.ContentApi.Models namespace Squidex.Controllers.ContentApi.Models
{ {
public class AssetsDto public sealed class AssetsDto
{ {
/// <summary> /// <summary>
/// The total number of content items. /// The total number of content items.

2
src/Squidex/Controllers/UI/Error/ErrorController.cs

@ -12,7 +12,7 @@ using NSwag.Annotations;
namespace Squidex.Controllers.UI.Error namespace Squidex.Controllers.UI.Error
{ {
[SwaggerIgnore] [SwaggerIgnore]
public class ErrorController : Controller public sealed class ErrorController : Controller
{ {
[Route("error")] [Route("error")]
public IActionResult Error() public IActionResult Error()

2
src/Squidex/Controllers/UI/Profile/PortalController.cs

@ -17,7 +17,7 @@ namespace Squidex.Controllers.UI.Profile
{ {
[Authorize] [Authorize]
[SwaggerIgnore] [SwaggerIgnore]
public class PortalController : Controller public sealed class PortalController : Controller
{ {
private readonly IAppPlanBillingManager appPlansBillingManager; private readonly IAppPlanBillingManager appPlansBillingManager;

2
src/Squidex/Controllers/UI/Profile/ProfileController.cs

@ -27,7 +27,7 @@ namespace Squidex.Controllers.UI.Profile
{ {
[Authorize] [Authorize]
[SwaggerIgnore] [SwaggerIgnore]
public class ProfileController : Controller public sealed class ProfileController : Controller
{ {
private readonly SignInManager<IUser> signInManager; private readonly SignInManager<IUser> signInManager;
private readonly UserManager<IUser> userManager; private readonly UserManager<IUser> userManager;

7
src/Squidex/Pipeline/Swagger/SwaggerHelper.cs

@ -94,9 +94,10 @@ namespace Squidex.Pipeline.Swagger
Scopes = new Dictionary<string, string> Scopes = new Dictionary<string, string>
{ {
{ Constants.ApiScope, "Read and write access to the API" }, { Constants.ApiScope, "Read and write access to the API" },
{ SquidexRoles.AppOwner, "You get this scope / role when you are owner of the app you are accessing." }, { SquidexRoles.AppOwner, "App contributor with Owner permission." },
{ SquidexRoles.AppEditor, "You get this scope / role when you are owner of the app you are accessing or when the subject is a client." }, { SquidexRoles.AppEditor, "Client (writer) or App contributor with Editor permission." },
{ SquidexRoles.AppDeveloper, "You get this scope / role when you are owner of the app you are accessing." } { SquidexRoles.AppReader, "Client (readonly) or App contributor with Editor permission." },
{ SquidexRoles.AppDeveloper, "App contributor with Developer permission." },
}, },
Description = securityDescription Description = securityDescription
}; };

2
src/Squidex/Startup.cs

@ -76,6 +76,8 @@ namespace Squidex
Configuration.GetSection("urls")); Configuration.GetSection("urls"));
services.Configure<MyIdentityOptions>( services.Configure<MyIdentityOptions>(
Configuration.GetSection("identity")); Configuration.GetSection("identity"));
services.Configure<MyUIOptions>(
Configuration.GetSection("ui"));
services.Configure<MyUsageOptions>( services.Configure<MyUsageOptions>(
Configuration.GetSection("usage")); Configuration.GetSection("usage"));

15
src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.html

@ -24,7 +24,20 @@
<label class="col col-3 col-form-label" for="field-pattern">Pattern</label> <label class="col col-3 col-form-label" for="field-pattern">Pattern</label>
<div class="col col-6"> <div class="col col-6">
<input type="text" class="form-control" id="field-pattern" formControlName="pattern" placeholder="Regex Pattern" /> <input type="text" class="form-control" id="field-pattern" formControlName="pattern" placeholder="Regex Pattern" #patternInput
autocomplete="off"
autocorrect="off"
autocapitalize="off"
(focus)="regexSuggestionsModal.show()" (blur)="regexSuggestionsModal.hide()" />
<div *ngIf="regexSuggestions.length > 0 && (regexSuggestionsModal.isOpen | async) && (showPatternSuggestions | async)" [sqxModalTarget]="patternInput" class="control-dropdown">
<h4>Suggestions</h4>
<div *ngFor="let suggestion of regexSuggestions" class="control-dropdown-item control-dropdown-item-selectable" (mousedown)="setPattern(suggestion.pattern)">
<div class="truncate">{{suggestion.name}}</div>
<div class="truncate text-muted">{{suggestion.pattern}}</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="form-group row" *ngIf="showPatternMessage | async"> <div class="form-group row" *ngIf="showPatternMessage | async">

27
src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.scss

@ -11,6 +11,33 @@
} }
} }
.control-dropdown {
& {
max-width: 285px;
min-height: 220px;
}
h4 {
padding: .5rem 0 0 .5rem;
}
&-item {
& {
font-size: .85rem;
}
&:hover {
.text-muted {
color: $color-dark-foreground !important;
}
}
}
}
.form-check-input { .form-check-input {
margin: 0; margin: 0;
}
.truncate {
@include truncate;
} }

45
src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts

@ -9,7 +9,12 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { StringFieldPropertiesDto } from 'shared'; import {
ModalView,
StringFieldPropertiesDto,
UIRegexSuggestionDto,
UIService
} from 'shared';
@Component({ @Component({
selector: 'sqx-string-validation', selector: 'sqx-string-validation',
@ -18,6 +23,7 @@ import { StringFieldPropertiesDto } from 'shared';
}) })
export class StringValidationComponent implements OnDestroy, OnInit { export class StringValidationComponent implements OnDestroy, OnInit {
private patternSubscription: Subscription; private patternSubscription: Subscription;
private uiSettingsSubscription: Subscription;
@Input() @Input()
public editForm: FormGroup; public editForm: FormGroup;
@ -27,9 +33,20 @@ export class StringValidationComponent implements OnDestroy, OnInit {
public showDefaultValue: Observable<boolean>; public showDefaultValue: Observable<boolean>;
public showPatternMessage: Observable<boolean>; public showPatternMessage: Observable<boolean>;
public showPatternSuggestions: Observable<boolean>;
public regexSuggestions: UIRegexSuggestionDto[] = [];
public regexSuggestionsModal = new ModalView(false, false);
constructor(
private readonly uiService: UIService
) {
}
public ngOnDestroy() { public ngOnDestroy() {
this.patternSubscription.unsubscribe(); this.patternSubscription.unsubscribe();
this.uiSettingsSubscription.unsubscribe();
} }
public ngOnInit() { public ngOnInit() {
@ -58,11 +75,27 @@ export class StringValidationComponent implements OnDestroy, OnInit {
.startWith('') .startWith('')
.map(x => x && x.trim().length > 0); .map(x => x && x.trim().length > 0);
this.showPatternSuggestions =
this.editForm.controls['pattern'].valueChanges
.startWith('')
.map(x => !x || x.trim().length === 0);
this.uiSettingsSubscription =
this.uiService.getSettings()
.subscribe(settings => {
this.regexSuggestions = settings.regexSuggestions;
});
this.patternSubscription = this.patternSubscription =
this.editForm.controls['pattern'].valueChanges.subscribe((value: string) => { this.editForm.controls['pattern'].valueChanges
if (!value || value.length === 0) { .subscribe((value: string) => {
this.editForm.controls['patternMessage'].setValue(undefined); if (!value || value.length === 0) {
} this.editForm.controls['patternMessage'].setValue(undefined);
}); }
});
}
public setPattern(pattern: string) {
this.editForm.controls['pattern'].setValue(pattern);
} }
} }

1
src/Squidex/app/shared/declarations-base.ts

@ -31,6 +31,7 @@ export * from './services/history.service';
export * from './services/languages.service'; export * from './services/languages.service';
export * from './services/plans.service'; export * from './services/plans.service';
export * from './services/schemas.service'; export * from './services/schemas.service';
export * from './services/ui.service';
export * from './services/usages.service'; export * from './services/usages.service';
export * from './services/users-provider.service'; export * from './services/users-provider.service';
export * from './services/users.service'; export * from './services/users.service';

2
src/Squidex/app/shared/module.ts

@ -44,6 +44,7 @@ import {
ResolveSchemaGuard, ResolveSchemaGuard,
SchemasService, SchemasService,
ResolveUserGuard, ResolveUserGuard,
UIService,
UsagesService, UsagesService,
UserDtoPicture, UserDtoPicture,
UserEmailPipe, UserEmailPipe,
@ -129,6 +130,7 @@ export class SqxSharedModule {
ResolveSchemaGuard, ResolveSchemaGuard,
ResolveUserGuard, ResolveUserGuard,
SchemasService, SchemasService,
UIService,
UsagesService, UsagesService,
UserManagementService, UserManagementService,
UsersProviderService, UsersProviderService,

79
src/Squidex/app/shared/services/ui.service.spec.ts

@ -0,0 +1,79 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { inject, TestBed } from '@angular/core/testing';
import {
ApiUrlConfig,
UIService,
UISettingsDto
} from './../';
describe('UIService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule
],
providers: [
UIService,
{ provide: ApiUrlConfig, useValue: new ApiUrlConfig('http://service/p/') }
]
});
});
afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
httpMock.verify();
}));
it('should make get request to get settings',
inject([UIService, HttpTestingController], (uiService: UIService, httpMock: HttpTestingController) => {
let settings1: UISettingsDto | null = null;
let settings2: UISettingsDto | null = null;
uiService.getSettings().subscribe(result => {
settings1 = result;
});
const response: UISettingsDto = { regexSuggestions: [] };
const req = httpMock.expectOne('http://service/p/api/ui/settings');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.flush(response);
uiService.getSettings().subscribe(result => {
settings2 = result;
});
expect(settings1).toEqual(response);
expect(settings2).toEqual(response);
}));
it('should return default settings when error occurs',
inject([UIService, HttpTestingController], (uiService: UIService, httpMock: HttpTestingController) => {
let settings: UISettingsDto | null = null;
uiService.getSettings().subscribe(result => {
settings = result;
});
const req = httpMock.expectOne('http://service/p/api/ui/settings');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
req.error(new ErrorEvent('500'));
expect(settings.regexSuggestions).toEqual([]);
}));
});

49
src/Squidex/app/shared/services/ui.service.ts

@ -0,0 +1,49 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import 'framework/angular/http-extensions';
import { ApiUrlConfig } from 'framework';
export interface UISettingsDto {
regexSuggestions: UIRegexSuggestionDto[];
}
export interface UIRegexSuggestionDto {
name: string; pattern: string;
}
@Injectable()
export class UIService {
private settings: UISettingsDto;
constructor(
private readonly http: HttpClient,
private readonly apiUrl: ApiUrlConfig
) {
}
public getSettings(): Observable<UISettingsDto> {
if (this.settings) {
return Observable.of(this.settings);
} else {
const url = this.apiUrl.buildUrl(`api/ui/settings`);
return this.http.get<UISettingsDto>(url)
.catch(error => {
return Observable.of({ regexSuggestions: [] });
})
.do(settings => {
this.settings = settings;
});
}
}
}

16
src/Squidex/appsettings.json

@ -6,6 +6,22 @@
"baseUrl": "http://localhost:5000" "baseUrl": "http://localhost:5000"
}, },
"ui": {
/*
* Regex suggestions for the UI
*/
"regexSuggestions": {
// Regex for emails.
"Email": "^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\\\.[a-zA-Z0-9-]+)*$",
// Regex for phone numbers.
"Phone": "^\\(*\\+*[1-9]{0,3}\\)*-*[1-9]{0,3}[-. /]*\\(*[2-9]\\d{2}\\)*[-. /]*\\d{3}[-. /]*\\d{4} *e*x*t*\\.* *\\d{0,4}$",
// Regex for slugs (e.g. hello-world).
"Slug": "^[a-z0-9]+(\\\\-[a-z0-9]+)*$",
// Regex for urls.
"Url": "^(?:http(s)?:\\/\\/)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$"
}
},
"logging": { "logging": {
/* /*
* Setting the flag to true, enables well formatteds json logs. * Setting the flag to true, enables well formatteds json logs.

Loading…
Cancel
Save