Browse Source

Merge branch 'release/4.x' of github.com:Squidex/squidex into release/4.x

pull/575/head
Sebastian 5 years ago
parent
commit
96ac1f33bf
  1. 7
      backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaAction.cs
  2. 4
      backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueAction.cs
  3. 3
      backend/extensions/Squidex.Extensions/Actions/Comment/CommentAction.cs
  4. 8
      backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseAction.cs
  5. 4
      backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs
  6. 19
      backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs
  7. 5
      backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyAction.cs
  8. 4
      backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyActionHandler.cs
  9. 3
      backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaAction.cs
  10. 7
      backend/extensions/Squidex.Extensions/Actions/Medium/MediumAction.cs
  11. 5
      backend/extensions/Squidex.Extensions/Actions/Notification/NotificationAction.cs
  12. 5
      backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderAction.cs
  13. 4
      backend/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs
  14. 7
      backend/extensions/Squidex.Extensions/Actions/Twitter/TweetAction.cs
  15. 3
      backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs
  16. 7
      backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookActionHandler.cs
  17. 9
      backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj
  18. 24
      backend/i18n/frontend_en.json
  19. 888
      backend/i18n/frontend_it.json
  20. 30
      backend/i18n/frontend_nl.json
  21. 4
      backend/i18n/source/backend__ignore.json
  22. 23
      backend/i18n/source/backend_en.json
  23. 320
      backend/i18n/source/backend_it.json
  24. 18
      backend/i18n/source/backend_nl.json
  25. 4
      backend/i18n/source/frontend__ignore.json
  26. 24
      backend/i18n/source/frontend_en.json
  27. 848
      backend/i18n/source/frontend_it.json
  28. 8
      backend/i18n/source/frontend_nl.json
  29. 2
      backend/i18n/translator/Squidex.Translator/Commands.cs
  30. 10
      backend/i18n/translator/Squidex.Translator/Processes/CheckBackend.cs
  31. 42
      backend/i18n/translator/Squidex.Translator/Processes/CheckFrontend.cs
  32. 12
      backend/i18n/translator/Squidex.Translator/Processes/GenerateBackendResources.cs
  33. 7
      backend/i18n/translator/Squidex.Translator/Processes/Helper.cs
  34. 9
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/HttpJintExtension.cs
  35. 1
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/StringTextValidator.cs
  36. 6
      backend/src/Squidex.Domain.Apps.Entities/Backup/TempFolderBackupArchiveLocation.cs
  37. 14
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs
  38. 39
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  39. 52
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs
  40. 11
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/IGraphModel.cs
  41. 9
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Middlewares.cs
  42. 20
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs
  43. 100
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppMutationsGraphType.cs
  44. 187
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppQueriesGraphType.cs
  45. 112
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetActions.cs
  46. 71
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs
  47. 66
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetResolvers.cs
  48. 11
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetsResultGraphType.cs
  49. 336
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentActions.cs
  50. 20
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataFlatGraphType.cs
  51. 35
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs
  52. 62
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataInputGraphType.cs
  53. 41
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs
  54. 25
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentInterfaceGraphType.cs
  55. 106
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentResolvers.cs
  56. 11
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentsResultGraphType.cs
  57. 27
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/EntityResolvers.cs
  58. 43
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs
  59. 85
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/InputFieldVisitor.cs
  60. 27
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedGraphType.cs
  61. 45
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedInputGraphType.cs
  62. 31
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs
  63. 79
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/Converters.cs
  64. 43
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/EntitySavedGraphType.cs
  65. 35
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/GeolocationInputGraphType.cs
  66. 55
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/GuidGraphType2.cs
  67. 32
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantConverter.cs
  68. 14
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantGraphType.cs
  69. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantValueNode.cs
  70. 44
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonConverter.cs
  71. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonGraphType.cs
  72. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonValueNode.cs
  73. 12
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/QueryExecutionContext.cs
  74. 2
      backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj
  75. 20
      backend/src/Squidex.Domain.Users/UserManagerExtensions.cs
  76. 1
      backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore.cs
  77. 6
      backend/src/Squidex.Infrastructure/Translations/DeepLTranslator.cs
  78. 5
      backend/src/Squidex.Infrastructure/Validation/LocalizedCompareAttribute.cs
  79. 30
      backend/src/Squidex.Infrastructure/Validation/LocalizedEmailAddressAttribute.cs
  80. 38
      backend/src/Squidex.Infrastructure/Validation/LocalizedRangeAttribute.cs
  81. 30
      backend/src/Squidex.Infrastructure/Validation/LocalizedRegularExpressionAttribute.cs
  82. 7
      backend/src/Squidex.Infrastructure/Validation/LocalizedRequiredAttribute.cs
  83. 38
      backend/src/Squidex.Infrastructure/Validation/LocalizedStringLengthAttribute.cs
  84. 1138
      backend/src/Squidex.Shared/Texts.it.resx
  85. 93
      backend/src/Squidex.Shared/Texts.nl.resx
  86. 93
      backend/src/Squidex.Shared/Texts.resx
  87. 2
      backend/src/Squidex.Web/ApiModelValidationAttribute.cs
  88. 3
      backend/src/Squidex.Web/EntityCreatedDto.cs
  89. 3
      backend/src/Squidex.Web/ErrorDto.cs
  90. 5
      backend/src/Squidex.Web/Resource.cs
  91. 5
      backend/src/Squidex.Web/ResourceLink.cs
  92. 9
      backend/src/Squidex.Web/Services/StringLocalizer.cs
  93. 4
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AddLanguageDto.cs
  94. 4
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AddRoleDto.cs
  95. 4
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AddWorkflowDto.cs
  96. 6
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  97. 8
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs
  98. 4
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguagesDto.cs
  99. 4
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs
  100. 8
      backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs

7
backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaAction.cs

@ -8,6 +8,7 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Algolia
{
@ -20,19 +21,19 @@ namespace Squidex.Extensions.Actions.Algolia
ReadMore = "https://www.algolia.com/")]
public sealed class AlgoliaAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Application Id", Description = "The application ID.")]
[DataType(DataType.Text)]
[Formattable]
public string AppId { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Api Key", Description = "The API key to grant access to Squidex.")]
[DataType(DataType.Text)]
[Formattable]
public string ApiKey { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Index Name", Description = "The name of the index.")]
[DataType(DataType.Text)]
[Formattable]

4
backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueAction.cs

@ -23,13 +23,13 @@ namespace Squidex.Extensions.Actions.AzureQueue
ReadMore = "https://azure.microsoft.com/en-us/services/storage/queues/")]
public sealed class AzureQueueAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Connection", Description = "The connection string to the storage account.")]
[DataType(DataType.Text)]
[Formattable]
public string ConnectionString { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Queue", Description = "The name of the queue.")]
[DataType(DataType.Text)]
[Formattable]

3
backend/extensions/Squidex.Extensions/Actions/Comment/CommentAction.cs

@ -8,6 +8,7 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Comment
{
@ -19,7 +20,7 @@ namespace Squidex.Extensions.Actions.Comment
Description = "Create a comment for a content event.")]
public sealed class CommentAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Text", Description = "The comment text.")]
[DataType(DataType.MultilineText)]
[Formattable]

8
backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseAction.cs

@ -23,22 +23,22 @@ namespace Squidex.Extensions.Actions.Discourse
public sealed class DiscourseAction : RuleAction
{
[AbsoluteUrl]
[Required]
[LocalizedRequired]
[Display(Name = "Server Url", Description = "The url to the discourse server.")]
[DataType(DataType.Url)]
public Uri Url { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Api Key", Description = "The api key to authenticate to your discourse server.")]
[DataType(DataType.Text)]
public string ApiKey { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Api User", Description = "The api username to authenticate to your discourse server.")]
[DataType(DataType.Text)]
public string ApiUsername { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Text", Description = "The text as markdown.")]
[DataType(DataType.MultilineText)]
[Formattable]

4
backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs

@ -23,12 +23,12 @@ namespace Squidex.Extensions.Actions.ElasticSearch
public sealed class ElasticSearchAction : RuleAction
{
[AbsoluteUrl]
[Required]
[LocalizedRequired]
[Display(Name = "Server Url", Description = "The url to the elastic search instance or cluster.")]
[DataType(DataType.Url)]
public Uri Host { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Index Name", Description = "The name of the index.")]
[DataType(DataType.Text)]
[Formattable]

19
backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs

@ -8,6 +8,7 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Email
{
@ -20,51 +21,51 @@ namespace Squidex.Extensions.Actions.Email
ReadMore = "https://en.wikipedia.org/wiki/Email")]
public sealed class EmailAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Server Host", Description = "The IP address or host to the SMTP server.")]
[DataType(DataType.Text)]
public string ServerHost { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Server Port", Description = "The port to the SMTP server.")]
[DataType(DataType.Text)]
public int ServerPort { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Use SSL", Description = "Specify whether the SMPT client uses Secure Sockets Layer (SSL) to encrypt the connection.")]
[DataType(DataType.Text)]
public bool ServerUseSsl { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Username", Description = "The username for the SMTP server.")]
[DataType(DataType.Text)]
[Formattable]
public string ServerUsername { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Password", Description = "The password for the SMTP server.")]
[DataType(DataType.Password)]
public string ServerPassword { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "From Address", Description = "The email sending address.")]
[DataType(DataType.Text)]
[Formattable]
public string MessageFrom { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "To Address", Description = "The email message will be sent to.")]
[DataType(DataType.Text)]
[Formattable]
public string MessageTo { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Subject", Description = "The subject line for this email message.")]
[DataType(DataType.Text)]
[Formattable]
public string MessageSubject { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Body", Description = "The message body.")]
[DataType(DataType.MultilineText)]
[Formattable]

5
backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyAction.cs

@ -8,6 +8,7 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Fastly
{
@ -20,12 +21,12 @@ namespace Squidex.Extensions.Actions.Fastly
ReadMore = "https://www.fastly.com/")]
public sealed class FastlyAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Api Key", Description = "The API key to grant access to Squidex.")]
[DataType(DataType.Text)]
public string ApiKey { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Service Id", Description = "The ID of the fastly service.")]
[DataType(DataType.Text)]
public string ServiceId { get; set; }

4
backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyActionHandler.cs

@ -50,14 +50,16 @@ namespace Squidex.Extensions.Actions.Fastly
httpClient.Timeout = TimeSpan.FromSeconds(2);
var requestUrl = $"https://api.fastly.com/service/{job.FastlyServiceID}/purge/{job.Key}";
var request = new HttpRequestMessage(HttpMethod.Post, requestUrl);
using (var request = new HttpRequestMessage(HttpMethod.Post, requestUrl))
{
request.Headers.Add("Fastly-Key", job.FastlyApiKey);
return await httpClient.OneWayRequestAsync(request, ct: ct);
}
}
}
}
public sealed class FastlyJob
{

3
backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaAction.cs

@ -8,6 +8,7 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Kafka
{
@ -20,7 +21,7 @@ namespace Squidex.Extensions.Actions.Kafka
ReadMore = "https://kafka.apache.org/quickstart")]
public sealed class KafkaAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Topic Name", Description = "The name of the topic.")]
[DataType(DataType.Text)]
[Formattable]

7
backend/extensions/Squidex.Extensions/Actions/Medium/MediumAction.cs

@ -8,6 +8,7 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Medium
{
@ -20,18 +21,18 @@ namespace Squidex.Extensions.Actions.Medium
ReadMore = "https://medium.com/")]
public sealed class MediumAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Access Token", Description = "The self issued access token.")]
[DataType(DataType.Text)]
public string AccessToken { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Title", Description = "The title, used for the url.")]
[DataType(DataType.Text)]
[Formattable]
public string Title { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Content", Description = "The content, either html or markdown.")]
[DataType(DataType.MultilineText)]
[Formattable]

5
backend/extensions/Squidex.Extensions/Actions/Notification/NotificationAction.cs

@ -8,6 +8,7 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Notification
{
@ -19,12 +20,12 @@ namespace Squidex.Extensions.Actions.Notification
Description = "Send an integrated notification to a user.")]
public sealed class NotificationAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "User", Description = "The user id or email.")]
[DataType(DataType.Text)]
public string User { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Title", Description = "The text to send.")]
[DataType(DataType.MultilineText)]
[Formattable]

5
backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderAction.cs

@ -8,6 +8,7 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Prerender
{
@ -20,13 +21,13 @@ namespace Squidex.Extensions.Actions.Prerender
ReadMore = "https://prerender.io")]
public sealed class PrerenderAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Token", Description = "The prerender token from your account.")]
[DataType(DataType.Text)]
[Formattable]
public string Token { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Url", Description = "The url to recache.")]
[DataType(DataType.Text)]
public string Url { get; set; }

4
backend/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs

@ -23,12 +23,12 @@ namespace Squidex.Extensions.Actions.Slack
public sealed class SlackAction : RuleAction
{
[AbsoluteUrl]
[Required]
[LocalizedRequired]
[Display(Name = "Webhook Url", Description = "The slack webhook url.")]
[DataType(DataType.Text)]
public Uri WebhookUrl { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Text", Description = "The text that is sent as message to slack.")]
[DataType(DataType.MultilineText)]
[Formattable]

7
backend/extensions/Squidex.Extensions/Actions/Twitter/TweetAction.cs

@ -8,6 +8,7 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Twitter
{
@ -20,17 +21,17 @@ namespace Squidex.Extensions.Actions.Twitter
ReadMore = "https://twitter.com")]
public sealed class TweetAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Access Token", Description = " The generated access token.")]
[DataType(DataType.Text)]
public string AccessToken { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Access Secret", Description = " The generated access secret.")]
[DataType(DataType.Text)]
public string AccessSecret { get; set; }
[Required]
[LocalizedRequired]
[Display(Name = "Text", Description = "The text that is sent as tweet to twitter.")]
[DataType(DataType.MultilineText)]
[Formattable]

3
backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs

@ -9,6 +9,7 @@ using System;
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure.Validation;
namespace Squidex.Extensions.Actions.Webhook
{
@ -21,7 +22,7 @@ namespace Squidex.Extensions.Actions.Webhook
ReadMore = "https://en.wikipedia.org/wiki/Webhook")]
public sealed class WebhookAction : RuleAction
{
[Required]
[LocalizedRequired]
[Display(Name = "Url", Description = "The url to the webhook.")]
[DataType(DataType.Text)]
[Formattable]

7
backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookActionHandler.cs

@ -57,11 +57,11 @@ namespace Squidex.Extensions.Actions.Webhook
{
using (var httpClient = httpClientFactory.CreateClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl)
using (var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl)
{
Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json")
};
})
{
request.Headers.Add("X-Signature", job.RequestSignature);
request.Headers.Add("X-Application", "Squidex Webhook");
request.Headers.Add("User-Agent", "Squidex Webhook");
@ -70,6 +70,7 @@ namespace Squidex.Extensions.Actions.Webhook
}
}
}
}
public sealed class WebhookJob
{

9
backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj

@ -8,14 +8,15 @@
<ProjectReference Include="..\..\src\Squidex.Web\Squidex.Web.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Algolia.Search" Version="6.8.0" />
<PackageReference Include="Algolia.Search" Version="6.6.0" />
<PackageReference Include="Confluent.Apache.Avro" Version="1.7.7.7" />
<PackageReference Include="Confluent.Kafka" Version="1.5.1" />
<PackageReference Include="Confluent.Kafka" Version="1.4.3" />
<PackageReference Include="Confluent.SchemaRegistry.Serdes" Version="1.3.0" />
<PackageReference Include="CoreTweet" Version="1.0.0.483" />
<PackageReference Include="Datadog.Trace" Version="1.19.1" />
<PackageReference Include="Elasticsearch.Net" Version="7.9.0" />
<PackageReference Include="Datadog.Trace" Version="1.17.0" />
<PackageReference Include="Elasticsearch.Net" Version="7.7.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.4" />
<PackageReference Include="Microsoft.OData.Core" Version="7.6.4" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NodaTime" Version="3.0.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />

24
backend/i18n/frontend_en.json

@ -90,6 +90,8 @@
"assets.searchByName": "Search by name",
"assets.searchByTags": "Search by tags",
"assets.selectMany": "Select assets",
"assets.specialFolder.parent": "<Parent>",
"assets.specialFolder.root": "Assets",
"assets.tabFocusPoint": "Focus Point",
"assets.tabHistory": "History",
"assets.tabImage": "Image",
@ -273,6 +275,16 @@
"common.preview": "Preview",
"common.product": "Squidex Headless CMS",
"common.project": "Project",
"common.queryOperators.contains": "contains",
"common.queryOperators.empty": "is empty",
"common.queryOperators.endsWith": "ends with",
"common.queryOperators.eq": "is equals to",
"common.queryOperators.ge": "is greater than or equals to",
"common.queryOperators.gt": "is greater than",
"common.queryOperators.le": "is less than pr equals to",
"common.queryOperators.lt": "is less than",
"common.queryOperators.ne": "is not equals to",
"common.queryOperators.startsWith": "starts with",
"common.refresh": "Refresh",
"common.rename": "Rename",
"common.requiredHint": "required",
@ -347,6 +359,7 @@
"contents.draftStatus": "New Version",
"contents.editPageTitle": "Edit Content",
"contents.editTitle": "Edit Content",
"contents.invariantFieldDescription": "The '{fieldName}' field of the content item.",
"contents.languageModeAll": "All Languages",
"contents.languageModeSingle": "Single Language",
"contents.lastModifiedByFieldDescription": "The user who modified the content item the last time.",
@ -357,6 +370,7 @@
"contents.loadDataFailed": "Failed to load data. Please reload.",
"contents.loadFailed": "Failed to load contents. Please reload.",
"contents.loadVersionFailed": "Failed to version a new version. Please reload.",
"contents.localizedFieldDescription": "The '{fieldName}' field of the content item (localized).",
"contents.newStatusFieldDescription": "The new status of the content item.",
"contents.noReference": "- No Reference -",
"contents.pendingChangesTextToChange": "You have unsaved changes.\n\nWhen you change the status you will loose them.\n\n**Do you want to continue anyway?**",
@ -383,6 +397,16 @@
"contents.statusQueries": "Status Queries",
"contents.stockPhotoEmpty": "Nothing selected",
"contents.stockPhotoSearch": "Search for Photos by Unsplash",
"contents.tableHeaders.created": "Created",
"contents.tableHeaders.createdBy": "Created By",
"contents.tableHeaders.createdByShort": "By",
"contents.tableHeaders.id": "Id",
"contents.tableHeaders.lastModified": "Updated",
"contents.tableHeaders.lastModifiedBy": "Updated By",
"contents.tableHeaders.lastModifiedByShort": "By",
"contents.tableHeaders.nextStatus": "Next Status",
"contents.tableHeaders.status": "Status",
"contents.tableHeaders.version": "Version",
"contents.unsavedChangesText": "You have unsaved changes. Do you want to load them now?",
"contents.unsavedChangesTitle": "Unsaved changes",
"contents.updated": "Content updated successfully.",

888
backend/i18n/frontend_it.json

@ -0,0 +1,888 @@
{
"api.contentApi": "Content API",
"api.generalApi": "General API",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.pageTitle": "API",
"api.title": "API",
"apps.allApps": "Tutte le Apps",
"apps.appLoadFailed": "Non è stato possibile caricare l'App. Per favore ricarica.",
"apps.appNameHint": "Puoi utilizzare solo lettere, numeri e trattini e non più di 40 caratteri.",
"apps.appNameValidationMessage": "Il nome può contenere lettere minuscole (a-z), numeri e trattini all'interno.",
"apps.appNameWarning": "Il nome della app non potrà essere cambiato in un secondo momento.",
"apps.appsButtonCreate": "Panoramica delle App",
"apps.appsButtonFallbackTitle": "Panoramica delle App",
"apps.archieve": "Archivia l'App",
"apps.archieveConfirmText": "Rimuovi il pattern",
"apps.archieveConfirmTitle": "Sei sicuro di voler archiviare questa app?",
"apps.archieveWarning": "Una volta archiviata una App, non è possibile tornare indietro. Sii certo.",
"apps.archiveFailed": "Non è stato possibile archiviare l'app. Per favore ricarica.",
"apps.create": "Crea un'App",
"apps.createBlankApp": "Nuova App.",
"apps.createBlankAppDescription": "Crea una app vuota senza contenuti o schema.",
"apps.createBlogApp": "Nuovo esempio di blog",
"apps.createBlogAppDescription": "Inizia con il nostro blog già pronto per l'uso.",
"apps.createFailed": "Non è stato possibile creare l'app. Per favore ricarica.",
"apps.createIdentityApp": "New Identity App",
"apps.createIdentityAppDescription": "Crea un app per Squidex Identity.",
"apps.createIdentityAppV2": "Nuova Identity App V2",
"apps.createIdentityAppV2Description": "Creare un app per Squidex Identity V2.",
"apps.createProfileApp": "Nuovo esempio di Profilo",
"apps.createProfileAppDescription": "Crea la tua pagina del profilo.",
"apps.createWithTemplate": "Create un esempio di {template}",
"apps.empty": "Non stai ancora collaborando su nessuna app",
"apps.generalSettings": "Generale",
"apps.generalSettingsDangerZone": "Generale",
"apps.image": "Immagine",
"apps.imageDrop": "Trascina il file per caricare",
"apps.listPageTitle": "App",
"apps.loadFailed": "Non è stato possibile caricare le App. Per favore ricarica.",
"apps.removeImage": "Rimuovi l'immagine",
"apps.removeImageFailed": "Non è stato possibile rimuovere l'immagine dell'app. Per favore ricarica.",
"apps.updateFailed": "Non è stato possibile aggiornare l'app. Per favore ricarica.",
"apps.upgradeHintCurrent": "Tu sei nel piano {plan}.",
"apps.upgradeHintUpgrade": "Aggiorna!",
"apps.uploadImage": "Trascina il file per sostituire l'immagine dell'app. Utilizza una dimensione quadrata.",
"apps.uploadImageButton": "Carica il File",
"apps.uploadImageFailed": "Non è stato possibile caricare l'immagine. Per favore ricarica.",
"apps.uploadImageTooBig": "L'immagine dell'app è troppo grande.",
"apps.welcomeSubtitle": "Benvenuto su Squidex.",
"apps.welcomeTitle": "Ciao {user}",
"assets.createFolder": "Crea cartella",
"assets.createFolderFailed": "Non è stato possibile creare la cartella degli asset. Per favore ricarica.",
"assets.createFolderTooltip": "Crea una nuova cartella (CTRL + SHIFT + G)",
"assets.deleteConfirmText": "Sei sicuro di voler cancellare la risorsa?",
"assets.deleteConfirmTitle": "Elimina la risorsa",
"assets.deleteFailed": "Non è stato possibile cancellare la risorsa. Per favore ricarica.",
"assets.deleteFolderConfirmText": "Sei sicuro di voler cancellare la cartella e tutte le risorse associati?",
"assets.deleteFolderConfirmTitle": "Elimina la cartella",
"assets.deleteMetadataConfirmText": "Sei sicuro di voler rimuovere questi metadati?",
"assets.deleteMetadataConfirmTitle": "Rimuovi metadati",
"assets.downloadVersion": "Scarica questa versione",
"assets.dropToUpdate": "Trascina il file per aggiornare",
"assets.duplicateFile": "La risorsa è già stata caricata.",
"assets.editor.flipHorizontally": "Capovolgi orizzontalmente",
"assets.editor.flipVertically": "Capovolgi verticalmente",
"assets.editor.focusPointLabel": "Clicca sull'immagine per impostare il focus",
"assets.editor.focusPointPreview": "Anteprima delle diverse dimensioni",
"assets.editor.rotateLeft": "Ruota a sinistra",
"assets.editor.rotateRight": "Ruota a destra",
"assets.fileTooBig": "La risorsa è troppo grande.",
"assets.folderName": "Nome della cartella",
"assets.folderNameHint": "Il nome della cartella viene usato solo per la visualizzazione e può non essere univoco.",
"assets.insertAssets": "Inserisci le risorse",
"assets.linkSelected": "Collega le risorse selezionate ({count})",
"assets.listPageTitle": "Risorse",
"assets.loadFailed": "Non è stato possibile caricare le risorse. Per favore ricarica.",
"assets.loadFoldersFailed": "Non è stato possibile caricare le cartelle delle risorse. Per favore ricarica.",
"assets.metadata": "Metadati",
"assets.metadataAdd": "Aggiungi i Metadati",
"assets.moveFailed": "Non è stato possibile spostare la risorsa. Per favore ricarica.",
"assets.protected": "Protetto",
"assets.refreshTooltip": "Aggiorna le risorse (CTRL + SHIFT + R)",
"assets.reloaded": "Risorse ricaricate.",
"assets.removeConfirmText": "Do you really want to remove the asset?",
"assets.removeConfirmTitle": "Remove asset",
"assets.renameFolder": "Rinomina la cartella",
"assets.replaceConfirmText": "Sei sicuro di voler sostituire la risorsa con una nuova versione?",
"assets.replaceConfirmTitle": "Sostituisco la risorsa?",
"assets.replaceFailed": "Non è stato possibile sostituire la risorsa. Per favore ricarica.",
"assets.searchByName": "Ricerca per nome",
"assets.searchByTags": "Ricerca per tag",
"assets.selectMany": "Seleziona le risorse",
"assets.specialFolder.parent": "<Parent>",
"assets.specialFolder.root": "Assets",
"assets.tabFocusPoint": "Punto focale",
"assets.tabHistory": "Cronologia",
"assets.tabImage": "Immagine",
"assets.tabMetadata": "Metadati",
"assets.updated": "La risorsa è stata aggiornata.",
"assets.updateFailed": "Non è stato possibile aggiornare la risorsa. Per favore ricarica.",
"assets.updateFolderFailed": "Non è stato possibile aggiornare la cartella delle risorse. Per favore ricarica.",
"assets.uploadByDialog": "Seleziona il(i) File",
"assets.uploadByDrop": "Trascina i file qui per il caricamento",
"assets.uploaderUploadHere": "Nessun caricamento in corso, trascina qui i file.",
"assets.uploadFailed": "Non è stato possibile caricare la risorsa. Per favore ricarica.",
"assets.uploadHint": "Trascina il file sull'elemento esistente per poterlo sostituire con una versione più recente.",
"backups.backupCountAssetsLabel": "Risorse",
"backups.backupCountAssetsTooltip": "Risorse archiviate",
"backups.backupCountEventsLabel": "Eventi",
"backups.backupCountEventsTooltip": "Eventi archiviati",
"backups.backupDownload": "Scarica",
"backups.backupDownloadLink": "Pronto",
"backups.backupDuration": "Durata",
"backups.deleteConfirmText": "Sei sicuro di voler cancellare il backup?",
"backups.deleteConfirmTitle": "Cancella il backup",
"backups.deleted": "Il backup sta per essere cancellato.",
"backups.deleteFailed": "Non è stato possibile cancellare il backup.",
"backups.empty": "Nessun backup è stato ancora creato.",
"backups.loadFailed": "Non è stato possibile caricare i backup.",
"backups.maximumReached": "Hai raggiunto il numero massimo di backup: 10.",
"backups.refreshTooltip": "Aggiorna i backup (CTRL + SHIFT + R)",
"backups.reloaded": "Backup aggiornati.",
"backups.restore": "Backup ripristinato",
"backups.restoreFailed": "Non è stato possibile avviare il ripristino.",
"backups.restoreLastStatus": "Ultima operazione di ripristino",
"backups.restoreLastUrl": "Url per il backup",
"backups.restoreNewAppName": "Nome dell'app opzionale",
"backups.restorePageTitle": "Ripristinare il Backup",
"backups.restoreStarted": "Ripristino avviato, il suo completamento potrebbe richiedere alcuni minuti.",
"backups.restoreStartedLabel": "Avviato",
"backups.restoreStoppedLabel": "Fermato",
"backups.restoreTitle": "Ripristinare il Backup",
"backups.start": "Avvia Backup",
"backups.started": "Backup avviato, il suo completamento potrebbe richiedere alcuni minuti.",
"backups.startedLabel": "Avviato",
"backups.startFailed": "Non è stato possibile avviare il backup.",
"clients.add": "Aggiungi un Client",
"clients.addFailed": "Non è stato possibile aggiungere un client. Per favore ricarica.",
"clients.allowAnonymous": "Consenti l'accesso anonimo.",
"clients.allowAnonymousHint": "Consenti l'accesso alle API senza token di accesso a tutte le risorse che sono configurate per il ruolo di questo client. E' possibile avere un solo client impostato con accesso anonimo.",
"clients.apiCallsLimit": "Max API Calls",
"clients.apiCallsLimitHint": "Limit the number of API calls this client can make per month to protect your API contingent for other clients that are more important.",
"clients.clientIdValidationMessage": "Il nome deve contenere solo lettere, numeri, trattini e spaziNa.",
"clients.clientNamePlaceholder": "Inserisci il nome del client",
"clients.connect": "Connettere",
"clients.connectWizard.cli": "Connettere con la Squidex CLI",
"clients.connectWizard.cliHint": "Scarica a CLI e collega questa app per iniziare il backup, sincronizzare gli schema ed esportare i contenuti.",
"clients.connectWizard.cliStep1": "Prendi l'ultima versione della Squidex CLI",
"clients.connectWizard.cliStep1Download": "[Scarica la CLI da Github](https://github.com/Squidex/squidex-samples/releases)",
"clients.connectWizard.cliStep1Hint": "Le release contengono file binari per tutte le operazioni di sistema maggiori e piccoli file da scariscare se hai installato il Core .NET Core.",
"clients.connectWizard.cliStep2": "Inserisci `<la directory per scaricare la tua Squidex CLI>` per impostare la variabile `$PATH`",
"clients.connectWizard.cliStep3": "Inserisci il nome della tua app per la configurazione della CLI",
"clients.connectWizard.cliStep3Hint": "E' possibile gestire le configurazionie per le diverse appi all'interno della CLI e passare ad un'app.",
"clients.connectWizard.cliStep4": "Passa alla tua app usando CLI",
"clients.connectWizard.manually": "Connetti manualmente",
"clients.connectWizard.manuallyHint": "Leggi le istruzioni su come stabilire una connessione utilizzando Postman o curl.",
"clients.connectWizard.manuallyStep1": "Ottenere un tocket usando curl",
"clients.connectWizard.manuallyStep2": "Utilizza il seguente token",
"clients.connectWizard.manuallyStep3": "Aggiungi il tocken come header HTTP header a tutte le richieste",
"clients.connectWizard.manuallyTokenHint": "Solitamente i Token scadono dopo 30 giorni, ma puoi richiedere token multipli.",
"clients.connectWizard.postManDocs": "Per il tutorial Postman inizia da questo link [Documentazione](https://docs.squidex.io/02-documentation/developer-guides/api-overview/postman).",
"clients.connectWizard.sdk": "Connetti la tua APP utilizzando SDK",
"clients.connectWizard.sdkHelp": "Hai bisogno di un altro SDK?",
"clients.connectWizard.sdkHelpLink": "Contattaci nel Forum di assistenza",
"clients.connectWizard.sdkHint": "Scarica l'SDK e connetti quest'app.",
"clients.connectWizard.sdkStep1": "Installa .NET SDK",
"clients.connectWizard.sdkStep1Download": "L'SDK è disponibile su [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.sdkStep2": "Crea un client manager",
"clients.connectWizard.step0Title": "Setup client",
"clients.connectWizard.step1Title": "Scegli la tipologia di connessione",
"clients.connectWizard.step2Title": "Collega",
"clients.deleteConfirmText": "Sei sicuro di voler rimuovere il client?",
"clients.deleteConfirmTitle": "Rimuovere il client",
"clients.empty": "Nessun client ancora creato.",
"clients.loadFailed": "Non è stato possibile caricare i client. Per favore ricarica.",
"clients.refreshTooltip": "Agigorna i client (CTRL + SHIFT + R)",
"clients.reloaded": "Client ricaricati.",
"clients.revokeFailed": "Non è stato possibile rimuovere il client. Per favore ricarica.",
"clients.tokenFailed": "Non è stato possibile creare il token. Per favore riprova.",
"comments.create": "Creare un commento",
"comments.createFailed": "Non è stato possibile creare un commento.",
"comments.deleteConfirmText": "Sei sicuro di voler cancellare il commmento?",
"comments.deleteConfirmTitle": "Cancella il comment",
"comments.deleteFailed": "Non è stato possibile cancellare il commento.",
"comments.follow": "Segui",
"comments.loadFailed": "Non è stato possibile caricare i commenti.",
"comments.title": "Commenti",
"comments.updateFailed": "Non è stato possibile aggiornare il commento.",
"common.actions": "Azioni",
"common.administration": "Amministrazione",
"common.administrationPageTitle": "Amministrazione",
"common.api": "API",
"common.apps": "App",
"common.aspectRatio": "Proporzioni",
"common.assets": "Risorse",
"common.back": "Indietro",
"common.backups": "Backup",
"common.bytes": "byte",
"common.cancel": "Annulla",
"common.clear": "Pulisci",
"common.clientId": "Id Client",
"common.clients": "Client",
"common.clientSecret": "Secret Client",
"common.clipboardAdded": "Il valore è stato aggiunto nei tuoi appunti.",
"common.clone": "Clona",
"common.cluster": "Raggruppamento",
"common.clusterPageTitle": "Raggruppamento",
"common.comments": "Commenti",
"common.confirm": "Conferma",
"common.consumers": "Utenti",
"common.content": "Contentenuto",
"common.contents": "Contentenuti",
"common.continue": "Continua",
"common.contributors": "Collaboratori",
"common.create": "Crea",
"common.created": "Creato",
"common.date": "Data",
"common.dateTimeEditor.local": "Local",
"common.dateTimeEditor.now": "Data e Ora attuale",
"common.dateTimeEditor.nowTooltip": "Imposta la data e l'ora attuale (UTC)",
"common.dateTimeEditor.today": "Oggi",
"common.dateTimeEditor.todayTooltip": "Imposta la data di oggi (UTC)",
"common.dateTimeEditor.utc": "UTC",
"common.delete": "Cancella",
"common.description": "Descrizione",
"common.displayName": "Nome visualizzato",
"common.edit": "Modifica",
"common.email": "Email",
"common.error": "Errore",
"common.errorBack": "Torna alla pagina precedente.",
"common.errorNoPermission": "Non hai i permessi per questo.",
"common.errorNotFound": "Non trovato",
"common.event": "Evento",
"common.events": "Eventi",
"common.executed": "Eseguito",
"common.expertMode": "Modalità esperto",
"common.failed": "Fallito",
"common.fallback": "Alternativa",
"common.field": "Campo",
"common.files": "Campi",
"common.filters": "Filtri",
"common.folders": "Cartelle",
"common.generalSettings": "Impostazioni generali",
"common.generate": "Genera",
"common.github": "Github",
"common.height": "Altezza",
"common.help": "Aiuto",
"common.helpTour": "Fare clic sull'icona della guida per mostrare una pagina della guida specifica per questo contesto. Vai a",
"common.hide": "Nascondi",
"common.hints": "Suggerimenti",
"common.history": "Cronologia",
"common.httpConflict": "Non è stato possibile effettuare l'aggiornamento. Un altro utente ha fatto delle modifiche. Per favore ricarica.",
"common.httpLimit": "Hai superato il limite massimo di chiamate API.",
"common.label": "Etichetta",
"common.languages": "Lingue",
"common.latitudeShort": "Lat",
"common.loading": "Caricamento",
"common.logout": "Esci",
"common.logs": "Log",
"common.longitudeShort": "Lon",
"common.mapHide": "Nascondi la mappa",
"common.mapShow": "Mostra la mappa",
"common.message": "Messaggio",
"common.name": "Nome",
"common.no": "No",
"common.nothingChanged": "Non è stato cambiato niente.",
"common.noValue": "- Nessun valore -",
"common.or": "o",
"common.pagerInfo": "{itemFirst}-{itemLast} of {numberOfItems}",
"common.password": "Password",
"common.passwordConfirm": "Conferma Password",
"common.pattern": "Pattern",
"common.patterns": "Pattern",
"common.permissions": "Permessi",
"common.preview": "Anteprima",
"common.product": "Squidex Headless CMS",
"common.project": "Progetto",
"common.queryOperators.contains": "contains",
"common.queryOperators.empty": "is empty",
"common.queryOperators.endsWith": "ends with",
"common.queryOperators.eq": "is equals to",
"common.queryOperators.ge": "is greater than or equals to",
"common.queryOperators.gt": "is greater than",
"common.queryOperators.le": "is less than pr equals to",
"common.queryOperators.lt": "is less than",
"common.queryOperators.ne": "is not equals to",
"common.queryOperators.startsWith": "starts with",
"common.refresh": "Aggiorna",
"common.rename": "Rinomina",
"common.requiredHint": "obbligatorio",
"common.reset": "Reimposta",
"common.restore": "Ripristina",
"common.role": "Ruolo",
"common.roles": "Ruoli",
"common.rules": "Regole",
"common.sampleCodeLabel": "Esempio di codice per",
"common.save": "Salva",
"common.saveShortcut": "CTRL + S",
"common.schemas": "Schemi",
"common.searchGoogleMaps": "Cerca su Google Maps",
"common.searchResults": "Risultati di ricerca",
"common.separateByLine": "Separato dalla linea",
"common.settings": "Impostazioni",
"common.sidebarTour": "La barra di navigazione laterale contiene specifici utili collegamenti per il contesto. Qui puoi visualizzare la cronologia dei cambiamenti di questo schema.",
"common.slug": "Slug",
"common.stars.max": "Non deve avere più di 15 stelle",
"common.status": "Stato",
"common.statusChangeTo": "Cambia in",
"common.submit": "Invia",
"common.subscription": "Abbonamenti",
"common.succeeded": "Successo",
"common.tagAdd": ", aggiungi al tag",
"common.tagAddReference": ", aggiungi al collegamento",
"common.tagAddSchema": ", aggiungi allo schema",
"common.tags": "Tag",
"common.tagsAll": "Tutti i tag",
"common.time": "Ora",
"common.update": "Aggiorna",
"common.url": "URL",
"common.users": "Utenti",
"common.value": "Valore",
"common.width": "Larghezza",
"common.workflow": "Workflow",
"common.workflows": "Workflow",
"common.yes": "Si",
"contents.arrayAddItem": "Aggiungi un elemento",
"contents.arrayCloneItem": "Clona questo elemento",
"contents.arrayCollapseAll": "Comprimi tutti gli elementi",
"contents.arrayCollapseItem": "Comprimi l'elemento",
"contents.arrayExpandAll": "Espandi tutti gli elementi",
"contents.arrayExpandItem": "Espandi questo elemento",
"contents.arrayMoveBottom": "Sposta questo elemento in basso",
"contents.arrayMoveDown": "Sposta giù questo elemento ",
"contents.arrayMoveTop": "Sposta in cima questo elemento",
"contents.arrayMoveUp": "Sposta su questo elemento",
"contents.arrayNoFields": "Aggiungi un primo campo annidato agli elementi.",
"contents.assetsUpload": "Trascina i file o clicca",
"contents.autotranslate": "Traduci in automatico dalla lingua principale",
"contents.changeStatusTo": "Cambia l'elemeto(i) del contenuti in {action}",
"contents.changeStatusToImmediately": "Imposta {action} immediatamente.",
"contents.changeStatusToLater": "Imposta {action} ad una data e ora successiva.",
"contents.contentNotValid": "Un elemento del contenuto non è valido, verifica il campo con la barra rossa per tutte le lingue impostate (se presenti).",
"contents.create": "Nuovo",
"contents.createContentTooltip": "Nuovo contenuto (CTRL + SHIFT + G)",
"contents.created": "Contenuto creato con successo.",
"contents.createdByFieldDescription": "L'utente che ha creato l'elemento del contenuto.",
"contents.createFailed": "Non è stato possibile creare il contenuto. Per favore ricarica.",
"contents.createFieldDescription": "La data e l'ora di creazione del contenuto.",
"contents.createPageTitle": "Crea un contenuto",
"contents.createTitle": "Nuovo Contenuto",
"contents.currentStatusLabel": "Versione corrente",
"contents.deleteConfirmText": "Sei sicuro di voler eliminare il contenuto?",
"contents.deleteConfirmTitle": "Elimina il contenuto",
"contents.deleteFailed": "Non è stato possibile eliminare il contenuto. Per favore ricarica.",
"contents.deleteManyConfirmText": "Sei sicuro di voler eliminare gli elementi del contenuto selezionati?",
"contents.deleteVersionConfirmText": "Do you really want to delete this version?",
"contents.deleteVersionFailed": "Non è stato possibile eliminare la versione. Per favore ricarica.",
"contents.draftNew": "Nuova bozza",
"contents.draftStatus": "Nuova versione",
"contents.editPageTitle": "Modifica contenuto",
"contents.editTitle": "Modifica il contenuto",
"contents.invariantFieldDescription": "The '{fieldName}' field of the content item.",
"contents.languageModeAll": "Tutte le lingue",
"contents.languageModeSingle": "Una sola lingua",
"contents.lastModifiedByFieldDescription": "L'utente che ha modificato l'elememto l'ultima voltaThe user who modified the content item the last time.",
"contents.lastModifiedFieldDescription": "La data e l'ora dell'ultima modifica del contenuto.",
"contents.lastUpdatedLabel": "Ultimo aggiornamento",
"contents.loadContent": "Carica",
"contents.loadContentFailed": "Non è stato possibile caricare il contenuto. Per favore ricarica.",
"contents.loadDataFailed": "Non è stato possibile caricare i dati. Per favore ricarica.",
"contents.loadFailed": "Non è stato possibile caricare i contenuti. Per favore ricarica.",
"contents.loadVersionFailed": "Non è stato possibile version a new version. Per favore ricarica.",
"contents.localizedFieldDescription": "The '{fieldName}' field of the content item (localized).",
"contents.newStatusFieldDescription": "The new status of the content item.",
"contents.noReference": "- Nessun collegamento -",
"contents.pendingChangesTextToChange": "Non hai salvato le modifiche.\n\nSe cambi lo stato perderai le modifiche.\n\n**Sei sicuro di voler continuare?**",
"contents.pendingChangesTextToClose": "Non hai salvato le modifiche.\n\nChiudendo il contenuto corrente perderai tutte le modifiche.\n\n**Sei sicuro di voler continuare?**",
"contents.pendingChangesTitle": "Modifiche non salvate",
"contents.referencesCreateNew": "Aggiungi nuovo",
"contents.referencesCreatePublish": "Crea e pubblica",
"contents.referencesLink": "Collega i contenuti selezionati ({count})",
"contents.referencesSelectExisting": "Seleziona da contenuti esistenti",
"contents.referencesSelectSchema": "Seleziona {schema}",
"contents.refreshTooltip": "Aggiorna i contenuti (CTRL + SHIFT + R)",
"contents.reloaded": "Contenuti aggiornati.",
"contents.removeConfirmText": "Do you really want to remove the content?",
"contents.removeConfirmTitle": "Remove content",
"contents.saveAndPublish": "Salva e pubblica",
"contents.scheduledAt": "alle",
"contents.scheduledAtLabel": "alle",
"contents.scheduledTo": "a",
"contents.schemasPageTitle": "Contenuti",
"contents.searchPlaceholder": "Ricerca testuale",
"contents.searchSchemasPlaceholder": "Cerca schemi...",
"contents.selectionCount": "{count} elementi selezionati",
"contents.statusFieldDescription": "Stato dell'elemento del contenuto.",
"contents.statusQueries": "Stato Query",
"contents.stockPhotoEmpty": "Nessuna selezione",
"contents.stockPhotoSearch": "Cerca foto su Unsplash",
"contents.tableHeaders.created": "Created",
"contents.tableHeaders.createdBy": "Created By",
"contents.tableHeaders.createdByShort": "By",
"contents.tableHeaders.id": "Id",
"contents.tableHeaders.lastModified": "Updated",
"contents.tableHeaders.lastModifiedBy": "Updated By",
"contents.tableHeaders.lastModifiedByShort": "By",
"contents.tableHeaders.nextStatus": "Next Status",
"contents.tableHeaders.status": "Status",
"contents.tableHeaders.version": "Version",
"contents.unsavedChangesText": "Non hai salvato le modifiche. Vuoi salvarle adesso?",
"contents.unsavedChangesTitle": "Modifiche non salvate",
"contents.updated": "Contenuto aggiornato con successo.",
"contents.updateFailed": "Non è stato possibile aggiornare il contenuto. Per favore ricarica.",
"contents.validationHint": "Ricorda di verificare tutte le lingue quando vedi errori di validazione.",
"contents.versionCompare": "Confronta",
"contents.versionDelete": "Cancella questa Versione",
"contents.versionFieldDescription": "La versione dell'elemento del contenuto",
"contents.versionViewing": "Stai guardando la versione **{version}**.",
"contents.viewLatest": "Visualizza l'ultima",
"contents.viewReset": "Imposta la visualizzazione predefinita",
"contributors.add": "Aggiungi un collaboratore",
"contributors.addFailed": "Non è stato possibile aggiungere collaboratori. Per favore ricarica.",
"contributors.contributorAssigned": "Un nuovo utente, con indirizzo email, è stato creato e aggiunto come collaboratore.",
"contributors.contributorAssignedExisting": "L'utente è stato assegnato",
"contributors.contributorAssignedInvited": "L'utente è stato invitato e assegnato.",
"contributors.contributorAssignedOld": "L'utente è stato aggiunto come collaboratore.",
"contributors.deleteConfirmText": "Sei sicuro di voler rimuovere il collaboratore?",
"contributors.deleteConfirmTitle": "Rimuovi il collaboratore",
"contributors.deleteFailed": "Non è stato possibile cancellare il collaboratore. Per favore ricarica.",
"contributors.emailPlaceholder": "Cerca un utente esistente o invitalo tramite email",
"contributors.empty": "Nessun collaboratore trovato.",
"contributors.import.emailsDetected": "Email trovate: {count}",
"contributors.import.run": "Aggiungi Collaboratori",
"contributors.import.run2": "Importa",
"contributors.importButton": "Aggiungi più collaboratori contemporaneamente",
"contributors.importHintg": "Team numeroso?",
"contributors.importTitle": "Importa collaboratori",
"contributors.loadFailed": "Non è stato possibile caricare contributors. Per favore ricarica.",
"contributors.planHint": "Il tuo piano prevede un numero massimo di {maxContributors} collaboratori.",
"contributors.refreshTooltip": "Aggiorna i collaboratori (CTRL + SHIFT + R)",
"contributors.reloaded": "Collaboratori aggiornati.",
"contributors.search": "Cerca",
"contributors.userNotFound": "L'utente non esiste.",
"dashboard.apiCallsCard": "Chiamate API ",
"dashboard.apiCallsChart": "Grafico delle chiamate API",
"dashboard.apiCallsLimitLabel": "Limite mensile",
"dashboard.apiCallsSummaryCard": "Riepilogo chiamate API",
"dashboard.apiDocumentationCard": "Documentazione delle API",
"dashboard.apiPerformanceCard": "Performance(ms) delle API: {summary}ms avg",
"dashboard.apiPerformanceChart": "Diagramma delle Performance delle API",
"dashboard.assetSizeCard": "Dimensione delle risorse (MB",
"dashboard.assetSizeLabel": "Dimensione totale",
"dashboard.assetSizeLimitLabel": "Limite totale",
"dashboard.assetTotalSize": "Dimensione totale dello Storage per le risorse",
"dashboard.assetUpdloadsCountChart": "Diagramma del numero di risorse caricate",
"dashboard.assetUploadsCard": "Risorse caricate",
"dashboard.assetUploadsSizeChart": "Diagramma della dimensione delle risorse caricate",
"dashboard.configSaved": "Configurazione salvata.",
"dashboard.contentApi": "API dei contenuti",
"dashboard.contentApiDescription": "Documentazione OpenAPI 3.0 compatibile per i contenuti della tua app.",
"dashboard.contentsSummaryCard": "Numero di elementi",
"dashboard.currentMonthLabel": "Questo mese",
"dashboard.downloadLog": "Scarica i Log",
"dashboard.editConfig": "Modifica la configurazione",
"dashboard.githubCard": "Github",
"dashboard.githubCardDescription": "Ottieni il codice sorgente da GitHub e segnala bug o richiedi assistenza.",
"dashboard.historyCard": "Cronologia",
"dashboard.pageTitle": "Dashboard",
"dashboard.resetConfigConfirmText": "Sei sicuro di voler riportare la dashboard alle impostazioni predefinite?",
"dashboard.resetConfigConfirmTitle": "Ripristina la configurazione",
"dashboard.schemaNewCard": "Nuovo Schema",
"dashboard.schemaNewCardDescription": "Uno schena definisce la struttura di un tipo di contenuto.",
"dashboard.schemasCard": "Schemi",
"dashboard.schemasCardDescription": "Panoramica del modello dei dati di questa app.",
"dashboard.stackedChart": "Istogramma in pila",
"dashboard.supportCard": "Feedback & Assistenza",
"dashboard.supportCardDescription": "Fornisci feedback e richiedi funzionalità per aiutarci a migliorare Squidex..",
"dashboard.trafficChart": "Diagramma del traffico delle API",
"dashboard.trafficHeader": "Traffico (MB)",
"dashboard.trafficLimitLabel": "Limite mensile",
"dashboard.trafficSummaryCard": "Riepilogo del traffico delle API",
"dashboard.welcomeText": "Benvenuto su **{app}** dashboard.",
"dashboard.welcomeTitle": "Ciao {user}",
"eventConsumers.loadFailed": "Non è stato possibile caricare event consumers. Per favore ricarica.",
"eventConsumers.pageTitle": "Eventi degli utenti",
"eventConsumers.position": "Posizione",
"eventConsumers.refreshTooltip": "Aggiorna gli eventi degli utenti (CTRL + SHIFT + R)",
"eventConsumers.reloaded": "Eventi degli utenti aggiornati.",
"eventConsumers.resetFailed": "Non è stato possibile ripristinare gli eventi degli utenti. Per favore ricarica.",
"eventConsumers.resetTooltip": "Ripristina gli event degli utenti",
"eventConsumers.startFailed": "Non è stato possibile far partire l'evento dell'utente. Per favore ricarica.",
"eventConsumers.startTooltip": "Inizia l'evento utente",
"eventConsumers.stopFailed": "Non è stato possibile fermare l'evento dell'utente. Per favore ricarica.",
"eventConsumers.stopTooltip": "Interrompi l'evento dell'utente",
"features.loadFailed": "Non è stato possibile caricare le funzionalità. Per favore ricarica.",
"history.loadFailed": "Non è stato possibile caricare la cronologia. Per favore ricarica.",
"history.title": "Attività",
"languages.add": "Aggiungi lingua",
"languages.addFailed": "Non è stato possibile aggiungere la lingua. Per favore ricarica.",
"languages.deleteConfirmText": "Sei sicuro di voler rimuovere la lingua?",
"languages.deleteConfirmTitle": "Rimuovi la lingua",
"languages.deleteFailed": "Non è stato possibile cancellare la lingua. Per favore ricarica.",
"languages.loadFailed": "Non è stato possibile caricare le lingue. Per favore ricarica.",
"languages.master": "è la principale",
"languages.masterHint": "Se non è stata impostata nessuna lingua come alternativa, le altre lingue hanno la lingua principale come alternativa.",
"languages.optional": "E' Opzionale",
"languages.optionalHint": "Se sono presenti campi obbligatori questi non devono essere compilati anche per le lingue opzionali.",
"languages.refreshTooltip": "Aggiorna le lingue (CTRL + SHIFT + R)",
"languages.reloaded": "Lingue ricaricate.",
"languages.updateFailed": "Non è stato possibile cambiare la lingua. Per favore ricarica.",
"news.headline": "Che cosa c'è di nuovo?",
"news.title": "Nuove funzionalità",
"notifo.subscripeTooltip": "Fai clic su questo pulsante per iscriverti a tutte le modifiche e ricevere le notifiche push.",
"patterns.deleteConfirmText": "Sei sicuro di voler rimuovere il pattern?",
"patterns.deleteConfirmTitle": "Cancella il pattern",
"patterns.deleteFailed": "Non è stato possibile rimuovere il pattern. Per favore ricarica.",
"patterns.empty": "Nessun pattern è stato ancora creato.",
"patterns.loadFailed": "Non è stato possibile aggiungere il pattern. Per favore ricarica.",
"patterns.nameValidationMessage": "Il nome può contenere solo lettere, numeri, trattini e spazi.",
"patterns.refreshTooltip": "Aggiorna i pattern (CTRL + SHIFT + R)",
"patterns.reloaded": "Pattern ricaricati.",
"patterns.updateFailed": "Non è stato possibile aggiornare pattern. Per favore ricarica.",
"plans.billingPortal": "Portal di Fatturazione",
"plans.billingPortalHint": "Vai al Portal di fatturazione per lo storico dei pagamenti e una panoramica per l'abbonamento.",
"plans.change": "Cambia",
"plans.changeConfirmTitle": "Cambia abbonamento",
"plans.changeFailed": "Non è stato possibile cambiare il piano. Per favore ricarica.",
"plans.includedCalls": "Chiamate API",
"plans.includedContributors": "Collaboratori",
"plans.includedStorage": "Spazio disco",
"plans.includedTraffic": "Traffico",
"plans.loadFailed": "Non è stato possibile caricare i piani. Per favore ricarica.",
"plans.noPlanConfigured": "Nessun piano è stato impostato, quest'app ha spazio disco ha un uso illiminato.",
"plans.notPlanOwner": "Non hai creato nessun abbonamento, pertanto non è possibile cambiare il piano.",
"plans.perMonth": "Al Mese",
"plans.perYear": "all'Anno",
"plans.refreshTooltip": "Aggiorna i piani (CTRL + SHIFT + R)",
"plans.reloaded": "Piano aggiornati.",
"plans.selected": "Selezionato",
"profile.title": "Profilo",
"profile.userEmail": "Accesso con",
"roles.add": "Aggiungi un ruolo",
"roles.addFailed": "Non è stato possibile aggiungere il ruolo. Per favore ricarica.",
"roles.default.owner": "Hai come amministratore tutte le funzionalità, compreso cancellare le app.",
"roles.default.reader": "Hai un'utenza in sola lettura sia per i contenuti che per le risorse.",
"roles.defaults.developer": "Hai un'utenza che può visualizzare le API, modificare le risorse, i contenuti, gli schema, le regole, i workflows e i pattern.",
"roles.defaults.editor": "Hai un'utenzaCan che può modificare le risorse, i conteuti e visualizzare i workflow.",
"roles.deleteConfirmText": "Cancella il ruolo",
"roles.deleteConfirmTitle": "Sei sicuro di voler eliminare il ruolo?",
"roles.loadFailed": "Non è stato possibile caricare i ruoli. Per favore ricarica.",
"roles.loadPermissionsFailed": "Non è stato possibile caricare i permessi. Per favore ricarica.",
"roles.refreshTooltip": "Aggiorna i ruoli (CTRL + SHIFT + R)",
"roles.reloaded": "Ruoli ricaricati.",
"roles.revokeFailed": "Non è stato possibile rimuovere il ruolo. Per favore ricarica.",
"roles.roleNamePlaceholder": "Inserisci il nome del ruolo",
"roles.updateFailed": "Non è stato possibile aggiornare il ruolo. Per favore ricarica.",
"rules.actionEdit": "Mdifica l'Azione",
"rules.cancelFailed": "Non è stato possibile eliminare la regola. Per favore ricarica.",
"rules.create": "Crea un nuova Regola",
"rules.createFailed": "Non è stato possibile creare una nuova regola. Per favore ricarica.",
"rules.createTooltip": "Nuova regola (CTRL + SHIFT + G)",
"rules.deleteConfirmText": "Sei sicuro di voler eliminare la regola?",
"rules.deleteConfirmTitle": "Cancella la regola",
"rules.deleteFailed": "Non è stato possibile eliminare la regola. Per favore ricarica.",
"rules.disableFailed": "Non è stato possibile disabilitare la regola. Per favore ricarica.",
"rules.empty": "Nessuna regola è stato ancora creata.",
"rules.emptyAddRule": "Aggiungi una regola",
"rules.enableFailed": "Non è stato possibile abilitare la regola. Per favore ricarica.",
"rules.enqueued": "La regola è stata aggiunta alle code.",
"rules.listPageTitle": "Regole",
"rules.loadFailed": "Non è stato possibile caricare le regole. Per favore ricarica.",
"rules.readMore": "Leggi di più",
"rules.refreshEventsTooltip": "Aggiorna gli Eventi (CTRL + SHIFT + R)",
"rules.refreshTooltip": "Aggiorna le Regole (CTRL + SHIFT + R)",
"rules.reloaded": "Regole ricaricate.",
"rules.restarted": "La Regola sarà eseguita fra pochi secondi.",
"rules.ruleEvents.cancelFailed": "Non è stato possibile cancellare l'evento della regola. Per favore ricarica.",
"rules.ruleEvents.enqueue": "Metti in coda",
"rules.ruleEvents.enqueued": "Eventi messo in coda. L'evento potrà essere rieseguto fra pochi secondi.",
"rules.ruleEvents.enqueueFailed": "Non è stato possibile mettere in coda l'evento della regola. Per favore ricarica.",
"rules.ruleEvents.lastInvokedLabel": "Ultima chiamata",
"rules.ruleEvents.listPageTitle": "Eventi della Regola",
"rules.ruleEvents.loadFailed": "Non è stato possibile caricare gli eventi. Per favore ricarica.",
"rules.ruleEvents.nextAttemptLabel": "Successivo",
"rules.ruleEvents.numAttemptsLabel": "Tentativi",
"rules.ruleEvents.reloaded": "Eventi della regola ricaricati.",
"rules.ruleSyntax.if": "If",
"rules.ruleSyntax.then": "then",
"rules.run": "Esegui",
"rules.runFailed": "Non è stato possibile eseguire la regola. Per favore ricarica.",
"rules.runningRule": "La regola '{name}' è attualmente in esecuzione.",
"rules.runRuleConfirmText": "Sei sicuro di voler eseguire la regola per tutti gli eventi?",
"rules.runRuleConfirmTitle": "Esegui la regola",
"rules.stop": "La regola si fermerà al più presto.",
"rules.triggerConfirmText": "Sei sicuro che voler attivare la regola?",
"rules.triggerConfirmTitle": "Attiva la regola",
"rules.triggerEdit": "Modifica l'Attivazione",
"rules.triggerFailed": "Non è stato possibile attivare la regola. Per favore ricarica.",
"rules.unnamed": "Regola senza nome",
"rules.updateFailed": "Non è stato possibile aggiornare la regola. Per favore ricarica.",
"rules.wizard.actionHint": "La selezione del tipo di azione non potrà essere modificata successivamente.",
"rules.wizard.selectAction": "Seleziona l'Azione",
"rules.wizard.selectTrigger": "Seleziona l'Attivazione",
"rules.wizard.triggerHint": "La selezione del tipo di attivazione non potrà essere modificata successivamente.",
"schemas.addField": "Aggiungi un Campo",
"schemas.addFieldAndClose": "Crea e chiudi",
"schemas.addFieldAndCreate": "Crea e aggiungi il campo",
"schemas.addFieldAndEdit": "Crea e modifica il campo",
"schemas.addFieldButton": "Aggiungi il Campo",
"schemas.addFieldFailed": "Non è stato possibile aggiungere il campo. Per favore ricarica.",
"schemas.addNestedField": "Aggiungi un campo annidato",
"schemas.changeCategoryFailed": "Non è stato possibile cambiare la categoria. Per favore ricarica.",
"schemas.clone": "Clona lo Schema",
"schemas.contextMenuTour": "Apri il menu per cancellare lo schema o per inserire alcuni script che modificano il contenuto.",
"schemas.create": "Crea uno Schema",
"schemas.createCategory": "Crea una nuova categoria...",
"schemas.createFailed": "Non è stato possibile creare lo schema. Per favore ricarica.",
"schemas.createSchemaTooltip": "Nuovo Schema (CTRL + SHIFT + G)",
"schemas.deleteConfirmText": "Sei sicuro di voler eliminare lo schema?",
"schemas.deleteConfirmTitle": "Cancella lo schema",
"schemas.deleteFailed": "Non è stato possibile cancellare lo schema. Per favore ricarica.",
"schemas.deleteFieldFailed": "Non è stato possibile cancellare il campo. Per favore ricarica.",
"schemas.deleteRuleConfirmText": "Sei sicuro di cancellare questa regola per il campo?",
"schemas.deleteRuleConfirmTitle": "Cancellare la regola per il campo",
"schemas.deleteUrlConfirmText": "Sei sicuro di voler rimuovere questa URL?",
"schemas.deleteUrlConfirmTitle": "Cancella URL",
"schemas.disableFieldFailed": "Non è stato possibile disabilitare il campo. Per favore ricarica.",
"schemas.enableFieldFailed": "Non è stato possibile abilitare il campo. Per favore ricarica.",
"schemas.export.deleteFields": "Cancella i campi",
"schemas.export.recreateFields": "Ricrea i campi",
"schemas.export.synchronize": "Sincronizza",
"schemas.field.allowedValues": "Valori consentiti",
"schemas.field.defaultValue": "Valori predefiniti",
"schemas.field.deleteConfirmText": "Sei sicuro di voler eliminare il campo?",
"schemas.field.deleteConfirmTitle": "Cancella il campo",
"schemas.field.disable": "Disabilita nella UI",
"schemas.field.disabledMarker": "Disabilitato",
"schemas.field.editor": "Editor",
"schemas.field.editorUrl": "Editor Url",
"schemas.field.editorUrlHint": "Url del tuo plugin se usi un custom editor.",
"schemas.field.empty": "Nessun campo è stato ancora creato.",
"schemas.field.enable": "Abilita nella UI",
"schemas.field.enabledMarker": "Abilitato",
"schemas.field.hiddenMarker": "Nasconsto",
"schemas.field.hide": "Nascondi nelle API",
"schemas.field.hintsHint": "Descrivi questo schema per la documentazione e le interfacce utente.",
"schemas.field.inlineEditable": "Modificabile sulla stessa linea",
"schemas.field.labelHint": "Nome da visualizzare per la documentazione e le interfacce utente.",
"schemas.field.localizable": "Consente la localizzazione",
"schemas.field.localizableHint": "Puoi impostare il campo per consentire la localizzazione, ossia che dipende dalla lingua che utilizzi come ad esempio i nomi delle città.",
"schemas.field.localizableMarker": "consente la localizzazione",
"schemas.field.lock": "Blocca e impedisce i cambiamenti",
"schemas.field.lockConfirmText": "Attenzione: Bloccare un campo è un'azione irreversibile! Se blocchi il campo non potrai più sbloccarlo o cancellarlo o cambiarlo. Sei sicuro di voler bloccare il campo?",
"schemas.field.lockedMarker": "Bloccato",
"schemas.field.nameHint": "Il nome del campo nelle chiamate API response.",
"schemas.field.namePlaceholder": "Inserisci il nome del campo",
"schemas.field.nameValidationMessage": "Il nome deve essere valido per il javascript in formato camel case.",
"schemas.field.placeholder": "Segnaposto",
"schemas.field.placeholderHint": "Definisci il segnaposto per la verifica dell'input.",
"schemas.field.required": "Obbligatorio",
"schemas.field.show": "Mostra nelle API",
"schemas.field.tabCommon": "Comune",
"schemas.field.tabEditing": "Modifica",
"schemas.field.tabValidation": "Validazione",
"schemas.field.tagsHint": "Tag per segnalare il tuo campo nei processi automatici.",
"schemas.field.unique": "Univoco",
"schemas.field.visibleMarker": "Visibile",
"schemas.fieldTypes.array.count": "Elementi",
"schemas.fieldTypes.array.countMax": "Max num. Elementi",
"schemas.fieldTypes.array.countMin": "Min num. Elementi",
"schemas.fieldTypes.array.description": "Lista di oggetti incorporati.",
"schemas.fieldTypes.assets.allowDuplicates": "Consente valori duplicati",
"schemas.fieldTypes.assets.count": "Conteggio",
"schemas.fieldTypes.assets.countMax": "Max num di Risorse",
"schemas.fieldTypes.assets.countMin": "Min num. di Risorse",
"schemas.fieldTypes.assets.description": "Immagini, video, documenti.",
"schemas.fieldTypes.assets.fileExtensions": "Estensioni dei File",
"schemas.fieldTypes.assets.mustBeImage": "Deve essere un'immagine",
"schemas.fieldTypes.assets.previewFileName": "Solamente il nome del file",
"schemas.fieldTypes.assets.previewImage": "Solamente thumbnail o il nome del file se non è un immagine",
"schemas.fieldTypes.assets.previewImageAndFileName": "Thumbnail e nome del file",
"schemas.fieldTypes.assets.previewMode": "Modalità anteprima",
"schemas.fieldTypes.assets.previewModeHint": "Anteprima delle risorse nella lista dei contenuti.",
"schemas.fieldTypes.assets.resolve": "Risolvi il nome della prima risorsa",
"schemas.fieldTypes.assets.resolveHint": "Mostra la prima risorsa collegata nella lista dei contenuti.",
"schemas.fieldTypes.assets.size": "Dimensione",
"schemas.fieldTypes.assets.sizeMax": "Dimensione Min",
"schemas.fieldTypes.assets.sizeMin": "Dimensione Max",
"schemas.fieldTypes.boolean.description": "Si o no, vero o falso.",
"schemas.fieldTypes.dateTime.defaultMode": "Modalità predefinita",
"schemas.fieldTypes.dateTime.description": "Data degli eventi, orari di apertura.",
"schemas.fieldTypes.dateTime.rangeMax": "Valore Max",
"schemas.fieldTypes.dateTime.rangeMin": "Valore Min",
"schemas.fieldTypes.geolocation.description": "Coordinate: latitudine e longitudine.",
"schemas.fieldTypes.json.description": "Dati in formato JSON, per gli sviluppatori.",
"schemas.fieldTypes.number.description": "ID, numero d'ordine, valutazione, quantità.",
"schemas.fieldTypes.number.range": "Intervallo",
"schemas.fieldTypes.number.rangeMax": "Valore Max",
"schemas.fieldTypes.number.rangeMin": "Valore Min ",
"schemas.fieldTypes.references.count": "Elementi",
"schemas.fieldTypes.references.countMax": "Numero Max Elementi",
"schemas.fieldTypes.references.countMin": "Numero Min Elementi",
"schemas.fieldTypes.references.description": "Link ad altri elementi del contenuto.",
"schemas.fieldTypes.references.resolveHint": "Mostra il nome dell'elemento collegato (reference) nella lista dei contenuti quando il numero massimo di elementi è impostato a 1.",
"schemas.fieldTypes.string.characters": "Characters",
"schemas.fieldTypes.string.charactersMax": "Max Characters",
"schemas.fieldTypes.string.charactersMin": "Min Characters",
"schemas.fieldTypes.string.contentType": "Content Type",
"schemas.fieldTypes.string.description": "Titoli, nomi, paragrafi.",
"schemas.fieldTypes.string.length": "Lunghezza",
"schemas.fieldTypes.string.lengthMax": "Lunghezza Max",
"schemas.fieldTypes.string.lengthMin": "Lunghezza Min",
"schemas.fieldTypes.string.pattern": "Regex Pattern",
"schemas.fieldTypes.string.patternMessage": "Messaggio del Pattern",
"schemas.fieldTypes.string.suggestions": "Suggestions",
"schemas.fieldTypes.string.words": "Words",
"schemas.fieldTypes.string.wordsMax": "Max Words",
"schemas.fieldTypes.string.wordsMin": "Min Words",
"schemas.fieldTypes.tags.count": "Items",
"schemas.fieldTypes.tags.countMax": "Max Items",
"schemas.fieldTypes.tags.countMin": "Min Items",
"schemas.fieldTypes.tags.description": "Formato speciale per i tag.",
"schemas.fieldTypes.ui.description": "Separatore per il pannello delle modifiche della UI.",
"schemas.hideFieldFailed": "Non è stato possibile nascondere il campo. Per favore ricarica.",
"schemas.import": "Importa uno schema",
"schemas.listFields": "Lista dei Campi",
"schemas.listFieldsEmpty": "Incolla qui il campo o riordina i campi da mostrare nella lista dei contenuti. Se non imposti una lista di campi da visualizzare, viene utilizzato il primo della lista.",
"schemas.loadFailed": "Non è stato possibile caricare gli schemi. Per favore ricarica.",
"schemas.loadSchemaFailed": "Non è stato possibile caricare lo schema. Per favore ricarica.",
"schemas.lockFieldFailed": "Non è stato possibile bloccare il campo. Per favore ricarica.",
"schemas.modeMultiple": "Contenuti multipli",
"schemas.modeMultipleDescription": "Ideale per contenuti multipli come post di blog, pagine, autori, prodotti...",
"schemas.modeSingle": "Singolo contenuto",
"schemas.modeSingleDescription": "Ideale per contenuti singoli come la pagina principale, privacy, impostazioni...",
"schemas.nameWarning": "Questi valori non possono essere cambiati in un secondo momento.",
"schemas.previewUrls.empty": "Nessuna url per l'anteprima è stato configurato.",
"schemas.previewUrls.help": "Riguardo all'URL dell'anteprima controlla la pagina della guida integrata per saperne di più.",
"schemas.previewUrls.namePlaceholder": "Web o Mobile",
"schemas.previewUrls.title": "URL dell'anteprima",
"schemas.previewUrls.urlPlaceholder": "URL con variabili",
"schemas.published": "Pubblicato",
"schemas.publishedTour": "!Per poter creare un contenuto devi prima aver pubblicato il relativo schema.",
"schemas.publishFailed": "Non è stato possibile pubblicare lo schema. Per favore ricarica.",
"schemas.referenceFields": "Campi per i collegamenti (Reference)",
"schemas.referenceFieldsEmpty": "Trascina qui il file qui oppure riordina i campi da visualizzare nella lista che viene visualizzata quando colleghi il contenuto ad un altro. Quando non è impostato alcun campo per il contenuto che si desidera collegare, sono visualizzati la lista dei campi in ordine di utilizzo.",
"schemas.reloaded": "Schemai ricaricati.",
"schemas.reorderFieldsFailed": "Non è stato possibile riordinare i campi. Per favore ricarica.",
"schemas.rules.action": "Azione",
"schemas.rules.condition": "Condizioni dentro il Javascript",
"schemas.rules.empty": "Non è stata configurata nessuna regola per il campo.",
"schemas.rules.title": "Regole per il campo",
"schemas.rules.when": "quando",
"schemas.saved": "Lo Schema è stato salvato con successo.",
"schemas.saveFieldAndClose": "Salva e chiudi",
"schemas.saveFieldAndNew": "Salva e aggiungi un campo",
"schemas.schemaHintsHint": "Descrivi questo schema per la documentazione e le interfacce utente.",
"schemas.schemaLabelHint": "Nome da visualizzare per la documentazione e le interfacce utente.",
"schemas.schemaNameHint": "Puoi utilizzare solo lettere, numeri e trattini e un numero massimo di 40 caratteri.",
"schemas.schemaNameValidationMessage": "Il nome può contenere solo lettere, numeri, trattini e spazi.",
"schemas.schemaTagsHint": "Tag per descrivere il tuo schema per i processi automatici.",
"schemas.searchPlaceholder": "Cerca negli schemi...",
"schemas.showFieldFailed": "Non è stato possibile mostrare il campo. Per favore ricarica.",
"schemas.synchronized": "Lo Schema è stato sincronizzato con successo.",
"schemas.synchronizeFailed": "Non è stato possibile sincronizzare lo schema. Per favore ricarica.",
"schemas.tabFields": "Campi",
"schemas.tabJson": "Json",
"schemas.tabMore": "Di più",
"schemas.tabScripts": "Script",
"schemas.tabUI": "UI",
"schemas.ui": "Campi assegnati",
"schemas.ui.unassignedFields": "UnCampi non assegnati",
"schemas.unpublished": "Non pubblicato",
"schemas.unpublishFailed": "Non è stato possibile togliere dalla pubblicazione lo schema. Per favore ricarica.",
"schemas.updateFailed": "Non è stato possibile aggiornare schema. Per favore ricarica.",
"schemas.updateFieldFailed": "Non è stato possibile aggiornare il campo. Per favore ricarica.",
"schemas.updatePreviewUrlsFailed": "Non è stato possibile configure le url dell'anteprima. Per favore ricarica.",
"schemas.updateRulesFailed": "Non è stato possibile aggiornare le regole dello schema. Per favore ricarica.",
"schemas.updateScriptsFailed": "Non è stato possibile aggiornare gli script dello schema. Per favore ricarica.",
"schemas.updateUIFieldsFailed": "Non è stato possibile aggiornare i campi della UI. Per favore ricarica.",
"search.addFilter": "Aggiungi un Filtro",
"search.addGroup": "Aggiungi un Gruppo",
"search.addSorting": "Aggiungi ordinamento",
"search.advancedTour": "Fai clic su questa icona per visualizzare il menu della ricerca avanzata!",
"search.customQuery": "Query personalizzata",
"search.fullTextTour": "Cerca contenuti utilizzando la ricerca testuale su tutti i campi e le lingue!",
"search.help": "Ulteriori informazioni sui filtri su [Documentation](https://https://docs.squidex.io/04-guides/02-api.html).",
"search.myQueries": "Le mie query",
"search.nameQuery": "Dai un nome alla query",
"search.queriesEmpty": "Ricerca per {types} e utilizza l'icona <i class=\"icon-star-empty\"></i> nella ricerca per salvare la query per tutti i collaboratori.",
"search.queryAllNewestFirst": "Tutto (newest first)",
"search.queryAllOldestFirst": "Tutto (oldest first)",
"search.quickNavPlaceholder": "Navigazione veloce (Press 'q')",
"search.saveQueryMyself": "Salva la query solamente per me.",
"search.searchFailed": "Non è stato possibile eseguire la ricerca. Per favore ricarica.",
"search.sharedQueries": "Query condivise",
"search.sorting": "Ordinamento",
"start.login": "Entra su Squidex",
"start.loginHint": "Il pulsante per accedere aprirà un popup. Una volta effettuato l'accesso sarai indirizzato al portale per la gestione di Squidex.",
"start.madeBy": "Realizzato con orgoglio da",
"start.madeByCopyright": "Sebastian Stehle e Collaboratori, 2016-2020",
"tour.joinForum": "Unisciti al nostro Forum",
"tour.joinGithub": "Unisciti a Github",
"tour.skip": "Salta il Tour",
"tour.step0Next": "Guardiamoci intorno",
"tour.step0Text": "Puoi iniziare subito a gestire e distribuire i tuoi contenuti, ma prima vorremmo illustrarti alcune nozioni di base...\n\nIn questo modo",
"tour.step1Next": "Continua",
"tour.step1Text": "Un'App è il repository per il tuo progetto, ad es. (un blog, un negozio sul web o una app per dispositivi mobili). Puoi assegnare collaboratori alla tua App per lavorare insieme.\n\nPuoi creare un numero illimitato di app in Squidex per gestire più progetti contemporaneamente.",
"tour.step2Next": "Vai avanti!",
"tour.step2Text": "Gli Schemi definiscono la struttura per i tuoi contenuti, i campi e i tipi di dato per ogni tipo di contenuto.\n\nPrima di creare un contenuto riferito al tuo schema, assicurati di aver pubblicato lo schema premendo il pulsante 'Pubblica' visualizzato nell'editor dei contenuti.",
"tour.step3Next": "Ci siamo quasi!",
"tour.step3Text": "I contenuti della tua app sono raggruppati tramite gli schemi.\n\nSeleziona uno schema pubblicato e poi crea il relativo contenuto.",
"tour.step4Next": "Fatto!",
"tour.step4Text": "Le risorse contengono tutti i file che tu puoi collegare ai tuoi contenuti. Per esempio immagini, video o documenti.\n\nPuoi caricare qui le risorse e usarle successivamente oppure caricarle direttamente quando stai creando un contenuto utilizzando un campo risorse.",
"tour.step5Text": "Ma non è tutto il supporto che possiamo fornire.\n\nPer maggiori informazioni visita il sito https://docs.squidex.io/>.\n\nVuoi far parte della nostra community?",
"tour.step5Title": "Fantastico, ora conosci le basi!",
"tour.tooltipConfirm": "Capito",
"tour.tooltipStop": "Ferma il Tour",
"tour.welcome": "Benvenuto su",
"tour.welcomeProduct": "Squidex CMS",
"translate.translateFailed": "Non è stato possibile tradurre il testo. Per favore ricarica.",
"usages.loadCallsFailed": "Non è stato possibile caricare la pagina per l'utilizzo delle chiamate. Per favore ricarica.",
"usages.loadMonthlyCallsFailed": "Non è stato possibile caricare le chiamate mensili delle API calls. Per favore ricarica.",
"usages.loadStorageFailed": "Non è stato possibile caricare lo spazio disco utilizzato. Per favore ricarica.",
"usages.loadTodayStorageFailed": "Non è stato possibile caricare lo spazio disco utilizzato oggi. Per favore ricarica.",
"users.create": "Nuovo",
"users.createFailed": "Non è stato possibile creare l'utente. Per favore ricarica.",
"users.createPageTitle": "Crea un utente",
"users.createTitle": "Nuovo utente",
"users.createTooltip": "Nuovo utente (CTRL + N)",
"users.editPageTitle": "Modifica l'utente",
"users.editTitle": "Modifica l'utente",
"users.listPageTitle": "Gestione Utente",
"users.listTitle": "Utenti",
"users.loadFailed": "Non è stato possibile caricare gli utenti. Per favore ricarica.",
"users.loadUserFailed": "Non è stato possibile caricare l'utente. Per favore ricarica.",
"users.lockTooltip": "Utente bloccato",
"users.passwordConfirmValidationMessage": "Le password devono essere uguali.",
"users.refreshTooltip": "Aggiorna gli Utenti (CTRL + SHIFT + R)",
"users.reloaded": "Utenti ricaricati.",
"users.search": "Cerca l'utente",
"users.unlockTooltip": "Sblocca l'utente",
"users.updateFailed": "Non è stato possibile aggiornare l'utente. Per favore ricarica.",
"validation.between": "{field} deve essere tra '{min}' e '{max}'.",
"validation.betweenlength": "{field|upper} deve essere tra {minlength} e {maxlength} elemento(i).",
"validation.betweenlengthstring": "{field|upper} deve essere tra {minlength} e {maxlength} carattere(i).",
"validation.email": "{field|upper} deve essere un indirizzo email.",
"validation.exactly": "{field|upper} deve essere esattamente '{expected}'.",
"validation.exactlylength": "{field|upper} deve essere esattamente {expected} elemento(i).",
"validation.exactlylengthstring": "{field|upper} deve essere esattamente {expected} carattere(i).",
"validation.match": "{message}",
"validation.max": "{field|upper} deve essere minore o uguale a '{max}'.",
"validation.maxlength": "{field|upper} non deve avere più di {requiredlength} elemento(i).",
"validation.maxlengthstring": "{field|upper} non deve avere più di {requiredlength} carattere(i).",
"validation.min": "{field|upper} deve essere maggiore o uguale a '{min}'.",
"validation.minlength": "{field|upper} deve avere almeno {requiredlength} elemento(i).",
"validation.minlengthstring": "{field|upper} deve avere almeno {requiredlength} carattere(i).",
"validation.pattern": "{field|upper} non corrisponde al pattern.",
"validation.patternmessage": "{message}",
"validation.required": "{field|upper} è obbligatorio.",
"validation.requiredTrue": "{field|upper} è obbligatorio.",
"validation.uniquestrings": "{field|upper} non deve contenere valori duplicati.",
"validation.validarrayvalues": "{field|upper} contiene valori non validicontains an invalid value: {invalidvalue}.",
"validation.validdatetime": "{field|upper} non è una data e ora valida.",
"validation.validvalues": "{field|upper} non è un valore valido.",
"workflows.add": "Aggiungi un Workflow",
"workflows.addStep": "Aggiungi uno Step",
"workflows.createFailed": "Non è stato possibile creare un workflow. Per favore ricarica.",
"workflows.deleteConfirmText": "Sei sicuro di voler eliminare il workflow?",
"workflows.deleteConfirmTitle": "Cancella il workflow",
"workflows.deleteFailed": "Non è stato possibile cancellare il Workflow. Per favore ricarica.",
"workflows.empty": "Nessun workflow è stato ancora creato.",
"workflows.loadFailed": "Non è stato possibile caricare i workflow. Per favore ricarica.",
"workflows.notNamed": "Workflow senza nome",
"workflows.preventUpdates": "Impedisci gli aggiornamenti",
"workflows.publishedNotRemovable": "Non è possibile rimuoverlo",
"workflows.refreshTooltip": "Aggiorna i workflow (CTRL + SHIFT + R)",
"workflows.reloaded": "Workflow ricaricati.",
"workflows.saved": "Il Workflow è stato salvato.",
"workflows.schemasHint": "Limita questo workflow ad uno schema specifico, o lascialo bianco per applicarlo a tutti gli schemi.",
"workflows.syntax.expression": "Espressione",
"workflows.syntax.for": "per",
"workflows.syntax.when": "quando",
"workflows.tabEdit": "Modifica",
"workflows.tabVisualize": "Visualizza",
"workflows.updateFailed": "Non è stato possibile aggiornare il Workflow. Per favore ricarica.",
"workflows.workflowNameHint": "Nome facoltativo per il workflow.",
"workflows.workflowNamePlaceholder": "Inserisci il nome del workflow"
}

30
backend/i18n/frontend_nl.json

@ -70,16 +70,16 @@
"assets.fileTooBig": "Asset is te groot.",
"assets.folderName": "Mapnaam",
"assets.folderNameHint": "De mapnaam wordt gebruikt als weergavenaam en mag niet uniek zijn.",
"assets.insertAssets": "Assets invoegen",
"assets.insertAssets": "Bestanden invoegen",
"assets.linkSelected": "Link geselecteerde items ({count})",
"assets.listPageTitle": "Assets",
"assets.listPageTitle": "Bestanden",
"assets.loadFailed": "Laden van bestanden is mislukt. Laad opnieuw.",
"assets.loadFoldersFailed": "Laden van mappen is mislukt. Laad opnieuw.",
"assets.metadata": "Metadata",
"assets.metadataAdd": "Metadata toevoegen",
"assets.moveFailed": "Verplaatsen van item is mislukt. Laad opnieuw.",
"assets.protected": "Beschermd",
"assets.refreshTooltip": "Assets vernieuwen (CTRL + SHIFT + R)",
"assets.refreshTooltip": "Bestanden vernieuwen (CTRL + SHIFT + R)",
"assets.reloaded": "Bestanden herladen.",
"assets.removeConfirmText": "Wil je het bestand echt verwijderen?",
"assets.removeConfirmTitle": "Verwijder bestand",
@ -90,6 +90,8 @@
"assets.searchByName": "Zoeken op naam",
"assets.searchByTags": "Zoeken op tags",
"assets.selectMany": "Selecteer middelen",
"assets.specialFolder.parent": "<Hoofdmap>",
"assets.specialFolder.root": "Bestanden",
"assets.tabFocusPoint": "Focuspunt",
"assets.tabHistory": "Geschiedenis",
"assets.tabImage": "Afbeelding",
@ -273,6 +275,16 @@
"common.preview": "Preview",
"common.product": "Squidex Headless CMS",
"common.project": "Project",
"common.queryOperators.contains": "contains",
"common.queryOperators.empty": "is empty",
"common.queryOperators.endsWith": "ends with",
"common.queryOperators.eq": "is equals to",
"common.queryOperators.ge": "is greater than or equals to",
"common.queryOperators.gt": "is greater than",
"common.queryOperators.le": "is less than pr equals to",
"common.queryOperators.lt": "is less than",
"common.queryOperators.ne": "is not equals to",
"common.queryOperators.startsWith": "starts with",
"common.refresh": "Vernieuwen",
"common.rename": "Hernoemen",
"common.requiredHint": "verplicht",
@ -347,6 +359,7 @@
"contents.draftStatus": "Nieuwe versie",
"contents.editPageTitle": "Inhoud bewerken",
"contents.editTitle": "Inhoud bewerken",
"contents.invariantFieldDescription": "The '{fieldName}' field of the content item.",
"contents.languageModeAll": "Alle talen",
"contents.languageModeSingle": "Enkele taal",
"contents.lastModifiedByFieldDescription": "De gebruiker die het inhoudsitem de laatste keer heeft gewijzigd.",
@ -357,6 +370,7 @@
"contents.loadDataFailed": "Laden van gegevens is mislukt. Laad opnieuw.",
"contents.loadFailed": "Laden van inhoud is mislukt. Laad opnieuw.",
"contents.loadVersionFailed": "Versie van een nieuwe versie is mislukt. Laad opnieuw.",
"contents.localizedFieldDescription": "The '{fieldName}' field of the content item (localized).",
"contents.newStatusFieldDescription": "De nieuwe status van het item.",
"contents.noReference": "- Geen referentie -",
"contents.pendingChangesTextToChange": "Je hebt niet-opgeslagen wijzigingen. \n \n Wanneer je de status wijzigt, raak je ze kwijt. \n \n **Wil je toch doorgaan?**",
@ -383,6 +397,16 @@
"contents.statusQueries": "Statusquery's",
"contents.stockPhotoEmpty": "Niets geselecteerd",
"contents.stockPhotoSearch": "Zoeken naar foto's op Unsplash",
"contents.tableHeaders.created": "Created",
"contents.tableHeaders.createdBy": "Created By",
"contents.tableHeaders.createdByShort": "By",
"contents.tableHeaders.id": "Id",
"contents.tableHeaders.lastModified": "Updated",
"contents.tableHeaders.lastModifiedBy": "Updated By",
"contents.tableHeaders.lastModifiedByShort": "By",
"contents.tableHeaders.nextStatus": "Next Status",
"contents.tableHeaders.status": "Status",
"contents.tableHeaders.version": "Version",
"contents.unsavedChangesText": "Je hebt niet-opgeslagen wijzigingen. Wil je ze nu laden?",
"contents.unsavedChangesTitle": "Niet-opgeslagen wijzigingen",
"contents.updated": "Inhoud succesvol bijgewerkt.",

4
backend/i18n/source/backend__ignore.json

@ -151,10 +151,10 @@
"/Squidex.Domain.Apps.Entities/Contents/Text/Elastic/ElasticSearchTextIndex.cs": [
"*"
],
"/Squidex.Domain.Apps.Entities/Contents/Text/Lucene/IndexManager_Impl.cs": [
"/Squidex.Domain.Apps.Entities/Contents/Text/Lucene/IndexManager.cs": [
"*"
],
"/Squidex.Domain.Apps.Entities/Contents/Text/Lucene/IndexManager.cs": [
"/Squidex.Domain.Apps.Entities/Contents/Text/Lucene/IndexManager_Impl.cs": [
"*"
],
"/Squidex.Domain.Apps.Entities/Notifications/NotificationEmailSender.cs": [

23
backend/i18n/source/backend_en.json

@ -1,7 +1,12 @@
{
"annotations_AbsoluteUrl": "The field {name|lower} must be an absolute URL.",
"annotations_Compare": "The field {name|lower} must be the same as {other|lower}.",
"annotations_EmailAddress": "The field {name|lower} is not a valid email address.",
"annotations_Range": "The field {name|lower} must be between {min} and {max}.",
"annotations_RegularExpression": "The field {name|lower} is not.",
"annotations_Required": "The field {name|lower} is required.",
"annotations_StringLength": "The field {name|lower} must be a string with a maximum length of {max}.",
"annotations_StringLengthMinimum": "The field {name|lower} must be a string with a minimum length of {min} and a maximum length of {max}.",
"apps.alreadyArchieved": "App has already been archived.",
"apps.clients.idAlreadyExists": "A client with the same id already exists.",
"apps.contributors.cannotChangeYourself": "You cannot change your own role.",
@ -23,9 +28,6 @@
"apps.roles.nameAlreadyExists": "A role with the same name already exists.",
"apps.roles.usedRoleByClientsNotRemovable": "Cannot remove a role when a client is assigned.",
"apps.roles.usedRoleByContributorsNotRemovable": "Cannot remove a role when a contributor is assigned.",
"aspnet_annotations_AbsoluteUrl": "The field {0} must be an absolute URL.",
"aspnet_annotations_Compare": "The field {0} must be the same as {other|lower}.",
"aspnet_annotations_Required": "The field {0} is required.",
"assets.assetAlreadyDeleted": "Asset has already been deleted",
"assets.assetFolderAlreadyDeleted": "Asset folder has already been deleted",
"assets.folderNotFound": "Asset folder does not exist.",
@ -180,6 +182,21 @@
"contents.validation.wordsBetween": "Must have between {min} and {max} word(s).",
"contents.workflowErorPublishing": "Content workflow prevents publishing.",
"contents.workflowErrorUpdate": "The workflow does not allow updates at status {status}",
"dotnet_identity_DefaultEror": "An unknown failure has occurred.",
"dotnet_identity_DuplicateEmail": "Email is already taken.",
"dotnet_identity_DuplicateUserName": "User name is already taken.",
"dotnet_identity_InvalidEmail": "Email is invalid.",
"dotnet_identity_InvalidUserName": "User name '{0}' is invalid, can only contain letters or digits.",
"dotnet_identity_LoginAlreadyAssociated": "A user with this login already exists.",
"dotnet_identity_PasswordMismatch": "Incorrect password.",
"dotnet_identity_PasswordRequiresDigit": "Passwords must have at least one digit ('0'-'9').",
"dotnet_identity_PasswordRequiresLower": "Passwords must have at least one lowercase ('a'-'z').",
"dotnet_identity_PasswordRequiresNonAlphanumeric": "Passwords must have at least one non alphanumeric character.",
"dotnet_identity_PasswordRequiresUniqueChars": "Passwords must use at least {0} different characters.",
"dotnet_identity_PasswordRequiresUpper": "Passwords must have at least one uppercase ('A'-'Z').",
"dotnet_identity_PasswordTooShort": "Passwords is too short.",
"dotnet_identity_PwnedError": "This password has previously appeared in a data breach and should never be used. If you have ever used it anywhere before, change it!",
"dotnet_identity_UserLockedOut": "User is locked out.",
"exception.invalidJsonQuery": "Json query not valid: {message}",
"exception.invalidJsonQueryJson": "Json query not valid json: {message}",
"exceptions.domainObjectDeleted": "Entity ({id}) has been deleted.",

320
backend/i18n/source/backend_it.json

@ -0,0 +1,320 @@
{
"annotations_AbsoluteUrl": "Il campo {name|lower} deve essere un URL assoluto.",
"annotations_Compare": "Il campo {name|lower} deve essere uguale a {other|lower}.",
"annotations_Required": "Il campo è {name|lower} obbligatorio.",
"apps.alreadyArchieved": "La App è stata già archiviata.",
"apps.clients.idAlreadyExists": "Un client con lo stesso id esiste già.",
"apps.contributors.cannotChangeYourself": "Non puoi cambiare il tuo ruolo.",
"apps.contributors.maxReached": "Hai raggiunto il numero massimo di contributori previsto per il tuo piano.",
"apps.contributors.onlyOneOwner": "Non è possibile rimuovere l'unico owner.",
"apps.languages.fallbackNotFound": "La App non ha configurato una lingua alternativa'{fallback}'.",
"apps.languages.languageAlreadyAdded": "La lingua è stata già inserita.",
"apps.languages.masterLanguageNoFallbacks": "La lingua master language non ha lingue alternative.",
"apps.languages.masterLanguageNotOptional": "La lingua master non può essere opzionale.",
"apps.languages.masterLanguageNotRemovable": "La lingua master non può essere rimossa.",
"apps.nameAlreadyExists": "Esiste già un'app con lo stesso nome.",
"apps.notImage": "Il file non è una immagine",
"apps.patterns.nameAlreadyExists": "Esiste già un pattern con lo stesso nome.",
"apps.patterns.patternAlreadyExists": "Questo pattern esiste già con un altro nome.",
"apps.plans.notFound": "Non esiste un piano con questo id.",
"apps.plans.notPlanOwner": "Solo l'utente che ha configurato il piano inizialmente può modificarlo.",
"apps.roles.defaultRoleNotRemovable": "Non è possibile cancellare un ruolo predefinito.",
"apps.roles.defaultRoleNotUpdateable": "Non è possibile modificare un ruolo predefinito.",
"apps.roles.nameAlreadyExists": "Esiste già un ruolo con lo stesso nome.",
"apps.roles.usedRoleByClientsNotRemovable": "Non è possibile rimuovere un ruolo quando è assegnato ad un ruolo.",
"apps.roles.usedRoleByContributorsNotRemovable": "Non è possibile rimuovere un ruolo quando questo è assegnato ad un collaboratore.",
"assets.assetAlreadyDeleted": "La risorsa è stata già eliminata",
"assets.assetFolderAlreadyDeleted": "La cartella delle risorse è stata già eliminata",
"assets.folderNotFound": "La cartella delle risorse non esiste.",
"assets.folderRecursion": "Non è possibile aggiungere una cartella al proprio figlio.",
"assets.maxSizeReached": "Hai raggiunto la dimensione massima consentito per le risorse.",
"backups.alreadyRunning": "E' in esecuzione una altro processo di backup.",
"backups.maxReached": "Non puoi avere più di {max} backup.",
"backups.restoreRunning": "E' in esecuzione un'operazione di restore.",
"comments.noPermissions": "Puoi solo accedere alle tue notifiche.",
"comments.notUserComment": "E' stato creato un commento da un altro utente.",
"common.action": "Azione",
"common.aspectHeight": "Altezza",
"common.aspectWidth": "Larghezza",
"common.calculatedDefaultValue": "Valore predefinito calcolato",
"common.clientd": "ID Client",
"common.clientId": "ID Client",
"common.clientSecret": "Secret Client",
"common.contributorId": "ID o Email del collaboratore",
"common.data": "Data",
"common.defaultValue": "Valore predefinito",
"common.displayName": "Nome da visualizzare",
"common.editor": "Redattore",
"common.email": "Email",
"common.field": "Campo",
"common.fieldIds": "Campi ID",
"common.fieldName": "Campo nome",
"common.file": "File",
"common.folderName": "Nome della cartella",
"common.fullTextNotSupported": "La query di ricerca non supporta queste condizioni.",
"common.httpContentTypeNotDefined": "Il content-type del file non è definito.",
"common.httpFileNameNotDefined": "Il nome del file name non è definito.",
"common.httpInvalidRequest": "Il modello non è valido.",
"common.httpInvalidRequestFormat": "Il corpo della request ha un formato non valido.",
"common.httpOnlyAsUser": "Non consentito per i client.",
"common.httpValidationError": "Errore di validazione",
"common.initialStep": "Step iniziale",
"common.jsError": "Esecuzione dello script fallita, Errore Javascript: {message}",
"common.jsNotAlloweed": "Uno script ha proibito l'operazione.",
"common.jsParseError": "Esecuzione dello script fallita, errore di sintassi nel Javascript: {message}",
"common.jsRejected": "Lo script ha rifiutato l'operazione.",
"common.language": "Codice della lingua",
"common.login": "Accedi",
"common.logout": "Esci",
"common.maxHeight": "Altezza massima",
"common.maxItems": "Numeri massimo di elementi",
"common.maxLength": "Lunghezza massima",
"common.maxSize": "Dimensione massima",
"common.maxValue": "Valore massimo",
"common.maxWidth": "Larghezza massima",
"common.minHeight": "Altezza minima",
"common.minItems": "Numero minimo elementi",
"common.minLength": "Lunghezza minima",
"common.minSize": "Dimensione minima",
"common.minValue": "Valore minimo",
"common.minWidth": "Larghezza massima",
"common.name": "Nome",
"common.notFoundValue": "- non trovato -",
"common.numDays": "Num. giorni",
"common.odataFailure": "Fallito parsando la query: {message}",
"common.odataFilterNotValid": "OData $filter condizione non valida: {ex.Message}",
"common.odataNotSupported": "OData operazione non supportata.",
"common.odataSearchNotValid": "OData $search condizione non valida: {ex.Message}",
"common.oldPassword": "Vecchia password",
"common.other": "Altro",
"common.partitioning": "Partizionamento",
"common.password": "Password",
"common.passwordConfirm": "Conferma password",
"common.pattern": "Pattern",
"common.permissions": "Permessi",
"common.planId": "ID del piano",
"common.previewUrls": "Anteprima URL",
"common.product": "Squidex Headless CMS",
"common.properties": "Proprietà",
"common.property": "Proprietà",
"common.readonlyMode": "Al momento l'applicazione è in sola lettura.",
"common.referenceNotFound": "Contiene un collegamento '{id}' non valido.",
"common.referenceToInvalidSchema": "Contiene dei collegamenti '{id}' ad uno schema errato.",
"common.remove": "Rimuovi",
"common.resultTooLarge": "Il numero di risultati è troppo grande per essere recuperato. Utilizza il parametro $take per ridurre il numero di elementi.",
"common.role": "Ruolo",
"common.save": "Salva",
"common.schemaId": "ID Schema",
"common.signup": "Iscriviti",
"common.text": "Testo",
"common.trigger": "Trigger",
"common.workflow": "Workflow",
"common.workflowStep": "Step",
"common.workflowTransition": "Transizione",
"contents.alreadyDeleted": "Il contento è stato già eliminato.",
"contents.bulkInsertQueryNotUnique": "Ci sono più contenuti che corrispondono alla query.",
"contents.draftNotCreateForUnpublished": "Puoi creare versioni del contenuto solo se questo è pubblicato.",
"contents.draftToDeleteNotFound": "Non c'è niente da eliminare.",
"contents.invalidArrayOfIds": "Errore nel json, atteso un array di string guid.",
"contents.invalidArrayOfObjects": "Errore nel json, attesp un array di objects.",
"contents.invalidArrayOfStrings": "Errore nel json, atteso un array di string.",
"contents.invalidBoolean": "Errore nel json, atteso un boolean.",
"contents.invalidGeolocation": "Errore nel json, atteso un object latitudine/longitudine.",
"contents.invalidGeolocationLatitude": "La latitudine deve essere tra -90 and 90.",
"contents.invalidGeolocationLongitude": "La longitude deve essere tra -180 and 180.",
"contents.invalidGeolocationMoreProperties": "E' possibile impostare la geolocalizzazione solo impostando latitudine e longitudine.",
"contents.invalidNumber": "Errore nel json,, atteso un number.",
"contents.invalidString": "Errore nel json, atteso una string.",
"contents.listReferences": "{count} Collegamenti(s)",
"contents.singletonNotChangeable": "Il contenuto singleton non può essere aggiornato",
"contents.singletonNotCreatable": "Il contenuto singleton non può essere creato.",
"contents.singletonNotDeletable": "Il contenuto singleton non può essere eliminato.",
"contents.statusSchedulingNotInFuture": "L'ora deve essere futura.",
"contents.statusTransitionNotAllowed": "Non è possibile cambiare stato da {oldStatus} a {newStatus}.",
"contents.validation.aspectRatio": "Deve essere le proporzioni {width}:{height}.",
"contents.validation.assetNotFound": "Id {id} non trovato.",
"contents.validation.between": "Deve essere tra {min} e {max}.",
"contents.validation.characterCount": "Deve avere esattamente {count} carattere(i).",
"contents.validation.charactersBetween": "Deve essere tra {min} e {max} carattere(i).",
"contents.validation.duplicates": "Non può avere valori duplicati.",
"contents.validation.exactValue": "Deve essere esattamente {value}.",
"contents.validation.extension": "Deve essere un'estensione consentita.",
"contents.validation.image": "Non è un'immagine.",
"contents.validation.invalid": "Valore non consentito.",
"contents.validation.itemCount": "Deve avere esattamente {count} elemento(i).",
"contents.validation.itemCountBetween": "Deve essere tra {min} e {max} elemento(i).",
"contents.validation.max": "Deve esseer minore o uguale a {max}.",
"contents.validation.maximumHeight": "L'altezza {height}px deve essere inferiore a {max}px.",
"contents.validation.maximumSize": "La dimensione {size} deve essere inferiore a {max}.",
"contents.validation.maximumWidth": "La larghezza {width}px deve essere inferiore a {max}px.",
"contents.validation.maxItems": "Non deve avere più di {max} elemento(i).",
"contents.validation.maxLength": "Non deve avere più di {max} carattere(i).",
"contents.validation.min": "Deve essere maggiore o uguale a {min}.",
"contents.validation.minimumHeight": "L'altezza {height}px deve essere maggiore di {min}px.",
"contents.validation.minimumSize": "La dimensione {size} deve essere maggiore di {min}.",
"contents.validation.minimumWidth": "La larghezza {width}px deve essere maggiore di {min}px.",
"contents.validation.minItems": "Deve avere almento {min} elemento(i).",
"contents.validation.minLength": "Deve avere almeno {min} carattere(i).",
"contents.validation.mustBeEmpty": "Il valore non deve essere definito.",
"contents.validation.notAllowed": "Non è un valore consentito.",
"contents.validation.pattern": "Deve seguire il pattern.",
"contents.validation.regexTooSlow": "La Regex è troppo lenta.",
"contents.validation.required": "Il campo è obbligatorio.",
"contents.validation.unique": "Esiste un altro contenuto con lo stesso valore.",
"contents.validation.unknownField": "Non è noto {fieldType}.",
"contents.workflowErorPublishing": "Il workflow del contenuto impedisce la pubblicazione.",
"contents.workflowErrorUpdate": "Il workflow non consente le modifiche per lo stato {status}",
"exception.invalidJsonQuery": "La query Json non è valida: {message}",
"exception.invalidJsonQueryJson": "La query Json non è valida: {message}",
"exceptions.domainObjectDeleted": "L'entità ({id}) è stata cancellata.",
"exceptions.domainObjectNotFound": "L'entità ({id}) non esiste.",
"exceptions.domainObjectVersion": "L'entità ({id}) requested version {expectedVersion}, but found {currentVersion}.",
"history.apps.clientAdded": "aggiunto client {[Id]} all'app",
"history.apps.clientRevoked": "Revocato client {[Id]}",
"history.apps.clientUpdated": "Aggiornato client {[Id]}",
"history.apps.contributoreAssigned": "Associato {user:[Contributor]} al ruolo {[Role]}",
"history.apps.contributoreRemoved": "Rimosso {user:[Contributor]} dall'app",
"history.apps.languagedAdded": "aggiunta lingua {[Language]}",
"history.apps.languagedRemoved": "rimossa lingua {[Language]}",
"history.apps.languagedSetToMaster": "cambiata la lingua master in {[Language]}",
"history.apps.languagedUpdated": "aggiornata la lingua {[Language]}",
"history.apps.patternAdded": "adggiunto pattern {[Name]}",
"history.apps.patternDeleted": "eliminato pattern {[PatternId]}",
"history.apps.patternUpdated": "modificato pattern {[Name]}",
"history.apps.planChanged": "cambiato il piano in {[Plan]}",
"history.apps.planReset": "riconfigurato il piano",
"history.apps.roleAdded": "aggiunto il ruolo {[Name]}",
"history.apps.roleDeleted": "eliminato role {[Name]}",
"history.apps.roleUpdated": "aggiornato role {[Name]}",
"history.assets.replaced": "risorsa sostituita.",
"history.assets.updated": "risorsa aggiornata.",
"history.assets.uploaded": "risorsa caricata.",
"history.contents.created": "creato il contenuto {[Schema]}.",
"history.contents.deleted": "cancellato il contenuto {[Schema]}.",
"history.contents.draftCreated": "creata una nuova bozza.",
"history.contents.draftDeleted": "cancellata la bozza.",
"history.contents.scheduleCompleted": "impostato per cambiare lo stato del contenuto {[Schema]} in {[Status]}.",
"history.contents.scheduleFailed": "fallita l'impostazione per cambiare lo stato del contenuto {[Schema]}.",
"history.contents.updated": "aggiornato il contenuto {[Schema]}.",
"history.schemas.created": "creato lo schema {[Name]}.",
"history.schemas.deleted": "cancellato lo schema {[Name]}.",
"history.schemas.fieldAdded": "aggiunto il campo {[Field]} allo schema {[Name]}.",
"history.schemas.fieldDeleted": "cancellato il campo {[Field]} dallo schema {[Name]}.",
"history.schemas.fieldDisabled": "disabilitato il campo {[Field]} allo schema {[Name]}.",
"history.schemas.fieldHidden": "nascosto il campo {[Field]} dallo schema {[Name]}.",
"history.schemas.fieldLocked": "bloccato il campo {[Field]} dallo schema {[Name]}.",
"history.schemas.fieldShown": "mostrato il campo {[Field]} dallo schema {[Name]}.",
"history.schemas.fieldsReordered": "riordinati i campi dello schema {[Name]}.",
"history.schemas.fieldUpdated": "aggiornato il campo {[Field]} dello schema {[Name]}.",
"history.schemas.published": "pubblicato lo schema {[Name]}.",
"history.schemas.scriptsConfigured": "configurato lo script per lo schema {[Name]}.",
"history.schemas.unpublished": "rimosso dalla pubblicazione lo schema {[Name]}.",
"history.schemas.updated": "aggiornato lo schema {[Name]}.",
"history.statusChanged": "cambiato lo stato del contenuto {[Schema]} in {[Status]}.",
"login.githubPrivateEmail": "Il tuo indirizzo email è impostato su privato in Github. Impostalo come pubblico per poter utilizzare il login Github.",
"rules.alreadyDeleted": "La regola è stata già cancellata.",
"rules.ruleAlreadyRunning": "E' in esecuzione un'altra regola.",
"schemas.alreadyDeleted": "Lo schema è stato già cancellato.",
"schemas.dateTimeCalculatedDefaultAndDefaultError": "Il valore predefinito calcolato e il valore predefinito non possono essere utilizzati insieme.",
"schemas.duplicateFieldName": "Il campo '{field}' è stato aggiunto due volte.",
"schemas.fieldCannotBeUIField": "Il campo non può essere un campo UI.",
"schemas.fieldIsLocked": "Il campo dello schema è bloccato.",
"schemas.fieldNameAlreadyExists": "Esiste già un campo con lo stesso nome.",
"schemas.fieldNotInSchema": "Il campo non appartiene allo schema.",
"schemas.fieldsNotCovered": "Non tutti i campi hanno degli ID associati.",
"schemas.nameAlreadyExists": "Esiste già uno schema con lo stesso nome.",
"schemas.noPermission": "Non hai i permessi per questo schema.",
"schemas.notFoundId": "Lo schema {id} non esiste.",
"schemas.number.inlineEditorError": "Non è consentita per l'editor di tipo radio la modifica in linea.",
"schemas.onlyArraysHaveNested": "Solo i campi di tipo array possono avere campi annidati.",
"schemas.onylArraysInRoot": "Non è possibile annidare un campo di tipo array.",
"schemas.references.resolveError": "E' possibile risolvere il nome del collegamento solamente quando il numero massimo di elementi è 1.",
"schemas.string.inlineEditorError": "E' possibile la modifica in linea solamente per dropdown menu, slugs e campi di input.",
"schemas.stringEditorsNeedAllowedValuesError": "I Radio button e dropdown menu hanno bisogno che siano definiti dei valori.",
"schemas.tags.editorNeedsAllowedValues": "Checkboxes e dropdown menu hanno bisogno che siano definiti dei valori.",
"schemas.uiFieldCannotBeDisabled": "Il campo UI non può essere disabilitato.",
"schemas.uiFieldCannotBeEnabled": "Il campo UI non può essere abilitato.",
"schemas.uiFieldCannotBeHidden": "Il campo UI non può essere nascosto.",
"schemas.uiFieldCannotBeShown": "Il campo UI non può essere mostrato.",
"search.contentResult": "Contenuto {name}",
"search.contentsResult": "Contenuti {name} ",
"search.schemaResult": "Schema {name}",
"security.passwordStolen": "Questa password risulta essere stata compromessa e non dovrebbe essere mai utilizzata. Se l'hai utilizzata in precedenza, cambiala!",
"users.accessDenied.text": "Questa operazione non è consentita, il tuo account potrebbe essere bloccato.",
"users.accessDenied.title": "Accesso negato",
"users.consent.agree": "Sono d'accordo!",
"users.consent.cookiesHeadline": "Cookie & Statistiche",
"users.consent.cookiesText": "<p>Comprendo e accetto che Squidex utilizzi i cookie allo scopo di assicurare una migliore esperienza di utilizzo della piattaforma e per archiviare lo status del login. </p><p> Comprendo e accetto che Squidex abbia integrato Google Analytics (con funzioni di anonimizzazione). Google Analytics è un servizio di analisi web per raccogliere e analizzare dati sul comportamento degli utenti. </p><p> Accetto il <a href=\"{privacyUrl}\" target=\"_blank\" rel=\"noopener\">privacy policies</a>.</p>",
"users.consent.emailHeadline": "E-Mail automatiche (Opzionale)",
"users.consent.emailText": "Comprendo e accetto che Squidex invii e-mail per informarmi su nuove funzionalità, modifiche importanti e tempi di inattività",
"users.consent.headline": "Abbiamo bisogno del tuo consenso",
"users.consent.needed": "Devi dare il consenso.",
"users.consent.piiHeadline": "Information Personali",
"users.consent.piiText": "Comprendo e accetto che Squidex raccolga le seguenti informazioni private che vengono recuperate da fornitori di autenticazione esterni come Google, Microsoft o Github. <ul class=\"personal-information\"> <li> Le informazioni personali di base (nome, cognome e immagine) vengono fornite a tutti gli altri utenti in modo che questi possano aggiungerti al loro spazio di lavoro. </li><li> In qualsiasi momento hai la possibilità di modificare queste informazioni per rendere anonimo il tuo account. </li><li> Il tuo account utente ha un identificatore univoco e per tutte le tue modifiche monitoriamo che tu abbia apportato queste modifiche e fornito queste informazioni ad altri utenti. </li></ul>",
"users.consent.title": "Acconsento",
"users.error.headline": "Operazione non riuscita",
"users.error.text": "Siamo dispiaciuti, qualcosa non ha funzionato correttamente.",
"users.error.title": "Errore",
"users.errorHappened": "Si è verificata un errore inaspettato.",
"users.lockedOutText": "Il tuo account è bloccato, si prega di contattare l'amministratore.",
"users.lockedOutTitle": "Account bloccato",
"users.lockYourselfError": "Non puoi bloccare te stesso.",
"users.login.askAdmin": "",
"users.login.emailPlaceholder": "Inserisci l'email",
"users.login.error": "Email o password non corretti",
"users.login.loginWith": "{action} con <strong>{provider}</strong>",
"users.login.noAccountLoginAction": "Clicca qui per accedere",
"users.login.noAccountLoginQuestion": "Sei già registrato?",
"users.login.noAccountSignupAction": "Clicca qui per registrarti",
"users.login.noAccountSignupQuestion": "Non hai ancora un account?",
"users.login.passwordPlaceholder": "Inserisci la password",
"users.login.separator": "o",
"users.logout.headline": "Uscito!",
"users.logout.text": "!Per favore chiudi questo popup.",
"users.logout.title": "Esci",
"users.profile.addLoginDone": "La login è stata aggiunta con successo.",
"users.profile.changePassword": "Cambia la password",
"users.profile.changePasswordDone": "Password cambiata con successo.",
"users.profile.clientHint": "Utilizza le credenziali client per accedere alle API utilizzando il profilo che ha le informazioni e i permessi corretti",
"users.profile.clientTitle": "Client",
"users.profile.confirmPassword": "Confermare",
"users.profile.generateClient": "Creato",
"users.profile.generateClientDone": "Secret client generato con successo.",
"users.profile.headline": "Profile modificato",
"users.profile.hideProfile": "Non mostrare il mio profilo agli altri utentiDo not show my profile to other users",
"users.profile.loginsTitle": "Login",
"users.profile.passwordTitle": "Password",
"users.profile.pii": "Informazioni personali",
"users.profile.propertiesHint": "Utilizza proprietà personalizzate per regole e script.",
"users.profile.propertiesTitle": "Proprietà",
"users.profile.propertyAdd": "Aggiungi una nuova proprietà",
"users.profile.removeLoginDone": "Il provider per l'autenticazione è stato rimosso correttamente.",
"users.profile.setPassword": "Imposta la Password",
"users.profile.setPasswordDone": "Password impostata con successo.",
"users.profile.title": "Profilo",
"users.profile.updateProfileDone": "Account aggiornato con successo.",
"users.profile.updatePropertiesDone": "Account aggiornato con successo.",
"users.profile.uploadPicture": "Carica un'immagine",
"users.profile.uploadPictureDone": "Immagine caricata con successo.",
"users.unlockYourselfError": "Non puoi sbloccare te stesso.",
"users.userLocked": "L'utente non ha i permessi per accedere.",
"users.userNotFound": "Non trovo l'utente.",
"validation.between": "{property|upper} deve essere tra {min} e {max}.",
"validation.greaterEqualsThan": "{property|upper} must be greater or equal to {other|lower}.",
"validation.greaterThan": "{property|upper} deve essere maggiore di {other|lower}.",
"validation.javascriptProperty": "{property|upper} non è un nome di una proprietà Javascript.",
"validation.lessEqualsThan": "{property|upper} deve essere minore o uguale a {other|lower}.",
"validation.lessThan": "{property|upper} deve essere minore di {other|lower}.",
"validation.notAnImage": "L'immagine non è un'immagine valida.",
"validation.onlyOneFile": "Puoi caricare solo un file.",
"validation.required": "{property|upper} è obbligatorio.",
"validation.requiredBoth": "Se {property1|lower} o {property2|lower} sono usate entrambe devono essere definite.",
"validation.requiredValue": "Il valore deve essere impostato.",
"validation.slug": "{property|upper} non è uno slug valido.",
"validation.valid": "{property|upper} non è un valore valido.",
"workflows.overlap": "Workflow multipli sono associati a tutti gli schema.",
"workflows.publishedIsInitial": "Lo step iniziale non può essere quello pubblico.",
"workflows.publishedNotDefined": "Il Workflow deve avere uno step pubbico.",
"workflows.publishedStepNotFound": "La Transition ha un obiettivo non valido.",
"workflows.schemaOverlap": "Lo schema '{schema}' è associato a diversi workflow."
}

18
backend/i18n/source/backend_nl.json

@ -23,9 +23,6 @@
"apps.roles.nameAlreadyExists": "Er bestaat al een rol met dezelfde naam.",
"apps.roles.usedRoleByClientsNotRemovable": "Kan een rol niet verwijderen wanneer een client is toegewezen.",
"apps.roles.usedRoleByContributorsNotRemovable": "Kan een rol niet verwijderen wanneer een bijdrager is toegewezen.",
"aspnet_annotations_AbsoluteUrl": "Het veld {0} moet een absolute URL zijn.",
"aspnet_annotations_Compare": "Het veld {0} moet hetzelfde zijn als {other|lower}.",
"aspnet_annotations_Required": "Het veld {0} is verplicht.",
"assets.assetAlreadyDeleted": "Asset is al verwijderd",
"assets.assetFolderAlreadyDeleted": "Assetmap is al verwijderd",
"assets.folderNotFound": "Assetmap bestaat niet.",
@ -180,6 +177,21 @@
"contents.validation.wordsBetween": "Moet tussen {min} en {max} woord (en) bevatten.",
"contents.workflowErorPublishing": "Contentworkflow verhindert publiceren.",
"contents.workflowErrorUpdate": "De werkstroom staat geen updates toe met status {status}",
"dotnet_identity_DefaultEror": "Er is een onbekende fout opgetreden.",
"dotnet_identity_DuplicateEmail": "E-mail is al in gebruik.",
"dotnet_identity_DuplicateUserName": "Gebruikersnaam is al in gebruik.",
"dotnet_identity_InvalidEmail": "E-mail is ongeldig.",
"dotnet_identity_InvalidUserName": "Gebruikersnaam '{0}' is ongeldig, mag alleen letters of cijfers bevatten.",
"dotnet_identity_LoginAlreadyAssociated": "Er bestaat al een gebruiker met deze login.",
"dotnet_identity_PasswordMismatch": "Onjuist wachtwoord.",
"dotnet_identity_PasswordRequiresDigit": "Wachtwoorden moeten minstens één cijfer bevatten ('0' - '9').",
"dotnet_identity_PasswordRequiresLower": "Wachtwoorden moeten minstens één kleine letter ('a' - 'z') bevatten.",
"dotnet_identity_PasswordRequiresNonAlphanumeric": "Wachtwoorden moeten minstens één niet-alfanumeriek teken bevatten.",
"dotnet_identity_PasswordRequiresUniqueChars": "Wachtwoorden moeten minstens {0} verschillende tekens bevatten.",
"dotnet_identity_PasswordRequiresUpper": "Wachtwoorden moeten minstens één hoofdletter ('A' - 'Z') hebben.",
"dotnet_identity_PasswordTooShort": "Wachtwoorden zijn te kort.",
"dotnet_identity_PwnedError": "Dit wachtwoord is eerder verschenen in een datalek en mag nooit worden gebruikt. Als je het ooit eerder ergens hebt gebruikt, verander het dan!",
"dotnet_identity_UserLockedOut": "Gebruiker is uitgesloten.",
"exception.invalidJsonQuery": "Json-query niet geldig: {message}",
"exception.invalidJsonQueryJson": "Json-query is niet geldig json: {message}",
"exceptions.domainObjectDeleted": "Entiteit ({id}) is verwijderd.",

4
backend/i18n/source/frontend__ignore.json

@ -19,8 +19,8 @@
"#{{index + 1}}"
],
"/features/content/shared/forms/field-editor.component.html": [
"{{field.displayName}} {{displaySuffix}}",
"*"
"*",
"{{field.displayName}} {{displaySuffix}}"
],
"/features/content/shared/references/references-editor.component.html": [
"&middot;"

24
backend/i18n/source/frontend_en.json

@ -90,6 +90,8 @@
"assets.searchByName": "Search by name",
"assets.searchByTags": "Search by tags",
"assets.selectMany": "Select assets",
"assets.specialFolder.parent": "<Parent>",
"assets.specialFolder.root": "Assets",
"assets.tabFocusPoint": "Focus Point",
"assets.tabHistory": "History",
"assets.tabImage": "Image",
@ -273,6 +275,16 @@
"common.preview": "Preview",
"common.product": "Squidex Headless CMS",
"common.project": "Project",
"common.queryOperators.contains": "contains",
"common.queryOperators.empty": "is empty",
"common.queryOperators.endsWith": "ends with",
"common.queryOperators.eq": "is equals to",
"common.queryOperators.ge": "is greater than or equals to",
"common.queryOperators.gt": "is greater than",
"common.queryOperators.le": "is less than pr equals to",
"common.queryOperators.lt": "is less than",
"common.queryOperators.ne": "is not equals to",
"common.queryOperators.startsWith": "starts with",
"common.refresh": "Refresh",
"common.rename": "Rename",
"common.requiredHint": "required",
@ -347,6 +359,7 @@
"contents.draftStatus": "New Version",
"contents.editPageTitle": "Edit Content",
"contents.editTitle": "Edit Content",
"contents.invariantFieldDescription": "The '{fieldName}' field of the content item.",
"contents.languageModeAll": "All Languages",
"contents.languageModeSingle": "Single Language",
"contents.lastModifiedByFieldDescription": "The user who modified the content item the last time.",
@ -357,6 +370,7 @@
"contents.loadDataFailed": "Failed to load data. Please reload.",
"contents.loadFailed": "Failed to load contents. Please reload.",
"contents.loadVersionFailed": "Failed to version a new version. Please reload.",
"contents.localizedFieldDescription": "The '{fieldName}' field of the content item (localized).",
"contents.newStatusFieldDescription": "The new status of the content item.",
"contents.noReference": "- No Reference -",
"contents.pendingChangesTextToChange": "You have unsaved changes.\n\nWhen you change the status you will loose them.\n\n**Do you want to continue anyway?**",
@ -383,6 +397,16 @@
"contents.statusQueries": "Status Queries",
"contents.stockPhotoEmpty": "Nothing selected",
"contents.stockPhotoSearch": "Search for Photos by Unsplash",
"contents.tableHeaders.created": "Created",
"contents.tableHeaders.createdBy": "Created By",
"contents.tableHeaders.createdByShort": "By",
"contents.tableHeaders.id": "Id",
"contents.tableHeaders.lastModified": "Updated",
"contents.tableHeaders.lastModifiedBy": "Updated By",
"contents.tableHeaders.lastModifiedByShort": "By",
"contents.tableHeaders.nextStatus": "Next Status",
"contents.tableHeaders.status": "Status",
"contents.tableHeaders.version": "Version",
"contents.unsavedChangesText": "You have unsaved changes. Do you want to load them now?",
"contents.unsavedChangesTitle": "Unsaved changes",
"contents.updated": "Content updated successfully.",

848
backend/i18n/source/frontend_it.json

@ -0,0 +1,848 @@
{
"api.contentApi": "Content API",
"api.generalApi": "General API",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.pageTitle": "API",
"api.title": "API",
"apps.allApps": "Tutte le Apps",
"apps.appLoadFailed": "Non è stato possibile caricare l'App. Per favore ricarica.",
"apps.appNameHint": "Puoi utilizzare solo lettere, numeri e trattini e non più di 40 caratteri.",
"apps.appNameValidationMessage": "Il nome può contenere lettere minuscole (a-z), numeri e trattini all'interno.",
"apps.appNameWarning": "Il nome della app non potrà essere cambiato in un secondo momento.",
"apps.appsButtonCreate": "Panoramica delle App",
"apps.appsButtonFallbackTitle": "Panoramica delle App",
"apps.archieve": "Archivia l'App",
"apps.archieveConfirmText": "Rimuovi il pattern",
"apps.archieveConfirmTitle": "Sei sicuro di voler archiviare questa app?",
"apps.archieveWarning": "Una volta archiviata una App, non è possibile tornare indietro. Sii certo.",
"apps.archiveFailed": "Non è stato possibile archiviare l'app. Per favore ricarica.",
"apps.create": "Crea un'App",
"apps.createBlankApp": "Nuova App.",
"apps.createBlankAppDescription": "Crea una app vuota senza contenuti o schema.",
"apps.createBlogApp": "Nuovo esempio di blog",
"apps.createBlogAppDescription": "Inizia con il nostro blog già pronto per l'uso.",
"apps.createFailed": "Non è stato possibile creare l'app. Per favore ricarica.",
"apps.createIdentityApp": "New Identity App",
"apps.createIdentityAppDescription": "Crea un app per Squidex Identity.",
"apps.createIdentityAppV2": "Nuova Identity App V2",
"apps.createIdentityAppV2Description": "Creare un app per Squidex Identity V2.",
"apps.createProfileApp": "Nuovo esempio di Profilo",
"apps.createProfileAppDescription": "Crea la tua pagina del profilo.",
"apps.createWithTemplate": "Create un esempio di {template}",
"apps.empty": "Non stai ancora collaborando su nessuna app",
"apps.generalSettings": "Generale",
"apps.generalSettingsDangerZone": "Generale",
"apps.image": "Immagine",
"apps.imageDrop": "Trascina il file per caricare",
"apps.listPageTitle": "App",
"apps.loadFailed": "Non è stato possibile caricare le App. Per favore ricarica.",
"apps.removeImage": "Rimuovi l'immagine",
"apps.removeImageFailed": "Non è stato possibile rimuovere l'immagine dell'app. Per favore ricarica.",
"apps.updateFailed": "Non è stato possibile aggiornare l'app. Per favore ricarica.",
"apps.upgradeHintCurrent": "Tu sei nel piano {plan}.",
"apps.upgradeHintUpgrade": "Aggiorna!",
"apps.uploadImage": "Trascina il file per sostituire l'immagine dell'app. Utilizza una dimensione quadrata.",
"apps.uploadImageButton": "Carica il File",
"apps.uploadImageFailed": "Non è stato possibile caricare l'immagine. Per favore ricarica.",
"apps.uploadImageTooBig": "L'immagine dell'app è troppo grande.",
"apps.welcomeSubtitle": "Benvenuto su Squidex.",
"apps.welcomeTitle": "Ciao {user}",
"assets.createFolder": "Crea cartella",
"assets.createFolderFailed": "Non è stato possibile creare la cartella degli asset. Per favore ricarica.",
"assets.createFolderTooltip": "Crea una nuova cartella (CTRL + SHIFT + G)",
"assets.deleteConfirmText": "Sei sicuro di voler cancellare la risorsa?",
"assets.deleteConfirmTitle": "Elimina la risorsa",
"assets.deleteFailed": "Non è stato possibile cancellare la risorsa. Per favore ricarica.",
"assets.deleteFolderConfirmText": "Sei sicuro di voler cancellare la cartella e tutte le risorse associati?",
"assets.deleteFolderConfirmTitle": "Elimina la cartella",
"assets.deleteMetadataConfirmText": "Sei sicuro di voler rimuovere questi metadati?",
"assets.deleteMetadataConfirmTitle": "Rimuovi metadati",
"assets.downloadVersion": "Scarica questa versione",
"assets.dropToUpdate": "Trascina il file per aggiornare",
"assets.duplicateFile": "La risorsa è già stata caricata.",
"assets.editor.flipHorizontally": "Capovolgi orizzontalmente",
"assets.editor.flipVertically": "Capovolgi verticalmente",
"assets.editor.focusPointLabel": "Clicca sull'immagine per impostare il focus",
"assets.editor.focusPointPreview": "Anteprima delle diverse dimensioni",
"assets.editor.rotateLeft": "Ruota a sinistra",
"assets.editor.rotateRight": "Ruota a destra",
"assets.fileTooBig": "La risorsa è troppo grande.",
"assets.folderName": "Nome della cartella",
"assets.folderNameHint": "Il nome della cartella viene usato solo per la visualizzazione e può non essere univoco.",
"assets.insertAssets": "Inserisci le risorse",
"assets.linkSelected": "Collega le risorse selezionate ({count})",
"assets.listPageTitle": "Risorse",
"assets.loadFailed": "Non è stato possibile caricare le risorse. Per favore ricarica.",
"assets.loadFoldersFailed": "Non è stato possibile caricare le cartelle delle risorse. Per favore ricarica.",
"assets.metadata": "Metadati",
"assets.metadataAdd": "Aggiungi i Metadati",
"assets.moveFailed": "Non è stato possibile spostare la risorsa. Per favore ricarica.",
"assets.protected": "Protetto",
"assets.refreshTooltip": "Aggiorna le risorse (CTRL + SHIFT + R)",
"assets.reloaded": "Risorse ricaricate.",
"assets.renameFolder": "Rinomina la cartella",
"assets.replaceConfirmText": "Sei sicuro di voler sostituire la risorsa con una nuova versione?",
"assets.replaceConfirmTitle": "Sostituisco la risorsa?",
"assets.replaceFailed": "Non è stato possibile sostituire la risorsa. Per favore ricarica.",
"assets.searchByName": "Ricerca per nome",
"assets.searchByTags": "Ricerca per tag",
"assets.selectMany": "Seleziona le risorse",
"assets.tabFocusPoint": "Punto focale",
"assets.tabHistory": "Cronologia",
"assets.tabImage": "Immagine",
"assets.tabMetadata": "Metadati",
"assets.updated": "La risorsa è stata aggiornata.",
"assets.updateFailed": "Non è stato possibile aggiornare la risorsa. Per favore ricarica.",
"assets.updateFolderFailed": "Non è stato possibile aggiornare la cartella delle risorse. Per favore ricarica.",
"assets.uploadByDialog": "Seleziona il(i) File",
"assets.uploadByDrop": "Trascina i file qui per il caricamento",
"assets.uploaderUploadHere": "Nessun caricamento in corso, trascina qui i file.",
"assets.uploadFailed": "Non è stato possibile caricare la risorsa. Per favore ricarica.",
"assets.uploadHint": "Trascina il file sull'elemento esistente per poterlo sostituire con una versione più recente.",
"backups.backupCountAssetsLabel": "Risorse",
"backups.backupCountAssetsTooltip": "Risorse archiviate",
"backups.backupCountEventsLabel": "Eventi",
"backups.backupCountEventsTooltip": "Eventi archiviati",
"backups.backupDownload": "Scarica",
"backups.backupDownloadLink": "Pronto",
"backups.backupDuration": "Durata",
"backups.deleteConfirmText": "Sei sicuro di voler cancellare il backup?",
"backups.deleteConfirmTitle": "Cancella il backup",
"backups.deleted": "Il backup sta per essere cancellato.",
"backups.deleteFailed": "Non è stato possibile cancellare il backup.",
"backups.empty": "Nessun backup è stato ancora creato.",
"backups.loadFailed": "Non è stato possibile caricare i backup.",
"backups.maximumReached": "Hai raggiunto il numero massimo di backup: 10.",
"backups.refreshTooltip": "Aggiorna i backup (CTRL + SHIFT + R)",
"backups.reloaded": "Backup aggiornati.",
"backups.restore": "Backup ripristinato",
"backups.restoreFailed": "Non è stato possibile avviare il ripristino.",
"backups.restoreLastStatus": "Ultima operazione di ripristino",
"backups.restoreLastUrl": "Url per il backup",
"backups.restoreNewAppName": "Nome dell'app opzionale",
"backups.restorePageTitle": "Ripristinare il Backup",
"backups.restoreStarted": "Ripristino avviato, il suo completamento potrebbe richiedere alcuni minuti.",
"backups.restoreStartedLabel": "Avviato",
"backups.restoreStoppedLabel": "Fermato",
"backups.restoreTitle": "Ripristinare il Backup",
"backups.start": "Avvia Backup",
"backups.started": "Backup avviato, il suo completamento potrebbe richiedere alcuni minuti.",
"backups.startedLabel": "Avviato",
"backups.startFailed": "Non è stato possibile avviare il backup.",
"clients.add": "Aggiungi un Client",
"clients.addFailed": "Non è stato possibile aggiungere un client. Per favore ricarica.",
"clients.allowAnonymous": "Consenti l'accesso anonimo.",
"clients.allowAnonymousHint": "Consenti l'accesso alle API senza token di accesso a tutte le risorse che sono configurate per il ruolo di questo client. E' possibile avere un solo client impostato con accesso anonimo.",
"clients.clientIdValidationMessage": "Il nome deve contenere solo lettere, numeri, trattini e spaziNa.",
"clients.clientNamePlaceholder": "Inserisci il nome del client",
"clients.connect": "Connettere",
"clients.connectWizard.cli": "Connettere con la Squidex CLI",
"clients.connectWizard.cliHint": "Scarica a CLI e collega questa app per iniziare il backup, sincronizzare gli schema ed esportare i contenuti.",
"clients.connectWizard.cliStep1": "Prendi l'ultima versione della Squidex CLI",
"clients.connectWizard.cliStep1Download": "[Scarica la CLI da Github](https://github.com/Squidex/squidex-samples/releases)",
"clients.connectWizard.cliStep1Hint": "Le release contengono file binari per tutte le operazioni di sistema maggiori e piccoli file da scariscare se hai installato il Core .NET Core.",
"clients.connectWizard.cliStep2": "Inserisci `<la directory per scaricare la tua Squidex CLI>` per impostare la variabile `$PATH`",
"clients.connectWizard.cliStep3": "Inserisci il nome della tua app per la configurazione della CLI",
"clients.connectWizard.cliStep3Hint": "E' possibile gestire le configurazionie per le diverse appi all'interno della CLI e passare ad un'app.",
"clients.connectWizard.cliStep4": "Passa alla tua app usando CLI",
"clients.connectWizard.manually": "Connetti manualmente",
"clients.connectWizard.manuallyHint": "Leggi le istruzioni su come stabilire una connessione utilizzando Postman o curl.",
"clients.connectWizard.manuallyStep1": "Ottenere un tocket usando curl",
"clients.connectWizard.manuallyStep2": "Utilizza il seguente token",
"clients.connectWizard.manuallyStep3": "Aggiungi il tocken come header HTTP header a tutte le richieste",
"clients.connectWizard.manuallyTokenHint": "Solitamente i Token scadono dopo 30 giorni, ma puoi richiedere token multipli.",
"clients.connectWizard.postManDocs": "Per il tutorial Postman inizia da questo link [Documentazione](https://docs.squidex.io/02-documentation/developer-guides/api-overview/postman).",
"clients.connectWizard.sdk": "Connetti la tua APP utilizzando SDK",
"clients.connectWizard.sdkHelp": "Hai bisogno di un altro SDK?",
"clients.connectWizard.sdkHelpLink": "Contattaci nel Forum di assistenza",
"clients.connectWizard.sdkHint": "Scarica l'SDK e connetti quest'app.",
"clients.connectWizard.sdkStep1": "Installa .NET SDK",
"clients.connectWizard.sdkStep1Download": "L'SDK è disponibile su [nuget](https://www.nuget.org/packages/Squidex.ClientLibrary/)",
"clients.connectWizard.sdkStep2": "Crea un client manager",
"clients.connectWizard.step0Title": "Setup client",
"clients.connectWizard.step1Title": "Scegli la tipologia di connessione",
"clients.connectWizard.step2Title": "Collega",
"clients.deleteConfirmText": "Sei sicuro di voler rimuovere il client?",
"clients.deleteConfirmTitle": "Rimuovere il client",
"clients.empty": "Nessun client ancora creato.",
"clients.loadFailed": "Non è stato possibile caricare i client. Per favore ricarica.",
"clients.refreshTooltip": "Agigorna i client (CTRL + SHIFT + R)",
"clients.reloaded": "Client ricaricati.",
"clients.revokeFailed": "Non è stato possibile rimuovere il client. Per favore ricarica.",
"clients.tokenFailed": "Non è stato possibile creare il token. Per favore riprova.",
"comments.create": "Creare un commento",
"comments.createFailed": "Non è stato possibile creare un commento.",
"comments.deleteConfirmText": "Sei sicuro di voler cancellare il commmento?",
"comments.deleteConfirmTitle": "Cancella il comment",
"comments.deleteFailed": "Non è stato possibile cancellare il commento.",
"comments.follow": "Segui",
"comments.loadFailed": "Non è stato possibile caricare i commenti.",
"comments.title": "Commenti",
"comments.updateFailed": "Non è stato possibile aggiornare il commento.",
"common.actions": "Azioni",
"common.administration": "Amministrazione",
"common.administrationPageTitle": "Amministrazione",
"common.api": "API",
"common.apps": "App",
"common.aspectRatio": "Proporzioni",
"common.assets": "Risorse",
"common.back": "Indietro",
"common.backups": "Backup",
"common.bytes": "byte",
"common.cancel": "Annulla",
"common.clear": "Pulisci",
"common.clientId": "Id Client",
"common.clients": "Client",
"common.clientSecret": "Secret Client",
"common.clipboardAdded": "Il valore è stato aggiunto nei tuoi appunti.",
"common.clone": "Clona",
"common.cluster": "Raggruppamento",
"common.clusterPageTitle": "Raggruppamento",
"common.comments": "Commenti",
"common.confirm": "Conferma",
"common.consumers": "Utenti",
"common.content": "Contentenuto",
"common.contents": "Contentenuti",
"common.continue": "Continua",
"common.contributors": "Collaboratori",
"common.create": "Crea",
"common.created": "Creato",
"common.date": "Data",
"common.dateTimeEditor.now": "Data e Ora attuale",
"common.dateTimeEditor.nowTooltip": "Imposta la data e l'ora attuale (UTC)",
"common.dateTimeEditor.today": "Oggi",
"common.dateTimeEditor.todayTooltip": "Imposta la data di oggi (UTC)",
"common.delete": "Cancella",
"common.description": "Descrizione",
"common.displayName": "Nome visualizzato",
"common.edit": "Modifica",
"common.email": "Email",
"common.error": "Errore",
"common.errorBack": "Torna alla pagina precedente.",
"common.errorNoPermission": "Non hai i permessi per questo.",
"common.errorNotFound": "Non trovato",
"common.event": "Evento",
"common.events": "Eventi",
"common.executed": "Eseguito",
"common.expertMode": "Modalità esperto",
"common.failed": "Fallito",
"common.fallback": "Alternativa",
"common.field": "Campo",
"common.files": "Campi",
"common.filters": "Filtri",
"common.folders": "Cartelle",
"common.generalSettings": "Impostazioni generali",
"common.generate": "Genera",
"common.github": "Github",
"common.height": "Altezza",
"common.help": "Aiuto",
"common.helpTour": "Fare clic sull'icona della guida per mostrare una pagina della guida specifica per questo contesto. Vai a",
"common.hide": "Nascondi",
"common.hints": "Suggerimenti",
"common.history": "Cronologia",
"common.httpConflict": "Non è stato possibile effettuare l'aggiornamento. Un altro utente ha fatto delle modifiche. Per favore ricarica.",
"common.httpLimit": "Hai superato il limite massimo di chiamate API.",
"common.label": "Etichetta",
"common.languages": "Lingue",
"common.latitudeShort": "Lat",
"common.loading": "Caricamento",
"common.logout": "Esci",
"common.logs": "Log",
"common.longitudeShort": "Lon",
"common.mapHide": "Nascondi la mappa",
"common.mapShow": "Mostra la mappa",
"common.message": "Messaggio",
"common.name": "Nome",
"common.no": "No",
"common.nothingChanged": "Non è stato cambiato niente.",
"common.noValue": "- Nessun valore -",
"common.or": "o",
"common.pagerInfo": "{itemFirst}-{itemLast} of {numberOfItems}",
"common.password": "Password",
"common.passwordConfirm": "Conferma Password",
"common.pattern": "Pattern",
"common.patterns": "Pattern",
"common.permissions": "Permessi",
"common.preview": "Anteprima",
"common.product": "Squidex Headless CMS",
"common.project": "Progetto",
"common.refresh": "Aggiorna",
"common.rename": "Rinomina",
"common.requiredHint": "obbligatorio",
"common.reset": "Reimposta",
"common.restore": "Ripristina",
"common.role": "Ruolo",
"common.roles": "Ruoli",
"common.rules": "Regole",
"common.sampleCodeLabel": "Esempio di codice per",
"common.save": "Salva",
"common.saveShortcut": "CTRL + S",
"common.schemas": "Schemi",
"common.searchGoogleMaps": "Cerca su Google Maps",
"common.searchResults": "Risultati di ricerca",
"common.separateByLine": "Separato dalla linea",
"common.settings": "Impostazioni",
"common.sidebarTour": "La barra di navigazione laterale contiene specifici utili collegamenti per il contesto. Qui puoi visualizzare la cronologia dei cambiamenti di questo schema.",
"common.slug": "Slug",
"common.stars.max": "Non deve avere più di 15 stelle",
"common.status": "Stato",
"common.statusChangeTo": "Cambia in",
"common.submit": "Invia",
"common.subscription": "Abbonamenti",
"common.succeeded": "Successo",
"common.tagAdd": ", aggiungi al tag",
"common.tagAddReference": ", aggiungi al collegamento",
"common.tagAddSchema": ", aggiungi allo schema",
"common.tags": "Tag",
"common.tagsAll": "Tutti i tag",
"common.time": "Ora",
"common.update": "Aggiorna",
"common.url": "URL",
"common.users": "Utenti",
"common.value": "Valore",
"common.width": "Larghezza",
"common.workflow": "Workflow",
"common.workflows": "Workflow",
"common.yes": "Si",
"contents.arrayAddItem": "Aggiungi un elemento",
"contents.arrayCloneItem": "Clona questo elemento",
"contents.arrayCollapseAll": "Comprimi tutti gli elementi",
"contents.arrayCollapseItem": "Comprimi l'elemento",
"contents.arrayExpandAll": "Espandi tutti gli elementi",
"contents.arrayExpandItem": "Espandi questo elemento",
"contents.arrayMoveBottom": "Sposta questo elemento in basso",
"contents.arrayMoveDown": "Sposta giù questo elemento ",
"contents.arrayMoveTop": "Sposta in cima questo elemento",
"contents.arrayMoveUp": "Sposta su questo elemento",
"contents.arrayNoFields": "Aggiungi un primo campo annidato agli elementi.",
"contents.assetsUpload": "Trascina i file o clicca",
"contents.autotranslate": "Traduci in automatico dalla lingua principale",
"contents.changeStatusTo": "Cambia l'elemeto(i) del contenuti in {action}",
"contents.changeStatusToImmediately": "Imposta {action} immediatamente.",
"contents.changeStatusToLater": "Imposta {action} ad una data e ora successiva.",
"contents.contentNotValid": "Un elemento del contenuto non è valido, verifica il campo con la barra rossa per tutte le lingue impostate (se presenti).",
"contents.create": "Nuovo",
"contents.createContentTooltip": "Nuovo contenuto (CTRL + SHIFT + G)",
"contents.created": "Contenuto creato con successo.",
"contents.createdByFieldDescription": "L'utente che ha creato l'elemento del contenuto.",
"contents.createFailed": "Non è stato possibile creare il contenuto. Per favore ricarica.",
"contents.createFieldDescription": "La data e l'ora di creazione del contenuto.",
"contents.createPageTitle": "Crea un contenuto",
"contents.createTitle": "Nuovo Contenuto",
"contents.currentStatusLabel": "Versione corrente",
"contents.deleteConfirmText": "Sei sicuro di voler eliminare il contenuto?",
"contents.deleteConfirmTitle": "Elimina il contenuto",
"contents.deleteFailed": "Non è stato possibile eliminare il contenuto. Per favore ricarica.",
"contents.deleteManyConfirmText": "Sei sicuro di voler eliminare gli elementi del contenuto selezionati?",
"contents.deleteVersionConfirmText": "Do you really want to delete this version?",
"contents.deleteVersionFailed": "Non è stato possibile eliminare la versione. Per favore ricarica.",
"contents.draftNew": "Nuova bozza",
"contents.draftStatus": "Nuova versione",
"contents.editPageTitle": "Modifica contenuto",
"contents.editTitle": "Modifica il contenuto",
"contents.languageModeAll": "Tutte le lingue",
"contents.languageModeSingle": "Una sola lingua",
"contents.lastModifiedByFieldDescription": "L'utente che ha modificato l'elememto l'ultima voltaThe user who modified the content item the last time.",
"contents.lastModifiedFieldDescription": "La data e l'ora dell'ultima modifica del contenuto.",
"contents.lastUpdatedLabel": "Ultimo aggiornamento",
"contents.loadContent": "Carica",
"contents.loadContentFailed": "Non è stato possibile caricare il contenuto. Per favore ricarica.",
"contents.loadDataFailed": "Non è stato possibile caricare i dati. Per favore ricarica.",
"contents.loadFailed": "Non è stato possibile caricare i contenuti. Per favore ricarica.",
"contents.loadVersionFailed": "Non è stato possibile version a new version. Per favore ricarica.",
"contents.noReference": "- Nessun collegamento -",
"contents.pendingChangesTextToChange": "Non hai salvato le modifiche.\n\nSe cambi lo stato perderai le modifiche.\n\n**Sei sicuro di voler continuare?**",
"contents.pendingChangesTextToClose": "Non hai salvato le modifiche.\n\nChiudendo il contenuto corrente perderai tutte le modifiche.\n\n**Sei sicuro di voler continuare?**",
"contents.pendingChangesTitle": "Modifiche non salvate",
"contents.referencesCreateNew": "Aggiungi nuovo",
"contents.referencesCreatePublish": "Crea e pubblica",
"contents.referencesLink": "Collega i contenuti selezionati ({count})",
"contents.referencesSelectExisting": "Seleziona da contenuti esistenti",
"contents.referencesSelectSchema": "Seleziona {schema}",
"contents.refreshTooltip": "Aggiorna i contenuti (CTRL + SHIFT + R)",
"contents.reloaded": "Contenuti aggiornati.",
"contents.saveAndPublish": "Salva e pubblica",
"contents.scheduledAt": "alle",
"contents.scheduledAtLabel": "alle",
"contents.scheduledTo": "a",
"contents.schemasPageTitle": "Contenuti",
"contents.searchPlaceholder": "Ricerca testuale",
"contents.searchSchemasPlaceholder": "Cerca schemi...",
"contents.selectionCount": "{count} elementi selezionati",
"contents.statusFieldDescription": "Stato dell'elemento del contenuto.",
"contents.statusQueries": "Stato Query",
"contents.stockPhotoEmpty": "Nessuna selezione",
"contents.stockPhotoSearch": "Cerca foto su Unsplash",
"contents.unsavedChangesText": "Non hai salvato le modifiche. Vuoi salvarle adesso?",
"contents.unsavedChangesTitle": "Modifiche non salvate",
"contents.updated": "Contenuto aggiornato con successo.",
"contents.updateFailed": "Non è stato possibile aggiornare il contenuto. Per favore ricarica.",
"contents.validationHint": "Ricorda di verificare tutte le lingue quando vedi errori di validazione.",
"contents.versionCompare": "Confronta",
"contents.versionDelete": "Cancella questa Versione",
"contents.versionFieldDescription": "La versione dell'elemento del contenuto",
"contents.versionViewing": "Stai guardando la versione **{version}**.",
"contents.viewLatest": "Visualizza l'ultima",
"contents.viewReset": "Imposta la visualizzazione predefinita",
"contributors.add": "Aggiungi un collaboratore",
"contributors.addFailed": "Non è stato possibile aggiungere collaboratori. Per favore ricarica.",
"contributors.contributorAssigned": "Un nuovo utente, con indirizzo email, è stato creato e aggiunto come collaboratore.",
"contributors.contributorAssignedExisting": "L'utente è stato assegnato",
"contributors.contributorAssignedInvited": "L'utente è stato invitato e assegnato.",
"contributors.contributorAssignedOld": "L'utente è stato aggiunto come collaboratore.",
"contributors.deleteConfirmText": "Sei sicuro di voler rimuovere il collaboratore?",
"contributors.deleteConfirmTitle": "Rimuovi il collaboratore",
"contributors.deleteFailed": "Non è stato possibile cancellare il collaboratore. Per favore ricarica.",
"contributors.emailPlaceholder": "Cerca un utente esistente o invitalo tramite email",
"contributors.empty": "Nessun collaboratore trovato.",
"contributors.import.emailsDetected": "Email trovate: {count}",
"contributors.import.run": "Aggiungi Collaboratori",
"contributors.import.run2": "Importa",
"contributors.importButton": "Aggiungi più collaboratori contemporaneamente",
"contributors.importHintg": "Team numeroso?",
"contributors.importTitle": "Importa collaboratori",
"contributors.loadFailed": "Non è stato possibile caricare contributors. Per favore ricarica.",
"contributors.planHint": "Il tuo piano prevede un numero massimo di {maxContributors} collaboratori.",
"contributors.refreshTooltip": "Aggiorna i collaboratori (CTRL + SHIFT + R)",
"contributors.reloaded": "Collaboratori aggiornati.",
"contributors.search": "Cerca",
"contributors.userNotFound": "L'utente non esiste.",
"dashboard.apiCallsCard": "Chiamate API ",
"dashboard.apiCallsChart": "Grafico delle chiamate API",
"dashboard.apiCallsLimitLabel": "Limite mensile",
"dashboard.apiCallsSummaryCard": "Riepilogo chiamate API",
"dashboard.apiDocumentationCard": "Documentazione delle API",
"dashboard.apiPerformanceCard": "Performance(ms) delle API: {summary}ms avg",
"dashboard.apiPerformanceChart": "Diagramma delle Performance delle API",
"dashboard.assetSizeCard": "Dimensione delle risorse (MB",
"dashboard.assetSizeLabel": "Dimensione totale",
"dashboard.assetSizeLimitLabel": "Limite totale",
"dashboard.assetTotalSize": "Dimensione totale dello Storage per le risorse",
"dashboard.assetUpdloadsCountChart": "Diagramma del numero di risorse caricate",
"dashboard.assetUploadsCard": "Risorse caricate",
"dashboard.assetUploadsSizeChart": "Diagramma della dimensione delle risorse caricate",
"dashboard.configSaved": "Configurazione salvata.",
"dashboard.contentApi": "API dei contenuti",
"dashboard.contentApiDescription": "Documentazione OpenAPI 3.0 compatibile per i contenuti della tua app.",
"dashboard.contentsSummaryCard": "Numero di elementi",
"dashboard.currentMonthLabel": "Questo mese",
"dashboard.downloadLog": "Scarica i Log",
"dashboard.editConfig": "Modifica la configurazione",
"dashboard.githubCard": "Github",
"dashboard.githubCardDescription": "Ottieni il codice sorgente da GitHub e segnala bug o richiedi assistenza.",
"dashboard.historyCard": "Cronologia",
"dashboard.pageTitle": "Dashboard",
"dashboard.resetConfigConfirmText": "Sei sicuro di voler riportare la dashboard alle impostazioni predefinite?",
"dashboard.resetConfigConfirmTitle": "Ripristina la configurazione",
"dashboard.schemaNewCard": "Nuovo Schema",
"dashboard.schemaNewCardDescription": "Uno schena definisce la struttura di un tipo di contenuto.",
"dashboard.schemasCard": "Schemi",
"dashboard.schemasCardDescription": "Panoramica del modello dei dati di questa app.",
"dashboard.stackedChart": "Istogramma in pila",
"dashboard.supportCard": "Feedback & Assistenza",
"dashboard.supportCardDescription": "Fornisci feedback e richiedi funzionalità per aiutarci a migliorare Squidex..",
"dashboard.trafficChart": "Diagramma del traffico delle API",
"dashboard.trafficHeader": "Traffico (MB)",
"dashboard.trafficLimitLabel": "Limite mensile",
"dashboard.trafficSummaryCard": "Riepilogo del traffico delle API",
"dashboard.welcomeText": "Benvenuto su **{app}** dashboard.",
"dashboard.welcomeTitle": "Ciao {user}",
"eventConsumers.loadFailed": "Non è stato possibile caricare event consumers. Per favore ricarica.",
"eventConsumers.pageTitle": "Eventi degli utenti",
"eventConsumers.position": "Posizione",
"eventConsumers.refreshTooltip": "Aggiorna gli eventi degli utenti (CTRL + SHIFT + R)",
"eventConsumers.reloaded": "Eventi degli utenti aggiornati.",
"eventConsumers.resetFailed": "Non è stato possibile ripristinare gli eventi degli utenti. Per favore ricarica.",
"eventConsumers.resetTooltip": "Ripristina gli event degli utenti",
"eventConsumers.startFailed": "Non è stato possibile far partire l'evento dell'utente. Per favore ricarica.",
"eventConsumers.startTooltip": "Inizia l'evento utente",
"eventConsumers.stopFailed": "Non è stato possibile fermare l'evento dell'utente. Per favore ricarica.",
"eventConsumers.stopTooltip": "Interrompi l'evento dell'utente",
"features.loadFailed": "Non è stato possibile caricare le funzionalità. Per favore ricarica.",
"history.loadFailed": "Non è stato possibile caricare la cronologia. Per favore ricarica.",
"history.title": "Attività",
"languages.add": "Aggiungi lingua",
"languages.addFailed": "Non è stato possibile aggiungere la lingua. Per favore ricarica.",
"languages.deleteConfirmText": "Sei sicuro di voler rimuovere la lingua?",
"languages.deleteConfirmTitle": "Rimuovi la lingua",
"languages.deleteFailed": "Non è stato possibile cancellare la lingua. Per favore ricarica.",
"languages.loadFailed": "Non è stato possibile caricare le lingue. Per favore ricarica.",
"languages.master": "è la principale",
"languages.masterHint": "Se non è stata impostata nessuna lingua come alternativa, le altre lingue hanno la lingua principale come alternativa.",
"languages.optional": "E' Opzionale",
"languages.optionalHint": "Se sono presenti campi obbligatori questi non devono essere compilati anche per le lingue opzionali.",
"languages.refreshTooltip": "Aggiorna le lingue (CTRL + SHIFT + R)",
"languages.reloaded": "Lingue ricaricate.",
"languages.updateFailed": "Non è stato possibile cambiare la lingua. Per favore ricarica.",
"news.headline": "Che cosa c'è di nuovo?",
"news.title": "Nuove funzionalità",
"notifo.subscripeTooltip": "Fai clic su questo pulsante per iscriverti a tutte le modifiche e ricevere le notifiche push.",
"patterns.deleteConfirmText": "Sei sicuro di voler rimuovere il pattern?",
"patterns.deleteConfirmTitle": "Cancella il pattern",
"patterns.deleteFailed": "Non è stato possibile rimuovere il pattern. Per favore ricarica.",
"patterns.empty": "Nessun pattern è stato ancora creato.",
"patterns.loadFailed": "Non è stato possibile aggiungere il pattern. Per favore ricarica.",
"patterns.nameValidationMessage": "Il nome può contenere solo lettere, numeri, trattini e spazi.",
"patterns.refreshTooltip": "Aggiorna i pattern (CTRL + SHIFT + R)",
"patterns.reloaded": "Pattern ricaricati.",
"patterns.updateFailed": "Non è stato possibile aggiornare pattern. Per favore ricarica.",
"plans.billingPortal": "Portal di Fatturazione",
"plans.billingPortalHint": "Vai al Portal di fatturazione per lo storico dei pagamenti e una panoramica per l'abbonamento.",
"plans.change": "Cambia",
"plans.changeConfirmTitle": "Cambia abbonamento",
"plans.changeFailed": "Non è stato possibile cambiare il piano. Per favore ricarica.",
"plans.includedCalls": "Chiamate API",
"plans.includedContributors": "Collaboratori",
"plans.includedStorage": "Spazio disco",
"plans.includedTraffic": "Traffico",
"plans.loadFailed": "Non è stato possibile caricare i piani. Per favore ricarica.",
"plans.noPlanConfigured": "Nessun piano è stato impostato, quest'app ha spazio disco ha un uso illiminato.",
"plans.notPlanOwner": "Non hai creato nessun abbonamento, pertanto non è possibile cambiare il piano.",
"plans.perMonth": "Al Mese",
"plans.perYear": "all'Anno",
"plans.refreshTooltip": "Aggiorna i piani (CTRL + SHIFT + R)",
"plans.reloaded": "Piano aggiornati.",
"plans.selected": "Selezionato",
"profile.title": "Profilo",
"profile.userEmail": "Accesso con",
"roles.add": "Aggiungi un ruolo",
"roles.addFailed": "Non è stato possibile aggiungere il ruolo. Per favore ricarica.",
"roles.default.owner": "Hai come amministratore tutte le funzionalità, compreso cancellare le app.",
"roles.default.reader": "Hai un'utenza in sola lettura sia per i contenuti che per le risorse.",
"roles.defaults.developer": "Hai un'utenza che può visualizzare le API, modificare le risorse, i contenuti, gli schema, le regole, i workflows e i pattern.",
"roles.defaults.editor": "Hai un'utenzaCan che può modificare le risorse, i conteuti e visualizzare i workflow.",
"roles.deleteConfirmText": "Cancella il ruolo",
"roles.deleteConfirmTitle": "Sei sicuro di voler eliminare il ruolo?",
"roles.loadFailed": "Non è stato possibile caricare i ruoli. Per favore ricarica.",
"roles.loadPermissionsFailed": "Non è stato possibile caricare i permessi. Per favore ricarica.",
"roles.refreshTooltip": "Aggiorna i ruoli (CTRL + SHIFT + R)",
"roles.reloaded": "Ruoli ricaricati.",
"roles.revokeFailed": "Non è stato possibile rimuovere il ruolo. Per favore ricarica.",
"roles.roleNamePlaceholder": "Inserisci il nome del ruolo",
"roles.updateFailed": "Non è stato possibile aggiornare il ruolo. Per favore ricarica.",
"rules.actionEdit": "Mdifica l'Azione",
"rules.cancelFailed": "Non è stato possibile eliminare la regola. Per favore ricarica.",
"rules.create": "Crea un nuova Regola",
"rules.createFailed": "Non è stato possibile creare una nuova regola. Per favore ricarica.",
"rules.createTooltip": "Nuova regola (CTRL + SHIFT + G)",
"rules.deleteConfirmText": "Sei sicuro di voler eliminare la regola?",
"rules.deleteConfirmTitle": "Cancella la regola",
"rules.deleteFailed": "Non è stato possibile eliminare la regola. Per favore ricarica.",
"rules.disableFailed": "Non è stato possibile disabilitare la regola. Per favore ricarica.",
"rules.empty": "Nessuna regola è stato ancora creata.",
"rules.emptyAddRule": "Aggiungi una regola",
"rules.enableFailed": "Non è stato possibile abilitare la regola. Per favore ricarica.",
"rules.enqueued": "La regola è stata aggiunta alle code.",
"rules.listPageTitle": "Regole",
"rules.loadFailed": "Non è stato possibile caricare le regole. Per favore ricarica.",
"rules.readMore": "Leggi di più",
"rules.refreshEventsTooltip": "Aggiorna gli Eventi (CTRL + SHIFT + R)",
"rules.refreshTooltip": "Aggiorna le Regole (CTRL + SHIFT + R)",
"rules.reloaded": "Regole ricaricate.",
"rules.restarted": "La Regola sarà eseguita fra pochi secondi.",
"rules.ruleEvents.cancelFailed": "Non è stato possibile cancellare l'evento della regola. Per favore ricarica.",
"rules.ruleEvents.enqueue": "Metti in coda",
"rules.ruleEvents.enqueued": "Eventi messo in coda. L'evento potrà essere rieseguto fra pochi secondi.",
"rules.ruleEvents.enqueueFailed": "Non è stato possibile mettere in coda l'evento della regola. Per favore ricarica.",
"rules.ruleEvents.lastInvokedLabel": "Ultima chiamata",
"rules.ruleEvents.listPageTitle": "Eventi della Regola",
"rules.ruleEvents.loadFailed": "Non è stato possibile caricare gli eventi. Per favore ricarica.",
"rules.ruleEvents.nextAttemptLabel": "Successivo",
"rules.ruleEvents.numAttemptsLabel": "Tentativi",
"rules.ruleEvents.reloaded": "Eventi della regola ricaricati.",
"rules.ruleSyntax.if": "If",
"rules.ruleSyntax.then": "then",
"rules.run": "Esegui",
"rules.runFailed": "Non è stato possibile eseguire la regola. Per favore ricarica.",
"rules.runningRule": "La regola '{name}' è attualmente in esecuzione.",
"rules.runRuleConfirmText": "Sei sicuro di voler eseguire la regola per tutti gli eventi?",
"rules.runRuleConfirmTitle": "Esegui la regola",
"rules.stop": "La regola si fermerà al più presto.",
"rules.triggerConfirmText": "Sei sicuro che voler attivare la regola?",
"rules.triggerConfirmTitle": "Attiva la regola",
"rules.triggerEdit": "Modifica l'Attivazione",
"rules.triggerFailed": "Non è stato possibile attivare la regola. Per favore ricarica.",
"rules.unnamed": "Regola senza nome",
"rules.updateFailed": "Non è stato possibile aggiornare la regola. Per favore ricarica.",
"rules.wizard.actionHint": "La selezione del tipo di azione non potrà essere modificata successivamente.",
"rules.wizard.selectAction": "Seleziona l'Azione",
"rules.wizard.selectTrigger": "Seleziona l'Attivazione",
"rules.wizard.triggerHint": "La selezione del tipo di attivazione non potrà essere modificata successivamente.",
"schemas.addField": "Aggiungi un Campo",
"schemas.addFieldAndClose": "Crea e chiudi",
"schemas.addFieldAndCreate": "Crea e aggiungi il campo",
"schemas.addFieldAndEdit": "Crea e modifica il campo",
"schemas.addFieldButton": "Aggiungi il Campo",
"schemas.addFieldFailed": "Non è stato possibile aggiungere il campo. Per favore ricarica.",
"schemas.addNestedField": "Aggiungi un campo annidato",
"schemas.changeCategoryFailed": "Non è stato possibile cambiare la categoria. Per favore ricarica.",
"schemas.clone": "Clona lo Schema",
"schemas.contextMenuTour": "Apri il menu per cancellare lo schema o per inserire alcuni script che modificano il contenuto.",
"schemas.create": "Crea uno Schema",
"schemas.createCategory": "Crea una nuova categoria...",
"schemas.createFailed": "Non è stato possibile creare lo schema. Per favore ricarica.",
"schemas.createSchemaTooltip": "Nuovo Schema (CTRL + SHIFT + G)",
"schemas.deleteConfirmText": "Sei sicuro di voler eliminare lo schema?",
"schemas.deleteConfirmTitle": "Cancella lo schema",
"schemas.deleteFailed": "Non è stato possibile cancellare lo schema. Per favore ricarica.",
"schemas.deleteFieldFailed": "Non è stato possibile cancellare il campo. Per favore ricarica.",
"schemas.deleteRuleConfirmText": "Sei sicuro di cancellare questa regola per il campo?",
"schemas.deleteRuleConfirmTitle": "Cancellare la regola per il campo",
"schemas.deleteUrlConfirmText": "Sei sicuro di voler rimuovere questa URL?",
"schemas.deleteUrlConfirmTitle": "Cancella URL",
"schemas.disableFieldFailed": "Non è stato possibile disabilitare il campo. Per favore ricarica.",
"schemas.enableFieldFailed": "Non è stato possibile abilitare il campo. Per favore ricarica.",
"schemas.export.deleteFields": "Cancella i campi",
"schemas.export.recreateFields": "Ricrea i campi",
"schemas.export.synchronize": "Sincronizza",
"schemas.field.allowedValues": "Valori consentiti",
"schemas.field.defaultValue": "Valori predefiniti",
"schemas.field.deleteConfirmText": "Sei sicuro di voler eliminare il campo?",
"schemas.field.deleteConfirmTitle": "Cancella il campo",
"schemas.field.disable": "Disabilita nella UI",
"schemas.field.disabledMarker": "Disabilitato",
"schemas.field.editor": "Editor",
"schemas.field.editorUrl": "Editor Url",
"schemas.field.editorUrlHint": "Url del tuo plugin se usi un custom editor.",
"schemas.field.empty": "Nessun campo è stato ancora creato.",
"schemas.field.enable": "Abilita nella UI",
"schemas.field.enabledMarker": "Abilitato",
"schemas.field.hiddenMarker": "Nasconsto",
"schemas.field.hide": "Nascondi nelle API",
"schemas.field.hintsHint": "Descrivi questo schema per la documentazione e le interfacce utente.",
"schemas.field.inlineEditable": "Modificabile sulla stessa linea",
"schemas.field.labelHint": "Nome da visualizzare per la documentazione e le interfacce utente.",
"schemas.field.localizable": "Consente la localizzazione",
"schemas.field.localizableHint": "Puoi impostare il campo per consentire la localizzazione, ossia che dipende dalla lingua che utilizzi come ad esempio i nomi delle città.",
"schemas.field.localizableMarker": "consente la localizzazione",
"schemas.field.lock": "Blocca e impedisce i cambiamenti",
"schemas.field.lockConfirmText": "Attenzione: Bloccare un campo è un'azione irreversibile! Se blocchi il campo non potrai più sbloccarlo o cancellarlo o cambiarlo. Sei sicuro di voler bloccare il campo?",
"schemas.field.lockedMarker": "Bloccato",
"schemas.field.nameHint": "Il nome del campo nelle chiamate API response.",
"schemas.field.namePlaceholder": "Inserisci il nome del campo",
"schemas.field.nameValidationMessage": "Il nome deve essere valido per il javascript in formato camel case.",
"schemas.field.placeholder": "Segnaposto",
"schemas.field.placeholderHint": "Definisci il segnaposto per la verifica dell'input.",
"schemas.field.required": "Obbligatorio",
"schemas.field.show": "Mostra nelle API",
"schemas.field.tabCommon": "Comune",
"schemas.field.tabEditing": "Modifica",
"schemas.field.tabValidation": "Validazione",
"schemas.field.tagsHint": "Tag per segnalare il tuo campo nei processi automatici.",
"schemas.field.unique": "Univoco",
"schemas.field.visibleMarker": "Visibile",
"schemas.fieldTypes.array.count": "Elementi",
"schemas.fieldTypes.array.countMax": "Max num. Elementi",
"schemas.fieldTypes.array.countMin": "Min num. Elementi",
"schemas.fieldTypes.array.description": "Lista di oggetti incorporati.",
"schemas.fieldTypes.assets.allowDuplicates": "Consente valori duplicati",
"schemas.fieldTypes.assets.count": "Conteggio",
"schemas.fieldTypes.assets.countMax": "Max num di Risorse",
"schemas.fieldTypes.assets.countMin": "Min num. di Risorse",
"schemas.fieldTypes.assets.description": "Immagini, video, documenti.",
"schemas.fieldTypes.assets.fileExtensions": "Estensioni dei File",
"schemas.fieldTypes.assets.mustBeImage": "Deve essere un'immagine",
"schemas.fieldTypes.assets.previewFileName": "Solamente il nome del file",
"schemas.fieldTypes.assets.previewImage": "Solamente thumbnail o il nome del file se non è un immagine",
"schemas.fieldTypes.assets.previewImageAndFileName": "Thumbnail e nome del file",
"schemas.fieldTypes.assets.previewMode": "Modalità anteprima",
"schemas.fieldTypes.assets.previewModeHint": "Anteprima delle risorse nella lista dei contenuti.",
"schemas.fieldTypes.assets.resolve": "Risolvi il nome della prima risorsa",
"schemas.fieldTypes.assets.resolveHint": "Mostra la prima risorsa collegata nella lista dei contenuti.",
"schemas.fieldTypes.assets.size": "Dimensione",
"schemas.fieldTypes.assets.sizeMax": "Dimensione Min",
"schemas.fieldTypes.assets.sizeMin": "Dimensione Max",
"schemas.fieldTypes.boolean.description": "Si o no, vero o falso.",
"schemas.fieldTypes.dateTime.defaultMode": "Modalità predefinita",
"schemas.fieldTypes.dateTime.description": "Data degli eventi, orari di apertura.",
"schemas.fieldTypes.dateTime.rangeMax": "Valore Max",
"schemas.fieldTypes.dateTime.rangeMin": "Valore Min",
"schemas.fieldTypes.geolocation.description": "Coordinate: latitudine e longitudine.",
"schemas.fieldTypes.json.description": "Dati in formato JSON, per gli sviluppatori.",
"schemas.fieldTypes.number.description": "ID, numero d'ordine, valutazione, quantità.",
"schemas.fieldTypes.number.range": "Intervallo",
"schemas.fieldTypes.number.rangeMax": "Valore Max",
"schemas.fieldTypes.number.rangeMin": "Valore Min ",
"schemas.fieldTypes.references.count": "Elementi",
"schemas.fieldTypes.references.countMax": "Numero Max Elementi",
"schemas.fieldTypes.references.countMin": "Numero Min Elementi",
"schemas.fieldTypes.references.description": "Link ad altri elementi del contenuto.",
"schemas.fieldTypes.references.resolveHint": "Mostra il nome dell'elemento collegato (reference) nella lista dei contenuti quando il numero massimo di elementi è impostato a 1.",
"schemas.fieldTypes.string.description": "Titoli, nomi, paragrafi.",
"schemas.fieldTypes.string.length": "Lunghezza",
"schemas.fieldTypes.string.lengthMax": "Lunghezza Max",
"schemas.fieldTypes.string.lengthMin": "Lunghezza Min",
"schemas.fieldTypes.string.pattern": "Regex Pattern",
"schemas.fieldTypes.string.patternMessage": "Messaggio del Pattern",
"schemas.fieldTypes.string.suggestions": "Suggestions",
"schemas.fieldTypes.tags.count": "Items",
"schemas.fieldTypes.tags.countMax": "Max Items",
"schemas.fieldTypes.tags.countMin": "Min Items",
"schemas.fieldTypes.tags.description": "Formato speciale per i tag.",
"schemas.fieldTypes.ui.description": "Separatore per il pannello delle modifiche della UI.",
"schemas.hideFieldFailed": "Non è stato possibile nascondere il campo. Per favore ricarica.",
"schemas.import": "Importa uno schema",
"schemas.listFields": "Lista dei Campi",
"schemas.listFieldsEmpty": "Incolla qui il campo o riordina i campi da mostrare nella lista dei contenuti. Se non imposti una lista di campi da visualizzare, viene utilizzato il primo della lista.",
"schemas.loadFailed": "Non è stato possibile caricare gli schemi. Per favore ricarica.",
"schemas.loadSchemaFailed": "Non è stato possibile caricare lo schema. Per favore ricarica.",
"schemas.lockFieldFailed": "Non è stato possibile bloccare il campo. Per favore ricarica.",
"schemas.modeMultiple": "Contenuti multipli",
"schemas.modeMultipleDescription": "Ideale per contenuti multipli come post di blog, pagine, autori, prodotti...",
"schemas.modeSingle": "Singolo contenuto",
"schemas.modeSingleDescription": "Ideale per contenuti singoli come la pagina principale, privacy, impostazioni...",
"schemas.nameWarning": "Questi valori non possono essere cambiati in un secondo momento.",
"schemas.previewUrls.empty": "Nessuna url per l'anteprima è stato configurato.",
"schemas.previewUrls.help": "Riguardo all'URL dell'anteprima controlla la pagina della guida integrata per saperne di più.",
"schemas.previewUrls.namePlaceholder": "Web o Mobile",
"schemas.previewUrls.title": "URL dell'anteprima",
"schemas.previewUrls.urlPlaceholder": "URL con variabili",
"schemas.published": "Pubblicato",
"schemas.publishedTour": "!Per poter creare un contenuto devi prima aver pubblicato il relativo schema.",
"schemas.publishFailed": "Non è stato possibile pubblicare lo schema. Per favore ricarica.",
"schemas.referenceFields": "Campi per i collegamenti (Reference)",
"schemas.referenceFieldsEmpty": "Trascina qui il file qui oppure riordina i campi da visualizzare nella lista che viene visualizzata quando colleghi il contenuto ad un altro. Quando non è impostato alcun campo per il contenuto che si desidera collegare, sono visualizzati la lista dei campi in ordine di utilizzo.",
"schemas.reloaded": "Schemai ricaricati.",
"schemas.reorderFieldsFailed": "Non è stato possibile riordinare i campi. Per favore ricarica.",
"schemas.rules.action": "Azione",
"schemas.rules.condition": "Condizioni dentro il Javascript",
"schemas.rules.empty": "Non è stata configurata nessuna regola per il campo.",
"schemas.rules.title": "Regole per il campo",
"schemas.rules.when": "quando",
"schemas.saved": "Lo Schema è stato salvato con successo.",
"schemas.saveFieldAndClose": "Salva e chiudi",
"schemas.saveFieldAndNew": "Salva e aggiungi un campo",
"schemas.schemaHintsHint": "Descrivi questo schema per la documentazione e le interfacce utente.",
"schemas.schemaLabelHint": "Nome da visualizzare per la documentazione e le interfacce utente.",
"schemas.schemaNameHint": "Puoi utilizzare solo lettere, numeri e trattini e un numero massimo di 40 caratteri.",
"schemas.schemaNameValidationMessage": "Il nome può contenere solo lettere, numeri, trattini e spazi.",
"schemas.schemaTagsHint": "Tag per descrivere il tuo schema per i processi automatici.",
"schemas.searchPlaceholder": "Cerca negli schemi...",
"schemas.showFieldFailed": "Non è stato possibile mostrare il campo. Per favore ricarica.",
"schemas.synchronized": "Lo Schema è stato sincronizzato con successo.",
"schemas.synchronizeFailed": "Non è stato possibile sincronizzare lo schema. Per favore ricarica.",
"schemas.tabFields": "Campi",
"schemas.tabJson": "Json",
"schemas.tabMore": "Di più",
"schemas.tabScripts": "Script",
"schemas.tabUI": "UI",
"schemas.ui": "Campi assegnati",
"schemas.ui.unassignedFields": "UnCampi non assegnati",
"schemas.unpublished": "Non pubblicato",
"schemas.unpublishFailed": "Non è stato possibile togliere dalla pubblicazione lo schema. Per favore ricarica.",
"schemas.updateFailed": "Non è stato possibile aggiornare schema. Per favore ricarica.",
"schemas.updateFieldFailed": "Non è stato possibile aggiornare il campo. Per favore ricarica.",
"schemas.updatePreviewUrlsFailed": "Non è stato possibile configure le url dell'anteprima. Per favore ricarica.",
"schemas.updateRulesFailed": "Non è stato possibile aggiornare le regole dello schema. Per favore ricarica.",
"schemas.updateScriptsFailed": "Non è stato possibile aggiornare gli script dello schema. Per favore ricarica.",
"schemas.updateUIFieldsFailed": "Non è stato possibile aggiornare i campi della UI. Per favore ricarica.",
"search.addFilter": "Aggiungi un Filtro",
"search.addGroup": "Aggiungi un Gruppo",
"search.addSorting": "Aggiungi ordinamento",
"search.advancedTour": "Fai clic su questa icona per visualizzare il menu della ricerca avanzata!",
"search.customQuery": "Query personalizzata",
"search.fullTextTour": "Cerca contenuti utilizzando la ricerca testuale su tutti i campi e le lingue!",
"search.help": "Ulteriori informazioni sui filtri su [Documentation](https://https://docs.squidex.io/04-guides/02-api.html).",
"search.myQueries": "Le mie query",
"search.nameQuery": "Dai un nome alla query",
"search.queriesEmpty": "Ricerca per {types} e utilizza l'icona <i class=\"icon-star-empty\"></i> nella ricerca per salvare la query per tutti i collaboratori.",
"search.queryAllNewestFirst": "Tutto (newest first)",
"search.queryAllOldestFirst": "Tutto (oldest first)",
"search.quickNavPlaceholder": "Navigazione veloce (Press 'q')",
"search.saveQueryMyself": "Salva la query solamente per me.",
"search.searchFailed": "Non è stato possibile eseguire la ricerca. Per favore ricarica.",
"search.sharedQueries": "Query condivise",
"search.sorting": "Ordinamento",
"start.login": "Entra su Squidex",
"start.loginHint": "Il pulsante per accedere aprirà un popup. Una volta effettuato l'accesso sarai indirizzato al portale per la gestione di Squidex.",
"start.madeBy": "Realizzato con orgoglio da",
"start.madeByCopyright": "Sebastian Stehle e Collaboratori, 2016-2020",
"tour.joinForum": "Unisciti al nostro Forum",
"tour.joinGithub": "Unisciti a Github",
"tour.skip": "Salta il Tour",
"tour.step0Next": "Guardiamoci intorno",
"tour.step0Text": "Puoi iniziare subito a gestire e distribuire i tuoi contenuti, ma prima vorremmo illustrarti alcune nozioni di base...\n\nIn questo modo",
"tour.step1Next": "Continua",
"tour.step1Text": "Un'App è il repository per il tuo progetto, ad es. (un blog, un negozio sul web o una app per dispositivi mobili). Puoi assegnare collaboratori alla tua App per lavorare insieme.\n\nPuoi creare un numero illimitato di app in Squidex per gestire più progetti contemporaneamente.",
"tour.step2Next": "Vai avanti!",
"tour.step2Text": "Gli Schemi definiscono la struttura per i tuoi contenuti, i campi e i tipi di dato per ogni tipo di contenuto.\n\nPrima di creare un contenuto riferito al tuo schema, assicurati di aver pubblicato lo schema premendo il pulsante 'Pubblica' visualizzato nell'editor dei contenuti.",
"tour.step3Next": "Ci siamo quasi!",
"tour.step3Text": "I contenuti della tua app sono raggruppati tramite gli schemi.\n\nSeleziona uno schema pubblicato e poi crea il relativo contenuto.",
"tour.step4Next": "Fatto!",
"tour.step4Text": "Le risorse contengono tutti i file che tu puoi collegare ai tuoi contenuti. Per esempio immagini, video o documenti.\n\nPuoi caricare qui le risorse e usarle successivamente oppure caricarle direttamente quando stai creando un contenuto utilizzando un campo risorse.",
"tour.step5Text": "Ma non è tutto il supporto che possiamo fornire.\n\nPer maggiori informazioni visita il sito https://docs.squidex.io/>.\n\nVuoi far parte della nostra community?",
"tour.step5Title": "Fantastico, ora conosci le basi!",
"tour.tooltipConfirm": "Capito",
"tour.tooltipStop": "Ferma il Tour",
"tour.welcome": "Benvenuto su",
"tour.welcomeProduct": "Squidex CMS",
"translate.translateFailed": "Non è stato possibile tradurre il testo. Per favore ricarica.",
"usages.loadCallsFailed": "Non è stato possibile caricare la pagina per l'utilizzo delle chiamate. Per favore ricarica.",
"usages.loadMonthlyCallsFailed": "Non è stato possibile caricare le chiamate mensili delle API calls. Per favore ricarica.",
"usages.loadStorageFailed": "Non è stato possibile caricare lo spazio disco utilizzato. Per favore ricarica.",
"usages.loadTodayStorageFailed": "Non è stato possibile caricare lo spazio disco utilizzato oggi. Per favore ricarica.",
"users.create": "Nuovo",
"users.createFailed": "Non è stato possibile creare l'utente. Per favore ricarica.",
"users.createPageTitle": "Crea un utente",
"users.createTitle": "Nuovo utente",
"users.createTooltip": "Nuovo utente (CTRL + N)",
"users.editPageTitle": "Modifica l'utente",
"users.editTitle": "Modifica l'utente",
"users.listPageTitle": "Gestione Utente",
"users.listTitle": "Utenti",
"users.loadFailed": "Non è stato possibile caricare gli utenti. Per favore ricarica.",
"users.loadUserFailed": "Non è stato possibile caricare l'utente. Per favore ricarica.",
"users.lockTooltip": "Utente bloccato",
"users.passwordConfirmValidationMessage": "Le password devono essere uguali.",
"users.refreshTooltip": "Aggiorna gli Utenti (CTRL + SHIFT + R)",
"users.reloaded": "Utenti ricaricati.",
"users.search": "Cerca l'utente",
"users.unlockTooltip": "Sblocca l'utente",
"users.updateFailed": "Non è stato possibile aggiornare l'utente. Per favore ricarica.",
"validation.between": "{field} deve essere tra '{min}' e '{max}'.",
"validation.betweenlength": "{field|upper} deve essere tra {minlength} e {maxlength} elemento(i).",
"validation.betweenlengthstring": "{field|upper} deve essere tra {minlength} e {maxlength} carattere(i).",
"validation.email": "{field|upper} deve essere un indirizzo email.",
"validation.exactly": "{field|upper} deve essere esattamente '{expected}'.",
"validation.exactlylength": "{field|upper} deve essere esattamente {expected} elemento(i).",
"validation.exactlylengthstring": "{field|upper} deve essere esattamente {expected} carattere(i).",
"validation.match": "{message}",
"validation.max": "{field|upper} deve essere minore o uguale a '{max}'.",
"validation.maxlength": "{field|upper} non deve avere più di {requiredlength} elemento(i).",
"validation.maxlengthstring": "{field|upper} non deve avere più di {requiredlength} carattere(i).",
"validation.min": "{field|upper} deve essere maggiore o uguale a '{min}'.",
"validation.minlength": "{field|upper} deve avere almeno {requiredlength} elemento(i).",
"validation.minlengthstring": "{field|upper} deve avere almeno {requiredlength} carattere(i).",
"validation.pattern": "{field|upper} non corrisponde al pattern.",
"validation.patternmessage": "{message}",
"validation.required": "{field|upper} è obbligatorio.",
"validation.requiredTrue": "{field|upper} è obbligatorio.",
"validation.uniquestrings": "{field|upper} non deve contenere valori duplicati.",
"validation.validarrayvalues": "{field|upper} contiene valori non validicontains an invalid value: {invalidvalue}.",
"validation.validdatetime": "{field|upper} non è una data e ora valida.",
"validation.validvalues": "{field|upper} non è un valore valido.",
"workflows.add": "Aggiungi un Workflow",
"workflows.addStep": "Aggiungi uno Step",
"workflows.createFailed": "Non è stato possibile creare un workflow. Per favore ricarica.",
"workflows.deleteConfirmText": "Sei sicuro di voler eliminare il workflow?",
"workflows.deleteConfirmTitle": "Cancella il workflow",
"workflows.deleteFailed": "Non è stato possibile cancellare il Workflow. Per favore ricarica.",
"workflows.empty": "Nessun workflow è stato ancora creato.",
"workflows.loadFailed": "Non è stato possibile caricare i workflow. Per favore ricarica.",
"workflows.notNamed": "Workflow senza nome",
"workflows.preventUpdates": "Impedisci gli aggiornamenti",
"workflows.publishedNotRemovable": "Non è possibile rimuoverlo",
"workflows.refreshTooltip": "Aggiorna i workflow (CTRL + SHIFT + R)",
"workflows.reloaded": "Workflow ricaricati.",
"workflows.saved": "Il Workflow è stato salvato.",
"workflows.schemasHint": "Limita questo workflow ad uno schema specifico, o lascialo bianco per applicarlo a tutti gli schemi.",
"workflows.syntax.expression": "Espressione",
"workflows.syntax.for": "per",
"workflows.syntax.when": "quando",
"workflows.tabEdit": "Modifica",
"workflows.tabVisualize": "Visualizza",
"workflows.updateFailed": "Non è stato possibile aggiornare il Workflow. Per favore ricarica.",
"workflows.workflowNameHint": "Nome facoltativo per il workflow.",
"workflows.workflowNamePlaceholder": "Inserisci il nome del workflow"
}

8
backend/i18n/source/frontend_nl.json

@ -70,16 +70,16 @@
"assets.fileTooBig": "Asset is te groot.",
"assets.folderName": "Mapnaam",
"assets.folderNameHint": "De mapnaam wordt gebruikt als weergavenaam en mag niet uniek zijn.",
"assets.insertAssets": "Assets invoegen",
"assets.insertAssets": "Bestanden invoegen",
"assets.linkSelected": "Link geselecteerde items ({count})",
"assets.listPageTitle": "Assets",
"assets.listPageTitle": "Bestanden",
"assets.loadFailed": "Laden van bestanden is mislukt. Laad opnieuw.",
"assets.loadFoldersFailed": "Laden van mappen is mislukt. Laad opnieuw.",
"assets.metadata": "Metadata",
"assets.metadataAdd": "Metadata toevoegen",
"assets.moveFailed": "Verplaatsen van item is mislukt. Laad opnieuw.",
"assets.protected": "Beschermd",
"assets.refreshTooltip": "Assets vernieuwen (CTRL + SHIFT + R)",
"assets.refreshTooltip": "Bestanden vernieuwen (CTRL + SHIFT + R)",
"assets.reloaded": "Bestanden herladen.",
"assets.removeConfirmText": "Wil je het bestand echt verwijderen?",
"assets.removeConfirmTitle": "Verwijder bestand",
@ -90,6 +90,8 @@
"assets.searchByName": "Zoeken op naam",
"assets.searchByTags": "Zoeken op tags",
"assets.selectMany": "Selecteer middelen",
"assets.specialFolder.parent": "<Hoofdmap>",
"assets.specialFolder.root": "Bestanden",
"assets.tabFocusPoint": "Focuspunt",
"assets.tabHistory": "Geschiedenis",
"assets.tabImage": "Afbeelding",

2
backend/i18n/translator/Squidex.Translator/Commands.cs

@ -120,7 +120,7 @@ namespace Squidex.Translator
throw new ArgumentException("Folder does not exist.");
}
var locales = new string[] { "en", "nl" };
var locales = new string[] { "en", "nl", "it" };
var translationsDirectory = new DirectoryInfo(Path.Combine(arguments.Folder, "backend", "i18n"));
var translationsService = new TranslationService(translationsDirectory, fileName, locales, arguments.SingleWords);

10
backend/i18n/translator/Squidex.Translator/Processes/CheckBackend.cs

@ -32,10 +32,12 @@ namespace Squidex.Translator.Processes
{
var content = File.ReadAllText(file.FullName);
var matches = Regex.Matches(content, "T\\.Get\\(\"(?<Key>[^\"]*)\"");
var translations = new HashSet<string>();
void AddTranslations(string regex)
{
var matches = Regex.Matches(content, regex, RegexOptions.Singleline);
foreach (Match match in matches)
{
var key = match.Groups["Key"].Value;
@ -44,6 +46,10 @@ namespace Squidex.Translator.Processes
all.Add(key);
}
}
AddTranslations("T\\.Get\\(\"(?<Key>[^\"]*)\"");
AddTranslations("\"(?<Key>history\\.[^\"]*)\"");
Helper.CheckForFile(service, relativeName, translations);
}

42
backend/i18n/translator/Squidex.Translator/Processes/CheckFrontend.cs

@ -64,27 +64,20 @@ namespace Squidex.Translator.Processes
var translations = new HashSet<string>();
var matches0 = Regex.Matches(content, "\"i18n\\:(?<Key>[^\"]+)\"", RegexOptions.Singleline);
foreach (Match match in matches0)
void AddTranslations(string regex)
{
translations.Add(match.Groups["Key"].Value);
}
var matches = Regex.Matches(content, regex, RegexOptions.Singleline);
var matches1 = Regex.Matches(content, "'i18n\\:(?<Key>[^\']+)'", RegexOptions.Singleline);
foreach (Match match in matches1)
foreach (Match match in matches)
{
translations.Add(match.Groups["Key"].Value);
}
var matches2 = Regex.Matches(content, "'(?<Key>[^\']+)' \\| sqxTranslate", RegexOptions.Singleline);
foreach (Match match in matches2)
{
translations.Add(match.Groups["Key"].Value);
}
AddTranslations("\"i18n\\:(?<Key>[^\"]+)\"");
AddTranslations("'i18n\\:(?<Key>[^\']+)'");
AddTranslations("'(?<Key>[^\']+)' \\| sqxTranslate");
return translations;
}
@ -94,27 +87,20 @@ namespace Squidex.Translator.Processes
var translations = new HashSet<string>();
var matches1 = Regex.Matches(content, "'i18n\\:(?<Key>[^\']+)'", RegexOptions.Singleline);
foreach (Match match in matches1)
void AddTranslations(string regex)
{
translations.Add(match.Groups["Key"].Value);
}
var matches = Regex.Matches(content, regex, RegexOptions.Singleline);
var matches2 = Regex.Matches(content, "localizer.get\\('(?<Key>[^\']+)'\\)", RegexOptions.Singleline);
foreach (Match match in matches2)
foreach (Match match in matches)
{
translations.Add(match.Groups["Key"].Value);
}
var matches3 = Regex.Matches(content, "localizer.getOrKey\\('(?<Key>[^\']+)'\\)", RegexOptions.Singleline);
foreach (Match match in matches3)
{
translations.Add(match.Groups["Key"].Value);
}
AddTranslations("'i18n\\:(?<Key>[^\']+)'");
AddTranslations("localizer.get\\('(?<Key>[^\']+)'\\)");
AddTranslations("localizer.getOrKey\\('(?<Key>[^\']+)'\\)");
return translations;
}
}

12
backend/i18n/translator/Squidex.Translator/Processes/GenerateBackendResources.cs

@ -5,8 +5,10 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.IO;
using System.Resources.NetStandard;
using System.Text.RegularExpressions;
using Squidex.Translator.State;
namespace Squidex.Translator.Processes
@ -41,6 +43,16 @@ namespace Squidex.Translator.Processes
foreach (var (key, value) in texts)
{
writer.AddResource(key, value);
if (key.StartsWith("annotations_", StringComparison.OrdinalIgnoreCase))
{
var i = 0;
var dotnetKey = $"dotnet_{key}";
var dotnetValue = Regex.Replace(value, "{[^}]*}", m => $"{{{i++}}}");
writer.AddResource(dotnetKey, dotnetValue);
}
}
}

7
backend/i18n/translator/Squidex.Translator/Processes/Helper.cs

@ -72,8 +72,9 @@ namespace Squidex.Translator.Processes
foreach (var key in service.MainTranslations.Keys)
{
if (!translations.Contains(key) &&
!key.StartsWith("validation.", StringComparison.OrdinalIgnoreCase) &&
!key.StartsWith("aspnet_", StringComparison.OrdinalIgnoreCase))
!key.StartsWith("common.", StringComparison.OrdinalIgnoreCase) &&
!key.StartsWith("dotnet_", StringComparison.OrdinalIgnoreCase) &&
!key.StartsWith("validation.", StringComparison.OrdinalIgnoreCase))
{
notUsed.Add(key);
}
@ -106,7 +107,7 @@ namespace Squidex.Translator.Processes
var parts = key.Split(".");
if (parts[0] != "common" && parts[0] != "validation")
if (parts.Length > 1 && parts[0] != "common" && parts[0] != "validation")
{
prefixes.Add(parts[0]);
}

9
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/HttpJintExtension.cs

@ -52,9 +52,10 @@ namespace Squidex.Domain.Apps.Core.Scripting.Extensions
{
using (var httpClient = httpClientFactory.CreateClient())
{
var request = CreateRequest(url, headers);
var response = await httpClient.SendAsync(request, context.CancellationToken);
using (var request = CreateRequest(url, headers))
{
using (var response = await httpClient.SendAsync(request, context.CancellationToken))
{
response.EnsureSuccessStatusCode();
var responseObject = await ParseResponse(context, response);
@ -64,6 +65,8 @@ namespace Squidex.Domain.Apps.Core.Scripting.Extensions
callback(responseObject);
}
}
}
}
catch (Exception ex)
{
context.Fail(ex);

1
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/StringTextValidator.cs

@ -7,7 +7,6 @@
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Translations;
using Squidex.Text;

6
backend/src/Squidex.Domain.Apps.Entities/Backup/TempFolderBackupArchiveLocation.cs

@ -54,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
client.Timeout = TimeSpan.FromHours(1);
response = await client.GetAsync(url);
response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
using (var sourceStream = await response.Content.ReadAsStreamAsync())
@ -67,6 +67,10 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
throw new BackupRestoreException($"Cannot download the archive. Got status code: {response?.StatusCode}.", ex);
}
finally
{
response?.Dispose();
}
}
try

14
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs

@ -8,7 +8,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.Utilities;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Squidex.Domain.Apps.Core;
@ -22,9 +22,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public sealed class CachingGraphQLService : CachingProviderBase, IGraphQLService
{
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10);
private readonly IDependencyResolver resolver;
private readonly IServiceProvider resolver;
public CachingGraphQLService(IMemoryCache cache, IDependencyResolver resolver)
public CachingGraphQLService(IMemoryCache cache, IServiceProvider resolver)
: base(cache)
{
Guard.NotNull(resolver, nameof(resolver));
@ -87,24 +87,24 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
entry.AbsoluteExpirationRelativeToNow = CacheDuration;
var allSchemas = await resolver.Resolve<IAppProvider>().GetSchemasAsync(app.Id);
var allSchemas = await resolver.GetRequiredService<IAppProvider>().GetSchemasAsync(app.Id);
return new GraphQLModel(app,
allSchemas,
GetPageSizeForContents(),
GetPageSizeForAssets(),
resolver.Resolve<IUrlGenerator>());
resolver.GetRequiredService<IUrlGenerator>());
});
}
private int GetPageSizeForContents()
{
return resolver.Resolve<IOptions<ContentOptions>>().Value.DefaultPageSizeGraphQl;
return resolver.GetRequiredService<IOptions<ContentOptions>>().Value.DefaultPageSizeGraphQl;
}
private int GetPageSizeForAssets()
{
return resolver.Resolve<IOptions<AssetOptions>>().Value.DefaultPageSizeGraphQl;
return resolver.GetRequiredService<IOptions<AssetOptions>>().Value.DefaultPageSizeGraphQl;
}
private static object CreateCacheKey(Guid appId, string etag)

39
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs

@ -11,11 +11,12 @@ using System.Linq;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.DataLoader;
using GraphQL.Utilities;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types;
using Squidex.Domain.Apps.Entities.Contents.Queries;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Log;
@ -24,34 +25,38 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public sealed class GraphQLExecutionContext : QueryExecutionContext
{
private static readonly List<IEnrichedAssetEntity> EmptyAssets = new List<IEnrichedAssetEntity>();
private static readonly List<IContentEntity> EmptyContents = new List<IContentEntity>();
private static readonly List<IEnrichedContentEntity> EmptyContents = new List<IEnrichedContentEntity>();
private readonly IDataLoaderContextAccessor dataLoaderContextAccessor;
private readonly IDependencyResolver resolver;
private readonly IServiceProvider resolver;
public IUrlGenerator UrlGenerator { get; }
public ICommandBus CommandBus { get; }
public ISemanticLog Log { get; }
public GraphQLExecutionContext(Context context, IDependencyResolver resolver)
public GraphQLExecutionContext(Context context, IServiceProvider resolver)
: base(context
.WithoutCleanup()
.WithoutContentEnrichment(),
resolver.Resolve<IAssetQueryService>(),
resolver.Resolve<IContentQueryService>())
resolver.GetRequiredService<IAssetQueryService>(),
resolver.GetRequiredService<IContentQueryService>())
{
UrlGenerator = resolver.Resolve<IUrlGenerator>();
UrlGenerator = resolver.GetRequiredService<IUrlGenerator>();
CommandBus = resolver.GetRequiredService<ICommandBus>();
dataLoaderContextAccessor = resolver.Resolve<IDataLoaderContextAccessor>();
dataLoaderContextAccessor = resolver.GetRequiredService<IDataLoaderContextAccessor>();
this.resolver = resolver;
}
public void Setup(ExecutionOptions execution)
{
var loader = resolver.Resolve<DataLoaderDocumentListener>();
var loader = resolver.GetRequiredService<DataLoaderDocumentListener>();
execution.Listeners.Add(loader);
execution.FieldMiddleware.Use(Middlewares.Logging(resolver.Resolve<ISemanticLog>()));
execution.FieldMiddleware.Use(Middlewares.Logging(resolver.GetRequiredService<ISemanticLog>()));
execution.FieldMiddleware.Use(Middlewares.Errors());
execution.UserContext = this;
@ -61,14 +66,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
var dataLoader = GetAssetsLoader();
return await dataLoader.LoadAsync(id);
return await dataLoader.LoadAsync(id).GetResultAsync();
}
public async Task<IContentEntity?> FindContentAsync(Guid id)
{
var dataLoader = GetContentsLoader();
return await dataLoader.LoadAsync(id);
return await dataLoader.LoadAsync(id).GetResultAsync();
}
public Task<IReadOnlyList<IEnrichedAssetEntity>> GetReferencedAssetsAsync(IJsonValue value)
@ -85,13 +90,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return LoadManyAsync(dataLoader, ids);
}
public Task<IReadOnlyList<IContentEntity>> GetReferencedContentsAsync(IJsonValue value)
public Task<IReadOnlyList<IEnrichedContentEntity>> GetReferencedContentsAsync(IJsonValue value)
{
var ids = ParseIds(value);
if (ids == null)
{
return Task.FromResult<IReadOnlyList<IContentEntity>>(EmptyContents);
return Task.FromResult<IReadOnlyList<IEnrichedContentEntity>>(EmptyContents);
}
var dataLoader = GetContentsLoader();
@ -110,9 +115,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
});
}
private IDataLoader<Guid, IContentEntity> GetContentsLoader()
private IDataLoader<Guid, IEnrichedContentEntity> GetContentsLoader()
{
return dataLoaderContextAccessor.Context.GetOrAddBatchLoader<Guid, IContentEntity>(nameof(GetContentsLoader),
return dataLoaderContextAccessor.Context.GetOrAddBatchLoader<Guid, IEnrichedContentEntity>(nameof(GetContentsLoader),
async batch =>
{
var result = await GetReferencedContentsAsync(new List<Guid>(batch));
@ -123,7 +128,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
private static async Task<IReadOnlyList<T>> LoadManyAsync<TKey, T>(IDataLoader<TKey, T> dataLoader, ICollection<TKey> keys) where T : class
{
var contents = await Task.WhenAll(keys.Select(dataLoader.LoadAsync));
var contents = await Task.WhenAll(keys.Select(x => dataLoader.LoadAsync(x).GetResultAsync()));
return contents.NotNull().ToList();
}

52
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs

@ -15,7 +15,6 @@ using GraphQL.Types;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils;
using Squidex.Domain.Apps.Entities.Schemas;
@ -58,6 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
graphQLSchema = BuildSchema(this, pageSizeContents, pageSizeAssets, allSchemas);
graphQLSchema.RegisterValueConverter(JsonConverter.Instance);
graphQLSchema.RegisterValueConverter(InstantConverter.Instance);
InitializeContentTypes();
}
@ -87,48 +87,19 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
var schema = new GraphQLSchema
{
Query = new AppQueriesGraphType(model, pageSizeContents, pageSizeAssets, schemas)
Query =
new AppQueriesGraphType(
model,
pageSizeContents,
pageSizeAssets,
schemas
),
Mutation = new AppMutationsGraphType(model, schemas)
};
return schema;
}
public IFieldResolver ResolveAssetUrl()
{
var resolver = new FuncFieldResolver<IAssetEntity, object>(c =>
{
var context = (GraphQLExecutionContext)c.UserContext;
return context.UrlGenerator.AssetContent(c.Source.Id);
});
return resolver;
}
public IFieldResolver ResolveAssetSourceUrl()
{
var resolver = new FuncFieldResolver<IAssetEntity, object?>(c =>
{
var context = (GraphQLExecutionContext)c.UserContext;
return context.UrlGenerator.AssetSource(c.Source.Id, c.Source.FileVersion);
});
return resolver;
}
public IFieldResolver ResolveAssetThumbnailUrl()
{
var resolver = new FuncFieldResolver<IAssetEntity, object?>(c =>
{
var context = (GraphQLExecutionContext)c.UserContext;
return context.UrlGenerator.AssetThumbnail(c.Source.Id, c.Source.Type);
});
return resolver;
}
public IFieldResolver ResolveContentUrl(ISchemaEntity schema)
{
var resolver = new FuncFieldResolver<IContentEntity, object>(c =>
@ -146,6 +117,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return partitionResolver(key);
}
public IGraphType? GetInputGraphType(ISchemaEntity schema, IField field, string fieldName)
{
return field.Accept(new InputFieldVisitor(schema, this, fieldName));
}
public (IGraphType?, ValueResolver?, QueryArguments?) GetGraphType(ISchemaEntity schema, IField field, string fieldName)
{
return field.Accept(new QueryGraphTypeVisitor(schema, contentTypes, this, assetListType, fieldName));

11
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/IGraphModel.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
@ -21,18 +20,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
IFieldPartitioning ResolvePartition(Partitioning key);
IFieldResolver ResolveAssetUrl();
IFieldResolver ResolveAssetSourceUrl();
IFieldResolver ResolveAssetThumbnailUrl();
IFieldResolver ResolveContentUrl(ISchemaEntity schema);
IObjectGraphType GetAssetType();
IObjectGraphType GetContentType(Guid schemaId);
IGraphType? GetInputGraphType(ISchemaEntity schema, IField field, string fieldName);
(IGraphType?, ValueResolver?, QueryArguments?) GetGraphType(ISchemaEntity schema, IField field, string fieldName);
}
}

9
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Middlewares.cs

@ -8,6 +8,7 @@
using System;
using GraphQL;
using GraphQL.Instrumentation;
using GraphQL.Types;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log;
@ -15,11 +16,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
public static class Middlewares
{
public static Func<FieldMiddlewareDelegate, FieldMiddlewareDelegate> Logging(ISemanticLog log)
public static Func<ISchema, FieldMiddlewareDelegate, FieldMiddlewareDelegate> Logging(ISemanticLog log)
{
Guard.NotNull(log, nameof(log));
return next =>
return (_, next) =>
{
return async context =>
{
@ -40,9 +41,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
};
}
public static Func<FieldMiddlewareDelegate, FieldMiddlewareDelegate> Errors()
public static Func<ISchema, FieldMiddlewareDelegate, FieldMiddlewareDelegate> Errors()
{
return next =>
return (_, next) =>
{
return async context =>
{

20
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs

@ -14,18 +14,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public static class AllTypes
{
public const string PathName = "path";
public static readonly Type None = typeof(NoopGraphType);
public static readonly Type NonNullTagsType = typeof(NonNullGraphType<ListGraphType<NonNullGraphType<StringGraphType>>>);
public static readonly IGraphType Int = new IntGraphType();
public static readonly IGraphType Guid = new GuidGraphType2();
public static readonly IGraphType Long = new LongGraphType();
public static readonly IGraphType Guid = new GuidGraphType();
public static readonly IGraphType Date = new InstantGraphType();
public static readonly IGraphType Tags = new ListGraphType(new NonNullGraphType(new StringGraphType()));
public static readonly IGraphType Json = new JsonGraphType();
public static readonly IGraphType Float = new FloatGraphType();
@ -36,8 +38,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
public static readonly IGraphType AssetType = new EnumerationGraphType<AssetType>();
public static readonly IGraphType References = new ListGraphType(new NonNullGraphType(new StringGraphType()));
public static readonly IGraphType NonNullInt = new NonNullGraphType(Int);
public static readonly IGraphType NonNullLong = new NonNullGraphType(Long);
public static readonly IGraphType NonNullGuid = new NonNullGraphType(Guid);
public static readonly IGraphType NonNullDate = new NonNullGraphType(Date);
@ -63,13 +69,5 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
public static readonly IGraphType NoopTags = new NoopGraphType("TagsScalar");
public static readonly IGraphType NoopGeolocation = new NoopGraphType("GeolocationScalar");
public static readonly QueryArguments PathArguments = new QueryArguments(new QueryArgument(None)
{
Name = PathName,
Description = "The path to the json value",
DefaultValue = null,
ResolvedType = String
});
}
}

100
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppMutationsGraphType.cs

@ -0,0 +1,100 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using GraphQL.Types;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed class AppMutationsGraphType : ObjectGraphType
{
public AppMutationsGraphType(IGraphModel model, IEnumerable<ISchemaEntity> schemas)
{
foreach (var schema in schemas)
{
var schemaId = schema.NamedId();
var schemaType = schema.TypeName();
var schemaName = schema.DisplayName();
var contentType = model.GetContentType(schema.Id);
var inputType = new ContentDataInputGraphType(schema, schemaName, schemaType, model);
AddContentCreate(schemaId, schemaType, schemaName, inputType, contentType);
AddContentUpdate(schemaType, schemaName, inputType, contentType);
AddContentPatch(schemaType, schemaName, inputType, contentType);
AddContentChangeStatus(schemaType, schemaName, contentType);
AddContentDelete(schemaType, schemaName);
}
Description = "The app mutations.";
}
private void AddContentCreate(NamedId<Guid> schemaId, string schemaType, string schemaName, ContentDataInputGraphType inputType, IGraphType contentType)
{
AddField(new FieldType
{
Name = $"create{schemaType}Content",
Arguments = ContentActions.Create.Arguments(inputType),
ResolvedType = contentType,
Resolver = ContentActions.Create.Resolver(schemaId),
Description = $"Creates an {schemaName} content."
});
}
private void AddContentUpdate(string schemaType, string schemaName, ContentDataInputGraphType inputType, IGraphType contentType)
{
AddField(new FieldType
{
Name = $"update{schemaType}Content",
Arguments = ContentActions.UpdateOrPatch.Arguments(inputType),
ResolvedType = contentType,
Resolver = ContentActions.UpdateOrPatch.Update,
Description = $"Update an {schemaName} content by id."
});
}
private void AddContentPatch(string schemaType, string schemaName, ContentDataInputGraphType inputType, IGraphType contentType)
{
AddField(new FieldType
{
Name = $"patch{schemaType}Content",
Arguments = ContentActions.UpdateOrPatch.Arguments(inputType),
ResolvedType = contentType,
Resolver = ContentActions.UpdateOrPatch.Patch,
Description = $"Patch an {schemaName} content by id."
});
}
private void AddContentChangeStatus(string schemaType, string schemaName, IGraphType contentType)
{
AddField(new FieldType
{
Name = $"publish{schemaType}Content",
Arguments = ContentActions.ChangeStatus.Arguments,
ResolvedType = contentType,
Resolver = ContentActions.ChangeStatus.Resolver,
Description = $"Publish a {schemaName} content."
});
}
private void AddContentDelete(string schemaType, string schemaName)
{
AddField(new FieldType
{
Name = $"delete{schemaType}Content",
Arguments = ContentActions.Delete.Arguments,
ResolvedType = EntitySavedGraphType.NonNull,
Resolver = ContentActions.Delete.Resolver,
Description = $"Delete an {schemaName} content."
});
}
}
}

187
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppQueriesGraphType.cs

@ -7,9 +7,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Entities.Schemas;
@ -44,14 +41,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
AddField(new FieldType
{
Name = "findAsset",
Arguments = CreateAssetFindArguments(),
Arguments = AssetActions.Find.Arguments,
ResolvedType = assetType,
Resolver = ResolveAsync((c, e) =>
{
var assetId = c.GetArgument<Guid>("id");
return e.FindAssetAsync(assetId);
}),
Resolver = AssetActions.Find.Resolver,
Description = "Find an asset by id."
});
}
@ -61,204 +53,57 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
AddField(new FieldType
{
Name = $"find{schemaType}Content",
Arguments = CreateContentFindArguments(schemaName),
Arguments = ContentActions.Find.Arguments,
ResolvedType = contentType,
Resolver = ResolveAsync((c, e) =>
{
var contentId = c.GetArgument<Guid>("id");
return e.FindContentAsync(contentId);
}),
Resolver = ContentActions.Find.Resolver,
Description = $"Find an {schemaName} content by id."
});
}
private void AddAssetsQueries(IGraphType assetType, int pageSize)
{
var resolver = AssetActions.Query.Resolver;
AddField(new FieldType
{
Name = "queryAssets",
Arguments = CreateAssetQueryArguments(pageSize),
Arguments = AssetActions.Query.Arguments(pageSize),
ResolvedType = new ListGraphType(new NonNullGraphType(assetType)),
Resolver = ResolveAsync((c, e) =>
{
var assetQuery = BuildODataQuery(c);
return e.QueryAssetsAsync(assetQuery);
}),
Resolver = resolver,
Description = "Get assets."
});
AddField(new FieldType
{
Name = "queryAssetsWithTotal",
Arguments = CreateAssetQueryArguments(pageSize),
Arguments = AssetActions.Query.Arguments(pageSize),
ResolvedType = new AssetsResultGraphType(assetType),
Resolver = ResolveAsync((c, e) =>
{
var assetQuery = BuildODataQuery(c);
return e.QueryAssetsAsync(assetQuery);
}),
Resolver = resolver,
Description = "Get assets and total count."
});
}
private void AddContentQueries(Guid schemaId, string schemaType, string schemaName, IGraphType contentType, int pageSize)
{
var resolver = ContentActions.Query.Resolver(schemaId);
AddField(new FieldType
{
Name = $"query{schemaType}Contents",
Arguments = CreateContentQueryArguments(pageSize),
Arguments = ContentActions.Query.Arguments(pageSize),
ResolvedType = new ListGraphType(new NonNullGraphType(contentType)),
Resolver = ResolveAsync((c, e) =>
{
var contentQuery = BuildODataQuery(c);
return e.QueryContentsAsync(schemaId.ToString(), contentQuery);
}),
Resolver = resolver,
Description = $"Query {schemaName} content items."
});
AddField(new FieldType
{
Name = $"query{schemaType}ContentsWithTotal",
Arguments = CreateContentQueryArguments(pageSize),
Arguments = ContentActions.Query.Arguments(pageSize),
ResolvedType = new ContentsResultGraphType(schemaType, schemaName, contentType),
Resolver = ResolveAsync((c, e) =>
{
var contentQuery = BuildODataQuery(c);
return e.QueryContentsAsync(schemaId.ToString(), contentQuery);
}),
Resolver = resolver,
Description = $"Query {schemaName} content items with total count."
});
}
private static QueryArguments CreateAssetFindArguments()
{
return new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "id",
Description = "The id of the asset (GUID).",
DefaultValue = string.Empty,
ResolvedType = AllTypes.NonNullGuid
}
};
}
private static QueryArguments CreateContentFindArguments(string schemaName)
{
return new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "id",
Description = $"The id of the {schemaName} content (GUID)",
DefaultValue = string.Empty,
ResolvedType = AllTypes.NonNullGuid
}
};
}
private static QueryArguments CreateAssetQueryArguments(int pageSize)
{
return new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "top",
Description = $"Optional number of assets to take (Default: {pageSize}).",
DefaultValue = pageSize,
ResolvedType = AllTypes.Int
},
new QueryArgument(AllTypes.None)
{
Name = "skip",
Description = "Optional number of assets to skip.",
DefaultValue = 0,
ResolvedType = AllTypes.Int
},
new QueryArgument(AllTypes.None)
{
Name = "filter",
Description = "Optional OData filter.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
},
new QueryArgument(AllTypes.None)
{
Name = "orderby",
Description = "Optional OData order definition.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
}
};
}
private static QueryArguments CreateContentQueryArguments(int pageSize)
{
return new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "top",
Description = $"Optional number of contents to take (Default: {pageSize}).",
DefaultValue = pageSize,
ResolvedType = AllTypes.Int
},
new QueryArgument(AllTypes.None)
{
Name = "skip",
Description = "Optional number of contents to skip.",
DefaultValue = 0,
ResolvedType = AllTypes.Int
},
new QueryArgument(AllTypes.None)
{
Name = "filter",
Description = "Optional OData filter.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
},
new QueryArgument(AllTypes.None)
{
Name = "search",
Description = "Optional OData full text search.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
},
new QueryArgument(AllTypes.None)
{
Name = "orderby",
Description = "Optional OData order definition.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
}
};
}
private static string BuildODataQuery(ResolveFieldContext c)
{
var odataQuery = "?" +
string.Join("&",
c.Arguments
.Select(x => new { x.Key, Value = x.Value.ToString() }).Where(x => !string.IsNullOrWhiteSpace(x.Value))
.Select(x => $"${x.Key}={x.Value}"));
return odataQuery;
}
private static IFieldResolver ResolveAsync<T>(Func<ResolveFieldContext, GraphQLExecutionContext, Task<T>> action)
{
return new FuncFieldResolver<Task<T>>(c =>
{
var e = (GraphQLExecutionContext)c.UserContext;
return action(c, e);
});
}
}
}

112
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetActions.cs

@ -0,0 +1,112 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Entities.Assets;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public static class AssetActions
{
public static class Metadata
{
public static readonly QueryArguments Arguments = new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "path",
Description = "The path to the json value",
DefaultValue = null,
ResolvedType = AllTypes.String
}
};
public static readonly IFieldResolver Resolver = new FuncFieldResolver<IEnrichedAssetEntity, object?>(c =>
{
if (c.Arguments.TryGetValue("path", out var path))
{
c.Source.Metadata.TryGetByPath(path as string, out var result);
return result;
}
return c.Source.Metadata;
});
}
public static class Find
{
public static readonly QueryArguments Arguments = new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "id",
Description = "The id of the asset (GUID).",
DefaultValue = string.Empty,
ResolvedType = AllTypes.NonNullGuid
}
};
public static readonly IFieldResolver Resolver = new FuncFieldResolver<object?>(c =>
{
var id = c.GetArgument<Guid>("id");
return ((GraphQLExecutionContext)c.UserContext).FindAssetAsync(id);
});
}
public static class Query
{
private static QueryArguments? resolver;
public static QueryArguments Arguments(int pageSize)
{
return resolver ??= new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "top",
Description = $"Optional number of assets to take (Default: {pageSize}).",
DefaultValue = pageSize,
ResolvedType = AllTypes.Int
},
new QueryArgument(AllTypes.None)
{
Name = "skip",
Description = "Optional number of assets to skip.",
DefaultValue = 0,
ResolvedType = AllTypes.Int
},
new QueryArgument(AllTypes.None)
{
Name = "filter",
Description = "Optional OData filter.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
},
new QueryArgument(AllTypes.None)
{
Name = "orderby",
Description = "Optional OData order definition.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
}
};
}
public static readonly IFieldResolver Resolver = new FuncFieldResolver<object?>(c =>
{
var query = c.BuildODataQuery();
return ((GraphQLExecutionContext)c.UserContext).QueryAssetsAsync(query);
});
}
}
}

71
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs

@ -5,12 +5,8 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Assets;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
@ -24,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "id",
ResolvedType = AllTypes.NonNullGuid,
Resolver = Resolve(x => x.Id.ToString()),
Resolver = EntityResolvers.Id,
Description = "The id of the asset."
});
@ -32,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "version",
ResolvedType = AllTypes.NonNullInt,
Resolver = Resolve(x => x.Version),
Resolver = EntityResolvers.Version,
Description = "The version of the asset."
});
@ -40,7 +36,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "created",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.Created),
Resolver = EntityResolvers.Created,
Description = "The date and time when the asset has been created."
});
@ -48,7 +44,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "createdBy",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.CreatedBy.ToString()),
Resolver = EntityResolvers.CreatedBy,
Description = "The user that has created the asset."
});
@ -56,7 +52,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "lastModified",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.LastModified),
Resolver = EntityResolvers.LastModified,
Description = "The date and time when the asset has been modified last."
});
@ -64,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "lastModifiedBy",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.LastModifiedBy.ToString()),
Resolver = EntityResolvers.LastModifiedBy,
Description = "The user that has updated the asset last."
});
@ -72,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "mimeType",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.MimeType),
Resolver = AssetResolvers.MimeType,
Description = "The mime type."
});
@ -80,7 +76,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "url",
ResolvedType = AllTypes.NonNullString,
Resolver = model.ResolveAssetUrl(),
Resolver = AssetResolvers.Url,
Description = "The url to the asset."
});
@ -88,7 +84,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "thumbnailUrl",
ResolvedType = AllTypes.String,
Resolver = model.ResolveAssetThumbnailUrl(),
Resolver = AssetResolvers.ThumbnailUrl,
Description = "The thumbnail url to the asset."
});
@ -96,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "fileName",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.FileName),
Resolver = AssetResolvers.FileName,
Description = "The file name."
});
@ -104,7 +100,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "fileHash",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.FileHash),
Resolver = AssetResolvers.FileHash,
Description = "The hash of the file. Can be null for old files."
});
@ -112,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "fileType",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.FileName!.FileType()),
Resolver = AssetResolvers.FileType,
Description = "The file type."
});
@ -120,7 +116,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "fileSize",
ResolvedType = AllTypes.NonNullInt,
Resolver = Resolve(x => x.FileSize),
Resolver = AssetResolvers.FileSize,
Description = "The size of the file in bytes."
});
@ -128,7 +124,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "fileVersion",
ResolvedType = AllTypes.NonNullInt,
Resolver = Resolve(x => x.FileVersion),
Resolver = AssetResolvers.FileVersion,
Description = "The version of the file."
});
@ -136,7 +132,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "slug",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.Slug),
Resolver = AssetResolvers.Slug,
Description = "The file name as slug."
});
@ -144,7 +140,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "isProtected",
ResolvedType = AllTypes.NonNullBoolean,
Resolver = Resolve(x => x.IsProtected),
Resolver = AssetResolvers.IsProtected,
Description = "True, when the asset is not public."
});
@ -152,7 +148,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "isImage",
ResolvedType = AllTypes.NonNullBoolean,
Resolver = Resolve(x => x.Type == AssetType.Image),
Resolver = AssetResolvers.IsImage,
Description = "Determines if the uploaded file is an image.",
DeprecationReason = "Use 'type' field instead."
});
@ -161,7 +157,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "pixelWidth",
ResolvedType = AllTypes.Int,
Resolver = Resolve(x => x.Metadata.GetPixelWidth()),
Resolver = AssetResolvers.PixelWidth,
Description = "The width of the image in pixels if the asset is an image.",
DeprecationReason = "Use 'metadata' field instead."
});
@ -170,7 +166,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "pixelHeight",
ResolvedType = AllTypes.Int,
Resolver = Resolve(x => x.Metadata.GetPixelHeight()),
Resolver = AssetResolvers.PixelHeight,
Description = "The height of the image in pixels if the asset is an image.",
DeprecationReason = "Use 'metadata' field instead."
});
@ -179,7 +175,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "type",
ResolvedType = AllTypes.NonNullAssetType,
Resolver = Resolve(x => x.Type),
Resolver = AssetResolvers.Type,
Description = "The type of the image."
});
@ -187,7 +183,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "metadataText",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.MetadataText),
Resolver = AssetResolvers.MetadataText,
Description = "The text representation of the metadata."
});
@ -195,7 +191,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "tags",
ResolvedType = null,
Resolver = Resolve(x => x.TagNames),
Resolver = AssetResolvers.Tags,
Description = "The asset tags.",
Type = AllTypes.NonNullTagsType
});
@ -203,9 +199,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
AddField(new FieldType
{
Name = "metadata",
Arguments = AllTypes.PathArguments,
Arguments = AssetActions.Metadata.Arguments,
ResolvedType = AllTypes.NoopJson,
Resolver = ResolveMetadata(),
Resolver = AssetActions.Metadata.Resolver,
Description = "The asset metadata."
});
@ -215,29 +211,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "sourceUrl",
ResolvedType = AllTypes.NonNullString,
Resolver = model.ResolveAssetSourceUrl(),
Resolver = AssetResolvers.SourceUrl,
Description = "The source url of the asset."
});
}
Description = "An asset";
}
private static IFieldResolver ResolveMetadata()
{
return new FuncFieldResolver<IEnrichedAssetEntity, object?>(c =>
{
var path = c.Arguments.GetOrDefault(AllTypes.PathName);
c.Source.Metadata.TryGetByPath(path as string, out var result);
return result;
});
}
private static IFieldResolver Resolve(Func<IEnrichedAssetEntity, object?> action)
{
return new FuncFieldResolver<IEnrichedAssetEntity, object?>(c => action(c.Source));
}
}
}

66
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetResolvers.cs

@ -0,0 +1,66 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL;
using GraphQL.Resolvers;
using Squidex.Domain.Apps.Core.Assets;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public static class AssetResolvers
{
public static readonly IFieldResolver Url = Resolve((asset, _, context) =>
{
return context.UrlGenerator.AssetContent(asset.Id);
});
public static readonly IFieldResolver SourceUrl = Resolve((asset, _, context) =>
{
return context.UrlGenerator.AssetSource(asset.Id, asset.FileVersion);
});
public static readonly IFieldResolver ThumbnailUrl = Resolve((asset, _, context) =>
{
return context.UrlGenerator.AssetThumbnail(asset.Id, asset.Type);
});
public static readonly IFieldResolver FileHash = Resolve(x => x.FileHash);
public static readonly IFieldResolver FileName = Resolve(x => x.FileName);
public static readonly IFieldResolver FileSize = Resolve(x => x.FileSize);
public static readonly IFieldResolver FileType = Resolve(x => x.FileName.FileType());
public static readonly IFieldResolver FileVersion = Resolve(x => x.FileVersion);
public static readonly IFieldResolver IsImage = Resolve(x => x.Type == AssetType.Image);
public static readonly IFieldResolver IsProtected = Resolve(x => x.IsProtected);
public static readonly IFieldResolver ListTotal = ResolveList(x => x.Total);
public static readonly IFieldResolver ListItems = ResolveList(x => x);
public static readonly IFieldResolver MetadataText = Resolve(x => x.MetadataText);
public static readonly IFieldResolver MimeType = Resolve(x => x.MimeType);
public static readonly IFieldResolver PixelHeight = Resolve(x => x.Metadata.GetPixelHeight());
public static readonly IFieldResolver PixelWidth = Resolve(x => x.Metadata.GetPixelWidth());
public static readonly IFieldResolver Slug = Resolve(x => x.Slug);
public static readonly IFieldResolver Tags = Resolve(x => x.TagNames);
public static readonly IFieldResolver Type = Resolve(x => x.Type);
private static IFieldResolver Resolve<T>(Func<IEnrichedAssetEntity, IResolveFieldContext, GraphQLExecutionContext, T> action)
{
return new FuncFieldResolver<IEnrichedAssetEntity, object?>(c => action(c.Source, c, (GraphQLExecutionContext)c.UserContext));
}
private static IFieldResolver Resolve<T>(Func<IEnrichedAssetEntity, T> action)
{
return new FuncFieldResolver<IEnrichedAssetEntity, object?>(c => action(c.Source));
}
private static IFieldResolver ResolveList<T>(Func<IResultList<IEnrichedAssetEntity>, T> action)
{
return new FuncFieldResolver<IResultList<IEnrichedAssetEntity>, object?>(c => action(c.Source));
}
}
}

11
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetsResultGraphType.cs

@ -5,8 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure;
@ -23,24 +21,19 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "total",
ResolvedType = AllTypes.Int,
Resolver = Resolve(x => x.Total),
Resolver = AssetResolvers.ListTotal,
Description = "The total count of assets."
});
AddField(new FieldType
{
Name = "items",
Resolver = Resolve(x => x),
Resolver = AssetResolvers.ListItems,
ResolvedType = new ListGraphType(new NonNullGraphType(assetType)),
Description = "The assets."
});
Description = "List of assets and total count of assets.";
}
private static IFieldResolver Resolve(Func<IResultList<IAssetEntity>, object> action)
{
return new FuncFieldResolver<IResultList<IAssetEntity>, object>(c => action(c.Source));
}
}
}

336
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentActions.cs

@ -0,0 +1,336 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.Resolvers;
using GraphQL.Types;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Validation;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public static class ContentActions
{
public static class Json
{
public static readonly QueryArguments Arguments = new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "path",
Description = "The path to the json value",
DefaultValue = null,
ResolvedType = AllTypes.String
}
};
public static readonly ValueResolver Resolver = new ValueResolver((value, c) =>
{
if (c.Arguments.TryGetValue("path", out var p) && p is string path)
{
value.TryGetByPath(path, out var result);
return result!;
}
return value;
});
}
public static readonly QueryArguments JsonPath = new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "path",
Description = "The path to the json value",
DefaultValue = null,
ResolvedType = AllTypes.String
}
};
public static class Find
{
public static readonly QueryArguments Arguments = new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "id",
Description = "The id of the content (GUID).",
DefaultValue = string.Empty,
ResolvedType = AllTypes.NonNullGuid
}
};
public static readonly IFieldResolver Resolver = new FuncFieldResolver<object?>(c =>
{
var id = c.GetArgument<Guid>("id");
return ((GraphQLExecutionContext)c.UserContext).FindContentAsync(id);
});
}
public static class Query
{
private static QueryArguments? arguments;
public static QueryArguments Arguments(int pageSize)
{
return arguments ??= new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "top",
Description = $"Optional number of contents to take (Default: {pageSize}).",
DefaultValue = pageSize,
ResolvedType = AllTypes.Int
},
new QueryArgument(AllTypes.None)
{
Name = "skip",
Description = "Optional number of contents to skip.",
DefaultValue = 0,
ResolvedType = AllTypes.Int
},
new QueryArgument(AllTypes.None)
{
Name = "filter",
Description = "Optional OData filter.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
},
new QueryArgument(AllTypes.None)
{
Name = "orderby",
Description = "Optional OData order definition.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
},
new QueryArgument(AllTypes.None)
{
Name = "search",
Description = "Optional OData full text search.",
DefaultValue = string.Empty,
ResolvedType = AllTypes.String
},
};
}
public static IFieldResolver Resolver(Guid schemaId)
{
var schemaIdValue = schemaId.ToString();
return new FuncFieldResolver<object?>(c =>
{
var query = c.BuildODataQuery();
return ((GraphQLExecutionContext)c.UserContext).QueryContentsAsync(schemaIdValue, query);
});
}
}
public static class Create
{
public static QueryArguments Arguments(IGraphType inputType)
{
return new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "data",
Description = "The data for the content.",
DefaultValue = null,
ResolvedType = new NonNullGraphType(inputType),
},
new QueryArgument(AllTypes.None)
{
Name = "publish",
Description = "Set to true to autopublish content.",
DefaultValue = false,
ResolvedType = AllTypes.Boolean
}
};
}
public static IFieldResolver Resolver(NamedId<Guid> schemaId)
{
return ResolveAsync<IEnrichedContentEntity>(c =>
{
var contentPublish = c.GetArgument<bool>("publish");
var contentData = GetContentData(c);
return new CreateContent { SchemaId = schemaId, Data = contentData, Publish = contentPublish };
});
}
}
public static class UpdateOrPatch
{
public static QueryArguments Arguments(IGraphType inputType)
{
return new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "id",
Description = "The id of the content (GUID)",
DefaultValue = string.Empty,
ResolvedType = AllTypes.NonNullGuid
},
new QueryArgument(AllTypes.None)
{
Name = "data",
Description = "The data for the content.",
DefaultValue = null,
ResolvedType = new NonNullGraphType(inputType),
},
new QueryArgument(AllTypes.None)
{
Name = "expectedVersion",
Description = "The expected version",
DefaultValue = EtagVersion.Any,
ResolvedType = AllTypes.Int
}
};
}
public static readonly IFieldResolver Update = ResolveAsync<IEnrichedContentEntity>(c =>
{
var contentId = c.GetArgument<Guid>("id");
var contentData = GetContentData(c);
return new UpdateContent { ContentId = contentId, Data = contentData };
});
public static readonly IFieldResolver Patch = ResolveAsync<IEnrichedContentEntity>(c =>
{
var contentId = c.GetArgument<Guid>("id");
var contentData = GetContentData(c);
return new PatchContent { ContentId = contentId, Data = contentData };
});
}
public static class ChangeStatus
{
public static readonly QueryArguments Arguments = new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "id",
Description = "The id of the content (GUID)",
DefaultValue = string.Empty,
ResolvedType = AllTypes.NonNullGuid
},
new QueryArgument(AllTypes.None)
{
Name = "status",
Description = "The new status",
DefaultValue = string.Empty,
ResolvedType = AllTypes.NonNullString
},
new QueryArgument(AllTypes.None)
{
Name = "dueTime",
Description = "When to change the status",
DefaultValue = EtagVersion.Any,
ResolvedType = AllTypes.Date
},
new QueryArgument(AllTypes.None)
{
Name = "expectedVersion",
Description = "The expected version",
DefaultValue = EtagVersion.Any,
ResolvedType = AllTypes.Int
}
};
public static readonly IFieldResolver Resolver = ResolveAsync<IEnrichedContentEntity>(c =>
{
var contentId = c.GetArgument<Guid>("id");
var contentStatus = new Status(c.GetArgument<string>("status"));
var contentDueTime = c.GetArgument<Instant?>("dueTime");
return new ChangeContentStatus { ContentId = contentId, Status = contentStatus, DueTime = contentDueTime };
});
}
public static class Delete
{
public static readonly QueryArguments Arguments = new QueryArguments
{
new QueryArgument(AllTypes.None)
{
Name = "id",
Description = "The id of the content (GUID)",
DefaultValue = string.Empty,
ResolvedType = AllTypes.NonNullGuid
},
new QueryArgument(AllTypes.None)
{
Name = "expectedVersion",
Description = "The expected version",
DefaultValue = EtagVersion.Any,
ResolvedType = AllTypes.Int
}
};
public static readonly IFieldResolver Resolver = ResolveAsync<EntitySavedResult>(c =>
{
var contentId = c.GetArgument<Guid>("id");
return new DeleteContent { ContentId = contentId };
});
}
private static NamedContentData GetContentData(IResolveFieldContext c)
{
var source = c.GetArgument<IDictionary<string, object>>("data");
return source.ToNamedContentData((IComplexGraphType)c.FieldDefinition.Arguments.Find("data").Flatten());
}
private static IFieldResolver ResolveAsync<T>(Func<IResolveFieldContext, SquidexCommand> action)
{
return new FuncFieldResolver<Task<T>>(async c =>
{
var e = (GraphQLExecutionContext)c.UserContext;
try
{
var command = action(c);
command.ExpectedVersion = c.GetArgument("expectedVersion", EtagVersion.Any);
var commandContext = await e.CommandBus.PublishAsync(command);
return commandContext.Result<T>();
}
catch (ValidationException ex)
{
c.Errors.Add(new ExecutionError(ex.Message));
throw;
}
catch (DomainException ex)
{
c.Errors.Add(new ExecutionError(ex.Message));
throw;
}
});
}
}
}

20
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataFlatGraphType.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
@ -28,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = fieldName,
Arguments = args,
Resolver = PartitionResolver(valueResolver, field.Name),
Resolver = ContentResolvers.FlatPartition(valueResolver, field.Name),
ResolvedType = resolvedType,
Description = field.RawProperties.Hints
});
@ -37,22 +36,5 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Description = $"The structure of the flat {schemaName} data type.";
}
private static FuncFieldResolver<object?> PartitionResolver(ValueResolver valueResolver, string key)
{
return new FuncFieldResolver<object?>(c =>
{
var source = (FlatContentData)c.Source;
if (source.TryGetValue(key, out var value) && value != null)
{
return valueResolver(value, c);
}
else
{
return null;
}
});
}
}
}

35
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs

@ -5,14 +5,10 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
@ -24,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
foreach (var (field, fieldName, typeName) in schema.SchemaDef.Fields.SafeFields())
{
var (resolvedType, valueResolver, args) = model.GetGraphType(schema, field, fieldName);
var (resolvedType, valueResolver, args) = model.GetGraphType(schema, field, typeName);
if (valueResolver != null)
{
@ -43,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = partitionKey.EscapePartition(),
Arguments = args,
Resolver = PartitionResolver(valueResolver, partitionKey),
Resolver = ContentResolvers.Partition(valueResolver, partitionKey),
ResolvedType = resolvedType,
Description = field.RawProperties.Hints
});
@ -54,7 +50,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
AddField(new FieldType
{
Name = fieldName,
Resolver = FieldResolver(field),
Resolver = ContentResolvers.Field(field),
ResolvedType = fieldGraphType,
Description = $"The {displayName} field."
});
@ -63,30 +59,5 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Description = $"The structure of the {schemaName} data type.";
}
private static FuncFieldResolver<object?> PartitionResolver(ValueResolver valueResolver, string key)
{
return new FuncFieldResolver<object?>(c =>
{
var source = (ContentFieldData)c.Source;
if (source.TryGetValue(key, out var value) && value != null)
{
return valueResolver(value, c);
}
else
{
return null;
}
});
}
private static FuncFieldResolver<NamedContentData, IReadOnlyDictionary<string, IJsonValue>?> FieldResolver(RootField field)
{
return new FuncFieldResolver<NamedContentData, IReadOnlyDictionary<string, IJsonValue>?>(c =>
{
return c.Source?.GetOrDefault(field.Name);
});
}
}
}

62
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataInputGraphType.cs

@ -0,0 +1,62 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Linq;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed class ContentDataInputGraphType : InputObjectGraphType
{
public ContentDataInputGraphType(ISchemaEntity schema, string schemaName, string schemaType, IGraphModel model)
{
Name = $"{schemaType}DataInputDto";
foreach (var (field, fieldName, typeName) in schema.SchemaDef.Fields.SafeFields().Where(x => x.Field.IsForApi(true)))
{
var resolvedType = model.GetInputGraphType(schema, field, typeName);
if (resolvedType != null)
{
var displayName = field.DisplayName();
var fieldGraphType = new InputObjectGraphType
{
Name = $"{schemaType}Data{typeName}InputDto"
};
var partitioning = model.ResolvePartition(field.Partitioning);
foreach (var partitionKey in partitioning.AllKeys)
{
fieldGraphType.AddField(new FieldType
{
Name = partitionKey.EscapePartition(),
Resolver = null,
ResolvedType = resolvedType,
Description = field.RawProperties.Hints
}).WithSourceName( partitionKey);
}
fieldGraphType.Description = $"The structure of the {displayName} field of the {schemaName} content input type.";
AddField(new FieldType
{
Name = fieldName,
Resolver = null,
ResolvedType = fieldGraphType,
Description = $"The {displayName} field."
}).WithSourceName(field.Name);
}
}
Description = $"The structure of the {schemaName} data input type.";
}
}
}

41
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs

@ -5,12 +5,8 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Entities.Schemas;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
@ -34,7 +30,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "id",
ResolvedType = AllTypes.NonNullGuid,
Resolver = Resolve(x => x.Id),
Resolver = EntityResolvers.Id,
Description = $"The id of the {schemaName} content."
});
@ -42,7 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "version",
ResolvedType = AllTypes.NonNullInt,
Resolver = Resolve(x => x.Version),
Resolver = EntityResolvers.Version,
Description = $"The version of the {schemaName} content."
});
@ -50,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "created",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.Created),
Resolver = EntityResolvers.Created,
Description = $"The date and time when the {schemaName} content has been created."
});
@ -58,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "createdBy",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.CreatedBy.ToString()),
Resolver = EntityResolvers.CreatedBy,
Description = $"The user that has created the {schemaName} content."
});
@ -66,7 +62,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "lastModified",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.LastModified),
Resolver = EntityResolvers.LastModified,
Description = $"The date and time when the {schemaName} content has been modified last."
});
@ -74,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "lastModifiedBy",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.LastModifiedBy.ToString()),
Resolver = EntityResolvers.LastModifiedBy,
Description = $"The user that has updated the {schemaName} content last."
});
@ -82,7 +78,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "status",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.Status.Name.ToUpperInvariant()),
Resolver = ContentResolvers.Status,
Description = $"The the status of the {schemaName} content."
});
@ -90,7 +86,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "statusColor",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.StatusColor),
Resolver = ContentResolvers.StatusColor,
Description = $"The color status of the {schemaName} content."
});
@ -112,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "url",
ResolvedType = AllTypes.NonNullString,
Resolver = model.ResolveContentUrl(schema),
Resolver = ContentResolvers.Url,
Description = $"The url to the the {schemaName} content."
});
@ -124,7 +120,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "data",
ResolvedType = new NonNullGraphType(contentDataType),
Resolver = Resolve(x => x.Data),
Resolver = ContentResolvers.Data,
Description = $"The data of the {schemaName} content."
});
}
@ -137,25 +133,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "flatData",
ResolvedType = new NonNullGraphType(contentDataTypeFlat),
Resolver = ResolveFlat(x => x.Data),
Resolver = ContentResolvers.FlatData,
Description = $"The flat data of the {schemaName} content."
});
}
}
private static IFieldResolver Resolve(Func<IEnrichedContentEntity, object?> action)
{
return new FuncFieldResolver<IEnrichedContentEntity, object?>(c => action(c.Source));
}
private static IFieldResolver ResolveFlat(Func<IEnrichedContentEntity, NamedContentData?> action)
{
return new FuncFieldResolver<IEnrichedContentEntity, FlatContentData?>(c =>
{
var context = (GraphQLExecutionContext)c.UserContext;
return action(c.Source)?.ToFlatten(context.Context.App.LanguagesConfig.Master);
});
}
}
}

25
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentInterfaceGraphType.cs

@ -5,13 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed class ContentInterfaceGraphType : InterfaceGraphType<IContentEntity>
public sealed class ContentInterfaceGraphType : InterfaceGraphType<IEnrichedContentEntity>
{
public ContentInterfaceGraphType()
{
@ -21,7 +19,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "id",
ResolvedType = AllTypes.NonNullGuid,
Resolver = Resolve(x => x.Id),
Resolver = EntityResolvers.Id,
Description = "The id of the content."
});
@ -29,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "version",
ResolvedType = AllTypes.NonNullInt,
Resolver = Resolve(x => x.Version),
Resolver = EntityResolvers.Version,
Description = "The version of the content."
});
@ -37,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "created",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.Created),
Resolver = EntityResolvers.Created,
Description = "The date and time when the content has been created."
});
@ -45,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "createdBy",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.CreatedBy.ToString()),
Resolver = EntityResolvers.CreatedBy,
Description = "The user that has created the content."
});
@ -53,7 +51,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "lastModified",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.LastModified),
Resolver = EntityResolvers.LastModified,
Description = "The date and time when the content has been modified last."
});
@ -61,7 +59,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "lastModifiedBy",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.LastModifiedBy.ToString()),
Resolver = EntityResolvers.LastModifiedBy,
Description = "The user that has updated the content last."
});
@ -69,7 +67,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "status",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.Status.Name.ToUpperInvariant()),
Resolver = ContentResolvers.Status,
Description = "The the status of the content."
});
@ -77,16 +75,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "statusColor",
ResolvedType = AllTypes.NonNullString,
Resolver = Resolve(x => x.StatusColor),
Resolver = ContentResolvers.StatusColor,
Description = "The color status of the content."
});
Description = "The structure of all content types.";
}
private static IFieldResolver Resolve(Func<IEnrichedContentEntity, object> action)
{
return new FuncFieldResolver<IEnrichedContentEntity, object>(c => action(c.Source));
}
}
}

106
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentResolvers.cs

@ -0,0 +1,106 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using GraphQL;
using GraphQL.Resolvers;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public static class ContentResolvers
{
public static IFieldResolver NestedValue(ValueResolver valueResolver, string key)
{
return new FuncFieldResolver<JsonObject, object?>(c =>
{
if (c.Source.TryGetValue(key, out var value))
{
return valueResolver(value, c);
}
return null;
});
}
public static IFieldResolver Partition(ValueResolver valueResolver, string key)
{
return new FuncFieldResolver<ContentFieldData, object?>(c =>
{
if (c.Source.TryGetValue(key, out var value) && value != null)
{
return valueResolver(value, c);
}
return null;
});
}
public static IFieldResolver FlatPartition(ValueResolver valueResolver, string key)
{
return new FuncFieldResolver<FlatContentData, object?>(c =>
{
if (c.Source.TryGetValue(key, out var value) && value != null)
{
return valueResolver(value, c);
}
return null;
});
}
public static IFieldResolver Field(RootField field)
{
var fieldName = field.Name;
return new FuncFieldResolver<NamedContentData, IReadOnlyDictionary<string, IJsonValue>?>(c =>
{
return c.Source?.GetOrDefault(fieldName);
});
}
public static readonly IFieldResolver Url = Resolve((content, _, context) =>
{
var appId = content.AppId;
return context.UrlGenerator.ContentUI(appId, content.SchemaId, content.Id);
});
public static readonly IFieldResolver FlatData = Resolve((content, c, context) =>
{
var language = context.Context.App.LanguagesConfig.Master;
return content.Data.ToFlatten(language);
});
public static readonly IFieldResolver Data = Resolve(x => x.Data);
public static readonly IFieldResolver Status = Resolve(x => x.Status.Name.ToUpperInvariant());
public static readonly IFieldResolver StatusColor = Resolve(x => x.StatusColor);
public static readonly IFieldResolver ListTotal = ResolveList(x => x.Total);
public static readonly IFieldResolver ListItems = ResolveList(x => x);
private static IFieldResolver Resolve<T>(Func<IEnrichedContentEntity, IResolveFieldContext, GraphQLExecutionContext, T> action)
{
return new FuncFieldResolver<IEnrichedContentEntity, object?>(c => action(c.Source, c, (GraphQLExecutionContext)c.UserContext));
}
private static IFieldResolver Resolve<T>(Func<IEnrichedContentEntity, T> action)
{
return new FuncFieldResolver<IEnrichedContentEntity, object?>(c => action(c.Source));
}
private static IFieldResolver ResolveList<T>(Func<IResultList<IEnrichedContentEntity>, T> action)
{
return new FuncFieldResolver<IResultList<IEnrichedContentEntity>, object?>(c => action(c.Source));
}
}
}

11
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentsResultGraphType.cs

@ -5,8 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Infrastructure;
@ -21,7 +19,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
AddField(new FieldType
{
Name = "total",
Resolver = Resolver(x => x.Total),
Resolver = ContentResolvers.ListTotal,
ResolvedType = AllTypes.NonNullInt,
Description = $"The total number of {schemaName} items."
});
@ -29,17 +27,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
AddField(new FieldType
{
Name = "items",
Resolver = Resolver(x => x),
Resolver = ContentResolvers.ListItems,
ResolvedType = new ListGraphType(new NonNullGraphType(contentType)),
Description = $"The {schemaName} items."
});
Description = $"List of {schemaName} items and total count.";
}
private static IFieldResolver Resolver(Func<IResultList<IContentEntity>, object> action)
{
return new FuncFieldResolver<IResultList<IContentEntity>, object>(c => action(c.Source));
}
}
}

27
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/EntityResolvers.cs

@ -0,0 +1,27 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL.Resolvers;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public static class EntityResolvers
{
public static readonly IFieldResolver Id = Resolve<IEntity>(x => x.Id.ToString());
public static readonly IFieldResolver Created = Resolve<IEntity>(x => x.Created);
public static readonly IFieldResolver CreatedBy = Resolve<IEntityWithCreatedBy>(x => x.CreatedBy.ToString());
public static readonly IFieldResolver LastModified = Resolve<IEntity>(x => x.LastModified.ToString());
public static readonly IFieldResolver LastModifiedBy = Resolve<IEntityWithLastModifiedBy>(x => x.LastModifiedBy.ToString());
public static readonly IFieldResolver Version = Resolve<IEntityWithVersion>(x => x.Version);
private static IFieldResolver Resolve<TSource>(Func<TSource, object> action)
{
return new FuncFieldResolver<TSource, object?>(c => action(c.Source));
}
}
}

43
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs

@ -7,7 +7,10 @@
using System.Collections.Generic;
using System.Linq;
using GraphQL;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Text;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
@ -27,7 +30,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
private static IEnumerable<(T Field, string Name, string Type)> FieldNames<T>(this IEnumerable<T> fields) where T : IField
{
return fields.ForApi().Select(field => (field, field.Name.ToCamelCase(), field.TypeName()));
return fields.ForApi(true).Select(field => (field, CasingExtensions.ToCamelCase(field.Name), field.TypeName()));
}
private static string SafeString(this string value, int index)
@ -44,5 +47,43 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
return value;
}
public static string BuildODataQuery(this IResolveFieldContext context)
{
var odataQuery = "?" +
string.Join("&",
context.Arguments
.Select(x => new { x.Key, Value = x.Value.ToString() }).Where(x => !string.IsNullOrWhiteSpace(x.Value))
.Select(x => $"${x.Key}={x.Value}"));
return odataQuery;
}
public static FieldType WithSourceName(this FieldType field, object value)
{
field.Metadata["sourceName"] = value;
return field;
}
public static string GetSourceName(this FieldType field)
{
return field.Metadata.GetOrAddDefault("sourceName") as string ?? field.Name;
}
public static IGraphType Flatten(this QueryArgument type)
{
return type.ResolvedType.Flatten();
}
public static IGraphType Flatten(this IGraphType type)
{
if (type is IProvideResolvedType provider)
{
return provider.ResolvedType.Flatten();
}
return type;
}
}
}

85
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/InputFieldVisitor.cs

@ -0,0 +1,85 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils;
using Squidex.Domain.Apps.Entities.Schemas;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed class InputFieldVisitor : IFieldVisitor<IGraphType?>
{
private readonly ISchemaEntity schema;
private readonly IGraphModel model;
private readonly string fieldName;
public InputFieldVisitor(ISchemaEntity schema, IGraphModel model, string fieldName)
{
this.model = model;
this.schema = schema;
this.fieldName = fieldName;
}
public IGraphType? Visit(IArrayField field)
{
var schemaFieldType = new ListGraphType(new NonNullGraphType(new NestedInputGraphType(model, schema, field, fieldName)));
return schemaFieldType;
}
public IGraphType? Visit(IField<AssetsFieldProperties> field)
{
return AllTypes.References;
}
public IGraphType? Visit(IField<BooleanFieldProperties> field)
{
return AllTypes.Boolean;
}
public IGraphType? Visit(IField<DateTimeFieldProperties> field)
{
return AllTypes.Date;
}
public IGraphType? Visit(IField<GeolocationFieldProperties> field)
{
return GeolocationInputGraphType.Nullable;
}
public IGraphType? Visit(IField<JsonFieldProperties> field)
{
return AllTypes.Json;
}
public IGraphType? Visit(IField<NumberFieldProperties> field)
{
return AllTypes.Float;
}
public IGraphType? Visit(IField<ReferencesFieldProperties> field)
{
return AllTypes.Json;
}
public IGraphType? Visit(IField<StringFieldProperties> field)
{
return AllTypes.String;
}
public IGraphType? Visit(IField<TagsFieldProperties> field)
{
return AllTypes.Tags;
}
public IGraphType? Visit(IField<UIFieldProperties> field)
{
return null;
}
}
}

27
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedGraphType.cs

@ -5,7 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL.Resolvers;
using System.Linq;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
@ -24,20 +24,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Name = $"{schemaType}{fieldName}ChildDto";
foreach (var (nestedField, nestedName, _) in field.Fields.SafeFields())
foreach (var (nestedField, nestedName, typeName) in field.Fields.SafeFields().Where(x => x.Field.IsForApi()))
{
var (resolveType, valueResolver, args) = model.GetGraphType(schema, nestedField, nestedName);
var (resolvedType, valueResolver, args) = model.GetGraphType(schema, nestedField, typeName);
if (resolveType != null && valueResolver != null)
if (resolvedType != null && valueResolver != null)
{
var resolver = ValueResolver(nestedField, valueResolver);
var resolver = ContentResolvers.NestedValue(valueResolver, nestedField.Name);
AddField(new FieldType
{
Name = nestedName,
Arguments = args,
ResolvedType = resolvedType,
Resolver = resolver,
ResolvedType = resolveType,
Description = $"The {fieldDisplayName}/{nestedField.DisplayName()} nested field."
});
}
@ -45,20 +45,5 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Description = $"The structure of the {schemaName}.{fieldDisplayName} nested schema.";
}
private static FuncFieldResolver<object?> ValueResolver(NestedField nestedField, ValueResolver resolver)
{
return new FuncFieldResolver<object?>(c =>
{
if (((JsonObject)c.Source).TryGetValue(nestedField.Name, out var value))
{
return resolver(value, c);
}
else
{
return null;
}
});
}
}
}

45
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedInputGraphType.cs

@ -0,0 +1,45 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Linq;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed class NestedInputGraphType : InputObjectGraphType
{
public NestedInputGraphType(IGraphModel model, ISchemaEntity schema, IArrayField field, string fieldName)
{
var schemaType = schema.TypeName();
var schemaName = schema.DisplayName();
var fieldDisplayName = field.DisplayName();
Name = $"{schemaType}{fieldName}InputChildDto";
foreach (var (nestedField, nestedName, typeName) in field.Fields.SafeFields().Where(x => x.Field.IsForApi(true)))
{
var resolvedType = model.GetInputGraphType(schema, nestedField, typeName);
if (resolvedType != null)
{
AddField(new FieldType
{
Name = nestedName,
Resolver = null,
ResolvedType = resolvedType,
Description = $"The {fieldDisplayName}/{nestedField.DisplayName()} nested field."
}).WithSourceName(nestedField.Name);
}
}
Description = $"The structure of the {schemaName}.{fieldDisplayName} nested schema.";
}
}
}

31
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GraphQL;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
@ -16,7 +17,7 @@ using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public delegate object ValueResolver(IJsonValue value, ResolveFieldContext context);
public delegate object ValueResolver(IJsonValue value, IResolveFieldContext context);
public sealed class QueryGraphTypeVisitor : IFieldVisitor<(IGraphType?, ValueResolver?, QueryArguments?)>
{
@ -42,7 +43,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
public (IGraphType?, ValueResolver?, QueryArguments?) Visit(IArrayField field)
{
return ResolveNested(field);
var schemaFieldType = new ListGraphType(new NonNullGraphType(new NestedGraphType(model, schema, field, fieldName)));
return (schemaFieldType, NoopResolver, null);
}
public (IGraphType?, ValueResolver?, QueryArguments?) Visit(IField<AssetsFieldProperties> field)
@ -90,30 +93,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
return (null, null, null);
}
private static (IGraphType?, ValueResolver?, QueryArguments?) ResolveDefault(IGraphType type)
public (IGraphType?, ValueResolver?, QueryArguments?) Visit(IField<JsonFieldProperties> field)
{
return (type, NoopResolver, null);
return (AllTypes.NoopJson, ContentActions.Json.Resolver, ContentActions.Json.Arguments);
}
private (IGraphType?, ValueResolver?, QueryArguments?) ResolveNested(IArrayField field)
{
var schemaFieldType = new ListGraphType(new NonNullGraphType(new NestedGraphType(model, schema, field, fieldName)));
return (schemaFieldType, NoopResolver, null);
}
public (IGraphType?, ValueResolver?, QueryArguments?) Visit(IField<JsonFieldProperties> field)
{
var resolver = new ValueResolver((value, c) =>
private static (IGraphType?, ValueResolver?, QueryArguments?) ResolveDefault(IGraphType type)
{
var path = c.Arguments.GetOrDefault(AllTypes.PathName);
value.TryGetByPath(path as string, out var result);
return result!;
});
return (AllTypes.NoopJson, resolver, AllTypes.PathArguments);
return (type, NoopResolver, null);
}
private (IGraphType?, ValueResolver?, QueryArguments?) ResolveAssets()

79
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/Converters.cs

@ -0,0 +1,79 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{
public static class Converters
{
public static NamedContentData ToNamedContentData(this IDictionary<string, object> source, IComplexGraphType type)
{
var result = new NamedContentData();
foreach (var field in type.Fields)
{
if (source.TryGetValue(field.Name, out var t) && t is IDictionary<string, object> nested && field.ResolvedType is IComplexGraphType complexType)
{
result[field.GetSourceName()] = nested.ToFieldData(complexType);
}
}
return result;
}
public static ContentFieldData ToFieldData(this IDictionary<string, object> source, IComplexGraphType type)
{
var result = new ContentFieldData();
foreach (var field in type.Fields)
{
if (source.TryGetValue(field.Name, out var value))
{
if (value is List<object> list && field.ResolvedType.Flatten() is IComplexGraphType nestedType)
{
var arr = new JsonArray();
foreach (var item in list)
{
if (item is IDictionary<string, object> nested)
{
arr.Add(nested.ToNestedData(nestedType));
}
}
result[field.GetSourceName()] = arr;
}
else
{
result[field.GetSourceName()] = JsonConverter.ParseJson(value);
}
}
}
return result;
}
public static IJsonValue ToNestedData(this IDictionary<string, object> source, IComplexGraphType type)
{
var result = JsonValue.Object();
foreach (var field in type.Fields)
{
if (source.TryGetValue(field.Name, out var value))
{
result[field.GetSourceName()] = JsonConverter.ParseJson(value);
}
}
return result;
}
}
}

43
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/EntitySavedGraphType.cs

@ -0,0 +1,43 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed class EntitySavedGraphType : ObjectGraphType<EntitySavedResult>
{
public static readonly IGraphType Nullable = new EntitySavedGraphType();
public static readonly IGraphType NonNull = new NonNullGraphType(Nullable);
private EntitySavedGraphType()
{
Name = "EntitySavedResultDto";
AddField(new FieldType
{
Name = "version",
Resolver = ResolveVersion(),
ResolvedType = AllTypes.NonNullLong,
Description = "The new version of the item."
});
Description = "The result of a mutation";
}
private static IFieldResolver ResolveVersion()
{
return new FuncFieldResolver<EntitySavedResult, long>(x =>
{
return x.Source.Version;
});
}
}
}

35
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/GeolocationInputGraphType.cs

@ -0,0 +1,35 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL.Types;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{
public sealed class GeolocationInputGraphType : InputObjectGraphType
{
public static readonly IGraphType Nullable = new GeolocationInputGraphType();
public static readonly IGraphType NonNull = new NonNullGraphType(Nullable);
private GeolocationInputGraphType()
{
Name = "GeolocationInputDto";
AddField(new FieldType
{
Name = "latitude",
ResolvedType = AllTypes.NonNullFloat
});
AddField(new FieldType
{
Name = "longitude",
ResolvedType = AllTypes.NonNullFloat
});
}
}
}

55
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/GuidGraphType2.cs

@ -1,55 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using GraphQL.Language.AST;
using GraphQL.Types;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{
public sealed class GuidGraphType2 : ScalarGraphType
{
public GuidGraphType2()
{
Name = "Guid";
Description = "The `Guid` scalar type global unique identifier";
}
public override object? Serialize(object value)
{
return ParseValue(value)?.ToString();
}
public override object? ParseValue(object value)
{
if (value is Guid guid)
{
return guid;
}
var inputValue = value?.ToString()?.Trim('"');
if (Guid.TryParse(inputValue, out guid))
{
return guid;
}
return null;
}
public override object? ParseLiteral(IValue value)
{
if (value is StringValue stringValue)
{
return ParseValue(stringValue.Value);
}
return null;
}
}
}

32
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantConverter.cs

@ -0,0 +1,32 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL.Language.AST;
using GraphQL.Types;
using NodaTime;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{
public sealed class InstantConverter : IAstFromValueConverter
{
public static readonly InstantConverter Instance = new InstantConverter();
private InstantConverter()
{
}
public IValue Convert(object value, IGraphType type)
{
return new InstantValueNode((Instant)value);
}
public bool Matches(object value, IGraphType type)
{
return type is InstantGraphType;
}
}
}

14
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantGraphType.cs

@ -25,17 +25,15 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
public override object? ParseLiteral(IValue value)
{
if (value is InstantValue timeValue)
{
return ParseValue(timeValue.Value);
}
if (value is StringValue stringValue)
switch (value)
{
case InstantValueNode timeValue:
return timeValue.Value;
case StringValue stringValue:
return ParseValue(stringValue.Value);
}
default:
return null;
}
}
}
}

6
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantValue.cs → backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantValueNode.cs

@ -10,16 +10,16 @@ using NodaTime;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{
public sealed class InstantValue : ValueNode<Instant>
public sealed class InstantValueNode : ValueNode<Instant>
{
public InstantValue(Instant value)
public InstantValueNode(Instant value)
{
Value = value;
}
protected override bool Equals(ValueNode<Instant> node)
{
return Value.Equals(node.Value);
return Equals(Value, node.Value);
}
}
}

44
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonConverter.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using GraphQL.Language.AST;
using GraphQL.Types;
using Squidex.Infrastructure.Json.Objects;
@ -21,12 +22,51 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
public IValue Convert(object value, IGraphType type)
{
return new JsonValueNode(value as JsonObject ?? JsonValue.Null);
return new JsonValueNode(ParseJson(value));
}
public bool Matches(object value, IGraphType type)
{
return value is JsonObject;
return type is JsonGraphType;
}
public static IJsonValue ParseJson(object value)
{
switch (value)
{
case ListValue listValue:
return ParseJson(listValue.Value);
case ObjectValue objectValue:
return ParseJson(objectValue.Value);
case Dictionary<string, object> dictionary:
{
var json = JsonValue.Object();
foreach (var (key, inner) in dictionary)
{
json[key] = ParseJson(inner);
}
return json;
}
case List<object> list:
{
var array = JsonValue.Array();
foreach (var item in list)
{
array.Add(ParseJson(item));
}
return array;
}
default:
return JsonValue.Create(value);
}
}
}
}

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonGraphType.cs

@ -26,7 +26,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
public override object ParseValue(object value)
{
return value;
return JsonConverter.ParseJson(value);
}
public override object ParseLiteral(IValue value)

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonValueNode.cs

@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
protected override bool Equals(ValueNode<IJsonValue> node)
{
return false;
return Equals(Value, node.Value);
}
}
}

12
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/QueryExecutionContext.cs

@ -15,9 +15,9 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents.Queries
{
public class QueryExecutionContext
public class QueryExecutionContext : Dictionary<string, object>
{
private readonly ConcurrentDictionary<Guid, IContentEntity?> cachedContents = new ConcurrentDictionary<Guid, IContentEntity?>();
private readonly ConcurrentDictionary<Guid, IEnrichedContentEntity?> cachedContents = new ConcurrentDictionary<Guid, IEnrichedContentEntity?>();
private readonly ConcurrentDictionary<Guid, IEnrichedAssetEntity?> cachedAssets = new ConcurrentDictionary<Guid, IEnrichedAssetEntity?>();
private readonly IContentQueryService contentQuery;
private readonly IAssetQueryService assetQuery;
@ -56,7 +56,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return asset;
}
public virtual async Task<IContentEntity?> FindContentAsync(Guid schemaId, Guid id)
public virtual async Task<IEnrichedContentEntity?> FindContentAsync(Guid schemaId, Guid id)
{
var content = cachedContents.GetOrDefault(id);
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return content;
}
public virtual async Task<IResultList<IAssetEntity>> QueryAssetsAsync(string query)
public virtual async Task<IResultList<IEnrichedAssetEntity>> QueryAssetsAsync(string query)
{
var assets = await assetQuery.QueryAsync(context, null, Q.Empty.WithODataQuery(query));
@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return assets;
}
public virtual async Task<IResultList<IContentEntity>> QueryContentsAsync(string schemaIdOrName, string query)
public virtual async Task<IResultList<IEnrichedContentEntity>> QueryContentsAsync(string schemaIdOrName, string query)
{
var result = await contentQuery.QueryAsync(context, schemaIdOrName, Q.Empty.WithODataQuery(query));
@ -116,7 +116,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
return ids.Select(cachedAssets.GetOrDefault).NotNull().ToList();
}
public virtual async Task<IReadOnlyList<IContentEntity>> GetReferencedContentsAsync(ICollection<Guid> ids)
public virtual async Task<IReadOnlyList<IEnrichedContentEntity>> GetReferencedContentsAsync(ICollection<Guid> ids)
{
Guard.NotNull(ids, nameof(ids));

2
backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj

@ -24,7 +24,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="GraphQL" Version="2.4.0" />
<PackageReference Include="GraphQL" Version="3.0.0" />
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00005" />
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00005" />
<PackageReference Include="Lucene.Net.Queries" Version="4.8.0-beta00005" />

20
backend/src/Squidex.Domain.Users/UserManagerExtensions.cs

@ -12,6 +12,7 @@ using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Translations;
using Squidex.Infrastructure.Validation;
namespace Squidex.Domain.Users
@ -241,7 +242,7 @@ namespace Squidex.Domain.Users
if (!result.Succeeded)
{
throw new ValidationException(result.Errors.Select(x => new ValidationError(x.Description)).ToList());
throw new ValidationException(result.Errors.Select(x => new ValidationError(x.Localize())).ToList());
}
}
@ -249,5 +250,22 @@ namespace Squidex.Domain.Users
{
return values.SyncClaims(userManager, user);
}
public static string Localize(this IdentityResult result)
{
return string.Join(". ", result.Errors.Select(x => x.Localize()));
}
public static string Localize(this IdentityError error)
{
if (!string.IsNullOrWhiteSpace(error.Code))
{
return T.Get($"dotnet_identity_{error.Code}", error.Description);
}
else
{
return error.Description;
}
}
}
}

1
backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore.cs

@ -9,7 +9,6 @@ using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Core.Clusters;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Infrastructure.EventSourcing

6
backend/src/Squidex.Infrastructure/Translations/DeepLTranslator.cs

@ -70,7 +70,10 @@ namespace Squidex.Infrastructure.Translations
parameters["source_lang"] = GetLanguageCode(sourceLanguage);
}
var response = await httpClient.PostAsync(Url, new FormUrlEncodedContent(parameters), ct);
var body = new FormUrlEncodedContent(parameters);
using (var response = await httpClient.PostAsync(Url, body, ct))
{
var responseString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
@ -90,6 +93,7 @@ namespace Squidex.Infrastructure.Translations
return new Translation(TranslationResult.Failed, resultText: responseString);
}
}
private static string GetLanguageCode(Language language)
{

5
backend/src/Squidex.Infrastructure/Translations/LocalizedCompareAttribute.cs → backend/src/Squidex.Infrastructure/Validation/LocalizedCompareAttribute.cs

@ -6,9 +6,10 @@
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Infrastructure.Translations;
using Squidex.Text;
namespace Squidex.Infrastructure.Translations
namespace Squidex.Infrastructure.Validation
{
public sealed class LocalizedCompareAttribute : CompareAttribute
{
@ -23,7 +24,7 @@ namespace Squidex.Infrastructure.Translations
var other = T.Get($"common.{OtherProperty.ToCamelCase()}", OtherProperty);
return T.Get("annotations_Compare", new { property, other });
return T.Get("annotations_Compare", base.FormatErrorMessage(name), new { property, other });
}
}
}

30
backend/src/Squidex.Infrastructure/Validation/LocalizedEmailAddressAttribute.cs

@ -0,0 +1,30 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using Squidex.Infrastructure.Translations;
using Squidex.Text;
namespace Squidex.Infrastructure.Validation
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class LocalizedEmailAddressAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
return !(value is string s) || s.IsEmail();
}
public override string FormatErrorMessage(string name)
{
var property = T.Get($"common.{name.ToCamelCase()}", name);
return T.Get("annotations_EmailAddress", base.FormatErrorMessage(name), new { property });
}
}
}

38
backend/src/Squidex.Infrastructure/Validation/LocalizedRangeAttribute.cs

@ -0,0 +1,38 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using Squidex.Infrastructure.Translations;
using Squidex.Text;
namespace Squidex.Infrastructure.Validation
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class LocalizedRangeAttribute : RangeAttribute
{
public LocalizedRangeAttribute(int minimum, int maximum)
: base(minimum, maximum)
{
}
public LocalizedRangeAttribute(double minimum, double maximum)
: base(minimum, maximum)
{
}
public override string FormatErrorMessage(string name)
{
var property = T.Get($"common.{name.ToCamelCase()}", name);
var min = Minimum;
var max = Maximum;
return T.Get("annotations_Range", base.FormatErrorMessage(name), new { property, min, max });
}
}
}

30
backend/src/Squidex.Infrastructure/Validation/LocalizedRegularExpressionAttribute.cs

@ -0,0 +1,30 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using Squidex.Infrastructure.Translations;
using Squidex.Text;
namespace Squidex.Infrastructure.Validation
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class LocalizedRegularExpressionAttribute : RegularExpressionAttribute
{
public LocalizedRegularExpressionAttribute(string pattern)
: base(pattern)
{
}
public override string FormatErrorMessage(string name)
{
var property = T.Get($"common.{name.ToCamelCase()}", name);
return T.Get("annotations_RegularExpression", base.FormatErrorMessage(name), new { property });
}
}
}

7
backend/src/Squidex.Infrastructure/Translations/LocalizedRequired.cs → backend/src/Squidex.Infrastructure/Validation/LocalizedRequiredAttribute.cs

@ -7,18 +7,19 @@
using System;
using System.ComponentModel.DataAnnotations;
using Squidex.Infrastructure.Translations;
using Squidex.Text;
namespace Squidex.Infrastructure.Translations
namespace Squidex.Infrastructure.Validation
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class LocalizedRequired : RequiredAttribute
public class LocalizedRequiredAttribute : RequiredAttribute
{
public override string FormatErrorMessage(string name)
{
var property = T.Get($"common.{name.ToCamelCase()}", name);
return T.Get("annotations_Required", new { property });
return T.Get("annotations_Required", base.FormatErrorMessage(name), new { property });
}
}
}

38
backend/src/Squidex.Infrastructure/Validation/LocalizedStringLengthAttribute.cs

@ -0,0 +1,38 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Infrastructure.Translations;
using Squidex.Text;
namespace Squidex.Infrastructure.Validation
{
public sealed class LocalizedStringLengthAttribute : StringLengthAttribute
{
public LocalizedStringLengthAttribute(int maximumLength)
: base(maximumLength)
{
}
public override string FormatErrorMessage(string name)
{
var property = T.Get($"common.{name.ToCamelCase()}", name);
var min = MinimumLength;
var max = MaximumLength;
var args = new { property, min, max };
if (min > 0)
{
return T.Get("annotations_StringLengthMinimum", base.FormatErrorMessage(name), args);
}
return T.Get("annotations_StringLength", base.FormatErrorMessage(name), args);
}
}
}

1138
backend/src/Squidex.Shared/Texts.it.resx

File diff suppressed because it is too large

93
backend/src/Squidex.Shared/Texts.nl.resx

@ -61,12 +61,51 @@
<data name="annotations_AbsoluteUrl" xml:space="preserve">
<value>Het veld {name|lower} moet een absolute URL zijn.</value>
</data>
<data name="dotnet_annotations_AbsoluteUrl" xml:space="preserve">
<value>Het veld {0} moet een absolute URL zijn.</value>
</data>
<data name="annotations_Compare" xml:space="preserve">
<value>Het veld {name|lower} moet hetzelfde zijn als {other|lower}.</value>
</data>
<data name="dotnet_annotations_Compare" xml:space="preserve">
<value>Het veld {0} moet hetzelfde zijn als {1}.</value>
</data>
<data name="annotations_EmailAddress" xml:space="preserve">
<value>The field {name|lower} is not a valid email address.</value>
</data>
<data name="dotnet_annotations_EmailAddress" xml:space="preserve">
<value>The field {0} is not a valid email address.</value>
</data>
<data name="annotations_Range" xml:space="preserve">
<value>The field {name|lower} must be between {min} and {max}.</value>
</data>
<data name="dotnet_annotations_Range" xml:space="preserve">
<value>The field {0} must be between {1} and {2}.</value>
</data>
<data name="annotations_RegularExpression" xml:space="preserve">
<value>The field {name|lower} is not.</value>
</data>
<data name="dotnet_annotations_RegularExpression" xml:space="preserve">
<value>The field {0} is not.</value>
</data>
<data name="annotations_Required" xml:space="preserve">
<value>Het veld {name|lower} is verplicht.</value>
</data>
<data name="dotnet_annotations_Required" xml:space="preserve">
<value>Het veld {0} is verplicht.</value>
</data>
<data name="annotations_StringLength" xml:space="preserve">
<value>The field {name|lower} must be a string with a maximum length of {max}.</value>
</data>
<data name="dotnet_annotations_StringLength" xml:space="preserve">
<value>The field {0} must be a string with a maximum length of {1}.</value>
</data>
<data name="annotations_StringLengthMinimum" xml:space="preserve">
<value>The field {name|lower} must be a string with a minimum length of {min} and a maximum length of {max}.</value>
</data>
<data name="dotnet_annotations_StringLengthMinimum" xml:space="preserve">
<value>The field {0} must be a string with a minimum length of {1} and a maximum length of {2}.</value>
</data>
<data name="apps.alreadyArchieved" xml:space="preserve">
<value>App is al gearchiveerd.</value>
</data>
@ -130,15 +169,6 @@
<data name="apps.roles.usedRoleByContributorsNotRemovable" xml:space="preserve">
<value>Kan een rol niet verwijderen wanneer een bijdrager is toegewezen.</value>
</data>
<data name="aspnet_annotations_AbsoluteUrl" xml:space="preserve">
<value>Het veld {0} moet een absolute URL zijn.</value>
</data>
<data name="aspnet_annotations_Compare" xml:space="preserve">
<value>Het veld {0} moet hetzelfde zijn als {other|lower}.</value>
</data>
<data name="aspnet_annotations_Required" xml:space="preserve">
<value>Het veld {0} is verplicht.</value>
</data>
<data name="assets.assetAlreadyDeleted" xml:space="preserve">
<value>Asset is al verwijderd</value>
</data>
@ -601,6 +631,51 @@
<data name="contents.workflowErrorUpdate" xml:space="preserve">
<value>De werkstroom staat geen updates toe met status {status}</value>
</data>
<data name="dotnet_identity_DefaultEror" xml:space="preserve">
<value>Er is een onbekende fout opgetreden.</value>
</data>
<data name="dotnet_identity_DuplicateEmail" xml:space="preserve">
<value>E-mail is al in gebruik.</value>
</data>
<data name="dotnet_identity_DuplicateUserName" xml:space="preserve">
<value>Gebruikersnaam is al in gebruik.</value>
</data>
<data name="dotnet_identity_InvalidEmail" xml:space="preserve">
<value>E-mail is ongeldig.</value>
</data>
<data name="dotnet_identity_InvalidUserName" xml:space="preserve">
<value>Gebruikersnaam '{0}' is ongeldig, mag alleen letters of cijfers bevatten.</value>
</data>
<data name="dotnet_identity_LoginAlreadyAssociated" xml:space="preserve">
<value>Er bestaat al een gebruiker met deze login.</value>
</data>
<data name="dotnet_identity_PasswordMismatch" xml:space="preserve">
<value>Onjuist wachtwoord.</value>
</data>
<data name="dotnet_identity_PasswordRequiresDigit" xml:space="preserve">
<value>Wachtwoorden moeten minstens één cijfer bevatten ('0' - '9').</value>
</data>
<data name="dotnet_identity_PasswordRequiresLower" xml:space="preserve">
<value>Wachtwoorden moeten minstens één kleine letter ('a' - 'z') bevatten.</value>
</data>
<data name="dotnet_identity_PasswordRequiresNonAlphanumeric" xml:space="preserve">
<value>Wachtwoorden moeten minstens één niet-alfanumeriek teken bevatten.</value>
</data>
<data name="dotnet_identity_PasswordRequiresUniqueChars" xml:space="preserve">
<value>Wachtwoorden moeten minstens {0} verschillende tekens bevatten.</value>
</data>
<data name="dotnet_identity_PasswordRequiresUpper" xml:space="preserve">
<value>Wachtwoorden moeten minstens één hoofdletter ('A' - 'Z') hebben.</value>
</data>
<data name="dotnet_identity_PasswordTooShort" xml:space="preserve">
<value>Wachtwoorden zijn te kort.</value>
</data>
<data name="dotnet_identity_PwnedError" xml:space="preserve">
<value>Dit wachtwoord is eerder verschenen in een datalek en mag nooit worden gebruikt. Als je het ooit eerder ergens hebt gebruikt, verander het dan!</value>
</data>
<data name="dotnet_identity_UserLockedOut" xml:space="preserve">
<value>Gebruiker is uitgesloten.</value>
</data>
<data name="exception.invalidJsonQuery" xml:space="preserve">
<value>Json-query niet geldig: {message}</value>
</data>

93
backend/src/Squidex.Shared/Texts.resx

@ -61,12 +61,51 @@
<data name="annotations_AbsoluteUrl" xml:space="preserve">
<value>The field {name|lower} must be an absolute URL.</value>
</data>
<data name="dotnet_annotations_AbsoluteUrl" xml:space="preserve">
<value>The field {0} must be an absolute URL.</value>
</data>
<data name="annotations_Compare" xml:space="preserve">
<value>The field {name|lower} must be the same as {other|lower}.</value>
</data>
<data name="dotnet_annotations_Compare" xml:space="preserve">
<value>The field {0} must be the same as {1}.</value>
</data>
<data name="annotations_EmailAddress" xml:space="preserve">
<value>The field {name|lower} is not a valid email address.</value>
</data>
<data name="dotnet_annotations_EmailAddress" xml:space="preserve">
<value>The field {0} is not a valid email address.</value>
</data>
<data name="annotations_Range" xml:space="preserve">
<value>The field {name|lower} must be between {min} and {max}.</value>
</data>
<data name="dotnet_annotations_Range" xml:space="preserve">
<value>The field {0} must be between {1} and {2}.</value>
</data>
<data name="annotations_RegularExpression" xml:space="preserve">
<value>The field {name|lower} is not.</value>
</data>
<data name="dotnet_annotations_RegularExpression" xml:space="preserve">
<value>The field {0} is not.</value>
</data>
<data name="annotations_Required" xml:space="preserve">
<value>The field {name|lower} is required.</value>
</data>
<data name="dotnet_annotations_Required" xml:space="preserve">
<value>The field {0} is required.</value>
</data>
<data name="annotations_StringLength" xml:space="preserve">
<value>The field {name|lower} must be a string with a maximum length of {max}.</value>
</data>
<data name="dotnet_annotations_StringLength" xml:space="preserve">
<value>The field {0} must be a string with a maximum length of {1}.</value>
</data>
<data name="annotations_StringLengthMinimum" xml:space="preserve">
<value>The field {name|lower} must be a string with a minimum length of {min} and a maximum length of {max}.</value>
</data>
<data name="dotnet_annotations_StringLengthMinimum" xml:space="preserve">
<value>The field {0} must be a string with a minimum length of {1} and a maximum length of {2}.</value>
</data>
<data name="apps.alreadyArchieved" xml:space="preserve">
<value>App has already been archived.</value>
</data>
@ -130,15 +169,6 @@
<data name="apps.roles.usedRoleByContributorsNotRemovable" xml:space="preserve">
<value>Cannot remove a role when a contributor is assigned.</value>
</data>
<data name="aspnet_annotations_AbsoluteUrl" xml:space="preserve">
<value>The field {0} must be an absolute URL.</value>
</data>
<data name="aspnet_annotations_Compare" xml:space="preserve">
<value>The field {0} must be the same as {other|lower}.</value>
</data>
<data name="aspnet_annotations_Required" xml:space="preserve">
<value>The field {0} is required.</value>
</data>
<data name="assets.assetAlreadyDeleted" xml:space="preserve">
<value>Asset has already been deleted</value>
</data>
@ -601,6 +631,51 @@
<data name="contents.workflowErrorUpdate" xml:space="preserve">
<value>The workflow does not allow updates at status {status}</value>
</data>
<data name="dotnet_identity_DefaultEror" xml:space="preserve">
<value>An unknown failure has occurred.</value>
</data>
<data name="dotnet_identity_DuplicateEmail" xml:space="preserve">
<value>Email is already taken.</value>
</data>
<data name="dotnet_identity_DuplicateUserName" xml:space="preserve">
<value>User name is already taken.</value>
</data>
<data name="dotnet_identity_InvalidEmail" xml:space="preserve">
<value>Email is invalid.</value>
</data>
<data name="dotnet_identity_InvalidUserName" xml:space="preserve">
<value>User name '{0}' is invalid, can only contain letters or digits.</value>
</data>
<data name="dotnet_identity_LoginAlreadyAssociated" xml:space="preserve">
<value>A user with this login already exists.</value>
</data>
<data name="dotnet_identity_PasswordMismatch" xml:space="preserve">
<value>Incorrect password.</value>
</data>
<data name="dotnet_identity_PasswordRequiresDigit" xml:space="preserve">
<value>Passwords must have at least one digit ('0'-'9').</value>
</data>
<data name="dotnet_identity_PasswordRequiresLower" xml:space="preserve">
<value>Passwords must have at least one lowercase ('a'-'z').</value>
</data>
<data name="dotnet_identity_PasswordRequiresNonAlphanumeric" xml:space="preserve">
<value>Passwords must have at least one non alphanumeric character.</value>
</data>
<data name="dotnet_identity_PasswordRequiresUniqueChars" xml:space="preserve">
<value>Passwords must use at least {0} different characters.</value>
</data>
<data name="dotnet_identity_PasswordRequiresUpper" xml:space="preserve">
<value>Passwords must have at least one uppercase ('A'-'Z').</value>
</data>
<data name="dotnet_identity_PasswordTooShort" xml:space="preserve">
<value>Passwords is too short.</value>
</data>
<data name="dotnet_identity_PwnedError" xml:space="preserve">
<value>This password has previously appeared in a data breach and should never be used. If you have ever used it anywhere before, change it!</value>
</data>
<data name="dotnet_identity_UserLockedOut" xml:space="preserve">
<value>User is locked out.</value>
</data>
<data name="exception.invalidJsonQuery" xml:space="preserve">
<value>Json query not valid: {message}</value>
</data>

2
backend/src/Squidex.Web/ApiModelValidationAttribute.cs

@ -43,7 +43,7 @@ namespace Squidex.Web
{
if (!string.IsNullOrWhiteSpace(error.ErrorMessage) && ShouldExpose(error))
{
errors.Add(new ValidationError(error.ErrorMessage, key));
errors.Add(new ValidationError(error.ErrorMessage));
}
else if (error.Exception is JsonException jsonException)
{

3
backend/src/Squidex.Web/EntityCreatedDto.cs

@ -7,12 +7,13 @@
using System.ComponentModel.DataAnnotations;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Validation;
namespace Squidex.Web
{
public sealed class EntityCreatedDto
{
[Required]
[LocalizedRequired]
[Display(Description = "Id of the created entity.")]
public string? Id { get; set; }

3
backend/src/Squidex.Web/ErrorDto.cs

@ -6,12 +6,13 @@
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Infrastructure.Validation;
namespace Squidex.Web
{
public sealed class ErrorDto
{
[Required]
[LocalizedRequired]
[Display(Description = "Error message.")]
public string? Message { get; set; }

5
backend/src/Squidex.Web/Resource.cs

@ -9,14 +9,15 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Validation;
namespace Squidex.Web
{
public abstract class Resource
{
[JsonProperty("_links")]
[Required]
[LocalizedRequired]
[Display(Description = "The links.")]
[JsonProperty("_links")]
public Dictionary<string, ResourceLink> Links { get; } = new Dictionary<string, ResourceLink>();
public void AddSelfLink(string href)

5
backend/src/Squidex.Web/ResourceLink.cs

@ -6,16 +6,17 @@
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Infrastructure.Validation;
namespace Squidex.Web
{
public class ResourceLink
{
[Required]
[LocalizedRequired]
[Display(Description = "The link url.")]
public string Href { get; set; }
[Required]
[LocalizedRequired]
[Display(Description = "The link method.")]
public string Method { get; set; }

9
backend/src/Squidex.Web/Services/StringLocalizer.cs

@ -39,12 +39,19 @@ namespace Squidex.Web.Services
TranslateProperty(name, arguments, currentCulture);
var (result, found) = translationService.Get(currentCulture, $"aspnet_{name}", name);
var (result, found) = translationService.Get(currentCulture, $"dotnet_{name}", name);
if (arguments != null && found)
{
try
{
result = string.Format(currentCulture, result, arguments);
}
catch (FormatException)
{
return new LocalizedString(name, name, true);
}
}
return new LocalizedString(name, result, !found);
}

4
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AddLanguageDto.cs

@ -5,10 +5,10 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation;
namespace Squidex.Areas.Api.Controllers.Apps.Models
{
@ -17,7 +17,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary>
/// The language to add.
/// </summary>
[Required]
[LocalizedRequired]
public Language Language { get; set; }
public AddLanguage ToCommand()

4
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AddRoleDto.cs

@ -5,8 +5,8 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Validation;
namespace Squidex.Areas.Api.Controllers.Apps.Models
{
@ -15,7 +15,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary>
/// The role name.
/// </summary>
[Required]
[LocalizedRequired]
public string Name { get; set; }
public AddRole ToCommand()

4
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AddWorkflowDto.cs

@ -5,8 +5,8 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Validation;
namespace Squidex.Areas.Api.Controllers.Apps.Models
{
@ -15,7 +15,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary>
/// The name of the workflow.
/// </summary>
[Required]
[LocalizedRequired]
public string Name { get; set; }
public AddWorkflow ToCommand()

6
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs

@ -7,7 +7,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using NodaTime;
using Squidex.Areas.Api.Controllers.Assets;
using Squidex.Areas.Api.Controllers.Backups;
@ -19,6 +18,7 @@ using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Plans;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Security;
using Squidex.Infrastructure.Validation;
using Squidex.Web;
using P = Squidex.Shared.Permissions;
@ -31,8 +31,8 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary>
/// The name of the app.
/// </summary>
[Required]
[RegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")]
[LocalizedRequired]
[LocalizedRegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")]
public string Name { get; set; }
/// <summary>

8
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs

@ -5,11 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models
@ -19,19 +19,19 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary>
/// The iso code of the language.
/// </summary>
[Required]
[LocalizedRequired]
public string Iso2Code { get; set; }
/// <summary>
/// The english name of the language.
/// </summary>
[Required]
[LocalizedRequired]
public string EnglishName { get; set; }
/// <summary>
/// The fallback languages.
/// </summary>
[Required]
[LocalizedRequired]
public Language[] Fallback { get; set; }
/// <summary>

4
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguagesDto.cs

@ -5,9 +5,9 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models
@ -17,7 +17,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary>
/// The languages.
/// </summary>
[Required]
[LocalizedRequired]
public AppLanguageDto[] Items { get; set; }
public static AppLanguagesDto FromApp(IAppEntity app, Resources resources)

4
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs

@ -5,9 +5,9 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation;
using Roles = Squidex.Domain.Apps.Core.Apps.Role;
namespace Squidex.Areas.Api.Controllers.Apps.Models
@ -17,7 +17,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary>
/// The id or email of the user to add to the app.
/// </summary>
[Required]
[LocalizedRequired]
public string ContributorId { get; set; }
/// <summary>

8
backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs

@ -5,9 +5,9 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Validation;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Apps.Models
@ -17,19 +17,19 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// <summary>
/// The client id.
/// </summary>
[Required]
[LocalizedRequired]
public string Id { get; set; }
/// <summary>
/// The client secret.
/// </summary>
[Required]
[LocalizedRequired]
public string Secret { get; set; }
/// <summary>
/// The client name.
/// </summary>
[Required]
[LocalizedRequired]
public string Name { get; set; }
/// <summary>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save