Browse Source

Merge branch 'master' of github.com:Squidex/squidex

pull/656/head
Sebastian 5 years ago
parent
commit
ee838c2f99
  1. 7
      backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs
  2. 2
      backend/extensions/Squidex.Extensions/Actions/Comment/CommentActionHandler.cs
  3. 2
      backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentActionHandler.cs
  4. 1
      backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs
  5. 2
      backend/extensions/Squidex.Extensions/Actions/Notification/NotificationActionHandler.cs
  6. 90
      backend/i18n/frontend_it.json
  7. 27
      backend/i18n/source/backend_it.json
  8. 89
      backend/i18n/source/frontend_it.json
  9. 5
      backend/src/Migrations/Migrations/ConvertEventStore.cs
  10. 5
      backend/src/Migrations/Migrations/ConvertEventStoreAppId.cs
  11. 36
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs
  12. 33
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsSurrogate.cs
  13. 36
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs
  14. 33
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsSurrogate.cs
  15. 37
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs
  16. 33
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsSurrogate.cs
  17. 18
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguageConfigSurrogate.cs
  18. 30
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs
  19. 27
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigSurrogate.cs
  20. 72
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RoleConverter.cs
  21. 59
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs
  22. 76
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesSurrogate.cs
  23. 25
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/ContentFieldData.cs
  24. 39
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/StatusConverter.cs
  25. 30
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowStepConverter.cs
  26. 33
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowStepSurrogate.cs
  27. 20
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowTransitionSurrogate.cs
  28. 37
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsConverter.cs
  29. 28
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsSurrogate.cs
  30. 14
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusInfo.cs
  31. 7
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusTypeConverter.cs
  32. 26
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs
  33. 18
      backend/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleSorrgate.cs
  34. 25
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/FieldSurrogate.cs
  35. 37
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs
  36. 26
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs
  37. 38
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaSurrogate.cs
  38. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueGenerator.cs
  39. 11
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs
  40. 4
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs
  41. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/Templates/Extensions/DateTimeFluidExtension.cs
  42. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/Templates/FluidTemplateEngine.cs
  43. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs
  44. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueValidator.cs
  45. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  46. 17
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs
  47. 2
      backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs
  48. 2
      backend/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEventConsumer.cs
  49. 8
      backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs
  50. 6
      backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs
  51. 2
      backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs
  52. 12
      backend/src/Squidex.Domain.Apps.Entities/Backup/UserMapping.cs
  53. 60
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs
  54. 39
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/DefaultDocumentWriter.cs
  55. 60
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  56. 18
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs
  57. 20
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLQuery.cs
  58. 5
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/IGraphQLService.cs
  59. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AppMutationsGraphType.cs
  60. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs
  61. 9
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs
  62. 8
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntityResolvers.cs
  63. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantGraphType.cs
  64. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs
  65. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs
  66. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs
  67. 2
      backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs
  68. 5
      backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEvent.cs
  69. 7
      backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventCommit.cs
  70. 32
      backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventStore.cs
  71. 2
      backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventStore_Writer.cs
  72. 3
      backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbSubscription.cs
  73. 13
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConvention.cs
  74. 53
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/JTokenSerializer.cs
  75. 2
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoRepositoryBase.cs
  76. 40
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs
  77. 30
      backend/src/Squidex.Infrastructure.MongoDb/MongoDb/TypeConverterStringSerializer.cs
  78. 12
      backend/src/Squidex.Infrastructure/CollectionExtensions.cs
  79. 2
      backend/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs
  80. 13
      backend/src/Squidex.Infrastructure/ISurrogate.cs
  81. 78
      backend/src/Squidex.Infrastructure/Json/ClaimsPrinicpalSurrogate.cs
  82. 2
      backend/src/Squidex.Infrastructure/Json/ISupportedTypes.cs
  83. 35
      backend/src/Squidex.Infrastructure/Json/JsonException.cs
  84. 62
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/ClaimsPrincipalConverter.cs
  85. 62
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/DomainIdConverter.cs
  86. 69
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/InstantConverter.cs
  87. 27
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/LanguageConverter.cs
  88. 41
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/NamedDomainIdConverter.cs
  89. 34
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/NamedGuidIdConverter.cs
  90. 34
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/NamedLongIdConverter.cs
  91. 41
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/NamedStringIdConverter.cs
  92. 48
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs
  93. 32
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/RefTokenConverter.cs
  94. 31
      backend/src/Squidex.Infrastructure/Json/Newtonsoft/SurrogateConverter.cs
  95. 4
      backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs
  96. 10
      backend/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs
  97. 2
      backend/src/Squidex.Infrastructure/Language.cs
  98. 36
      backend/src/Squidex.Infrastructure/LanguageTypeConverter.cs
  99. 83
      backend/src/Squidex.Infrastructure/NamedIdTypeConverter.cs
  100. 165
      backend/src/Squidex.Infrastructure/Queries/Json/FilterConverter.cs

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

@ -84,8 +84,9 @@ namespace Squidex.Extensions.Actions.Algolia
json = new JObject(new JProperty("error", $"Invalid JSON: {ex.Message}"));
}
ruleJob.Content = json;
ruleJob.Content["objectID"] = contentId;
json["objectID"] = contentId;
ruleJob.Content = json.ToString();
}
return (ruleDescription, ruleJob);
@ -135,6 +136,6 @@ namespace Squidex.Extensions.Actions.Algolia
public string IndexName { get; set; }
public JObject Content { get; set; }
public string Content { get; set; }
}
}

2
backend/extensions/Squidex.Extensions/Actions/Comment/CommentActionHandler.cs

@ -41,7 +41,7 @@ namespace Squidex.Extensions.Actions.Comment
if (!string.IsNullOrEmpty(action.Client))
{
ruleJob.Actor = new RefToken(RefTokenType.Client, action.Client);
ruleJob.Actor = RefToken.Client(action.Client);
}
else
{

2
backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentActionHandler.cs

@ -61,7 +61,7 @@ namespace Squidex.Extensions.Actions.CreateContent
if (!string.IsNullOrEmpty(action.Client))
{
ruleJob.Actor = new RefToken(RefTokenType.Client, action.Client);
ruleJob.Actor = RefToken.Client(action.Client);
}
else if (@event is EnrichedUserEventBase userEvent)
{

1
backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaProducer.cs

@ -17,7 +17,6 @@ using Confluent.Kafka;
using Confluent.SchemaRegistry;
using Confluent.SchemaRegistry.Serdes;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Log;

2
backend/extensions/Squidex.Extensions/Actions/Notification/NotificationActionHandler.cs

@ -45,7 +45,7 @@ namespace Squidex.Extensions.Actions.Notification
if (!string.IsNullOrEmpty(action.Client))
{
actor = new RefToken(RefTokenType.Client, action.Client);
actor = RefToken.Client(action.Client);
}
var user = await userResolver.FindByIdOrEmailAsync(action.User);

90
backend/i18n/frontend_it.json

@ -1,6 +1,6 @@
{
"api.contentApi": "Content API",
"api.generalApi": "General API",
"api.contentApi": "API dei contenuti",
"api.generalApi": "API generali",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.pageTitle": "API",
@ -82,7 +82,7 @@
"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.metadataAdd": "Aggiungi un metadato",
"assets.moveFailed": "Non è stato possibile spostare la risorsa. Per favore ricarica.",
"assets.protected": "Protetto",
"assets.refreshTooltip": "Aggiorna le risorse (CTRL + SHIFT + R)",
@ -107,8 +107,8 @@
"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.uploadByDialog": "Seleziona i file",
"assets.uploadByDrop": "Trascina qui i file 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.",
@ -145,7 +145,7 @@
"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.allowAnonymousHint": "Consenti l'accesso alle API senza token di accesso a tutte le risorse che sono configurate per il ruolo di questo client. È possibile avere un solo client impostato con accesso anonimo.",
"clients.apiCallsLimit": "Numero Max di chiamate alle API",
"clients.apiCallsLimitHint": "Limita il numero di chiamate al mese effettuabili dal client alle API calls per riservare ai client più importanti il numero di chiamate API disponili.",
"clients.clientIdValidationMessage": "Il nome deve contenere solo lettere, numeri, trattini e spaziNa.",
@ -155,16 +155,16 @@
"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.cliStep1Hint": "Le release contengono file binari per tutte le operazioni di sistema maggiori e piccoli file da scaricare 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.cliStep3Hint": "È possibile gestire le configurazione 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.manuallyStep1": "Ottenere un token 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.manuallyStep3": "Aggiungi il token 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",
@ -181,13 +181,13 @@
"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.refreshTooltip": "Aggiorna 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.deleteConfirmText": "Sei sicuro di voler cancellare il commento?",
"comments.deleteConfirmTitle": "Cancella il comment",
"comments.deleteFailed": "Non è stato possibile cancellare il commento.",
"comments.follow": "Segui",
@ -208,9 +208,9 @@
"common.cancel": "Annulla",
"common.category": "Categoria",
"common.clear": "Pulisci",
"common.clientId": "Id Client",
"common.clientId": "Client Id",
"common.clients": "Client",
"common.clientSecret": "Secret Client",
"common.clientSecret": "Client Secret",
"common.clipboardAdded": "Il valore è stato aggiunto nei tuoi appunti.",
"common.clone": "Clona",
"common.cluster": "Cluster",
@ -291,12 +291,12 @@
"common.queryOperators.contains": "contiene",
"common.queryOperators.empty": "è vuoto",
"common.queryOperators.endsWith": "finisce con",
"common.queryOperators.eq": "è uguale a",
"common.queryOperators.ge": "è maggiore di o uguale a",
"common.queryOperators.eq": "è uguale a",
"common.queryOperators.ge": "è maggiore o uguale a",
"common.queryOperators.gt": "è maggiore di",
"common.queryOperators.le": "è minore di o uguale a",
"common.queryOperators.le": "è minore o uguale a",
"common.queryOperators.lt": "è minore di",
"common.queryOperators.ne": "è uguale a",
"common.queryOperators.ne": "non è uguale a",
"common.queryOperators.startsWith": "inizia con",
"common.refresh": "Aggiorna",
"common.remember": "Ricorda la mia decisione",
@ -324,9 +324,9 @@
"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.tagAdd": ", aggiungi tag",
"common.tagAddReference": ", aggiungi collegamento",
"common.tagAddSchema": ", aggiungi schema",
"common.tags": "Tag",
"common.tagsAll": "Tutti i tag",
"common.time": "Ora",
@ -338,7 +338,7 @@
"common.width": "Larghezza",
"common.workflow": "Workflow",
"common.workflows": "Workflow",
"common.yes": "Si",
"common.yes": "Sì",
"contents.arrayAddItem": "Aggiungi un elemento",
"contents.arrayClear": "Pulisci",
"contents.arrayClearConfirmText": "Sei sicuro di voler cancellare l'array?",
@ -356,7 +356,7 @@
"contents.assetsUpload": "Trascina i file o clicca",
"contents.autotranslate": "Traduci in automatico dalla lingua principale",
"contents.bulkFailed": "Non è stato possibile eliminare il contenuto. Per favore ricarica.",
"contents.changeStatusTo": "Cambia l'elemeto(i) del contenuti in {action}",
"contents.changeStatusTo": "Cambia gli elementi del contenuto 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).",
@ -400,7 +400,7 @@
"contents.noReferencing": "Questo contenuto non è collegato da altri contenuti.",
"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.pendingChangesTextToPreview": "You have unsaved changes.\n\nYou will not see them on preview.\n\n**Do you want to continue anyway?**",
"contents.pendingChangesTextToPreview": "Hai modifiche non salvate.\n\nNon li visualizzerai nell'anteprima.\n\n**Sei sicuro di voler continuare?**",
"contents.pendingChangesTitle": "Modifiche non salvate",
"contents.publishAll": "Pubblica tutto",
"contents.referencesCreateNew": "Aggiungi nuovo",
@ -438,12 +438,12 @@
"contents.unpublishReferrerConfirmTitle": "Rimuovi dalla pubblicazione il contenuto",
"contents.unsavedChangesText": "Non hai salvato le modifiche. Vuoi salvarle adesso?",
"contents.unsavedChangesTitle": "Modifiche non salvate",
"contents.unsetValue": "Valore non impostato",
"contents.unsetValueConfirmText": "Se annulli il valore impostato potresti perdere le tue modifiche.\n\nSei sicuro di voler procedere?",
"contents.unsetValue": "Cancella il valore",
"contents.unsetValueConfirmText": "Se cancelli il valore impostato potresti perdere le tue modifiche.\n\nSei sicuro di voler procedere?",
"contents.unsetValueConfirmTitle": "Sei sicuro di voler annullare il valore impostato?",
"contents.updated": "Contenuto aggiornato con successo.",
"contents.updateFailed": "Non è stato possibile aggiornare il contenuto. Per favore ricarica.",
"contents.validate": "Validare",
"contents.validate": "Convalida",
"contents.validationHint": "Ricorda di verificare tutte le lingue quando vedi errori di validazione.",
"contents.versionCompare": "Confronta",
"contents.versionDelete": "Cancella questa Versione",
@ -502,7 +502,7 @@
"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.schemaNewCardDescription": "Uno schema 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",
@ -537,7 +537,7 @@
"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.optional": "È 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.",
@ -554,8 +554,8 @@
"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.billingPortal": "Portale di fatturazione",
"plans.billingPortalHint": "Vai al portale 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.",
@ -564,10 +564,10 @@
"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.noPlanConfigured": "Nessun piano è stato impostato, quest'app ha un uso illimitato dello spazio su disco.",
"plans.notPlanOwner": "Non hai creato nessun abbonamento, pertanto non è possibile cambiare il piano.",
"plans.perMonth": "Al Mese",
"plans.perYear": "all'Anno",
"plans.perMonth": "Al mese",
"plans.perYear": "all'anno",
"plans.refreshTooltip": "Aggiorna i piani (CTRL + SHIFT + R)",
"plans.reloaded": "Piano aggiornati.",
"plans.selected": "Selezionato",
@ -578,7 +578,7 @@
"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 workflow e i pattern.",
"roles.defaults.editor": "Hai un'utenza che può modificare le risorse, i conteuti e visualizzare i workflow.",
"roles.defaults.editor": "Hai un'utenza che può modificare le risorse, i contenuti 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.",
@ -620,7 +620,7 @@
"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.enqueued": "Eventi messo in coda. L'evento potrà essere rieseguito 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",
@ -628,8 +628,8 @@
"rules.ruleEvents.nextAttemptLabel": "Successivo",
"rules.ruleEvents.numAttemptsLabel": "Tentativi",
"rules.ruleEvents.reloaded": "Eventi della regola ricaricati.",
"rules.ruleSyntax.if": "If",
"rules.ruleSyntax.then": "then",
"rules.ruleSyntax.if": "Se",
"rules.ruleSyntax.then": "Allora",
"rules.run": "Esegui",
"rules.runFailed": "Non è stato possibile eseguire la regola. Per favore ricarica.",
"rules.runFromSnapshots": "Esegui con l'ultimo stato",
@ -656,9 +656,9 @@
"schemas.addNestedField": "Aggiungi un campo annidato",
"schemas.changeCategoryFailed": "Non è stato possibile cambiare la categoria. Per favore ricarica.",
"schemas.clone": "Clona lo Schema",
"schemas.contentSidebarUrl": "Estensione della barra di navigazione laterale dei contenuti",
"schemas.contentSidebarUrl": "Estensione della barra di navigazione laterale (contenuti)",
"schemas.contentSidebarUrlHint": "URL del plug-in per la barra di navigazione laterale nella visualizzazione dei dettagli.",
"schemas.contentsSidebarUrl": "Contenuti Estensione della Barra di Navigazione Laterale",
"schemas.contentsSidebarUrl": "Estensione della barra di navigazione laterale (liste)",
"schemas.contentsSidebarUrlHint": "URL del plug-in per la barra di navigazione laterale nella visualizzazione delle liste.",
"schemas.contextMenuTour": "Apri il menu per cancellare lo schema o per inserire alcuni script che modificano il contenuto.",
"schemas.create": "Crea uno Schema",
@ -732,8 +732,8 @@
"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.previewImage": "Solamente l'anteprima o il nome del file se non è un immagine",
"schemas.fieldTypes.assets.previewImageAndFileName": "L'anteprima e il 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",
@ -798,7 +798,7 @@
"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 (Riferimento)",
"schemas.referenceFields": "Campi per i collegamenti (riferimenti)",
"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.",
@ -845,8 +845,8 @@
"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.queryAllNewestFirst": "Tutto (prima i più recenti)",
"search.queryAllOldestFirst": "Tutto (prima i meno recenti)",
"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.",

27
backend/i18n/source/backend_it.json

@ -31,11 +31,11 @@
"assets.folderRecursion": "Non è possibile aggiungere una cartella al proprio figlio.",
"assets.maxSizeReached": "Hai raggiunto la dimensione massima consentito per le risorse.",
"assets.referenced": "La risorsa è collegata ad un contenuto pertanto non può essere cancellata.",
"backups.alreadyRunning": "E' in esecuzione una altro processo di backup.",
"backups.alreadyRunning": "È 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.",
"backups.restoreRunning": "È 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.",
"comments.notUserComment": "È stato creato un commento da un altro utente.",
"common.action": "Azione",
"common.aspectHeight": "Altezza",
"common.aspectWidth": "Larghezza",
@ -51,6 +51,7 @@
"common.displayName": "Nome da visualizzare",
"common.editor": "Redattore",
"common.email": "Email",
"common.errorNoPermission": "Non hai i permessi necessari.",
"common.field": "Campo",
"common.fieldIds": "Campi ID",
"common.fieldName": "Campo nome",
@ -90,7 +91,7 @@
"common.name": "Nome",
"common.notFoundValue": "- non trovato -",
"common.numDays": "Num. giorni",
"common.odataFailure": "Fallito parsando la query: {message}",
"common.odataFailure": "Fallito il parsing della query: {message}",
"common.odataFilterNotValid": "OData $filter condizione non valida: {message}",
"common.odataNotSupported": "OData operazione non supportata.",
"common.odataSearchNotValid": "OData $search condizione non valida: {message}",
@ -123,7 +124,7 @@
"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.invalidArrayOfObjects": "Errore nel json, attesp un array di objects.",
"contents.invalidArrayOfObjects": "Errore nel json, atteso 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.",
@ -151,7 +152,7 @@
"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.max": "Deve essere minore o uguale a {max}.",
"contents.validation.maxCharacters": "Il testo non deve avere più di {max} carattere(i).",
"contents.validation.maximumHeight": "L'altezza {height}px deve essere inferiore a {max}px.",
"contents.validation.maximumSize": "La dimensione {size} deve essere inferiore a {max}.",
@ -163,7 +164,7 @@
"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.minItems": "Deve avere almeno {min} elemento(i).",
"contents.validation.minLength": "Deve avere almeno {min} carattere(i).",
"contents.validation.minNormalCharacters": "Deve avere almeno un testo di {min} carattere(i).",
"contents.validation.minWords": "Deve avere almeno {min} parola(e).",
@ -172,7 +173,7 @@
"contents.validation.normalCharactersBetween": "Deve essere un testo tra {min} e {max} carattere(i).",
"contents.validation.notAllowed": "Non è un valore consentito.",
"contents.validation.pattern": "Deve seguire il pattern.",
"contents.validation.reference": "La Geolocalizzazione può avere come campi solamente come latidutine e longitudine.",
"contents.validation.reference": "La geolocalizzazione può avere come campi solamente come latitudine e longitudine.",
"contents.validation.referenceNotFound": "Contiene un collegamento '{id}' non valido.",
"contents.validation.referenceToInvalidSchema": "Contiene dei collegamenti '{id}' ad uno schema errato.",
"contents.validation.regexTooSlow": "La regular expression è troppo lenta.",
@ -192,7 +193,7 @@
"dotnet_identity_PasswordMismatch": "Password errata.",
"dotnet_identity_PasswordRequiresDigit": "La Password devono contenere almeno un numero ('0'-'9').",
"dotnet_identity_PasswordRequiresLower": "La password deve avere almeno una lettera minuscola ('a'-'z').",
"dotnet_identity_PasswordRequiresNonAlphanumeric": "La passowrd deve avere almeno un carattere non alfanumerico.",
"dotnet_identity_PasswordRequiresNonAlphanumeric": "La password deve avere almeno un carattere non alfanumerico.",
"dotnet_identity_PasswordRequiresUniqueChars": "La password deve essere composta almeno da {0} caratteri differenti.",
"dotnet_identity_PasswordRequiresUpper": "La password deve avere almeno una lettera maiuscola ('A'-'Z').",
"dotnet_identity_PasswordTooShort": "La password è troppo corta.",
@ -247,7 +248,7 @@
"history.schemas.updated": "ha aggiornato lo schema {[Name]}.",
"history.statusChanged": "ha 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.ruleAlreadyRunning": "E' in esecuzione un'altra regola.",
"rules.ruleAlreadyRunning": "È in esecuzione un'altra regola.",
"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.",
@ -261,8 +262,8 @@
"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.references.resolveError": "È possibile risolvere il nome del collegamento solamente quando il numero massimo di elementi è 1.",
"schemas.string.inlineEditorError": "È 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": "Checkbox e dropdown menu hanno bisogno che siano definiti dei valori.",
"schemas.uiFieldCannotBeDisabled": "Il campo UI non può essere disabilitato.",
@ -370,7 +371,7 @@
"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.publishedNotDefined": "Il Workflow deve avere uno step pubblico.",
"workflows.publishedStepNotFound": "La Transition ha un obiettivo non valido.",
"workflows.schemaOverlap": "Lo schema '{schema}' è associato a diversi workflow."
}

89
backend/i18n/source/frontend_it.json

@ -1,6 +1,6 @@
{
"api.contentApi": "Content API",
"api.generalApi": "General API",
"api.contentApi": "API dei contenuti",
"api.generalApi": "API generali",
"api.graphql": "GraphQL",
"api.graphqlPageTitle": "GraphQL",
"api.pageTitle": "API",
@ -82,7 +82,7 @@
"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.metadataAdd": "Aggiungi un metadato",
"assets.moveFailed": "Non è stato possibile spostare la risorsa. Per favore ricarica.",
"assets.protected": "Protetto",
"assets.refreshTooltip": "Aggiorna le risorse (CTRL + SHIFT + R)",
@ -107,8 +107,8 @@
"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.uploadByDialog": "Seleziona i file",
"assets.uploadByDrop": "Trascina qui i file 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.",
@ -145,7 +145,7 @@
"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.allowAnonymousHint": "Consenti l'accesso alle API senza token di accesso a tutte le risorse che sono configurate per il ruolo di questo client. È possibile avere un solo client impostato con accesso anonimo.",
"clients.apiCallsLimit": "Numero Max di chiamate alle API",
"clients.apiCallsLimitHint": "Limita il numero di chiamate al mese effettuabili dal client alle API calls per riservare ai client più importanti il numero di chiamate API disponili.",
"clients.clientIdValidationMessage": "Il nome deve contenere solo lettere, numeri, trattini e spaziNa.",
@ -155,16 +155,16 @@
"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.cliStep1Hint": "Le release contengono file binari per tutte le operazioni di sistema maggiori e piccoli file da scaricare 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.cliStep3Hint": "È possibile gestire le configurazione 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.manuallyStep1": "Ottenere un token 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.manuallyStep3": "Aggiungi il token 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",
@ -181,13 +181,13 @@
"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.refreshTooltip": "Aggiorna 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.deleteConfirmText": "Sei sicuro di voler cancellare il commento?",
"comments.deleteConfirmTitle": "Cancella il comment",
"comments.deleteFailed": "Non è stato possibile cancellare il commento.",
"comments.follow": "Segui",
@ -208,9 +208,9 @@
"common.cancel": "Annulla",
"common.category": "Categoria",
"common.clear": "Pulisci",
"common.clientId": "Id Client",
"common.clientId": "Client Id",
"common.clients": "Client",
"common.clientSecret": "Secret Client",
"common.clientSecret": "Client Secret",
"common.clipboardAdded": "Il valore è stato aggiunto nei tuoi appunti.",
"common.clone": "Clona",
"common.cluster": "Cluster",
@ -291,12 +291,12 @@
"common.queryOperators.contains": "contiene",
"common.queryOperators.empty": "è vuoto",
"common.queryOperators.endsWith": "finisce con",
"common.queryOperators.eq": "è uguale a",
"common.queryOperators.ge": "è maggiore di o uguale a",
"common.queryOperators.eq": "è uguale a",
"common.queryOperators.ge": "è maggiore o uguale a",
"common.queryOperators.gt": "è maggiore di",
"common.queryOperators.le": "è minore di o uguale a",
"common.queryOperators.le": "è minore o uguale a",
"common.queryOperators.lt": "è minore di",
"common.queryOperators.ne": "è uguale a",
"common.queryOperators.ne": "non è uguale a",
"common.queryOperators.startsWith": "inizia con",
"common.refresh": "Aggiorna",
"common.remember": "Ricorda la mia decisione",
@ -324,9 +324,9 @@
"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.tagAdd": ", aggiungi tag",
"common.tagAddReference": ", aggiungi collegamento",
"common.tagAddSchema": ", aggiungi schema",
"common.tags": "Tag",
"common.tagsAll": "Tutti i tag",
"common.time": "Ora",
@ -338,7 +338,7 @@
"common.width": "Larghezza",
"common.workflow": "Workflow",
"common.workflows": "Workflow",
"common.yes": "Si",
"common.yes": "Sì",
"contents.arrayAddItem": "Aggiungi un elemento",
"contents.arrayClear": "Pulisci",
"contents.arrayClearConfirmText": "Sei sicuro di voler cancellare l'array?",
@ -356,7 +356,7 @@
"contents.assetsUpload": "Trascina i file o clicca",
"contents.autotranslate": "Traduci in automatico dalla lingua principale",
"contents.bulkFailed": "Non è stato possibile eliminare il contenuto. Per favore ricarica.",
"contents.changeStatusTo": "Cambia l'elemeto(i) del contenuti in {action}",
"contents.changeStatusTo": "Cambia gli elementi del contenuto 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).",
@ -400,6 +400,7 @@
"contents.noReferencing": "Questo contenuto non è collegato da altri contenuti.",
"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.pendingChangesTextToPreview": "Hai modifiche non salvate.\n\nNon li visualizzerai nell'anteprima.\n\n**Sei sicuro di voler continuare?**",
"contents.pendingChangesTitle": "Modifiche non salvate",
"contents.publishAll": "Pubblica tutto",
"contents.referencesCreateNew": "Aggiungi nuovo",
@ -437,12 +438,12 @@
"contents.unpublishReferrerConfirmTitle": "Rimuovi dalla pubblicazione il contenuto",
"contents.unsavedChangesText": "Non hai salvato le modifiche. Vuoi salvarle adesso?",
"contents.unsavedChangesTitle": "Modifiche non salvate",
"contents.unsetValue": "Valore non impostato",
"contents.unsetValueConfirmText": "Se annulli il valore impostato potresti perdere le tue modifiche.\n\nSei sicuro di voler procedere?",
"contents.unsetValue": "Cancella il valore",
"contents.unsetValueConfirmText": "Se cancelli il valore impostato potresti perdere le tue modifiche.\n\nSei sicuro di voler procedere?",
"contents.unsetValueConfirmTitle": "Sei sicuro di voler annullare il valore impostato?",
"contents.updated": "Contenuto aggiornato con successo.",
"contents.updateFailed": "Non è stato possibile aggiornare il contenuto. Per favore ricarica.",
"contents.validate": "Validare",
"contents.validate": "Convalida",
"contents.validationHint": "Ricorda di verificare tutte le lingue quando vedi errori di validazione.",
"contents.versionCompare": "Confronta",
"contents.versionDelete": "Cancella questa Versione",
@ -501,7 +502,7 @@
"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.schemaNewCardDescription": "Uno schema 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",
@ -536,7 +537,7 @@
"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.optional": "È 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.",
@ -553,8 +554,8 @@
"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.billingPortal": "Portale di fatturazione",
"plans.billingPortalHint": "Vai al portale 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.",
@ -563,10 +564,10 @@
"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.noPlanConfigured": "Nessun piano è stato impostato, quest'app ha un uso illimitato dello spazio su disco.",
"plans.notPlanOwner": "Non hai creato nessun abbonamento, pertanto non è possibile cambiare il piano.",
"plans.perMonth": "Al Mese",
"plans.perYear": "all'Anno",
"plans.perMonth": "Al mese",
"plans.perYear": "all'anno",
"plans.refreshTooltip": "Aggiorna i piani (CTRL + SHIFT + R)",
"plans.reloaded": "Piano aggiornati.",
"plans.selected": "Selezionato",
@ -577,7 +578,7 @@
"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 workflow e i pattern.",
"roles.defaults.editor": "Hai un'utenza che può modificare le risorse, i conteuti e visualizzare i workflow.",
"roles.defaults.editor": "Hai un'utenza che può modificare le risorse, i contenuti 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.",
@ -619,7 +620,7 @@
"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.enqueued": "Eventi messo in coda. L'evento potrà essere rieseguito 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",
@ -627,8 +628,8 @@
"rules.ruleEvents.nextAttemptLabel": "Successivo",
"rules.ruleEvents.numAttemptsLabel": "Tentativi",
"rules.ruleEvents.reloaded": "Eventi della regola ricaricati.",
"rules.ruleSyntax.if": "If",
"rules.ruleSyntax.then": "then",
"rules.ruleSyntax.if": "Se",
"rules.ruleSyntax.then": "Allora",
"rules.run": "Esegui",
"rules.runFailed": "Non è stato possibile eseguire la regola. Per favore ricarica.",
"rules.runFromSnapshots": "Esegui con l'ultimo stato",
@ -655,9 +656,9 @@
"schemas.addNestedField": "Aggiungi un campo annidato",
"schemas.changeCategoryFailed": "Non è stato possibile cambiare la categoria. Per favore ricarica.",
"schemas.clone": "Clona lo Schema",
"schemas.contentSidebarUrl": "Estensione della barra di navigazione laterale dei contenuti",
"schemas.contentSidebarUrl": "Estensione della barra di navigazione laterale (contenuti)",
"schemas.contentSidebarUrlHint": "URL del plug-in per la barra di navigazione laterale nella visualizzazione dei dettagli.",
"schemas.contentsSidebarUrl": "Contenuti Estensione della Barra di Navigazione Laterale",
"schemas.contentsSidebarUrl": "Estensione della barra di navigazione laterale (liste)",
"schemas.contentsSidebarUrlHint": "URL del plug-in per la barra di navigazione laterale nella visualizzazione delle liste.",
"schemas.contextMenuTour": "Apri il menu per cancellare lo schema o per inserire alcuni script che modificano il contenuto.",
"schemas.create": "Crea uno Schema",
@ -731,8 +732,8 @@
"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.previewImage": "Solamente l'anteprima o il nome del file se non è un immagine",
"schemas.fieldTypes.assets.previewImageAndFileName": "L'anteprima e il 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",
@ -797,7 +798,7 @@
"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 (Riferimento)",
"schemas.referenceFields": "Campi per i collegamenti (riferimenti)",
"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.",
@ -844,8 +845,8 @@
"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.queryAllNewestFirst": "Tutto (prima i più recenti)",
"search.queryAllOldestFirst": "Tutto (prima i meno recenti)",
"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.",

5
backend/src/Migrations/Migrations/ConvertEventStore.cs

@ -9,7 +9,6 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Migrations;
@ -53,10 +52,10 @@ namespace Migrations.Migrations
{
foreach (BsonDocument @event in commit["Events"].AsBsonArray)
{
var meta = JObject.Parse(@event["Metadata"].AsString);
var meta = BsonDocument.Parse(@event["Metadata"].AsString);
@event.Remove("EventId");
@event["Metadata"] = meta.ToBson();
@event["Metadata"] = meta;
}
await WriteAsync(new ReplaceOneModel<BsonDocument>(filter.Eq("_id", commit["_id"].AsString), commit), false);

5
backend/src/Migrations/Migrations/ConvertEventStoreAppId.cs

@ -10,7 +10,6 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Migrations;
@ -60,11 +59,11 @@ namespace Migrations.Migrations
foreach (BsonDocument @event in commit["Events"].AsBsonArray)
{
var data = JObject.Parse(@event["Payload"].AsString);
var data = BsonDocument.Parse(@event["Payload"].AsString);
if (data.TryGetValue("appId", out var appIdValue))
{
var appId = NamedId<Guid>.Parse(appIdValue.ToString(), Guid.TryParse).Id.ToString();
var appId = NamedId<Guid>.Parse(appIdValue.AsString, Guid.TryParse).Id.ToString();
var eventUpdate = updater.Set($"Events.{index}.Metadata.AppId", appId);

36
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs

@ -1,36 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class AppClientsConverter : JsonClassConverter<AppClients>
{
protected override void WriteValue(JsonWriter writer, AppClients value, JsonSerializer serializer)
{
var json = new Dictionary<string, AppClient>(value.Count);
foreach (var (key, client) in value)
{
json.Add(key, client);
}
serializer.Serialize(writer, json);
}
protected override AppClients ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<string, AppClient>>(reader)!;
return new AppClients(json);
}
}
}

33
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsSurrogate.cs

@ -0,0 +1,33 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class AppClientsSurrogate : Dictionary<string, AppClient>, ISurrogate<AppClients>
{
public void FromSource(AppClients source)
{
foreach (var (key, client) in source)
{
Add(key, client);
}
}
public AppClients ToSource()
{
if (Count == 0)
{
return AppClients.Empty;
}
return new AppClients(this);
}
}
}

36
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs

@ -1,36 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class AppContributorsConverter : JsonClassConverter<AppContributors>
{
protected override void WriteValue(JsonWriter writer, AppContributors value, JsonSerializer serializer)
{
var json = new Dictionary<string, string>(value.Count);
foreach (var (userId, role) in value)
{
json.Add(userId, role);
}
serializer.Serialize(writer, json);
}
protected override AppContributors ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<string, string>>(reader)!;
return new AppContributors(json!);
}
}
}

33
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsSurrogate.cs

@ -0,0 +1,33 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class AppContributorsSurrogate : Dictionary<string, string>, ISurrogate<AppContributors>
{
public void FromSource(AppContributors source)
{
foreach (var (userId, role) in source)
{
Add(userId, role);
}
}
public AppContributors ToSource()
{
if (Count == 0)
{
return AppContributors.Empty;
}
return new AppContributors(this);
}
}
}

37
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs

@ -1,37 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class AppPatternsConverter : JsonClassConverter<AppPatterns>
{
protected override void WriteValue(JsonWriter writer, AppPatterns value, JsonSerializer serializer)
{
var json = new Dictionary<DomainId, AppPattern>(value.Count);
foreach (var (key, pattern) in value)
{
json.Add(key, pattern);
}
serializer.Serialize(writer, json);
}
protected override AppPatterns ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<DomainId, AppPattern>>(reader)!;
return new AppPatterns(json);
}
}
}

33
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsSurrogate.cs

@ -0,0 +1,33 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class AppPatternsSurrogate : Dictionary<DomainId, AppPattern>, ISurrogate<AppPatterns>
{
public void FromSource(AppPatterns source)
{
foreach (var (key, pattern) in source)
{
Add(key, pattern);
}
}
public AppPatterns ToSource()
{
if (Count == 0)
{
return AppPatterns.Empty;
}
return new AppPatterns(this);
}
}
}

18
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonLanguageConfig.cs → backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguageConfigSurrogate.cs

@ -6,32 +6,24 @@
// ==========================================================================
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public class JsonLanguageConfig
public sealed class LanguageConfigSurrogate : ISurrogate<LanguageConfig>
{
[JsonProperty]
public Language[]? Fallback { get; set; }
[JsonProperty]
public bool IsOptional { get; set; }
public JsonLanguageConfig()
public void FromSource(LanguageConfig source)
{
}
public JsonLanguageConfig(LanguageConfig config)
{
SimpleMapper.Map(config, this);
IsOptional = source.IsOptional;
Fallback = config.Fallbacks.ToArray();
Fallback = source.Fallbacks.ToArray();
}
public LanguageConfig ToConfig()
public LanguageConfig ToSource()
{
if (!IsOptional && (Fallback == null || Fallback.Length == 0))
{

30
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs

@ -1,30 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class LanguagesConfigConverter : JsonClassConverter<LanguagesConfig>
{
protected override void WriteValue(JsonWriter writer, LanguagesConfig value, JsonSerializer serializer)
{
var json = new JsonLanguagesConfig(value);
serializer.Serialize(writer, json);
}
protected override LanguagesConfig ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<JsonLanguagesConfig>(reader)!;
return json.ToConfig();
}
}
}

27
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonLanguagesConfig.cs → backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigSurrogate.cs

@ -7,32 +7,33 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class JsonLanguagesConfig
public sealed class LanguagesConfigSurrogate : ISurrogate<LanguagesConfig>
{
[JsonProperty]
public Dictionary<string, JsonLanguageConfig> Languages { get; set; }
public Dictionary<string, LanguageConfigSurrogate> Languages { get; set; }
[JsonProperty]
public string Master { get; set; }
public JsonLanguagesConfig()
public void FromSource(LanguagesConfig source)
{
}
Languages = source.Languages.ToDictionary(x => x.Key, source =>
{
var surrogate = new LanguageConfigSurrogate();
public JsonLanguagesConfig(LanguagesConfig value)
{
Languages = value.Languages.ToDictionary(x => x.Key, x => new JsonLanguageConfig(x.Value));
surrogate.FromSource(source.Value);
return surrogate;
});
Master = value.Master;
Master = source.Master;
}
public LanguagesConfig ToConfig()
public LanguagesConfig ToSource()
{
var languages = Languages.ToDictionary(x => x.Key, x => x.Value.ToConfig());
var languages = Languages.ToDictionary(x => x.Key, x => x.Value.ToSource());
var master = Master ?? languages.Keys.First();

72
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RoleConverter.cs

@ -1,72 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class RoleConverter : JsonClassConverter<JsonRole>
{
protected override void WriteValue(JsonWriter writer, JsonRole value, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName("permissions");
serializer.Serialize(writer, value.Permissions);
writer.WritePropertyName("properties");
serializer.Serialize(writer, value.Properties);
writer.WriteEndObject();
}
protected override JsonRole ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var permissions = Array.Empty<string>();
var properties = (JsonObject?)null;
if (reader.TokenType == JsonToken.StartArray)
{
permissions = serializer.Deserialize<string[]>(reader)!;
}
else
{
while (reader.Read() && reader.TokenType != JsonToken.EndObject)
{
if (reader.TokenType == JsonToken.PropertyName)
{
var propertyName = reader.Value!.ToString()!;
if (!reader.Read())
{
throw new JsonSerializationException("Unexpected end when reading role.");
}
switch (propertyName.ToLowerInvariant())
{
case "permissions":
permissions = serializer.Deserialize<string[]>(reader)!;
break;
case "properties":
properties = serializer.Deserialize<JsonObject>(reader)!;
break;
}
}
}
}
return new JsonRole
{
Permissions = permissions,
Properties = properties ?? JsonValue.Object()
};
}
}
}

59
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs

@ -1,59 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
using Squidex.Infrastructure.Security;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class RolesConverter : JsonClassConverter<Roles>
{
protected override void WriteValue(JsonWriter writer, Roles value, JsonSerializer serializer)
{
var json = new Dictionary<string, JsonRole>(value.CustomCount);
foreach (var role in value.Custom)
{
json.Add(role.Name, new JsonRole
{
Permissions = role.Permissions.ToIds().ToArray(),
Properties = role.Properties
});
}
serializer.Serialize(writer, json);
}
protected override Roles ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<string, JsonRole>>(reader)!;
if (json.Count == 0)
{
return Roles.Empty;
}
return new Roles(json.ToDictionary(x => x.Key, x =>
{
var (key, value) = x;
var permissions = PermissionSet.Empty;
if (value.Permissions.Length > 0)
{
permissions = new PermissionSet(value.Permissions);
}
return new Role(key, permissions, value.Properties);
}));
}
}
}

76
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesSurrogate.cs

@ -0,0 +1,76 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Security;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public sealed class RolesSurrogate : Dictionary<string, IJsonValue>, ISurrogate<Roles>
{
public void FromSource(Roles source)
{
foreach (var customRole in source.Custom)
{
var permissions = JsonValue.Array();
foreach (var permission in customRole.Permissions)
{
permissions.Add(JsonValue.Create(permission.Id));
}
var role =
JsonValue.Object()
.Add("permissions", permissions)
.Add("properties", customRole.Properties);
Add(customRole.Name, role);
}
}
public Roles ToSource()
{
if (Count == 0)
{
return Roles.Empty;
}
return new Roles(this.ToDictionary(x => x.Key, x =>
{
var (key, value) = x;
var properties = JsonValue.Object();
var permissions = PermissionSet.Empty;
if (value is JsonArray array)
{
if (array.Count > 0)
{
permissions = new PermissionSet(array.OfType<JsonString>().Select(x => x.ToString()));
}
}
else if (value is JsonObject obj)
{
if (obj.TryGetValue("permissions", out array!) && array.Count > 0)
{
permissions = new PermissionSet(array.OfType<JsonString>().Select(x => x.ToString()));
}
if (!obj.TryGetValue<JsonObject>("properties", out properties))
{
properties = JsonValue.Object();
}
}
return new Role(key, permissions, properties);
}));
}
}
}

25
backend/src/Squidex.Domain.Apps.Core.Model/Contents/ContentFieldData.cs

@ -30,35 +30,18 @@ namespace Squidex.Domain.Apps.Core.Contents
{
}
public ContentFieldData AddValue(object? value)
public ContentFieldData AddInvariant(object? value)
{
return AddJsonValue(JsonValue.Create(value));
}
public ContentFieldData AddValue(string key, object? value)
{
return AddJsonValue(key, JsonValue.Create(value));
}
public ContentFieldData AddJsonValue(IJsonValue value)
{
this[InvariantPartitioning.Key] = value;
this[InvariantPartitioning.Key] = JsonValue.Create(value);
return this;
}
public ContentFieldData AddJsonValue(string key, IJsonValue value)
public ContentFieldData AddLocalized(string key, object? value)
{
Guard.NotNullOrEmpty(key, nameof(key));
if (Language.IsValidLanguage(key))
{
this[key] = value;
}
else
{
this[key] = value;
}
this[key] = JsonValue.Create(value);
return this;
}

39
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/StatusConverter.cs

@ -1,39 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Contents.Json
{
public sealed class StatusConverter : JsonConverter, ISupportedTypes
{
public IEnumerable<Type> SupportedTypes
{
get { yield return typeof(Status); }
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
writer.WriteValue(value!.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader)!;
return new Status(value);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Status);
}
}
}

30
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowStepConverter.cs

@ -1,30 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Contents.Json
{
public sealed class WorkflowStepConverter : JsonClassConverter<WorkflowStep>
{
protected override void WriteValue(JsonWriter writer, WorkflowStep value, JsonSerializer serializer)
{
var json = new JsonWorkflowStep(value);
serializer.Serialize(writer, json);
}
protected override WorkflowStep ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<JsonWorkflowStep>(reader)!;
return json.ToStep();
}
}
}

33
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/JsonWorkflowStep.cs → backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowStepSurrogate.cs

@ -8,17 +8,14 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Contents.Json
{
public sealed class JsonWorkflowStep
public sealed class WorkflowStepSurrogate : ISurrogate<WorkflowStep>
{
[JsonProperty]
public Dictionary<Status, JsonWorkflowTransition> Transitions { get; set; }
[JsonProperty]
public string? Color { get; set; }
public Dictionary<Status, WorkflowTransitionSurrogate> Transitions { get; set; }
[JsonProperty("noUpdate")]
public bool NoUpdateFlag { get; set; }
@ -26,21 +23,23 @@ namespace Squidex.Domain.Apps.Core.Contents.Json
[JsonProperty("noUpdateRules")]
public NoUpdate? NoUpdate { get; set; }
public JsonWorkflowStep()
{
}
public string? Color { get; set; }
public JsonWorkflowStep(WorkflowStep step)
public void FromSource(WorkflowStep source)
{
SimpleMapper.Map(step, this);
SimpleMapper.Map(source, this);
Transitions =
step.Transitions.ToDictionary(
x => x.Key,
x => new JsonWorkflowTransition(x.Value));
Transitions = source.Transitions.ToDictionary(x => x.Key, source =>
{
var surrogate = new WorkflowTransitionSurrogate();
surrogate.FromSource(source.Value);
return surrogate;
});
}
public WorkflowStep ToStep()
public WorkflowStep ToSource()
{
var noUpdate = NoUpdate;
@ -52,7 +51,7 @@ namespace Squidex.Domain.Apps.Core.Contents.Json
var transitions =
Transitions?.ToDictionary(
x => x.Key,
x => x.Value.ToTransition());
x => x.Value.ToSource());
return new WorkflowStep(transitions, Color, noUpdate);
}

20
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/JsonWorkflowTransition.cs → backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowTransitionSurrogate.cs

@ -6,32 +6,26 @@
// ==========================================================================
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Contents.Json
{
public class JsonWorkflowTransition
public sealed class WorkflowTransitionSurrogate : ISurrogate<WorkflowTransition>
{
[JsonProperty]
public string? Expression { get; set; }
[JsonProperty]
public string? Role { get; set; }
[JsonProperty]
public string[]? Roles { get; set; }
public JsonWorkflowTransition()
{
}
public JsonWorkflowTransition(WorkflowTransition transition)
public void FromSource(WorkflowTransition source)
{
Roles = transition.Roles?.ToArray();
Roles = source.Roles?.ToArray();
Expression = transition.Expression;
Expression = source.Expression;
}
public WorkflowTransition ToTransition()
public WorkflowTransition ToSource()
{
var roles = Roles;

37
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsConverter.cs

@ -1,37 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Contents.Json
{
public sealed class WorkflowsConverter : JsonClassConverter<Workflows>
{
protected override void WriteValue(JsonWriter writer, Workflows value, JsonSerializer serializer)
{
var json = new Dictionary<DomainId, Workflow>(value.Count);
foreach (var (key, workflow) in value)
{
json.Add(key, workflow);
}
serializer.Serialize(writer, json);
}
protected override Workflows ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<DomainId, Workflow>>(reader);
return new Workflows(json!);
}
}
}

28
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsSurrogate.cs

@ -0,0 +1,28 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Contents.Json
{
public sealed class WorkflowsSurrogate : Dictionary<DomainId, Workflow>, ISurrogate<Workflows>
{
public void FromSource(Workflows source)
{
foreach (var (key, workflow) in source)
{
Add(key, workflow);
}
}
public Workflows ToSource()
{
return new Workflows(this);
}
}
}

14
backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusInfo.cs

@ -5,19 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class StatusInfo
public sealed record StatusInfo(Status Status, string Color)
{
public Status Status { get; }
public string Color { get; }
public StatusInfo(Status status, string color)
{
Status = status;
Color = color;
}
}
}

7
backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusTypeConverter.cs

@ -25,12 +25,7 @@ namespace Squidex.Domain.Apps.Core.Contents
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string s)
{
return new Status(s);
}
return default(Status);
return new Status((string)value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)

26
backend/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs

@ -1,26 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Rules.Json
{
public sealed class RuleConverter : JsonClassConverter<Rule>
{
protected override void WriteValue(JsonWriter writer, Rule value, JsonSerializer serializer)
{
serializer.Serialize(writer, new JsonRule(value));
}
protected override Rule ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
return serializer.Deserialize<JsonRule>(reader)!.ToRule();
}
}
}

18
backend/src/Squidex.Domain.Apps.Core.Model/Rules/Json/JsonRule.cs → backend/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleSorrgate.cs

@ -5,36 +5,28 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Migrations;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Rules.Json
{
public sealed class JsonRule
public sealed class RuleSorrgate : ISurrogate<Rule>
{
[JsonProperty]
public RuleTrigger Trigger { get; set; }
[JsonProperty]
public RuleAction Action { get; set; }
[JsonProperty]
public bool IsEnabled { get; set; }
[JsonProperty]
public string Name { get; set; }
public JsonRule()
public void FromSource(Rule source)
{
SimpleMapper.Map(source, this);
}
public JsonRule(Rule rule)
{
SimpleMapper.Map(rule, this);
}
public Rule ToRule()
public Rule ToSource()
{
var trigger = Trigger;

25
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonFieldModel.cs → backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/FieldSurrogate.cs

@ -6,45 +6,35 @@
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using P = Squidex.Domain.Apps.Core.Partitioning;
using System.Linq;
namespace Squidex.Domain.Apps.Core.Schemas.Json
{
public sealed class JsonFieldModel : IFieldSettings
public sealed class FieldSurrogate : IFieldSettings
{
[JsonProperty]
public long Id { get; set; }
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public string Partitioning { get; set; }
[JsonProperty]
public bool IsHidden { get; set; }
[JsonProperty]
public bool IsLocked { get; set; }
[JsonProperty]
public bool IsDisabled { get; set; }
[JsonProperty]
public FieldProperties Properties { get; set; }
[JsonProperty]
public JsonNestedFieldModel[]? Children { get; set; }
public FieldSurrogate[]? Children { get; set; }
public RootField ToField()
{
var partitioning = P.FromString(Partitioning);
var partitioning = Core.Partitioning.FromString(Partitioning);
if (Properties is ArrayFieldProperties arrayProperties)
{
var nested = Children?.Map(n => n.ToNestedField()) ?? Array.Empty<NestedField>();
var nested = Children?.Select(n => n.ToNestedField()).ToArray() ?? Array.Empty<NestedField>();
return new ArrayField(Id, Name, partitioning, nested, arrayProperties, this);
}
@ -53,5 +43,10 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
return Properties.CreateRootField(Id, Name, partitioning, this);
}
}
public NestedField ToNestedField()
{
return Properties.CreateNestedField(Id, Name, this);
}
}
}

37
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs

@ -1,37 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json;
namespace Squidex.Domain.Apps.Core.Schemas.Json
{
public sealed class JsonNestedFieldModel : IFieldSettings
{
[JsonProperty]
public long Id { get; set; }
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public bool IsHidden { get; set; }
[JsonProperty]
public bool IsLocked { get; set; }
[JsonProperty]
public bool IsDisabled { get; set; }
[JsonProperty]
public FieldProperties Properties { get; set; }
public NestedField ToNestedField()
{
return Properties.CreateNestedField(Id, Name, this);
}
}
}

26
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs

@ -1,26 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Schemas.Json
{
public sealed class SchemaConverter : JsonClassConverter<Schema>
{
protected override void WriteValue(JsonWriter writer, Schema value, JsonSerializer serializer)
{
serializer.Serialize(writer, new JsonSchemaModel(value));
}
protected override Schema ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
return serializer.Deserialize<JsonSchemaModel>(reader)!.ToSchema();
}
}
}

38
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs → backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaSurrogate.cs

@ -8,58 +8,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Schemas.Json
{
public sealed class JsonSchemaModel
public sealed class SchemaSurrogate : ISurrogate<Schema>
{
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public string Category { get; set; }
[JsonProperty]
public bool IsSingleton { get; set; }
[JsonProperty]
public bool IsPublished { get; set; }
[JsonProperty]
public SchemaProperties Properties { get; set; }
[JsonProperty]
public SchemaScripts? Scripts { get; set; }
[JsonProperty]
public FieldNames? FieldsInLists { get; set; }
[JsonProperty]
public FieldNames? FieldsInReferences { get; set; }
[JsonProperty]
public FieldRules? FieldRules { get; set; }
[JsonProperty]
public JsonFieldModel[] Fields { get; set; }
public FieldSurrogate[] Fields { get; set; }
[JsonProperty]
public Dictionary<string, string>? PreviewUrls { get; set; }
public JsonSchemaModel()
public void FromSource(Schema source)
{
}
public JsonSchemaModel(Schema schema)
{
SimpleMapper.Map(schema, this);
SimpleMapper.Map(source, this);
Fields =
schema.Fields.Select(x =>
new JsonFieldModel
source.Fields.Select(x =>
new FieldSurrogate
{
Id = x.Id,
Name = x.Name,
@ -71,15 +55,15 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
Properties = x.RawProperties
}).ToArray();
PreviewUrls = schema.PreviewUrls.ToDictionary(x => x.Key, x => x.Value);
PreviewUrls = source.PreviewUrls.ToDictionary(x => x.Key, x => x.Value);
}
private static JsonNestedFieldModel[]? CreateChildren(IField field)
private static FieldSurrogate[]? CreateChildren(IField field)
{
if (field is ArrayField arrayField)
{
return arrayField.Fields.Select(x =>
new JsonNestedFieldModel
new FieldSurrogate
{
Id = x.Id,
Name = x.Name,
@ -93,9 +77,9 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
return null;
}
public Schema ToSchema()
public Schema ToSource()
{
var fields = Fields.Map(f => f.ToField()) ?? Array.Empty<RootField>();
var fields = Fields?.Select(f => f.ToField()).ToArray() ?? Array.Empty<RootField>();
var schema = new Schema(Name, fields, Properties, IsPublished, IsSingleton);

2
backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueGenerator.cs

@ -66,7 +66,7 @@ namespace Squidex.Domain.Apps.Core.DefaultValues
if (!fieldData.TryGetValue(partitionKey, out var value) || ShouldApplyDefaultValue(field, value))
{
fieldData.AddJsonValue(partitionKey, defaultValue);
fieldData.AddLocalized(partitionKey, defaultValue);
}
}

11
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs

@ -16,6 +16,17 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{
public static class ContentReferencesExtensions
{
public static HashSet<DomainId> GetReferencedIds(this ContentData source, Schema schema, int referencesPerField = int.MaxValue)
{
Guard.NotNull(schema, nameof(schema));
var ids = new HashSet<DomainId>();
AddReferencedIds(source, schema, ids, referencesPerField);
return ids;
}
public static void AddReferencedIds(this ContentData source, Schema schema, HashSet<DomainId> result, int referencesPerField = int.MaxValue)
{
Guard.NotNull(schema, nameof(schema));

4
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs

@ -287,7 +287,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
break;
case "timestamp":
{
var instant = InstantPattern.General.Parse(text);
var instant = InstantPattern.ExtendedIso.Parse(text);
if (instant.Success)
{
@ -299,7 +299,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
case "timestamp_sec":
{
var instant = InstantPattern.General.Parse(text);
var instant = InstantPattern.ExtendedIso.Parse(text);
if (instant.Success)
{

2
backend/src/Squidex.Domain.Apps.Core.Operations/Templates/Extensions/DateTimeFluidExtension.cs

@ -58,7 +58,7 @@ namespace Squidex.Domain.Apps.Core.Templates.Extensions
{
var value = stringValue.ToStringValue();
var instant = InstantPattern.General.Parse(value);
var instant = InstantPattern.ExtendedIso.Parse(value);
if (instant.Success)
{

2
backend/src/Squidex.Domain.Apps.Core.Operations/Templates/FluidTemplateEngine.cs

@ -45,6 +45,8 @@ namespace Squidex.Domain.Apps.Core.Templates
FluidValue.SetTypeMapping(type, x => new StringValue(x.ToString()));
}
FluidValue.SetTypeMapping<RefTokenType>(x => new StringValue(x.ToString().ToLowerInvariant()));
globalTypes.Register<NamedId<DomainId>>();
globalTypes.Register<NamedId<Guid>>();
globalTypes.Register<NamedId<string>>();

2
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs

@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
{
if (args.Value.Type == JsonValueType.String)
{
var parseResult = InstantPattern.General.Parse(args.Value.ToString());
var parseResult = InstantPattern.ExtendedIso.Parse(args.Value.ToString());
if (!parseResult.Success)
{

2
backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueValidator.cs

@ -89,7 +89,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent
{
if (args.Value.Type == JsonValueType.String)
{
var parseResult = InstantPattern.General.Parse(args.Value.ToString());
var parseResult = InstantPattern.ExtendedIso.Parse(args.Value.ToString());
return parseResult.Success;
}

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs

@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Hosting;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Queries;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
@ -26,10 +27,11 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
private readonly MongoContentCollection collectionAll;
private readonly MongoContentCollection collectionPublished;
private readonly IAppProvider appProvider;
static MongoContentRepository()
{
StatusSerializer.Register();
TypeConverterStringSerializer<Status>.Register();
}
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider, bool useWildcardIndex)
@ -43,6 +45,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
collectionPublished =
new MongoContentCollection(
"States_Contents_Published3", database, appProvider, useWildcardIndex);
this.appProvider = appProvider;
}
public async Task InitializeAsync(CancellationToken ct = default)

17
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs

@ -9,6 +9,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ExtractReferenceIds;
using Squidex.Domain.Apps.Entities.Contents.DomainObject;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
@ -93,9 +94,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
private async Task UpsertDraftContentAsync(ContentDomainObject.State value, long oldVersion, long newVersion)
{
var content = CreateContent(value, newVersion);
var content = await CreateContentAsync(value, value.Data, newVersion);
content.Data = value.Data;
content.ScheduledAt = value.ScheduleJob?.DueTime;
content.ScheduleJob = value.ScheduleJob;
content.NewStatus = value.NewStatus;
@ -105,9 +105,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
private async Task UpsertPublishedContentAsync(ContentDomainObject.State value, long oldVersion, long newVersion)
{
var content = CreateContent(value, newVersion);
var content = await CreateContentAsync(value, value.CurrentVersion.Data, newVersion);
content.Data = value.CurrentVersion.Data;
content.ScheduledAt = null;
content.ScheduleJob = null;
content.NewStatus = null;
@ -115,15 +114,23 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
await collectionPublished.UpsertVersionedAsync(content.DocumentId, oldVersion, content);
}
private static MongoContentEntity CreateContent(ContentDomainObject.State value, long newVersion)
private async Task<MongoContentEntity> CreateContentAsync(ContentDomainObject.State value, ContentData data, long newVersion)
{
var content = SimpleMapper.Map(value, new MongoContentEntity());
content.Data = data;
content.DocumentId = value.UniqueId;
content.IndexedAppId = value.AppId.Id;
content.IndexedSchemaId = value.SchemaId.Id;
content.Version = newVersion;
var schema = await appProvider.GetSchemaAsync(value.AppId.Id, value.SchemaId.Id, true);
if (schema != null)
{
content.ReferencedIds = content.Data.GetReferencedIds(schema.SchemaDef);
}
return content;
}
}

2
backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs

@ -332,7 +332,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject
CreateInitialLanguage()
};
if (command.Actor.IsSubject)
if (command.Actor.IsUser)
{
events.Add(CreateInitialOwner(command.Actor));
}

2
backend/src/Squidex.Domain.Apps.Entities/Apps/Invitation/InvitationEventConsumer.cs

@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Invitation
if (@event.Payload is AppContributorAssigned appContributorAssigned)
{
if (!appContributorAssigned.Actor.IsSubject || !appContributorAssigned.IsAdded)
if (!appContributorAssigned.Actor.IsUser || !appContributorAssigned.IsAdded)
{
return;
}

8
backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs

@ -60,10 +60,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
new ContentData()
.AddField("title",
new ContentFieldData()
.AddValue("My first post with Squidex"))
.AddInvariant("My first post with Squidex"))
.AddField("text",
new ContentFieldData()
.AddValue("Just created a blog with Squidex. I love it!")),
.AddInvariant("Just created a blog with Squidex. I love it!")),
Publish = true
});
}
@ -79,10 +79,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
new ContentData()
.AddField("title",
new ContentFieldData()
.AddValue("About Me"))
.AddInvariant("About Me"))
.AddField("text",
new ContentFieldData()
.AddValue("I love Squidex and SciFi!")),
.AddInvariant("I love Squidex and SciFi!")),
Publish = true
});
}

6
backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs

@ -64,13 +64,13 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates
new ContentData()
.AddField("firstName",
new ContentFieldData()
.AddValue("John"))
.AddInvariant("John"))
.AddField("lastName",
new ContentFieldData()
.AddValue("Doe"))
.AddInvariant("Doe"))
.AddField("profession",
new ContentFieldData()
.AddValue("Software Developer")),
.AddInvariant("Software Developer")),
SchemaId = postsId
});
}

2
backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs

@ -247,7 +247,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
var actor = CurrentJob.Actor;
if (actor?.IsSubject == true)
if (actor?.IsUser == true)
{
try
{

12
backend/src/Squidex.Domain.Apps.Entities/Backup/UserMapping.cs

@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
Guard.NotNull(token, nameof(token));
if (!token.IsSubject)
if (!token.IsUser)
{
return;
}
@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
if (!userMap.ContainsKey(userId))
{
userMap[userId] = new RefToken(RefTokenType.Subject, userId);
userMap[userId] = RefToken.User(userId);
}
}
@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
if (user != null)
{
userMap[userId] = new RefToken(RefTokenType.Subject, user.Id);
userMap[userId] = RefToken.User(user.Id);
}
}
}
@ -87,13 +87,14 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
Guard.NotNullOrEmpty(userId, nameof(userId));
result = initiator;
if (userMap.TryGetValue(userId, out var mapped))
{
result = mapped;
return true;
}
result = initiator;
return false;
}
@ -101,6 +102,8 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
Guard.NotNull(token, nameof(token));
result = initiator;
if (token.IsClient)
{
result = token;
@ -113,7 +116,6 @@ namespace Squidex.Domain.Apps.Entities.Backup
return true;
}
result = initiator;
return false;
}
}

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

@ -6,8 +6,8 @@
// ==========================================================================
using System;
using System.Linq;
using System.Threading.Tasks;
using GraphQL;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NodaTime;
@ -19,6 +19,7 @@ using Squidex.Infrastructure;
using Squidex.Log;
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
#pragma warning disable RECS0082 // Parameter has the same name as a member and hides it
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
@ -30,7 +31,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
private readonly IServiceProvider serviceProvider;
private readonly GraphQLOptions options;
public sealed record CacheEntry(GraphQLModel Model, string Hash, Instant Created);
private sealed record CacheEntry(GraphQLModel Model, string Hash, Instant Created);
public IServiceProvider Services
{
get { return serviceProvider; }
}
public CachingGraphQLService(IBackgroundCache cache, ISchemasHash schemasHash, IServiceProvider serviceProvider, IOptions<GraphQLOptions> options)
{
@ -45,55 +51,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
this.options = options.Value;
}
public async Task<(bool HasError, object Response)> QueryAsync(Context context, params GraphQLQuery[] queries)
{
Guard.NotNull(context, nameof(context));
Guard.NotNull(queries, nameof(queries));
var model = await GetModelAsync(context.App);
var executionContext =
serviceProvider.GetRequiredService<GraphQLExecutionContext>()
.WithContext(context);
var result = await Task.WhenAll(queries.Select(q => QueryInternalAsync(model, executionContext, q)));
return (result.Any(x => x.HasError), result.Map(x => x.Response));
}
public async Task<(bool HasError, object Response)> QueryAsync(Context context, GraphQLQuery query)
public async Task<ExecutionResult> ExecuteAsync(ExecutionOptions options)
{
Guard.NotNull(context, nameof(context));
Guard.NotNull(query, nameof(query));
var context = ((GraphQLExecutionContext)options.UserContext).Context;
var model = await GetModelAsync(context.App);
var executionContext =
serviceProvider.GetRequiredService<GraphQLExecutionContext>()
.WithContext(context);
var result = await QueryInternalAsync(model, executionContext, query);
return result;
}
private static async Task<(bool HasError, object Response)> QueryInternalAsync(GraphQLModel model, GraphQLExecutionContext context, GraphQLQuery query)
{
if (string.IsNullOrWhiteSpace(query.Query))
{
return (false, new { data = new object() });
}
var (data, errors) = await model.ExecuteAsync(context, query);
if (errors?.Any() == true)
{
return (false, new { data, errors });
}
else
{
return (false, new { data });
}
return await model.ExecuteAsync(options);
}
private async Task<GraphQLModel> GetModelAsync(IAppEntity app)
@ -144,4 +108,4 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return $"GraphQLModel_{appId}_{etag}";
}
}
}
}

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

@ -0,0 +1,39 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using GraphQL;
using Microsoft.AspNetCore.WebUtilities;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
public sealed class DefaultDocumentWriter : IDocumentWriter
{
private readonly IJsonSerializer jsonSerializer;
public DefaultDocumentWriter(IJsonSerializer jsonSerializer)
{
Guard.NotNull(jsonSerializer, nameof(jsonSerializer));
this.jsonSerializer = jsonSerializer;
}
public async Task WriteAsync<T>(Stream stream, T value, CancellationToken cancellationToken = default)
{
await using (var buffer = new FileBufferingWriteStream())
{
jsonSerializer.Serialize(value, buffer, true);
await buffer.DrainBufferAsync(stream, cancellationToken);
}
}
}
}

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

@ -8,7 +8,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.DataLoader;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Entities.Assets;
@ -24,55 +23,32 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
private static readonly List<IEnrichedAssetEntity> EmptyAssets = new List<IEnrichedAssetEntity>();
private static readonly List<IEnrichedContentEntity> EmptyContents = new List<IEnrichedContentEntity>();
private readonly IDataLoaderContextAccessor dataLoaderContextAccessor;
private readonly DataLoaderDocumentListener dataLoaderDocumentListener;
private readonly IUrlGenerator urlGenerator;
private readonly ISemanticLog log;
private readonly ICommandBus commandBus;
private Context context;
public IUrlGenerator UrlGenerator
{
get { return urlGenerator; }
}
private readonly IDataLoaderContextAccessor dataLoaders;
public ICommandBus CommandBus
{
get { return commandBus; }
}
public IUrlGenerator UrlGenerator { get; }
public ISemanticLog Log
{
get { return log; }
}
public ICommandBus CommandBus { get; }
public override Context Context
{
get { return context; }
}
public ISemanticLog Log { get; }
public override Context Context { get; }
public GraphQLExecutionContext(IAssetQueryService assetQuery, IContentQueryService contentQuery,
IDataLoaderContextAccessor dataLoaderContextAccessor, DataLoaderDocumentListener dataLoaderDocumentListener, ICommandBus commandBus, IUrlGenerator urlGenerator, ISemanticLog log)
Context context,
IDataLoaderContextAccessor dataLoaders, ICommandBus commandBus, IUrlGenerator urlGenerator, ISemanticLog log)
: base(assetQuery, contentQuery)
{
this.commandBus = commandBus;
this.dataLoaderContextAccessor = dataLoaderContextAccessor;
this.dataLoaderDocumentListener = dataLoaderDocumentListener;
this.urlGenerator = urlGenerator;
this.log = log;
}
this.dataLoaders = dataLoaders;
public GraphQLExecutionContext WithContext(Context newContext)
{
context = newContext.Clone(b => b.WithoutCleanup().WithoutContentEnrichment());
CommandBus = commandBus;
return this;
}
UrlGenerator = urlGenerator;
public void Setup(ExecutionOptions execution)
{
execution.Listeners.Add(dataLoaderDocumentListener);
execution.UserContext = this;
Context = context.Clone(b => b
.WithoutCleanup()
.WithoutContentEnrichment());
Log = log;
}
public async Task<IEnrichedAssetEntity?> FindAssetAsync(DomainId id)
@ -119,7 +95,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
private IDataLoader<DomainId, IEnrichedAssetEntity> GetAssetsLoader()
{
return dataLoaderContextAccessor.Context.GetOrAddBatchLoader<DomainId, IEnrichedAssetEntity>(nameof(GetAssetsLoader),
return dataLoaders.Context.GetOrAddBatchLoader<DomainId, IEnrichedAssetEntity>(nameof(GetAssetsLoader),
async batch =>
{
var result = await GetReferencedAssetsAsync(new List<DomainId>(batch));
@ -130,7 +106,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
private IDataLoader<DomainId, IEnrichedContentEntity> GetContentsLoader()
{
return dataLoaderContextAccessor.Context.GetOrAddBatchLoader<DomainId, IEnrichedContentEntity>(nameof(GetContentsLoader),
return dataLoaders.Context.GetOrAddBatchLoader<DomainId, IEnrichedContentEntity>(nameof(GetContentsLoader),
async batch =>
{
var result = await GetReferencedContentsAsync(new List<DomainId>(batch));

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

@ -12,7 +12,6 @@ using GraphQL;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Log;
using GraphQLSchema = GraphQL.Types.Schema;
@ -31,18 +30,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
schema = new Builder(app, typeFactory).BuildSchema(schemas);
}
public async Task<(object Data, object[]? Errors)> ExecuteAsync(GraphQLExecutionContext context, GraphQLQuery query)
public async Task<ExecutionResult> ExecuteAsync(ExecutionOptions options)
{
Guard.NotNull(context, nameof(context));
options.Schema = schema;
var result = await Executor.ExecuteAsync(execution =>
{
context.Setup(execution);
execution.Schema = schema;
execution.Inputs = query.Inputs;
execution.Query = query.Query;
});
var result = await Executor.ExecuteAsync(options);
if (result.Errors != null && result.Errors.Any())
{
@ -58,9 +50,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}));
}
var errors = result.Errors?.Select(x => (object)new { x.Message, x.Locations }).ToArray();
return (result.Data, errors);
return result;
}
}
}

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

@ -1,20 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
public sealed class GraphQLQuery
{
public string OperationName { get; set; }
public string Query { get; set; }
public Inputs? Inputs { get; set; }
}
}

5
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/IGraphQLService.cs

@ -6,13 +6,12 @@
// ==========================================================================
using System.Threading.Tasks;
using GraphQL;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
public interface IGraphQLService
{
Task<(bool HasError, object Response)> QueryAsync(Context context, params GraphQLQuery[] queries);
Task<(bool HasError, object Response)> QueryAsync(Context context, GraphQLQuery query);
Task<ExecutionResult> ExecuteAsync(ExecutionOptions options);
}
}

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

@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
foreach (var schemaInfo in schemas.Where(x => x.Fields.Count > 0))
{
var contentType = builder.GetContentType(schemaInfo);
var contentType = new NonNullGraphType(builder.GetContentType(schemaInfo));
var inputType = new DataInputGraphType(builder, schemaInfo);

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

@ -44,7 +44,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
ValueConverter.Register<JsonNumber, double>(x => x.Value);
ValueConverter.Register<JsonString, string>(x => x.Value);
ValueConverter.Register<JsonString, DateTimeOffset>(x => DateTimeOffset.Parse(x.Value, CultureInfo.InvariantCulture));
ValueConverter.Register<string, DomainId>(DomainId.Create);
ValueConverter.Register<string, Status>(x => new Status(x));
}

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

@ -207,11 +207,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
public static readonly IFieldResolver Resolver = ResolveAsync(Permissions.AppContentsCreate, c =>
{
var contentPublish = c.GetArgument<bool>("publish");
var publish = c.GetArgument<bool>("publish");
var contentData = GetContentData(c);
var contentId = c.GetArgument<string?>("id");
var command = new CreateContent { Data = contentData, Publish = contentPublish };
var command = new CreateContent { Data = contentData, Publish = publish };
if (!string.IsNullOrWhiteSpace(contentId))
{
@ -263,13 +263,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents
public static readonly IFieldResolver Resolver = ResolveAsync(Permissions.AppContentsUpsert, c =>
{
var contentPublish = c.GetArgument<bool>("publish");
var publish = c.GetArgument<bool>("publish");
var contentData = GetContentData(c);
var contentId = c.GetArgument<string>("id");
var id = DomainId.Create(contentId);
return new UpsertContent { ContentId = id, Data = contentData, Publish = contentPublish };
return new UpsertContent { ContentId = id, Data = contentData, Publish = publish };
});
}

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

@ -12,11 +12,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives
{
internal static class EntityResolvers
{
public static readonly IFieldResolver Id = Resolve<IEntity>(x => x.Id.ToString());
public static readonly IFieldResolver Id = Resolve<IEntity>(x => x.Id);
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 CreatedBy = Resolve<IEntityWithCreatedBy>(x => x.CreatedBy);
public static readonly IFieldResolver LastModified = Resolve<IEntity>(x => x.LastModified);
public static readonly IFieldResolver LastModifiedBy = Resolve<IEntityWithLastModifiedBy>(x => x.LastModifiedBy);
public static readonly IFieldResolver Version = Resolve<IEntityWithVersion>(x => x.Version);
private static IFieldResolver Resolve<TSource>(Func<TSource, object> resolver)

6
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantGraphType.cs

@ -11,16 +11,16 @@ using NodaTime.Text;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives
{
internal sealed class InstantGraphType : DateGraphType
internal sealed class InstantGraphType : DateTimeGraphType
{
public override object Serialize(object value)
{
return ParseValue(value);
return value;
}
public override object ParseValue(object value)
{
return InstantPattern.General.Parse(value.ToString()!).Value;
return InstantPattern.ExtendedIso.Parse(value.ToString()!).Value;
}
public override object? ParseLiteral(IValue value)

6
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs

@ -32,19 +32,19 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
IAppProvider appProvider,
IContentEnricher contentEnricher,
IContentRepository contentRepository,
IContentLoader assetLoader,
IContentLoader contentLoader,
ContentQueryParser queryParser)
{
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(contentEnricher, nameof(contentEnricher));
Guard.NotNull(contentRepository, nameof(contentRepository));
Guard.NotNull(assetLoader, nameof(assetLoader));
Guard.NotNull(contentLoader, nameof(contentLoader));
Guard.NotNull(queryParser, nameof(queryParser));
this.appProvider = appProvider;
this.contentEnricher = contentEnricher;
this.contentRepository = contentRepository;
this.contentLoader = assetLoader;
this.contentLoader = contentLoader;
this.queryParser = queryParser;
this.queryParser = queryParser;
}

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs

@ -103,7 +103,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
requestCache.AddDependency(referencedAsset.UniqueId, referencedAsset.Version);
fieldReference.AddJsonValue(partitionKey, array);
fieldReference.AddLocalized(partitionKey, array);
}
}
}

4
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveReferences.cs

@ -100,13 +100,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
var value = formatted.GetOrAdd(reference, x => Format(x, context, referencedSchema));
fieldReference.AddJsonValue(partition, value);
fieldReference.AddLocalized(partition, value);
}
else if (referencedContents.Count > 1)
{
var value = CreateFallback(context, referencedContents);
fieldReference.AddJsonValue(partition, value);
fieldReference.AddLocalized(partition, value);
}
}
}

2
backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs

@ -273,7 +273,7 @@ namespace Squidex.Domain.Apps.Entities.History
private static void SetUser(AppEvent appEvent, PublishDto publishRequest)
{
if (appEvent.Actor.IsSubject)
if (appEvent.Actor.IsUser)
{
publishRequest.CreatorId = appEvent.Actor.Identifier;
}

5
backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEvent.cs

@ -5,19 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json;
namespace Squidex.Infrastructure.EventSourcing
{
internal sealed class CosmosDbEvent
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("payload")]
public string Payload { get; set; }
[JsonProperty("header")]
public EnvelopeHeaders Headers { get; set; }
public static CosmosDbEvent FromEventData(EventData data)

7
backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventCommit.cs

@ -6,28 +6,21 @@
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.EventSourcing
{
internal sealed class CosmosDbEventCommit
{
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("events")]
public CosmosDbEvent[] Events { get; set; }
[JsonProperty("eventStreamOffset")]
public long EventStreamOffset { get; set; }
[JsonProperty("eventsCount")]
public long EventsCount { get; set; }
[JsonProperty("eventStream")]
public string EventStream { get; set; }
[JsonProperty("timestamp")]
public long Timestamp { get; set; }
}
}

32
backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventStore.cs

@ -11,8 +11,8 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Newtonsoft.Json;
using Squidex.Hosting;
using Squidex.Infrastructure.Json;
using Index = Microsoft.Azure.Documents.Index;
namespace Squidex.Infrastructure.EventSourcing
@ -22,47 +22,35 @@ namespace Squidex.Infrastructure.EventSourcing
private readonly DocumentClient documentClient;
private readonly Uri collectionUri;
private readonly Uri databaseUri;
private readonly string masterKey;
private readonly string databaseId;
private readonly JsonSerializerSettings serializerSettings;
public JsonSerializerSettings SerializerSettings
{
get { return serializerSettings; }
}
public IJsonSerializer JsonSerializer { get; }
public string DatabaseId
{
get { return databaseId; }
}
public string DatabaseId { get; }
public string MasterKey
{
get { return masterKey; }
}
public string MasterKey { get; }
public Uri ServiceUri
{
get { return documentClient.ServiceEndpoint; }
}
public CosmosDbEventStore(DocumentClient documentClient, string masterKey, string database, JsonSerializerSettings serializerSettings)
public CosmosDbEventStore(DocumentClient documentClient, string masterKey, string database, IJsonSerializer jsonSerializer)
{
Guard.NotNull(documentClient, nameof(documentClient));
Guard.NotNull(serializerSettings, nameof(serializerSettings));
Guard.NotNull(jsonSerializer, nameof(jsonSerializer));
Guard.NotNullOrEmpty(masterKey, nameof(masterKey));
Guard.NotNullOrEmpty(database, nameof(database));
this.documentClient = documentClient;
databaseUri = UriFactory.CreateDatabaseUri(database);
databaseId = database;
DatabaseId = database;
collectionUri = UriFactory.CreateDocumentCollectionUri(database, Constants.Collection);
this.masterKey = masterKey;
MasterKey = masterKey;
this.serializerSettings = serializerSettings;
JsonSerializer = jsonSerializer;
}
protected override void DisposeObject(bool disposing)
@ -75,7 +63,7 @@ namespace Squidex.Infrastructure.EventSourcing
public async Task InitializeAsync(CancellationToken ct = default)
{
await documentClient.CreateDatabaseIfNotExistsAsync(new Database { Id = databaseId });
await documentClient.CreateDatabaseIfNotExistsAsync(new Database { Id = DatabaseId });
await documentClient.CreateDocumentCollectionIfNotExistsAsync(databaseUri,
new DocumentCollection

2
backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbEventStore_Writer.cs

@ -36,7 +36,7 @@ namespace Squidex.Infrastructure.EventSourcing
return documentClient.QueryAsync(collectionUri, query, commit =>
{
var documentUri = UriFactory.CreateDocumentUri(databaseId, Constants.Collection, commit.Id.ToString());
var documentUri = UriFactory.CreateDocumentUri(DatabaseId, Constants.Collection, commit.Id.ToString());
return documentClient.DeleteDocumentAsync(documentUri, deleteOptions);
});

3
backend/src/Squidex.Infrastructure.Azure/EventSourcing/CosmosDbSubscription.cs

@ -12,7 +12,6 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.ChangeFeedProcessor.FeedProcessing;
using Newtonsoft.Json;
using Builder = Microsoft.Azure.Documents.ChangeFeedProcessor.ChangeFeedProcessorBuilder;
using Collection = Microsoft.Azure.Documents.ChangeFeedProcessor.DocumentCollectionInfo;
using Options = Microsoft.Azure.Documents.ChangeFeedProcessor.ChangeFeedProcessorOptions;
@ -117,7 +116,7 @@ namespace Squidex.Infrastructure.EventSourcing
if (regex == null || regex.IsMatch(streamName))
{
var commit = JsonConvert.DeserializeObject<CosmosDbEventCommit>(document.ToString(), store.SerializerSettings)!;
var commit = store.JsonSerializer.Deserialize<CosmosDbEventCommit>(document.ToString());
var eventStreamOffset = (int)commit.EventStreamOffset;

13
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConvention.cs

@ -12,7 +12,6 @@ using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Squidex.Infrastructure.MongoDb
{
@ -35,18 +34,6 @@ namespace Squidex.Infrastructure.MongoDb
memberMap.SetSerializer((IBsonSerializer)bsonSerializer!);
}
else if (memberMap.MemberType == typeof(JToken))
{
memberMap.SetSerializer(JTokenSerializer<JToken>.Instance);
}
else if (memberMap.MemberType == typeof(JObject))
{
memberMap.SetSerializer(JTokenSerializer<JObject>.Instance);
}
else if (memberMap.MemberType == typeof(JValue))
{
memberMap.SetSerializer(JTokenSerializer<JValue>.Instance);
}
});
ConventionRegistry.Register("json", pack, t => true);

53
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/JTokenSerializer.cs

@ -1,53 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
using Newtonsoft.Json.Linq;
namespace Squidex.Infrastructure.MongoDb
{
public sealed class JTokenSerializer<T> : ClassSerializerBase<T?> where T : JToken
{
public static readonly JTokenSerializer<T> Instance = new JTokenSerializer<T>();
public override T? Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var bsonReader = context.Reader;
if (bsonReader.GetCurrentBsonType() == BsonType.Null)
{
bsonReader.ReadNull();
return null;
}
else
{
var jsonReader = new BsonJsonReader(bsonReader);
return (T)JToken.ReadFrom(jsonReader);
}
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T? value)
{
var bsonWriter = context.Writer;
if (value == null)
{
bsonWriter.WriteNull();
}
else
{
var jsonWriter = new BsonJsonWriter(bsonWriter);
value.WriteTo(jsonWriter);
}
}
}
}

2
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoRepositoryBase.cs

@ -53,7 +53,7 @@ namespace Squidex.Infrastructure.MongoDb
static MongoRepositoryBase()
{
RefTokenSerializer.Register();
TypeConverterStringSerializer<RefToken>.Register();
InstantSerializer.Register();

40
backend/src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs

@ -1,40 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
namespace Squidex.Infrastructure.MongoDb
{
public class RefTokenSerializer : ClassSerializerBase<RefToken>
{
public static void Register()
{
try
{
BsonSerializer.RegisterSerializer(new RefTokenSerializer());
}
catch (BsonSerializationException)
{
return;
}
}
protected override RefToken DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var value = context.Reader.ReadString();
return RefToken.Parse(value);
}
protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, RefToken value)
{
context.Writer.WriteString(value.ToString());
}
}
}

30
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/StatusSerializer.cs → backend/src/Squidex.Infrastructure.MongoDb/MongoDb/TypeConverterStringSerializer.cs

@ -5,27 +5,22 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
namespace Squidex.Infrastructure.MongoDb
{
public sealed class StatusSerializer : SerializerBase<Status>
public sealed class TypeConverterStringSerializer<T> : SerializerBase<T>
{
private readonly TypeConverter typeConverter;
public static void Register()
{
try
{
try
{
BsonSerializer.RegisterSerializer(new StatusSerializer());
}
catch (BsonSerializationException)
{
return;
}
BsonSerializer.RegisterSerializer(new TypeConverterStringSerializer<T>());
}
catch (BsonSerializationException)
{
@ -33,16 +28,21 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
}
}
public override Status Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
public TypeConverterStringSerializer()
{
typeConverter = TypeDescriptor.GetConverter(typeof(T));
}
public override T Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var value = context.Reader.ReadString();
return new Status(value);
return (T)typeConverter.ConvertFromInvariantString(value);
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, Status value)
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T value)
{
context.Writer.WriteString(value.Name);
context.Writer.WriteString(value!.ToString());
}
}
}

12
backend/src/Squidex.Infrastructure/CollectionExtensions.cs

@ -150,18 +150,6 @@ namespace Squidex.Infrastructure
return source.Concat(Enumerable.Repeat(value, 1));
}
public static TResult[] Map<TResult, T>(this T[] value, Func<T, TResult> convert)
{
var result = new TResult[value.Length];
for (var i = 0; i < value.Length; i++)
{
result[i] = convert(value[i]);
}
return result;
}
public static int SequentialHashCode<T>(this IEnumerable<T> collection)
{
return collection.SequentialHashCode(EqualityComparer<T>.Default);

2
backend/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs

@ -121,7 +121,7 @@ namespace Squidex.Infrastructure.EventSourcing
{
if (obj.TryGetValue(key, out var v))
{
if (v.Type == JsonValueType.String && InstantPattern.General.Parse(v.ToString()).TryGetValue(default, out var instant))
if (v.Type == JsonValueType.String && InstantPattern.ExtendedIso.Parse(v.ToString()).TryGetValue(default, out var instant))
{
return instant;
}

13
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonRole.cs → backend/src/Squidex.Infrastructure/ISurrogate.cs

@ -5,17 +5,12 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Core.Apps.Json
namespace Squidex.Infrastructure
{
public sealed class JsonRole
public interface ISurrogate<T>
{
[JsonProperty]
public string[] Permissions { get; set; }
void FromSource(T source);
[JsonProperty]
public JsonObject Properties { get; set; }
T ToSource();
}
}

78
backend/src/Squidex.Infrastructure/Json/ClaimsPrinicpalSurrogate.cs

@ -0,0 +1,78 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
namespace Squidex.Infrastructure.Json
{
public sealed class ClaimsPrinicpalSurrogate : List<ClaimsIdentitySurrogate>, ISurrogate<ClaimsPrincipal>
{
public void FromSource(ClaimsPrincipal source)
{
foreach (var identity in source.Identities)
{
var surrogate = new ClaimsIdentitySurrogate();
surrogate.FromSource(identity);
Add(surrogate);
}
}
public ClaimsPrincipal ToSource()
{
return new ClaimsPrincipal(this.Select(x => x.ToSource()));
}
}
public sealed class ClaimsIdentitySurrogate : ISurrogate<ClaimsIdentity>
{
public string? AuthenticationType { get; set; }
public ClaimSurrogate[] Claims { get; set; }
public void FromSource(ClaimsIdentity source)
{
AuthenticationType = source.AuthenticationType;
Claims = source.Claims.Select(claim =>
{
var surrogate = new ClaimSurrogate();
surrogate.FromSource(claim);
return surrogate;
}).ToArray();
}
public ClaimsIdentity ToSource()
{
return new ClaimsIdentity(Claims.Select(x => x.ToSource()), AuthenticationType);
}
}
public sealed class ClaimSurrogate : ISurrogate<Claim>
{
public string Type { get; set; }
public string Value { get; set; }
public void FromSource(Claim source)
{
Type = source.Type;
Value = source.Value;
}
public Claim ToSource()
{
return new Claim(Type, Value);
}
}
}

2
backend/src/Squidex.Infrastructure/Json/Newtonsoft/ISupportedTypes.cs → backend/src/Squidex.Infrastructure/Json/ISupportedTypes.cs

@ -8,7 +8,7 @@
using System;
using System.Collections.Generic;
namespace Squidex.Infrastructure.Json.Newtonsoft
namespace Squidex.Infrastructure.Json
{
public interface ISupportedTypes
{

35
backend/src/Squidex.Infrastructure/Json/JsonException.cs

@ -0,0 +1,35 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Runtime.Serialization;
namespace Squidex.Infrastructure.Json
{
[Serializable]
public class JsonException : Exception
{
public JsonException()
{
}
public JsonException(string message)
: base(message)
{
}
public JsonException(string message, Exception inner)
: base(message, inner)
{
}
protected JsonException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

62
backend/src/Squidex.Infrastructure/Json/Newtonsoft/ClaimsPrincipalConverter.cs

@ -1,62 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Linq;
using System.Security.Claims;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class ClaimsPrincipalConverter : JsonClassConverter<ClaimsPrincipal>
{
private sealed class JsonIdentity
{
[JsonProperty]
public string AuthenticationType { get; set; }
[JsonProperty]
public JsonClaim[] Claims { get; set; }
}
private sealed class JsonClaim
{
[JsonProperty]
public string Type { get; set; }
[JsonProperty]
public string Value { get; set; }
}
protected override void WriteValue(JsonWriter writer, ClaimsPrincipal value, JsonSerializer serializer)
{
var jsonIdentities =
value.Identities.Select(identity =>
new JsonIdentity
{
Claims = identity.Claims.Select(c =>
{
return new JsonClaim { Type = c.Type, Value = c.Value };
}).ToArray(),
AuthenticationType = identity.AuthenticationType!
}).ToArray();
serializer.Serialize(writer, jsonIdentities);
}
protected override ClaimsPrincipal ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var jsonIdentities = serializer.Deserialize<JsonIdentity[]>(reader)!;
return new ClaimsPrincipal(
jsonIdentities.Select(identity =>
new ClaimsIdentity(
identity.Claims.Select(c => new Claim(c.Type, c.Value)),
identity.AuthenticationType)));
}
}
}

62
backend/src/Squidex.Infrastructure/Json/Newtonsoft/DomainIdConverter.cs

@ -1,62 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class DomainIdConverter : JsonConverter, ISupportedTypes
{
public IEnumerable<Type> SupportedTypes
{
get
{
yield return typeof(DomainId);
yield return typeof(DomainId?);
}
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value != null)
{
writer.WriteValue(value.ToString());
}
else
{
writer.WriteNull();
}
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
if (reader.Value == null)
{
return null;
}
if (reader.TokenType == JsonToken.String)
{
return DomainId.Create(reader.Value.ToString()!);
}
if (reader.TokenType == JsonToken.Null && objectType == typeof(DomainId?))
{
return null;
}
throw new JsonException($"Not a valid date time, expected String, but got {reader.TokenType}.");
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DomainId) || objectType == typeof(DomainId?);
}
}
}

69
backend/src/Squidex.Infrastructure/Json/Newtonsoft/InstantConverter.cs

@ -1,69 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using NodaTime;
using NodaTime.Text;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class InstantConverter : JsonConverter, ISupportedTypes
{
public IEnumerable<Type> SupportedTypes
{
get
{
yield return typeof(Instant);
yield return typeof(Instant?);
}
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value != null)
{
writer.WriteValue(value.ToString());
}
else
{
writer.WriteNull();
}
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
if (reader.Value == null)
{
return null;
}
if (reader.TokenType == JsonToken.String)
{
return InstantPattern.General.Parse(reader.Value.ToString()!).Value;
}
if (reader.TokenType == JsonToken.Date)
{
return Instant.FromDateTimeUtc((DateTime)reader.Value);
}
if (reader.TokenType == JsonToken.Null && objectType == typeof(Instant?))
{
return null;
}
throw new JsonException($"Not a valid date time, expected String or Date, but got {reader.TokenType}.");
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Instant) || objectType == typeof(Instant?);
}
}
}

27
backend/src/Squidex.Infrastructure/Json/Newtonsoft/LanguageConverter.cs

@ -1,27 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class LanguageConverter : JsonClassConverter<Language>
{
protected override void WriteValue(JsonWriter writer, Language value, JsonSerializer serializer)
{
writer.WriteValue(value.Iso2Code);
}
protected override Language ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader)!;
return Language.GetLanguage(value);
}
}
}

41
backend/src/Squidex.Infrastructure/Json/Newtonsoft/NamedDomainIdConverter.cs

@ -1,41 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class NamedDomainIdConverter : JsonClassConverter<NamedId<DomainId>>
{
private static readonly Parser<DomainId> Parser = ParseString;
protected override void WriteValue(JsonWriter writer, NamedId<DomainId> value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
protected override NamedId<DomainId> ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader)!;
if (!NamedId<DomainId>.TryParse(value, Parser, out var result))
{
throw new JsonException("Named id must have at least 2 parts divided by comma.");
}
return result;
}
private static bool ParseString(ReadOnlySpan<char> value, out DomainId result)
{
result = DomainId.Create(new string(value));
return true;
}
}
}

34
backend/src/Squidex.Infrastructure/Json/Newtonsoft/NamedGuidIdConverter.cs

@ -1,34 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class NamedGuidIdConverter : JsonClassConverter<NamedId<Guid>>
{
private static readonly Parser<Guid> Parser = Guid.TryParse;
protected override void WriteValue(JsonWriter writer, NamedId<Guid> value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
protected override NamedId<Guid> ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader)!;
if (!NamedId<Guid>.TryParse(value, Parser, out var result))
{
throw new JsonException("Named id must have more than 2 parts divided by commata.");
}
return result;
}
}
}

34
backend/src/Squidex.Infrastructure/Json/Newtonsoft/NamedLongIdConverter.cs

@ -1,34 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class NamedLongIdConverter : JsonClassConverter<NamedId<long>>
{
private static readonly Parser<long> Parser = long.TryParse;
protected override void WriteValue(JsonWriter writer, NamedId<long> value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
protected override NamedId<long> ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader)!;
if (!NamedId<long>.TryParse(value, Parser, out var result))
{
throw new JsonException("Named id must have at least 2 parts divided by commata.");
}
return result;
}
}
}

41
backend/src/Squidex.Infrastructure/Json/Newtonsoft/NamedStringIdConverter.cs

@ -1,41 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class NamedStringIdConverter : JsonClassConverter<NamedId<string>>
{
private static readonly Parser<string> Parser = ParseString;
protected override void WriteValue(JsonWriter writer, NamedId<string> value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
protected override NamedId<string> ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader)!;
if (!NamedId<string>.TryParse(value, Parser, out var result))
{
throw new JsonException("Named id must have at least 2 parts divided by commata.");
}
return result;
}
private static bool ParseString(ReadOnlySpan<char> value, out string result)
{
result = new string(value);
return true;
}
}
}

48
backend/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs

@ -8,6 +8,7 @@
using System;
using System.IO;
using Newtonsoft.Json;
using NewtonsoftException = Newtonsoft.Json.JsonException;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
@ -32,38 +33,59 @@ namespace Squidex.Infrastructure.Json.Newtonsoft
public void Serialize<T>(T value, Stream stream, bool leaveOpen = false)
{
using (var writer = new StreamWriter(stream, leaveOpen: leaveOpen))
try
{
serializer.Serialize(writer, value);
using (var writer = new StreamWriter(stream, leaveOpen: leaveOpen))
{
serializer.Serialize(writer, value);
writer.Flush();
writer.Flush();
}
}
catch (NewtonsoftException ex)
{
throw new JsonException(ex.Message, ex);
}
}
public T Deserialize<T>(string value, Type? actualType = null)
{
using (var textReader = new StringReader(value))
try
{
actualType ??= typeof(T);
using (var reader = GetReader(textReader))
using (var textReader = new StringReader(value))
{
return (T)serializer.Deserialize(reader, actualType)!;
actualType ??= typeof(T);
using (var reader = GetReader(textReader))
{
return (T)serializer.Deserialize(reader, actualType)!;
}
}
}
catch (NewtonsoftException ex)
{
throw new JsonException(ex.Message, ex);
}
}
public T Deserialize<T>(Stream stream, Type? actualType = null, bool leaveOpen = false)
{
using (var textReader = new StreamReader(stream, leaveOpen: leaveOpen))
try
{
actualType ??= typeof(T);
using (var reader = GetReader(textReader))
using (var textReader = new StreamReader(stream, leaveOpen: leaveOpen))
{
return (T)serializer.Deserialize(reader, actualType)!;
actualType ??= typeof(T);
using (var reader = GetReader(textReader))
{
return (T)serializer.Deserialize(reader, actualType)!;
}
}
}
catch (NewtonsoftException ex)
{
throw new JsonException(ex.Message, ex);
}
}
private static JsonTextReader GetReader(TextReader textReader)

32
backend/src/Squidex.Infrastructure/Json/Newtonsoft/RefTokenConverter.cs

@ -1,32 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class RefTokenConverter : JsonClassConverter<RefToken>
{
protected override void WriteValue(JsonWriter writer, RefToken value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
protected override RefToken ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader)!;
if (!RefToken.TryParse(value, out var result))
{
throw new JsonException("Named id must have at least 2 parts divided by colon.");
}
return result;
}
}
}

31
backend/src/Squidex.Infrastructure/Json/Newtonsoft/SurrogateConverter.cs

@ -0,0 +1,31 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class SurrogateConverter<T, TSurrogate> : JsonClassConverter<T> where T : class where TSurrogate : ISurrogate<T>, new()
{
protected override T ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var surrogate = serializer.Deserialize<TSurrogate>(reader);
return surrogate!.ToSource();
}
protected override void WriteValue(JsonWriter writer, T value, JsonSerializer serializer)
{
var surrogate = new TSurrogate();
surrogate.FromSource(value);
serializer.Serialize(writer, surrogate);
}
}
}

4
backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs

@ -35,12 +35,12 @@ namespace Squidex.Infrastructure.Json.Objects
{
}
internal JsonArray(params object?[] values)
internal JsonArray(IEnumerable<object?>? values)
: base(ToList(values))
{
}
private static List<IJsonValue> ToList(IEnumerable<object?> values)
private static List<IJsonValue> ToList(IEnumerable<object?>? values)
{
return values?.Select(JsonValue.Create).ToList() ?? new List<IJsonValue>();
}

10
backend/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using NodaTime;
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
@ -32,9 +33,14 @@ namespace Squidex.Infrastructure.Json.Objects
return new JsonArray();
}
public static JsonArray Array(params object?[] values)
public static JsonArray Array<T>(IEnumerable<T> values)
{
return new JsonArray(values);
return new JsonArray(values?.OfType<object>());
}
public static JsonArray Array<T>(params T?[] values)
{
return new JsonArray(values?.OfType<object>());
}
public static JsonObject Object()

2
backend/src/Squidex.Infrastructure/Language.cs

@ -7,11 +7,13 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
namespace Squidex.Infrastructure
{
[TypeConverter(typeof(LanguageTypeConverter))]
public partial record Language
{
private static readonly Regex CultureRegex = new Regex("^([a-z]{2})(\\-[a-z]{2})?$", RegexOptions.IgnoreCase);

36
backend/src/Squidex.Infrastructure/LanguageTypeConverter.cs

@ -0,0 +1,36 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.ComponentModel;
using System.Globalization;
namespace Squidex.Infrastructure
{
public sealed class LanguageTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return Language.GetLanguage((string)value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
return ((Language)value).Iso2Code;
}
}
}

83
backend/src/Squidex.Infrastructure/NamedIdTypeConverter.cs

@ -0,0 +1,83 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.ComponentModel;
using System.Globalization;
namespace Squidex.Infrastructure
{
internal sealed class NamedIdTypeConverter : TypeConverter
{
private static readonly Parser<Guid> ParserGuid = Guid.TryParse;
private static readonly Parser<DomainId> ParserDomainId = ParseDomainId;
private static readonly Parser<string> ParserString = ParseString;
private static readonly Parser<long> ParserLong = long.TryParse;
private readonly Func<string, object>? converter;
public NamedIdTypeConverter(Type type)
{
var genericType = type?.GetGenericArguments()?[0];
if (genericType == typeof(Guid))
{
converter = v => NamedId<Guid>.Parse(v, ParserGuid);
}
else if (genericType == typeof(DomainId))
{
converter = v => NamedId<DomainId>.Parse(v, ParserDomainId);
}
else if (genericType == typeof(string))
{
converter = v => NamedId<string>.Parse(v, ParserString);
}
else if (genericType == typeof(long))
{
converter = v => NamedId<long>.Parse(v, ParserLong);
}
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (converter == null)
{
throw new NotSupportedException();
}
return converter((string)value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
return value.ToString()!;
}
private static bool ParseDomainId(ReadOnlySpan<char> value, out DomainId result)
{
result = DomainId.Create(new string(value));
return true;
}
private static bool ParseString(ReadOnlySpan<char> value, out string result)
{
result = new string(value);
return true;
}
}
}

165
backend/src/Squidex.Infrastructure/Queries/Json/FilterConverter.cs

@ -1,165 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Validation;
namespace Squidex.Infrastructure.Queries.Json
{
public sealed class FilterConverter : JsonClassConverter<FilterNode<IJsonValue>>
{
public override IEnumerable<Type> SupportedTypes
{
get
{
yield return typeof(CompareFilter<IJsonValue>);
yield return typeof(FilterNode<IJsonValue>);
yield return typeof(LogicalFilter<IJsonValue>);
yield return typeof(NegateFilter<IJsonValue>);
}
}
public override bool CanConvert(Type objectType)
{
return SupportedTypes.Contains(objectType);
}
protected override FilterNode<IJsonValue> ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
throw new JsonException($"Expected StartObject, but got {reader.TokenType}.");
}
FilterNode<IJsonValue>? result = null;
PropertyPath? comparePath = null;
var compareOperator = (CompareOperator)99;
IJsonValue? compareValue = null;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.PropertyName:
var propertyName = reader.Value!.ToString()!;
if (!reader.Read())
{
throw new JsonSerializationException("Unexpected end when reading filter.");
}
if (result != null)
{
throw new JsonSerializationException($"Unexpected property {propertyName}");
}
switch (propertyName.ToLowerInvariant())
{
case "not":
var filter = serializer.Deserialize<FilterNode<IJsonValue>>(reader)!;
result = new NegateFilter<IJsonValue>(filter);
break;
case "and":
var andFilters = serializer.Deserialize<List<FilterNode<IJsonValue>>>(reader)!;
result = new LogicalFilter<IJsonValue>(LogicalFilterType.And, andFilters);
break;
case "or":
var orFilters = serializer.Deserialize<List<FilterNode<IJsonValue>>>(reader)!;
result = new LogicalFilter<IJsonValue>(LogicalFilterType.Or, orFilters);
break;
case "path":
comparePath = serializer.Deserialize<PropertyPath>(reader);
break;
case "op":
compareOperator = ReadOperator(reader, serializer);
break;
case "value":
compareValue = serializer.Deserialize<IJsonValue>(reader);
break;
}
break;
case JsonToken.Comment:
break;
case JsonToken.EndObject:
if (result != null)
{
return result;
}
if (comparePath == null)
{
throw new JsonSerializationException("Path not defined.");
}
if (compareValue == null && compareOperator != CompareOperator.Empty)
{
throw new JsonSerializationException("Value not defined.");
}
if (!compareOperator.IsEnumValue())
{
throw new JsonSerializationException("Operator not defined.");
}
return new CompareFilter<IJsonValue>(comparePath, compareOperator, compareValue ?? JsonValue.Null);
}
}
throw new JsonSerializationException("Unexpected end when reading filter.");
}
private static CompareOperator ReadOperator(JsonReader reader, JsonSerializer serializer)
{
var value = serializer.Deserialize<string>(reader)!;
switch (value.ToLowerInvariant())
{
case "eq":
return CompareOperator.Equals;
case "ne":
return CompareOperator.NotEquals;
case "lt":
return CompareOperator.LessThan;
case "le":
return CompareOperator.LessThanOrEqual;
case "gt":
return CompareOperator.GreaterThan;
case "ge":
return CompareOperator.GreaterThanOrEqual;
case "empty":
return CompareOperator.Empty;
case "contains":
return CompareOperator.Contains;
case "endswith":
return CompareOperator.EndsWith;
case "startswith":
return CompareOperator.StartsWith;
case "in":
return CompareOperator.In;
}
throw new JsonSerializationException($"Unexpected compare operator, got {value}.");
}
protected override void WriteValue(JsonWriter writer, FilterNode<IJsonValue> value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
}
}

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

Loading…
Cancel
Save