Browse Source

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

pull/1132/head
Sebastian 2 years ago
parent
commit
74a0a2e243
  1. 23
      .github/workflows/dev.yml
  2. 12
      .github/workflows/make-screenshots.yml
  3. 21
      .github/workflows/release.yml
  4. 23
      CHANGELOG.md
  5. 4
      Dockerfile
  6. 10
      backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj
  7. 2
      backend/extensions/Squidex.Extensions/Text/Azure/AzureIndexDefinition.cs
  8. 18
      backend/i18n/frontend_en.json
  9. 12
      backend/i18n/frontend_fr.json
  10. 12
      backend/i18n/frontend_it.json
  11. 12
      backend/i18n/frontend_nl.json
  12. 12
      backend/i18n/frontend_pt.json
  13. 12
      backend/i18n/frontend_zh.json
  14. 18
      backend/i18n/source/frontend_en.json
  15. 2
      backend/src/Migrations/Migrations.csproj
  16. 10
      backend/src/Squidex.Domain.Apps.Core.Model/Assets/Asset.cs
  17. 6
      backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/ContentFieldDataConverter.cs
  18. 2
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldNames.cs
  19. 3
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldEditor.cs
  20. 2
      backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj
  21. 8
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/CacheParser.cs
  22. 8
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs
  23. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptExecutionContext.cs
  24. 14
      backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
  25. 7
      backend/src/Squidex.Domain.Apps.Core.Operations/Templates/Extensions/JsonArrayFluidValue.cs
  26. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Apps/MongoAppRepository.cs
  27. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs
  28. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs
  29. 42
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Extensions.cs
  30. 5
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryInDedicatedCollection.cs
  31. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs
  32. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCountCollection.cs
  33. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs
  34. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleRepository.cs
  35. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs
  36. 4
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj
  37. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Teams/MongoTeamRepository.cs
  38. 6
      backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/MongoTextIndexBase.cs
  39. 12
      backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs
  40. 2
      backend/src/Squidex.Domain.Apps.Entities/Assets/AssetSlug.cs
  41. 34
      backend/src/Squidex.Domain.Apps.Entities/Assets/SvgAssetMetadataSource.cs
  42. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateContents.cs
  43. 1
      backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/EnrichContentDefaults.cs
  44. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/UpsertContent.cs
  45. 12
      backend/src/Squidex.Domain.Apps.Entities/Contents/ContentHeaders.cs
  46. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs
  47. 20
      backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ValidationExtensions.cs
  48. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  49. 17
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/UserGraphType.cs
  50. 9
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs
  51. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs
  52. 2
      backend/src/Squidex.Domain.Apps.Entities/Jobs/JobWorker.cs
  53. 12
      backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs
  54. 8
      backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj
  55. 2
      backend/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj
  56. 6
      backend/src/Squidex.Domain.Users.MongoDb/MongoUserStore.cs
  57. 4
      backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj
  58. 6
      backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj
  59. 10
      backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj
  60. 6
      backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore.cs
  61. 6
      backend/src/Squidex.Infrastructure.MongoDb/Log/MongoRequestLogRepository.cs
  62. 6
      backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj
  63. 4
      backend/src/Squidex.Infrastructure/Commands/DefaultDomainObjectFactory.cs
  64. 14
      backend/src/Squidex.Infrastructure/Json/System/PolymorphicConverter.cs
  65. 4
      backend/src/Squidex.Infrastructure/Json/System/StringConverter.cs
  66. 2
      backend/src/Squidex.Infrastructure/Migrations/Migrator.cs
  67. 2
      backend/src/Squidex.Infrastructure/Queries/Json/JsonFilterSurrogate.cs
  68. 20
      backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
  69. 2
      backend/src/Squidex.Infrastructure/States/StoreExtensions.cs
  70. 2
      backend/src/Squidex.Infrastructure/Tasks/Scheduler.cs
  71. 5
      backend/src/Squidex.Infrastructure/ThrowHelper.cs
  72. 2
      backend/src/Squidex.Infrastructure/Translations/MissingKeys.cs
  73. 3
      backend/src/Squidex.Shared/PermissionIds.cs
  74. 2
      backend/src/Squidex.Shared/Squidex.Shared.csproj
  75. 1
      backend/src/Squidex.Web/ApiExceptionConverter.cs
  76. 2
      backend/src/Squidex.Web/GraphQL/DynamicUserContextBuilder.cs
  77. 8
      backend/src/Squidex.Web/Squidex.Web.csproj
  78. 2
      backend/src/Squidex/Areas/Api/Config/OpenApi/SecurityProcessor.cs
  79. 5
      backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  80. 35
      backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsSharedController.cs
  81. 2
      backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationBuilder.cs
  82. 5
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateContentsDto.cs
  83. 8
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs
  84. 29
      backend/src/Squidex/Areas/Api/Controllers/Contents/Models/EnrichContentDefaultsDto.cs
  85. 8
      backend/src/Squidex/Areas/Api/Controllers/UploadModel.cs
  86. 161
      backend/src/Squidex/Areas/IdentityServer/Views/Account/Login.cshtml
  87. 87
      backend/src/Squidex/Areas/IdentityServer/Views/Setup/Setup.cshtml
  88. 2
      backend/src/Squidex/Config/Domain/EventSourcingServices.cs
  89. 4
      backend/src/Squidex/Config/Domain/HealthCheckServices.cs
  90. 5
      backend/src/Squidex/Config/Domain/ResizeServices.cs
  91. 2
      backend/src/Squidex/Config/Domain/StoreServices.cs
  92. 1
      backend/src/Squidex/Config/Domain/TelemetryServices.cs
  93. 70
      backend/src/Squidex/Squidex.csproj
  94. 2
      backend/src/Squidex/appsettings.json
  95. 1
      backend/src/Squidex/wwwroot/editor/squidex-editor.css
  96. 531
      backend/src/Squidex/wwwroot/editor/squidex-editor.js
  97. 7
      backend/src/Squidex/wwwroot/scripts/editor-context.html
  98. 6
      backend/src/Squidex/wwwroot/scripts/editor-log.html
  99. 32
      backend/src/Squidex/wwwroot/scripts/editor-sdk.js
  100. 2
      backend/src/Squidex/wwwroot/scripts/embed-sdk.js

23
.github/workflows/dev.yml

@ -22,27 +22,26 @@ jobs:
uses: rlespinasse/github-slug-action@v4.5.0
- name: Prepare - Setup QEMU
uses: docker/setup-qemu-action@v3.0.0
uses: docker/setup-qemu-action@v3.2.0
- name: Prepare - Setup Docker Buildx
uses: docker/setup-buildx-action@v3.3.0
uses: docker/setup-buildx-action@v3.6.1
- name: Prepare - Setup Node
uses: actions/setup-node@v4.0.2
uses: actions/setup-node@v4.0.4
with:
node-version: 18
- name: Build - BUILD
uses: docker/build-push-action@v5.4.0
uses: docker/build-push-action@v6.7.0
with:
load: true
build-args: "SQUIDEX__RUNTIME__VERSION=7.0.0-dev-${{ env.BUILD_NUMBER }}"
cache-from: type=gha
cache-to: type=gha,mode=max
tags: squidex-local
- name: Test - Start Compose
run: docker-compose up -d
run: docker compose up -d
working-directory: tools/TestSuite
- name: Test - RUN
@ -96,14 +95,14 @@ jobs:
working-directory: './tools/e2e'
- name: Test - Run Playwright Tests
run: npx playwright test
run: npx playwright test --retries=3
working-directory: './tools/e2e'
env:
BASE__URL: http://localhost:8080
- name: Test - Upload Playwright Artifacts
if: always()
uses: actions/upload-artifact@v4.3.3
uses: actions/upload-artifact@v4.4.0
with:
name: playwright-report
path: tools/e2e/playwright-report/
@ -111,7 +110,7 @@ jobs:
- name: Test - Upload Screenshots
if: failure()
uses: actions/upload-artifact@v4.3.3
uses: actions/upload-artifact@v4.4.0
with:
path: |
tools/TestSuite/TestSuite.ApiTests/bin/Debug/net8.0/screenshots/
@ -125,7 +124,7 @@ jobs:
- name: Test - Cleanup
if: always()
run: docker-compose down
run: docker compose down
working-directory: tools/TestSuite
- name: Publish - Remove unnecessary files
@ -142,14 +141,14 @@ jobs:
- name: Publish - Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3.2.0
uses: docker/login-action@v3.3.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Publish - Build & Push for Multi-Platforms
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v5.4.0
uses: docker/build-push-action@v6.7.0
with:
build-args: "SQUIDEX__RUNTIME__VERSION=7.0.0-dev-${{ env.BUILD_NUMBER }}"
cache-from: type=gha

12
.github/workflows/make-screenshots.yml

@ -12,18 +12,18 @@ jobs:
uses: actions/checkout@v4.1.7
- name: Prepare - Setup QEMU
uses: docker/setup-qemu-action@v3.0.0
uses: docker/setup-qemu-action@v3.2.0
- name: Prepare - Setup Docker Buildx
uses: docker/setup-buildx-action@v3.3.0
uses: docker/setup-buildx-action@v3.6.1
- name: Prepare - Setup Node
uses: actions/setup-node@v4.0.2
uses: actions/setup-node@v4.0.4
with:
node-version: 18
- name: Build - BUILD
uses: docker/build-push-action@v5.4.0
uses: docker/build-push-action@v6.7.0
with:
load: true
cache-from: type=gha
@ -31,7 +31,7 @@ jobs:
tags: squidex-local
- name: Test - Start Compose
run: docker-compose up -d
run: docker compose up -d
working-directory: tools/TestSuite
- name: Test - Install Playwright Dependencies
@ -50,7 +50,7 @@ jobs:
- name: Test - Upload Playwright Artifacts
if: always()
uses: actions/upload-artifact@v4.3.3
uses: actions/upload-artifact@v4.4.0
with:
name: snapshots
path: tools/e2e/snapshots/

21
.github/workflows/release.yml

@ -17,18 +17,18 @@ jobs:
uses: rlespinasse/github-slug-action@v4.5.0
- name: Prepare - Setup QEMU
uses: docker/setup-qemu-action@v3.0.0
uses: docker/setup-qemu-action@v3.2.0
- name: Prepare - Setup Docker Buildx
uses: docker/setup-buildx-action@v3.3.0
uses: docker/setup-buildx-action@v3.6.1
- name: Prepare - Setup Node
uses: actions/setup-node@v4.0.2
uses: actions/setup-node@v4.0.4
with:
node-version: 18
- name: Build - BUILD
uses: docker/build-push-action@v5.4.0
uses: docker/build-push-action@v6.7.0
with:
load: true
build-args: "SQUIDEX__BUILD__VERSION=${{ env.GITHUB_REF_SLUG }},SQUIDEX__RUNTIME__VERSION=${{ env.GITHUB_REF_SLUG }}"
@ -37,7 +37,7 @@ jobs:
tags: squidex-local
- name: Test - Start Compose
run: docker-compose up -d
run: docker compose up -d
working-directory: tools/TestSuite
- name: Test - RUN
@ -91,14 +91,14 @@ jobs:
working-directory: './tools/e2e'
- name: Test - Run Playwright Tests
run: npx playwright test
run: npx playwright test --retries=3
working-directory: './tools/e2e'
env:
BASE__URL: http://localhost:8080
- name: Test - Upload Playwright Artifacts
if: always()
uses: actions/upload-artifact@v4.3.3
uses: actions/upload-artifact@v4.4.0
with:
name: playwright-report
path: tools/e2e/playwright-report/
@ -113,7 +113,7 @@ jobs:
- name: Test - Cleanup
if: always()
run: docker-compose down
run: docker compose down
working-directory: tools/TestSuite
- name: Publish - Remove unnecessary files
@ -136,19 +136,20 @@ jobs:
fi
- name: Publish - Login to Docker Hub
uses: docker/login-action@v3.2.0
uses: docker/login-action@v3.3.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Publish - Build & Push for Multi-Platforms
uses: docker/build-push-action@v5.3.0
uses: docker/build-push-action@v6.7.0
with:
build-args: "SQUIDEX__BUILD__VERSION=${{ env.GITHUB_REF_SLUG }},SQUIDEX__RUNTIME__VERSION=${{ env.GITHUB_REF_SLUG }}"
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
tags: squidex/squidex:${{ env.GITHUB_REF_SLUG }},squidex/squidex:${{ steps.version.outputs._0 }}${{env.STABLE_VERSION == 'true' && ',squidex/squidex:latest'}}
push: true
- name: Release - Make directories
run: sudo mkdir /build /release

23
CHANGELOG.md

@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [7.15.0] - 2024-08-23
### Fixed
* **Contents**: Fix to also deliver fields that have the same name as meta fields.
* **Contents**: Never return null statuses for content queries.
* **Migration**: Fix log message in migration.
* **UI**: Correctly remove the filter row, when the delete button is returned.
* **UI**: Fix autocompletion for code editors.
* **UI**: Fix clipboard behavior for rich text editor.
* **UI**: Fix seperator in login screen.
* **UI**: Navigate to the correct page when the back button on the content page is fixed.
* **UI**: Show filters in query dialog when selecting references.
* **UI**: Use correct npm packages to prevent version conflicts.
### Added
* **Contents**: New flag to also enrich required fields.
* **Contents**: New header flag to not enrich queried content items with default values.
* **UI**: Make the code editor editable in the markdown mode of the rich text editor.
* **UI**: New event for editor extensions when the context has been changed.
* **UI**: Support data attributes for Squidex tokens in embed SDK.
## [7.14.0] - 2024-07-03
### Fixed

4
Dockerfile

@ -2,7 +2,7 @@
# Stage 1, Build Backend
#
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 as backend
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS backend
ARG SQUIDEX__BUILD__VERSION=7.0.0
@ -43,7 +43,7 @@ RUN dotnet tool install --tool-path /tools dotnet-counters \
#
# Stage 2, Build Frontend
#
FROM squidex/frontend-build:20.9 as frontend
FROM squidex/frontend-build:20.9 AS frontend
WORKDIR /src

10
backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj

@ -12,22 +12,22 @@
<ItemGroup>
<PackageReference Include="Algolia.Search" Version="6.17.0" />
<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.3.0" />
<PackageReference Include="Azure.Search.Documents" Version="11.5.1" />
<PackageReference Include="Confluent.SchemaRegistry.Serdes.Avro" Version="2.4.0" />
<PackageReference Include="Azure.Search.Documents" Version="11.6.0" />
<PackageReference Include="Confluent.SchemaRegistry.Serdes.Avro" Version="2.5.2" />
<PackageReference Include="CoreTweet" Version="1.0.0.483" />
<PackageReference Include="Elasticsearch.Net" Version="7.17.5" />
<PackageReference Include="Google.Cloud.Diagnostics.Common" Version="5.2.0" />
<PackageReference Include="Google.Cloud.Logging.V2" Version="4.4.0" />
<PackageReference Include="Google.Cloud.Monitoring.V3" Version="3.11.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Azure.CognitiveServices.Vision.ComputerVision" Version="7.0.1" />
<PackageReference Include="Microsoft.Azure.SignalR.Management" Version="1.25.2" />
<PackageReference Include="Microsoft.Azure.SignalR.Management" Version="1.27.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.OData.Core" Version="7.21.3" />
<PackageReference Include="Microsoft.OData.Core" Version="8.0.1" />
<PackageReference Include="NodaTime" Version="3.1.11" />
<PackageReference Include="OpenSearch.Net" Version="1.7.1" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />

2
backend/extensions/Squidex.Extensions/Text/Azure/AzureIndexDefinition.cs

@ -25,7 +25,7 @@ public static class AzureIndexDefinition
.GetProperties(BindingFlags.Public | BindingFlags.Static)
.Select(x => x.GetValue(null)!)
.Select(x => x.ToString()!)
.OrderBy(x => x)
.Order()
.ToList();
var addedLanguage = new HashSet<string>();

18
backend/i18n/frontend_en.json

@ -180,6 +180,8 @@
"clients.connectWizard.step0Title": "Setup client",
"clients.connectWizard.step1Title": "Choose connection method",
"clients.connectWizard.step2Title": "Connect",
"clients.copyClientId": "Copy Client ID",
"clients.copyClientSecret": "Copy Client Secret",
"clients.deleteConfirmText": "Do you really want to revoke the client?",
"clients.deleteConfirmTitle": "Revoke client",
"clients.empty": "No client created yet.",
@ -194,6 +196,7 @@
"comments.follow": "Follow",
"comments.title": "Comments",
"common.actions": "Actions",
"common.add": "Add",
"common.administration": "Administration",
"common.administrationPageTitle": "Administration",
"common.api": "API",
@ -251,6 +254,7 @@
"common.disabled": "Disabled",
"common.displayName": "Display Name",
"common.documentation": "Documentation",
"common.download": "Download",
"common.edit": "Edit",
"common.editing": "Editing",
"common.editInNewTab": "Open in new tab",
@ -310,6 +314,7 @@
"common.more": "More",
"common.name": "Name",
"common.next": "Continue",
"common.nextPage": "Next Page",
"common.no": "No",
"common.nothingChanged": "Nothing has been changed.",
"common.notSupported": "Not Supported",
@ -320,6 +325,7 @@
"common.pagerInfo": "{itemFirst}-{itemLast} of {numberOfItems}",
"common.pagerInfoNoTotal": "{itemFirst}-{itemLast} of total?",
"common.pagerReload": "Click to reload view and get total number of items",
"common.pageSize": "Page Size",
"common.password": "Password",
"common.passwordConfirm": "Confirm Password",
"common.pattern": "Pattern",
@ -328,6 +334,7 @@
"common.permissions": "Permissions",
"common.prev": "Back",
"common.preview": "Preview",
"common.prevPage": "Previous Page",
"common.product": "Squidex Headless CMS",
"common.project": "Project",
"common.queryOperators.contains": "contains",
@ -359,6 +366,9 @@
"common.rules": "Rules",
"common.sampleCodeLabel": "Sample Code at",
"common.save": "Save",
"common.saveAdd": "Save & add another",
"common.saveClose": "Save & close",
"common.saveEdit": "Save & editor",
"common.schema": "Schema",
"common.schemas": "Schemas",
"common.search": "Search",
@ -480,6 +490,8 @@
"contents.removeConfirmText": "Do you really want to remove the content?",
"contents.removeConfirmTitle": "Remove content",
"contents.saveAndPublish": "Save and Publish",
"contents.saveAndPublishAdd": "Save and Publish & add another",
"contents.saveAndPublishClose": "Save and Publish & close",
"contents.scheduledAt": "at",
"contents.scheduledBy": "by",
"contents.scheduledTo": "to",
@ -785,9 +797,9 @@
"rules.updateFailed": "Failed to update rule. Please reload.",
"rules.when": "When",
"schemas.addField": "Add Field",
"schemas.addFieldAndClose": "Create and close",
"schemas.addFieldAndCreate": "Create and add field",
"schemas.addFieldAndEdit": "Create and edit field",
"schemas.addFieldAndClose": "Create",
"schemas.addFieldAndCreate": "Create & add another",
"schemas.addFieldAndEdit": "Create & edit properties",
"schemas.addFieldButton": "Add Field",
"schemas.addFieldFailed": "Failed to add field. Please reload.",
"schemas.addNestedField": "Add Nested Field",

12
backend/i18n/frontend_fr.json

@ -180,6 +180,8 @@
"clients.connectWizard.step0Title": "Configurer le client",
"clients.connectWizard.step1Title": "Choisissez la méthode de connexion",
"clients.connectWizard.step2Title": "Connecter",
"clients.copyClientId": "Copy Client ID",
"clients.copyClientSecret": "Copy Client Secret",
"clients.deleteConfirmText": "Voulez-vous vraiment révoquer le client\u00A0?",
"clients.deleteConfirmTitle": "Révoquer le client",
"clients.empty": "Aucun client créé pour le moment.",
@ -194,6 +196,7 @@
"comments.follow": "Suivre",
"comments.title": "commentaires",
"common.actions": "Actions",
"common.add": "Add",
"common.administration": "Administration",
"common.administrationPageTitle": "Administration",
"common.api": "API",
@ -251,6 +254,7 @@
"common.disabled": "Désactivé",
"common.displayName": "Afficher un nom",
"common.documentation": "Documentation",
"common.download": "Download",
"common.edit": "Modifier",
"common.editing": "Édition",
"common.editInNewTab": "Open in new tab",
@ -310,6 +314,7 @@
"common.more": "Plus",
"common.name": "Nom",
"common.next": "Continue",
"common.nextPage": "Next Page",
"common.no": "Non",
"common.nothingChanged": "Rien n'a été changé.",
"common.notSupported": "Non supporté",
@ -320,6 +325,7 @@
"common.pagerInfo": "{itemFirst}-{itemLast} de {numberOfItems}",
"common.pagerInfoNoTotal": "{itemFirst}-{itemLast} Du total?",
"common.pagerReload": "Cliquez pour recharger la vue et obtenir le nombre total d'articles",
"common.pageSize": "Page Size",
"common.password": "Mot de passe",
"common.passwordConfirm": "Confirmez le mot de passe",
"common.pattern": "Modèle",
@ -328,6 +334,7 @@
"common.permissions": "Autorisations",
"common.prev": "Back",
"common.preview": "Aperçu",
"common.prevPage": "Previous Page",
"common.product": "CMS sans tête Squidex",
"common.project": "Projet",
"common.queryOperators.contains": "contient",
@ -359,6 +366,9 @@
"common.rules": "Règles",
"common.sampleCodeLabel": "Exemple de code à",
"common.save": "Sauvegarder",
"common.saveAdd": "Save & add another",
"common.saveClose": "Save & close",
"common.saveEdit": "Save & editor",
"common.schema": "Schéma",
"common.schemas": "Schémas",
"common.search": "Recherche",
@ -480,6 +490,8 @@
"contents.removeConfirmText": "Voulez-vous vraiment supprimer le contenu\u00A0?",
"contents.removeConfirmTitle": "Supprimer le contenu",
"contents.saveAndPublish": "Enregistrer et publier",
"contents.saveAndPublishAdd": "Save and Publish & add another",
"contents.saveAndPublishClose": "Save and Publish & close",
"contents.scheduledAt": "à",
"contents.scheduledBy": "par",
"contents.scheduledTo": "pour",

12
backend/i18n/frontend_it.json

@ -180,6 +180,8 @@
"clients.connectWizard.step0Title": "Setup client",
"clients.connectWizard.step1Title": "Scegli la tipologia di connessione",
"clients.connectWizard.step2Title": "Collega",
"clients.copyClientId": "Copy Client ID",
"clients.copyClientSecret": "Copy Client Secret",
"clients.deleteConfirmText": "Sei sicuro di voler rimuovere il client?",
"clients.deleteConfirmTitle": "Rimuovere il client",
"clients.empty": "Nessun client ancora creato.",
@ -194,6 +196,7 @@
"comments.follow": "Segui",
"comments.title": "Commenti",
"common.actions": "Azioni",
"common.add": "Add",
"common.administration": "Amministrazione",
"common.administrationPageTitle": "Amministrazione",
"common.api": "API",
@ -251,6 +254,7 @@
"common.disabled": "Disabled",
"common.displayName": "Nome visualizzato",
"common.documentation": "Documentation",
"common.download": "Download",
"common.edit": "Modifica",
"common.editing": "Editing",
"common.editInNewTab": "Open in new tab",
@ -310,6 +314,7 @@
"common.more": "More",
"common.name": "Nome",
"common.next": "Continue",
"common.nextPage": "Next Page",
"common.no": "No",
"common.nothingChanged": "Non è stato cambiato niente.",
"common.notSupported": "Not Supported",
@ -320,6 +325,7 @@
"common.pagerInfo": "{itemFirst}-{itemLast} of {numberOfItems}",
"common.pagerInfoNoTotal": "{itemFirst}-{itemLast} of total?",
"common.pagerReload": "Click to reload view and get total number of items",
"common.pageSize": "Page Size",
"common.password": "Password",
"common.passwordConfirm": "Conferma Password",
"common.pattern": "Pattern",
@ -328,6 +334,7 @@
"common.permissions": "Permessi",
"common.prev": "Back",
"common.preview": "Anteprima",
"common.prevPage": "Previous Page",
"common.product": "Squidex Headless CMS",
"common.project": "Progetto",
"common.queryOperators.contains": "contiene",
@ -359,6 +366,9 @@
"common.rules": "Regole",
"common.sampleCodeLabel": "Esempio di codice per",
"common.save": "Salva",
"common.saveAdd": "Save & add another",
"common.saveClose": "Save & close",
"common.saveEdit": "Save & editor",
"common.schema": "Schema",
"common.schemas": "Schemi",
"common.search": "Search",
@ -480,6 +490,8 @@
"contents.removeConfirmText": "Sei sicuro di voler rimuovere il contenuto?",
"contents.removeConfirmTitle": "Cancella il contenuto",
"contents.saveAndPublish": "Salva e pubblica",
"contents.saveAndPublishAdd": "Save and Publish & add another",
"contents.saveAndPublishClose": "Save and Publish & close",
"contents.scheduledAt": "alle",
"contents.scheduledBy": "by",
"contents.scheduledTo": "a",

12
backend/i18n/frontend_nl.json

@ -180,6 +180,8 @@
"clients.connectWizard.step0Title": "Client instellen",
"clients.connectWizard.step1Title": "Kies verbindingsmethode",
"clients.connectWizard.step2Title": "Verbinden",
"clients.copyClientId": "Copy Client ID",
"clients.copyClientSecret": "Copy Client Secret",
"clients.deleteConfirmText": "Wil je de client echt intrekken?",
"clients.deleteConfirmTitle": "Client intrekken",
"clients.empty": "Nog geen client aangemaakt.",
@ -194,6 +196,7 @@
"comments.follow": "Volgen",
"comments.title": "Reacties",
"common.actions": "Acties",
"common.add": "Add",
"common.administration": "Administratie",
"common.administrationPageTitle": "Administratie",
"common.api": "API",
@ -251,6 +254,7 @@
"common.disabled": "Uitgezet",
"common.displayName": "Weergavenaam",
"common.documentation": "Documentation",
"common.download": "Download",
"common.edit": "Bewerken",
"common.editing": "Bewerken",
"common.editInNewTab": "Open in new tab",
@ -310,6 +314,7 @@
"common.more": "Meer",
"common.name": "Naam",
"common.next": "Continue",
"common.nextPage": "Next Page",
"common.no": "Nee",
"common.nothingChanged": "Er is niets veranderd.",
"common.notSupported": "Not Supported",
@ -320,6 +325,7 @@
"common.pagerInfo": "{itemFirst} - {itemLast} van {numberOfItems}",
"common.pagerInfoNoTotal": "{itemFirst}-{itemLast} of total?",
"common.pagerReload": "Click to reload view and get total number of items",
"common.pageSize": "Page Size",
"common.password": "Wachtwoord",
"common.passwordConfirm": "Bevestig wachtwoord",
"common.pattern": "Patroon",
@ -328,6 +334,7 @@
"common.permissions": "Rechten",
"common.prev": "Back",
"common.preview": "Preview",
"common.prevPage": "Previous Page",
"common.product": "Squidex Headless CMS",
"common.project": "Project",
"common.queryOperators.contains": "bevat",
@ -359,6 +366,9 @@
"common.rules": "Regels",
"common.sampleCodeLabel": "Voorbeeldcode bij",
"common.save": "Opslaan",
"common.saveAdd": "Save & add another",
"common.saveClose": "Save & close",
"common.saveEdit": "Save & editor",
"common.schema": "Schema",
"common.schemas": "Schema's",
"common.search": "Zoeken",
@ -480,6 +490,8 @@
"contents.removeConfirmText": "Wil je de inhoud echt verwijderen?",
"contents.removeConfirmTitle": "Verwijder inhoud",
"contents.saveAndPublish": "Opslaan en publiceren",
"contents.saveAndPublishAdd": "Save and Publish & add another",
"contents.saveAndPublishClose": "Save and Publish & close",
"contents.scheduledAt": "bij",
"contents.scheduledBy": "door",
"contents.scheduledTo": "naar",

12
backend/i18n/frontend_pt.json

@ -180,6 +180,8 @@
"clients.connectWizard.step0Title": "Cliente de configuração",
"clients.connectWizard.step1Title": "Escolha o método de ligação",
"clients.connectWizard.step2Title": "Ligar",
"clients.copyClientId": "Copy Client ID",
"clients.copyClientSecret": "Copy Client Secret",
"clients.deleteConfirmText": "Quer mesmo revogar o cliente?",
"clients.deleteConfirmTitle": "Revogar cliente",
"clients.empty": "Nenhum cliente foi criado ainda.",
@ -194,6 +196,7 @@
"comments.follow": "Seguir",
"comments.title": "Comentários",
"common.actions": "Ações",
"common.add": "Add",
"common.administration": "Administração",
"common.administrationPageTitle": "Administração",
"common.api": "API",
@ -251,6 +254,7 @@
"common.disabled": "Deficientes",
"common.displayName": "Nome do visor",
"common.documentation": "Documentação",
"common.download": "Download",
"common.edit": "Editar",
"common.editing": "Edição",
"common.editInNewTab": "Open in new tab",
@ -310,6 +314,7 @@
"common.more": "Mais",
"common.name": "Nome",
"common.next": "Continue",
"common.nextPage": "Next Page",
"common.no": "Não",
"common.nothingChanged": "Nada foi mudado.",
"common.notSupported": "Não suportado",
@ -320,6 +325,7 @@
"common.pagerInfo": "{itemFirst}-{itemLast} de {numberOfItems}",
"common.pagerInfoNoTotal": "{itemFirst}-{itemLast} do total?",
"common.pagerReload": "Clique para recarregar a vista e obter o número total de itens",
"common.pageSize": "Page Size",
"common.password": "Password",
"common.passwordConfirm": "Confirmar Password",
"common.pattern": "Padrão",
@ -328,6 +334,7 @@
"common.permissions": "Permissões",
"common.prev": "Back",
"common.preview": "Previsualizar",
"common.prevPage": "Previous Page",
"common.product": "CMS Headless Squidex",
"common.project": "Projeto",
"common.queryOperators.contains": "contém",
@ -359,6 +366,9 @@
"common.rules": "Regras",
"common.sampleCodeLabel": "Código de amostra em",
"common.save": "Salvar",
"common.saveAdd": "Save & add another",
"common.saveClose": "Save & close",
"common.saveEdit": "Save & editor",
"common.schema": "Esquema",
"common.schemas": "Esquemas",
"common.search": "Pesquisar",
@ -480,6 +490,8 @@
"contents.removeConfirmText": "Quer mesmo remover o conteúdo?",
"contents.removeConfirmTitle": "Remover conteúdo",
"contents.saveAndPublish": "Salvar e Publicar",
"contents.saveAndPublishAdd": "Save and Publish & add another",
"contents.saveAndPublishClose": "Save and Publish & close",
"contents.scheduledAt": "em",
"contents.scheduledBy": "por",
"contents.scheduledTo": "Para",

12
backend/i18n/frontend_zh.json

@ -180,6 +180,8 @@
"clients.connectWizard.step0Title": "设置客户端",
"clients.connectWizard.step1Title": "选择连接方式",
"clients.connectWizard.step2Title": "连接",
"clients.copyClientId": "Copy Client ID",
"clients.copyClientSecret": "Copy Client Secret",
"clients.deleteConfirmText": "你真的要撤销客户端吗?",
"clients.deleteConfirmTitle": "撤销客户端",
"clients.empty": "尚未创建客户端。",
@ -194,6 +196,7 @@
"comments.follow": "关注",
"comments.title": "评论",
"common.actions": "动作",
"common.add": "Add",
"common.administration": "管理",
"common.administrationPageTitle": "管理",
"common.api": "API",
@ -251,6 +254,7 @@
"common.disabled": "已禁用",
"common.displayName": "显示名称",
"common.documentation": "Documentation",
"common.download": "Download",
"common.edit": "编辑",
"common.editing": "Editing",
"common.editInNewTab": "Open in new tab",
@ -310,6 +314,7 @@
"common.more": "More",
"common.name": "名称",
"common.next": "Continue",
"common.nextPage": "Next Page",
"common.no": "不",
"common.nothingChanged": "什么都没有改变。",
"common.notSupported": "Not Supported",
@ -320,6 +325,7 @@
"common.pagerInfo": "{itemFirst}-{itemLast} 的 {numberOfItems}",
"common.pagerInfoNoTotal": "{itemFirst}-{itemLast} of total?",
"common.pagerReload": "Click to reload view and get total number of items",
"common.pageSize": "Page Size",
"common.password": "密码",
"common.passwordConfirm": "确认密码",
"common.pattern": "模式",
@ -328,6 +334,7 @@
"common.permissions": "权限",
"common.prev": "Back",
"common.preview": "预览",
"common.prevPage": "Previous Page",
"common.product": "Squidex Headless CMS",
"common.project": "项目",
"common.queryOperators.contains": "包含",
@ -359,6 +366,9 @@
"common.rules": "规则",
"common.sampleCodeLabel": "示例代码在",
"common.save": "保存",
"common.saveAdd": "Save & add another",
"common.saveClose": "Save & close",
"common.saveEdit": "Save & editor",
"common.schema": "Schema",
"common.schemas": "Schemas",
"common.search": "搜索",
@ -480,6 +490,8 @@
"contents.removeConfirmText": "您真的要删除内容吗?",
"contents.removeConfirmTitle": "删除内容",
"contents.saveAndPublish": "保存并发布",
"contents.saveAndPublishAdd": "Save and Publish & add another",
"contents.saveAndPublishClose": "Save and Publish & close",
"contents.scheduledAt": "at",
"contents.scheduledBy": "by",
"contents.scheduledTo": "to",

18
backend/i18n/source/frontend_en.json

@ -180,6 +180,8 @@
"clients.connectWizard.step0Title": "Setup client",
"clients.connectWizard.step1Title": "Choose connection method",
"clients.connectWizard.step2Title": "Connect",
"clients.copyClientId": "Copy Client ID",
"clients.copyClientSecret": "Copy Client Secret",
"clients.deleteConfirmText": "Do you really want to revoke the client?",
"clients.deleteConfirmTitle": "Revoke client",
"clients.empty": "No client created yet.",
@ -194,6 +196,7 @@
"comments.follow": "Follow",
"comments.title": "Comments",
"common.actions": "Actions",
"common.add": "Add",
"common.administration": "Administration",
"common.administrationPageTitle": "Administration",
"common.api": "API",
@ -251,6 +254,7 @@
"common.disabled": "Disabled",
"common.displayName": "Display Name",
"common.documentation": "Documentation",
"common.download": "Download",
"common.edit": "Edit",
"common.editing": "Editing",
"common.editInNewTab": "Open in new tab",
@ -310,6 +314,7 @@
"common.more": "More",
"common.name": "Name",
"common.next": "Continue",
"common.nextPage": "Next Page",
"common.no": "No",
"common.nothingChanged": "Nothing has been changed.",
"common.notSupported": "Not Supported",
@ -320,6 +325,7 @@
"common.pagerInfo": "{itemFirst}-{itemLast} of {numberOfItems}",
"common.pagerInfoNoTotal": "{itemFirst}-{itemLast} of total?",
"common.pagerReload": "Click to reload view and get total number of items",
"common.pageSize": "Page Size",
"common.password": "Password",
"common.passwordConfirm": "Confirm Password",
"common.pattern": "Pattern",
@ -328,6 +334,7 @@
"common.permissions": "Permissions",
"common.prev": "Back",
"common.preview": "Preview",
"common.prevPage": "Previous Page",
"common.product": "Squidex Headless CMS",
"common.project": "Project",
"common.queryOperators.contains": "contains",
@ -359,6 +366,9 @@
"common.rules": "Rules",
"common.sampleCodeLabel": "Sample Code at",
"common.save": "Save",
"common.saveAdd": "Save & add another",
"common.saveClose": "Save & close",
"common.saveEdit": "Save & editor",
"common.schema": "Schema",
"common.schemas": "Schemas",
"common.search": "Search",
@ -480,6 +490,8 @@
"contents.removeConfirmText": "Do you really want to remove the content?",
"contents.removeConfirmTitle": "Remove content",
"contents.saveAndPublish": "Save and Publish",
"contents.saveAndPublishAdd": "Save and Publish & add another",
"contents.saveAndPublishClose": "Save and Publish & close",
"contents.scheduledAt": "at",
"contents.scheduledBy": "by",
"contents.scheduledTo": "to",
@ -785,9 +797,9 @@
"rules.updateFailed": "Failed to update rule. Please reload.",
"rules.when": "When",
"schemas.addField": "Add Field",
"schemas.addFieldAndClose": "Create and close",
"schemas.addFieldAndCreate": "Create and add field",
"schemas.addFieldAndEdit": "Create and edit field",
"schemas.addFieldAndClose": "Create",
"schemas.addFieldAndCreate": "Create & add another",
"schemas.addFieldAndEdit": "Create & edit properties",
"schemas.addFieldButton": "Add Field",
"schemas.addFieldFailed": "Failed to add field. Please reload.",
"schemas.addNestedField": "Add Nested Field",

2
backend/src/Migrations/Migrations.csproj

@ -6,7 +6,7 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

10
backend/src/Squidex.Domain.Apps.Core.Model/Assets/Asset.cs

@ -53,27 +53,27 @@ public record Asset : AssetItem
if (fileName != null && !string.Equals(FileName, fileName, StringComparison.OrdinalIgnoreCase))
{
result = this with { FileName = fileName };
result = result with { FileName = fileName };
}
if (slug != null && !string.Equals(Slug, slug, StringComparison.OrdinalIgnoreCase))
{
result = this with { Slug = slug };
result = result with { Slug = slug };
}
if (isProtected != null && IsProtected != isProtected.Value)
{
result = this with { IsProtected = isProtected.Value };
result = result with { IsProtected = isProtected.Value };
}
if (tags != null && !Tags.SetEquals(tags))
{
result = this with { Tags = tags };
result = result with { Tags = tags };
}
if (metadata != null && !Metadata.EqualsDictionary(metadata))
{
result = this with { Metadata = metadata };
result = result with { Metadata = metadata };
}
return result;

6
backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/ContentFieldDataConverter.cs

@ -27,7 +27,8 @@ public sealed class ContentFieldDataConverter : JsonConverter<ContentFieldData>
if (!reader.Read())
{
throw new JsonException("Unexpected end when reading Object.");
ThrowHelper.JsonSystemException("Unexpected end when reading Object.");
return result;
}
var value = JsonSerializer.Deserialize<JsonValue>(ref reader, options)!;
@ -48,7 +49,8 @@ public sealed class ContentFieldDataConverter : JsonConverter<ContentFieldData>
}
}
throw new JsonException("Unexpected end when reading Object.");
ThrowHelper.JsonSystemException("Unexpected end when reading Object.");
return result;
}
public override void Write(Utf8JsonWriter writer, ContentFieldData value, JsonSerializerOptions options)

2
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldNames.cs

@ -32,7 +32,7 @@ public sealed class FieldNames : ReadonlyList<string>
"translationStatusAverage"
];
public static readonly new FieldNames Empty = new FieldNames(new List<string>());
public static readonly new FieldNames Empty = new FieldNames([]);
private FieldNames()
{

3
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldEditor.cs

@ -13,5 +13,6 @@ public enum ReferencesFieldEditor
Dropdown,
Tags,
Checkboxes,
Input
Input,
Radio
}

2
backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj

@ -12,7 +12,7 @@
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

8
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/Parser.cs → backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/CacheParser.cs

@ -5,26 +5,26 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Esprima.Ast;
using Acornima.Ast;
using Jint;
using Microsoft.Extensions.Caching.Memory;
namespace Squidex.Domain.Apps.Core.Scripting.Internal;
internal sealed class Parser
internal sealed class CacheParser
{
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10);
private readonly IMemoryCache cache;
public Parser(IMemoryCache cache)
public CacheParser(IMemoryCache cache)
{
this.cache = cache;
}
public Prepared<Script> Parse(string script)
{
var cacheKey = $"{typeof(Parser)}_Script_{script}";
var cacheKey = $"{typeof(CacheParser)}_Script_{script}";
return cache.GetOrCreate(cacheKey, entry =>
{

8
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs

@ -6,7 +6,7 @@
// ==========================================================================
using System.Diagnostics;
using Esprima;
using Acornima;
using Jint;
using Jint.Native;
using Jint.Runtime;
@ -26,13 +26,13 @@ namespace Squidex.Domain.Apps.Core.Scripting;
public sealed class JintScriptEngine : IScriptEngine, IScriptDescriptor
{
private readonly IJintExtension[] extensions;
private readonly Parser parser;
private readonly CacheParser parser;
private readonly TimeSpan timeoutScript;
private readonly TimeSpan timeoutExecution;
public JintScriptEngine(IMemoryCache cache, IOptions<JintScriptOptions> options, IEnumerable<IJintExtension>? extensions = null)
{
parser = new Parser(cache);
parser = new CacheParser(cache);
timeoutScript = options.Value.TimeoutScript;
timeoutExecution = options.Value.TimeoutExecution;
@ -203,7 +203,7 @@ public sealed class JintScriptEngine : IScriptEngine, IScriptDescriptor
return BuildException("common.jsParseError", inner.Message);
case JavaScriptException:
return BuildException("common.jsError", inner.Message);
case ParserException:
case ParseErrorException:
return BuildException("common.jsError", inner.Message);
case DomainException:
return inner;

2
backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptExecutionContext.cs

@ -5,7 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Esprima.Ast;
using Acornima.Ast;
using Jint;
using Jint.Native;
using Squidex.Infrastructure.Tasks;

14
backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj

@ -18,18 +18,18 @@
<ProjectReference Include="..\Squidex.Shared\Squidex.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fluid.Core" Version="2.10.0" />
<PackageReference Include="GeoJSON.Net" Version="1.2.19" />
<PackageReference Include="Jint" Version="3.1.4" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Fluid.Core" Version="2.11.1" />
<PackageReference Include="GeoJSON.Net" Version="1.4.1" />
<PackageReference Include="Jint" Version="4.0.2" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="NJsonSchema" Version="11.0.1" />
<PackageReference Include="NJsonSchema" Version="11.0.2" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.AI" Version="6.18.0" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="6.18.0" />
<PackageReference Include="Squidex.AI" Version="6.19.0" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="6.19.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />

7
backend/src/Squidex.Domain.Apps.Core.Operations/Templates/Extensions/JsonArrayFluidValue.cs

@ -98,6 +98,13 @@ public sealed class JsonArrayFluidValue : FluidValue
}
}
public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, CultureInfo cultureInfo)
{
writer.Write(value);
return default;
}
[Obsolete("Made obsolete in base library.")]
public override void WriteTo(TextWriter writer, TextEncoder encoder, CultureInfo cultureInfo)
{
writer.Write(value);

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Apps/MongoAppRepository.cs

@ -28,8 +28,8 @@ public sealed class MongoAppRepository : MongoSnapshotStoreBase<App, MongoAppEnt
protected override Task SetupCollectionAsync(IMongoCollection<MongoAppEntity> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoAppEntity>(
Index
.Ascending(x => x.IndexedName)),
@ -39,7 +39,7 @@ public sealed class MongoAppRepository : MongoSnapshotStoreBase<App, MongoAppEnt
new CreateIndexModel<MongoAppEntity>(
Index
.Ascending(x => x.IndexedTeamId))
}, ct);
], ct);
}
Task IDeleter.DeleteAppAsync(App app,

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs

@ -33,14 +33,14 @@ public sealed partial class MongoAssetFolderRepository : MongoRepositoryBase<Mon
protected override Task SetupCollectionAsync(IMongoCollection<MongoAssetFolderEntity> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoAssetFolderEntity>(
Index
.Ascending(x => x.IndexedAppId)
.Ascending(x => x.ParentId)
.Ascending(x => x.IsDeleted))
}, ct);
], ct);
}
public async Task<IResultList<AssetFolder>> QueryAsync(DomainId appId, DomainId? parentId,

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs

@ -49,8 +49,8 @@ public sealed partial class MongoAssetRepository : MongoRepositoryBase<MongoAsse
protected override Task SetupCollectionAsync(IMongoCollection<MongoAssetEntity> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoAssetEntity>(
Index
.Descending(x => x.LastModified)
@ -71,7 +71,7 @@ public sealed partial class MongoAssetRepository : MongoRepositoryBase<MongoAsse
Index
.Ascending(x => x.Id)
.Ascending(x => x.IsDeleted))
}, ct);
], ct);
}
public async IAsyncEnumerable<Asset> StreamAll(DomainId appId,

42
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/Extensions.cs

@ -129,7 +129,7 @@ public static class Extensions
PipelineDefinitionBuilder.For<MongoContentEntity>()
.Match(LookupMatch)
.Project(
BuildProjection2<MongoContentEntity>(q.Fields)),
BuildProjection<MongoContentEntity>(q.Fields)),
x => x.Joined)
.Project<IdOnly>(
Builders<IdOnly>.Projection.Include(x => x.Joined))
@ -165,15 +165,15 @@ public static class Extensions
public static IFindFluent<T, T> SelectFields<T>(this IFindFluent<T, T> find, IEnumerable<string>? fields)
{
return find.Project<T>(BuildProjection2<T>(fields));
return find.Project<T>(BuildProjection<T>(fields));
}
public static IAggregateFluent<T> SelectFields<T>(this IAggregateFluent<T> find, IEnumerable<string>? fields)
{
return find.Project<T>(BuildProjection2<T>(fields));
return find.Project<T>(BuildProjection<T>(fields));
}
public static ProjectionDefinition<T, T> BuildProjection2<T>(IEnumerable<string>? fields)
public static ProjectionDefinition<T, T> BuildProjection<T>(IEnumerable<string>? fields)
{
var projector = Builders<T>.Projection;
var projections = new List<ProjectionDefinition<T>>();
@ -186,30 +186,38 @@ public static class Extensions
foreach (var field in fields)
{
var dataField =
FieldNames.IsDataField(field, out var fieldName) ?
fieldName :
field;
yield return $"{dataPrefix}.{dataField}";
var actualFieldName = field;
// Only add data fields, because we add all meta fields anyway.
if (FieldNames.IsDataField(field, out var dataField))
{
actualFieldName = dataField;
}
var fullName = $"{dataPrefix}.{actualFieldName}";
if (!MetaFields.ContainsKey(fullName))
{
yield return fullName;
}
}
}
var addedFields = new List<string>();
// Sort the fields to start with prefixes first.
var allFields = GetDataFields(fields).Union(MetaFields.Values).OrderBy(x => x);
var allFields = GetDataFields(fields).Union(MetaFields.Values).Order();
foreach (var field in allFields)
{
// If there is at least one field that is a prefix of the current field, we cannot add that.
if (addedFields.Exists(x => field.StartsWith(x, StringComparison.OrdinalIgnoreCase)))
if (addedFields.Exists(x => IsPrefix(field, x)))
{
continue;
}
projections.Add(projector.Include(field));
// Track added prefixes.
addedFields.Add(field);
}
}
@ -219,5 +227,15 @@ public static class Extensions
}
return projector.Combine(projections);
static bool IsPrefix(string field, string prefix)
{
if (!field.StartsWith(prefix, StringComparison.Ordinal))
{
return false;
}
return field.Length == prefix.Length || field[prefix.Length] == '.';
}
}
}

5
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Operations/QueryInDedicatedCollection.cs

@ -45,8 +45,7 @@ internal sealed class QueryInDedicatedCollection : MongoBase<MongoContentEntity>
var schemaCollection = schemaDatabase.GetCollection<MongoContentEntity>($"{prefixCollection}_{schemaId}");
await schemaCollection.Indexes.CreateManyAsync(
new[]
{
[
new CreateIndexModel<MongoContentEntity>(Index
.Descending(x => x.LastModified)
.Ascending(x => x.Id)
@ -56,7 +55,7 @@ internal sealed class QueryInDedicatedCollection : MongoBase<MongoContentEntity>
.Ascending(x => x.IndexedSchemaId)
.Ascending(x => x.IsDeleted)
.Descending(x => x.LastModified))
});
]);
return schemaCollection;
});

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs

@ -44,8 +44,8 @@ public sealed class MongoHistoryEventRepository : MongoRepositoryBase<HistoryEve
protected override Task SetupCollectionAsync(IMongoCollection<HistoryEvent> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<HistoryEvent>(
Index
.Ascending(x => x.OwnerId)
@ -57,7 +57,7 @@ public sealed class MongoHistoryEventRepository : MongoRepositoryBase<HistoryEve
.Ascending(x => x.OwnerId)
.Descending(x => x.Created)
.Descending(x => x.Version))
}, ct);
], ct);
}
async Task IDeleter.DeleteAppAsync(App app,

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCountCollection.cs

@ -78,9 +78,9 @@ internal sealed class MongoCountCollection : MongoRepositoryBase<MongoCountEntit
await Collection.UpdateOneAsync(x => x.Key == key,
Update
.Set(x => x.Key, key)
.SetOnInsert(x => x.Count, actualCount)
.SetOnInsert(x => x.Created, now),
.SetOnInsert(x => x.Key, key)
.Set(x => x.Count, actualCount)
.Set(x => x.Created, now),
Upsert, ct);
return actualCount;

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs

@ -31,8 +31,8 @@ public sealed class MongoRuleEventRepository : MongoRepositoryBase<MongoRuleEven
protected override async Task SetupCollectionAsync(IMongoCollection<MongoRuleEventEntity> collection,
CancellationToken ct)
{
await collection.Indexes.CreateManyAsync(new[]
{
await collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoRuleEventEntity>(
Index.Ascending(x => x.NextAttempt)),
@ -46,7 +46,7 @@ public sealed class MongoRuleEventRepository : MongoRepositoryBase<MongoRuleEven
{
ExpireAfter = TimeSpan.Zero
})
}, ct);
], ct);
}
async Task IDeleter.DeleteAppAsync(App app,

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleRepository.cs

@ -29,12 +29,12 @@ public sealed class MongoRuleRepository : MongoSnapshotStoreBase<Rule, MongoRule
protected override Task SetupCollectionAsync(IMongoCollection<MongoRuleEntity> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoRuleEntity>(
Index
.Ascending(x => x.IndexedAppId))
}, ct);
], ct);
}
Task IDeleter.DeleteAppAsync(App app,

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs

@ -29,13 +29,13 @@ public sealed class MongoSchemaRepository : MongoSnapshotStoreBase<Schema, Mongo
protected override Task SetupCollectionAsync(IMongoCollection<MongoSchemaEntity> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoSchemaEntity>(
Index
.Ascending(x => x.IndexedAppId)
.Ascending(x => x.IndexedName))
}, ct);
], ct);
}
Task IDeleter.DeleteAppAsync(App app,

4
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj

@ -19,11 +19,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Lucene.Net.QueryParser" Version="4.8.0-beta00016" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MongoDB.Driver" Version="2.27.0" />
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Teams/MongoTeamRepository.cs

@ -28,12 +28,12 @@ public sealed class MongoTeamRepository : MongoSnapshotStoreBase<Team, MongoTeam
protected override Task SetupCollectionAsync(IMongoCollection<MongoTeamEntity> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoTeamEntity>(
Index
.Ascending(x => x.IndexedUserIds))
}, ct);
], ct);
}
public async Task<List<Team>> QueryAllAsync(string contributorId,

6
backend/src/Squidex.Domain.Apps.Entities.MongoDb/Text/MongoTextIndexBase.cs

@ -46,8 +46,8 @@ public abstract class MongoTextIndexBase<T> : MongoRepositoryBase<MongoTextIndex
protected override Task SetupCollectionAsync(IMongoCollection<MongoTextIndexEntity<T>> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoTextIndexEntity<T>>(
Index
.Ascending(x => x.AppId)
@ -59,7 +59,7 @@ public abstract class MongoTextIndexBase<T> : MongoRepositoryBase<MongoTextIndex
.Ascending(x => x.SchemaId)
.Ascending(x => x.GeoField)
.Geo2DSphere(x => x.GeoObject))
}, ct);
], ct);
}
protected override string CollectionName()

12
backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs

@ -227,11 +227,11 @@ public sealed class AppsIndex : IAppsIndex, ICommandMiddleware, IInitializable
}
// Do not use cancellation here as we already so far.
await appCache.AddAsync(new[]
{
await appCache.AddAsync(
[
new KeyValuePair<string, object?>(GetCacheKey(app.Id), app),
new KeyValuePair<string, object?>(GetCacheKey(app.Name), app),
}, options.CacheDuration);
], options.CacheDuration);
return app;
}
@ -244,10 +244,10 @@ public sealed class AppsIndex : IAppsIndex, ICommandMiddleware, IInitializable
}
// Do not use cancellation here as we already so far.
return appCache.RemoveAsync(new[]
{
return appCache.RemoveAsync(
[
GetCacheKey(id),
GetCacheKey(name)
});
]);
}
}

2
backend/src/Squidex.Domain.Apps.Entities/Assets/AssetSlug.cs

@ -11,7 +11,7 @@ namespace Squidex.Domain.Apps.Entities.Assets;
public static class AssetSlug
{
private static readonly HashSet<char> Dot = new HashSet<char>(new[] { '.' });
private static readonly HashSet<char> Dot = new HashSet<char>(['.']);
public static string ToAssetSlug(this string value)
{

34
backend/src/Squidex.Domain.Apps.Entities/Assets/SvgAssetMetadataSource.cs

@ -52,11 +52,28 @@ public sealed class SvgAssetMetadataSource : IAssetMetadataSource
if (!string.IsNullOrWhiteSpace(width) && !string.IsNullOrWhiteSpace(height))
{
command.Metadata[KnownMetadataKeys.SvgWidth] = width;
command.Metadata[KnownMetadataKeys.SvgHeight] = height;
var hasNumericWidth = TryParseInt(width, out var w);
var hasNumericHeight = TryParseInt(height, out var h);
if (int.TryParse(width, NumberStyles.Integer, CultureInfo.InvariantCulture, out var w) &&
int.TryParse(height, NumberStyles.Integer, CultureInfo.InvariantCulture, out var h))
if (hasNumericWidth)
{
command.Metadata[KnownMetadataKeys.SvgWidth] = w;
}
else
{
command.Metadata[KnownMetadataKeys.SvgWidth] = width;
}
if (hasNumericWidth)
{
command.Metadata[KnownMetadataKeys.SvgHeight] = h;
}
else
{
command.Metadata[KnownMetadataKeys.SvgHeight] = height;
}
if (hasNumericWidth && hasNumericHeight)
{
command.Metadata[KnownMetadataKeys.PixelWidth] = w;
command.Metadata[KnownMetadataKeys.PixelHeight] = h;
@ -79,6 +96,11 @@ public sealed class SvgAssetMetadataSource : IAssetMetadataSource
}
}
private static bool TryParseInt(string value, out int result)
{
return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
}
public IEnumerable<string> Format(Asset asset)
{
var isSvg =
@ -90,8 +112,8 @@ public sealed class SvgAssetMetadataSource : IAssetMetadataSource
yield break;
}
if (asset.Metadata.TryGetString(KnownMetadataKeys.SvgWidth, out var w) &&
asset.Metadata.TryGetString(KnownMetadataKeys.SvgHeight, out var h))
if (asset.Metadata.TryGetValue(KnownMetadataKeys.SvgWidth, out var w) &&
asset.Metadata.TryGetValue(KnownMetadataKeys.SvgHeight, out var h))
{
yield return $"{w}x{h}";
}

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateContents.cs

@ -29,5 +29,7 @@ public sealed class BulkUpdateContents : SquidexCommand, IAppCommand, ISchemaCom
public bool OptimizeValidation { get; set; }
public bool EnrichRequiredFields { get; set; }
public BulkUpdateJob[]? Jobs { get; set; }
}

1
backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/EnrichContentDefaults.cs

@ -9,4 +9,5 @@ namespace Squidex.Domain.Apps.Entities.Contents.Commands;
public sealed class EnrichContentDefaults : ContentCommand
{
public bool EnrichRequiredFields { get; set; }
}

2
backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/UpsertContent.cs

@ -21,6 +21,8 @@ public sealed class UpsertContent : ContentDataCommand, ISchemaCommand
public bool EnrichDefaults { get; set; }
public bool EnrichRequiredFields { get; set; }
public UpsertContent()
{
ContentId = DomainId.NewGuid();

12
backend/src/Squidex.Domain.Apps.Entities/Contents/ContentHeaders.cs

@ -19,6 +19,7 @@ public static class ContentHeaders
public const string KeyLanguages = "X-Languages";
public const string KeyNoCleanup = "X-NoCleanup";
public const string KeyNoEnrichment = "X-NoEnrichment";
public const string KeyNoDefaults = "X-NoDefaults";
public const string KeyNoResolveLanguages = "X-NoResolveLanguages";
public const string KeyResolveFlow = "X-ResolveFlow";
public const string KeyResolveUrls = "X-ResolveUrls";
@ -32,6 +33,7 @@ public static class ContentHeaders
cache.AddHeader(KeyLanguages);
cache.AddHeader(KeyNoCleanup);
cache.AddHeader(KeyNoEnrichment);
cache.AddHeader(KeyNoDefaults);
cache.AddHeader(KeyNoResolveLanguages);
cache.AddHeader(KeyResolveFlow);
cache.AddHeader(KeyResolveUrls);
@ -63,6 +65,16 @@ public static class ContentHeaders
return builder.WithBoolean(KeyNoEnrichment, value);
}
public static bool NoDefaults(this Context context)
{
return context.AsBoolean(KeyNoDefaults);
}
public static ICloneBuilder WithNoDefaults(this ICloneBuilder builder, bool value = true)
{
return builder.WithBoolean(KeyNoDefaults, value);
}
public static bool Unpublished(this Context context)
{
return context.AsBoolean(KeyUnpublished);

6
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.cs

@ -211,7 +211,7 @@ public partial class ContentDomainObject : DomainObject<WriteContent>
{
var operation = await ContentOperation.CreateAsync(serviceProvider, c, () => Snapshot);
var newData = operation.GenerateDefaultValues(Snapshot.EditingData.Clone());
var newData = operation.GenerateDefaultValues(Snapshot.EditingData.Clone(), !c.EnrichRequiredFields);
if (!newData.Equals(Snapshot.EditingData))
{
@ -263,7 +263,7 @@ public partial class ContentDomainObject : DomainObject<WriteContent>
c.Data = await operation.ExecuteCreateScriptAsync(c.Data, status, ct);
}
c.Data = operation.GenerateDefaultValues(c.Data);
c.Data = operation.GenerateDefaultValues(c.Data, false);
if (!c.DoNotValidate)
{
@ -336,7 +336,7 @@ public partial class ContentDomainObject : DomainObject<WriteContent>
if (c.EnrichDefaults)
{
newData = operation.GenerateDefaultValues(newData);
newData = operation.GenerateDefaultValues(newData, true);
}
if (newData.Equals(Snapshot.EditingData))

20
backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ValidationExtensions.cs

@ -47,7 +47,8 @@ public static class ValidationExtensions
operation.ThrowOnErrors();
}
public static async Task ValidateInputAsync(this ContentOperation operation, ContentData data, bool optimize, bool published,
public static async Task ValidateInputAsync(this ContentOperation operation,
ContentData data, bool optimize, bool published,
CancellationToken ct)
{
var validator = GetValidator(operation, optimize, published);
@ -57,7 +58,8 @@ public static class ValidationExtensions
operation.AddErrors(validator.Errors).ThrowOnErrors();
}
public static async Task ValidateInputPartialAsync(this ContentOperation operation, ContentData data, bool optimize, bool published,
public static async Task ValidateInputPartialAsync(this ContentOperation operation,
ContentData data, bool optimize, bool published,
CancellationToken ct)
{
var validator = GetValidator(operation, optimize, published);
@ -67,7 +69,8 @@ public static class ValidationExtensions
operation.AddErrors(validator.Errors).ThrowOnErrors();
}
public static async Task ValidateContentAsync(this ContentOperation operation, ContentData data, bool optimize, bool published,
public static async Task ValidateContentAsync(this ContentOperation operation,
ContentData data, bool optimize, bool published,
CancellationToken ct)
{
var validator = GetValidator(operation, optimize, published);
@ -77,7 +80,8 @@ public static class ValidationExtensions
operation.AddErrors(validator.Errors).ThrowOnErrors();
}
public static async Task ValidateContentAndInputAsync(this ContentOperation operation, ContentData data, bool optimize, bool published,
public static async Task ValidateContentAndInputAsync(this ContentOperation operation,
ContentData data, bool optimize, bool published,
CancellationToken ct)
{
var validator = GetValidator(operation, optimize, published);
@ -87,18 +91,20 @@ public static class ValidationExtensions
operation.AddErrors(validator.Errors).ThrowOnErrors();
}
public static ContentData GenerateDefaultValues(this ContentOperation operation, ContentData data)
public static ContentData GenerateDefaultValues(this ContentOperation operation,
ContentData data, bool ignoreRequired)
{
var converter =
new ContentConverter(
operation.Components,
operation.Schema);
converter.Add(new AddDefaultValues(operation.Partition()) { IgnoreRequiredFields = true });
converter.Add(new AddDefaultValues(operation.Partition()) { IgnoreRequiredFields = ignoreRequired });
return converter.Convert(data);
}
public static ContentData InvokeUpdates(this ContentOperation operation, ContentData data, ContentData currentData, bool canUnset)
public static ContentData InvokeUpdates(this ContentOperation operation, ContentData data,
ContentData currentData, bool canUnset)
{
var converter =
new ContentConverter(

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

@ -5,12 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL;
using GraphQL.DataLoader;
using Microsoft.Extensions.Options;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Contents.GraphQL.Cache;
using Squidex.Domain.Apps.Entities.Contents.Queries;
using Squidex.Infrastructure;
using Squidex.Shared;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL;
@ -27,6 +29,8 @@ public sealed class GraphQLExecutionContext : QueryExecutionContext
public override Context Context { get; }
public bool CanExposePII { get; }
public GraphQLExecutionContext(
IDataLoaderContextAccessor dataLoaders,
IAssetQueryService assetQuery,
@ -57,6 +61,8 @@ public sealed class GraphQLExecutionContext : QueryExecutionContext
{
batchSize = Math.Max(MinBatchSize, Math.Min(MaxBatchSize, batchSize));
}
CanExposePII = Context.UserPermissions.Allows(PermissionIds.ForApp(PermissionIds.AppPii, context.App.Name));
}
public async ValueTask<IUser?> FindUserAsync(RefToken refToken,

17
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/UserGraphType.cs

@ -35,7 +35,7 @@ internal sealed class UserGraphType : SharedObjectGraphType<IUser>
AddField(new FieldType
{
Name = "displayName",
Resolver = Resolve(x => x.Claims.DisplayName()),
Resolver = ResolveOrHide(x => x.Claims.DisplayName()),
ResolvedType = Scalars.String,
Description = FieldDescriptions.UserDisplayName
});
@ -43,7 +43,7 @@ internal sealed class UserGraphType : SharedObjectGraphType<IUser>
AddField(new FieldType
{
Name = "email",
Resolver = Resolve(x => x.Email),
Resolver = ResolveOrHide(x => x.Email),
ResolvedType = Scalars.String,
Description = FieldDescriptions.UserEmail
});
@ -55,4 +55,17 @@ internal sealed class UserGraphType : SharedObjectGraphType<IUser>
{
return Resolvers.Sync(resolver);
}
private static IFieldResolver ResolveOrHide(Func<IUser, string?> resolver)
{
return Resolvers.Sync<IUser, string?>((source, _, context) =>
{
if (context.CanExposePII)
{
return resolver(source);
}
return "Hidden";
});
}
}

9
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs

@ -67,6 +67,7 @@ public sealed class ContentEnricher : IContentEnricher
{
var result = SimpleMapper.Map(content, new EnrichedContent());
// Clone the data to keep the existing value intact (for example when cached in memory).
if (cloneData)
{
using (Telemetry.Activities.StartActivity("ContentEnricher/CloneData"))
@ -86,12 +87,8 @@ public sealed class ContentEnricher : IContentEnricher
{
return schemaCache.GetOrAdd(id, async x =>
{
var schema = await appProvider.GetSchemaAsync(context.App.Id, x, false, ct);
if (schema == null)
{
throw new DomainObjectNotFoundException(x.ToString());
}
var schema = await appProvider.GetSchemaAsync(context.App.Id, x, false, ct)
?? throw new DomainObjectNotFoundException(x.ToString());
var components = await appProvider.GetComponentsAsync(schema, ct);

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

@ -16,8 +16,6 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Tasks;
#pragma warning disable MA0073 // Avoid comparison with bool constant
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps;
public sealed class ConvertData : IContentEnricherStep
@ -133,7 +131,7 @@ public sealed class ConvertData : IContentEnricherStep
converter.Add(new ResolveFromPreviousPartitioning(context.App.Languages));
if (!context.IsFrontendClient)
if (!context.IsFrontendClient && !context.NoDefaults())
{
converter.Add(new AddDefaultValues(context.App.PartitionResolver())
{

2
backend/src/Squidex.Domain.Apps.Entities/Jobs/JobWorker.cs

@ -27,7 +27,7 @@ public sealed class JobWorker :
processorFactory = key =>
{
return (JobProcessor)objectFactory(serviceProvider, new object[] { key });
return (JobProcessor)objectFactory(serviceProvider, [key]);
};
}

12
backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs

@ -218,11 +218,11 @@ public sealed class SchemasIndex : ICommandMiddleware, ISchemasIndex
}
// Do not use cancellation here as we already so far.
await schemaCache.AddAsync(new[]
{
await schemaCache.AddAsync(
[
new KeyValuePair<string, object?>(GetCacheKey(schema.AppId.Id, schema.Id), schema),
new KeyValuePair<string, object?>(GetCacheKey(schema.AppId.Id, schema.Name), schema),
}, options.CacheDuration);
], options.CacheDuration);
return schema;
}
@ -235,10 +235,10 @@ public sealed class SchemasIndex : ICommandMiddleware, ISchemasIndex
}
// Do not use cancellation here as we already so far.
return schemaCache.RemoveAsync(new[]
{
return schemaCache.RemoveAsync(
[
GetCacheKey(appId, id),
GetCacheKey(appId, name)
});
]);
}
}

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

@ -25,16 +25,16 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.0.1" />
<PackageReference Include="GraphQL" Version="7.8.0" />
<PackageReference Include="GraphQL.DataLoader" Version="7.8.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="GraphQL" Version="8.0.2" />
<PackageReference Include="GraphQL.DataLoader" Version="8.0.2" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Notifo.SDK" Version="1.7.5" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.CLI.Core" Version="13.0.0" />
<PackageReference Include="Squidex.CLI.Core" Version="13.3.0" />
<PackageReference Include="Squidex.YDotNet.Extensions" Version="0.2.9" />
<PackageReference Include="Squidex.YDotNet.Server" Version="0.2.9" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />

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

@ -14,7 +14,7 @@
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

6
backend/src/Squidex.Domain.Users.MongoDb/MongoUserStore.cs

@ -136,8 +136,8 @@ public sealed class MongoUserStore :
protected override Task SetupCollectionAsync(IMongoCollection<MongoUser> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoUser>(
Index
.Ascending("Logins.LoginProvider")
@ -156,7 +156,7 @@ public sealed class MongoUserStore :
{
Unique = true
})
}, ct);
], ct);
}
protected override MongoCollectionSettings CollectionSettings()

4
backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj

@ -19,12 +19,12 @@
<ProjectReference Include="..\Squidex.Shared\Squidex.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.27.0" />
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />

6
backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj

@ -18,13 +18,13 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="IdentityModel" Version="7.0.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="8.0.6" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="8.0.8" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="OpenIddict.AspNetCore" Version="5.7.0" />
<PackageReference Include="OpenIddict.AspNetCore" Version="5.8.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="SharpPwned.NET" Version="2.0.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />

10
backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj

@ -11,11 +11,11 @@
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EventStore.Client.Grpc.PersistentSubscriptions" Version="23.3.3" />
<PackageReference Include="EventStore.Client.Grpc.ProjectionManagement" Version="23.3.3" />
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.3" />
<PackageReference Include="Grpc.Net.Client" Version="2.63.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="EventStore.Client.Grpc.PersistentSubscriptions" Version="23.3.4" />
<PackageReference Include="EventStore.Client.Grpc.ProjectionManagement" Version="23.3.4" />
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.4" />
<PackageReference Include="Grpc.Net.Client" Version="2.65.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

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

@ -44,8 +44,8 @@ public partial class MongoEventStore : MongoRepositoryBase<MongoEventCommit>, IE
protected override async Task SetupCollectionAsync(IMongoCollection<MongoEventCommit> collection,
CancellationToken ct)
{
await collection.Indexes.CreateManyAsync(new[]
{
await collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoEventCommit>(
Index
.Ascending(x => x.EventStream)
@ -62,7 +62,7 @@ public partial class MongoEventStore : MongoRepositoryBase<MongoEventCommit>, IE
{
Unique = true
})
}, ct);
], ct);
var clusterVersion = await Database.GetMajorVersionAsync(ct);
var clusteredAsReplica = Database.Client.Cluster.Description.Type == ClusterType.ReplicaSet;

6
backend/src/Squidex.Infrastructure.MongoDb/Log/MongoRequestLogRepository.cs

@ -32,8 +32,8 @@ public sealed class MongoRequestLogRepository : MongoRepositoryBase<MongoRequest
protected override Task SetupCollectionAsync(IMongoCollection<MongoRequest> collection,
CancellationToken ct)
{
return collection.Indexes.CreateManyAsync(new[]
{
return collection.Indexes.CreateManyAsync(
[
new CreateIndexModel<MongoRequest>(
Index
.Ascending(x => x.Key)
@ -45,7 +45,7 @@ public sealed class MongoRequestLogRepository : MongoRepositoryBase<MongoRequest
{
ExpireAfter = TimeSpan.FromDays(options.StoreRetentionInDays)
})
}, ct);
], ct);
}
public Task InsertManyAsync(IEnumerable<Request> items,

6
backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj

@ -14,12 +14,12 @@
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MongoDB.Driver" Version="2.27.0" />
<PackageReference Include="MongoDB.Driver.GridFS" Version="2.27.0" />
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
<PackageReference Include="MongoDB.Driver.GridFS" Version="2.28.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />

4
backend/src/Squidex.Infrastructure/Commands/DefaultDomainObjectFactory.cs

@ -23,7 +23,7 @@ public sealed class DefaultDomainObjectFactory : IDomainObjectFactory
public static T Create(IServiceProvider serviceProvider, DomainId id)
{
return (T)ObjectFactory(serviceProvider, new object[] { id });
return (T)ObjectFactory(serviceProvider, [id]);
}
}
@ -34,7 +34,7 @@ public sealed class DefaultDomainObjectFactory : IDomainObjectFactory
public static T Create(IServiceProvider serviceProvider, DomainId id, IPersistenceFactory<TState> persistenceFactory)
{
return (T)ObjectFactory(serviceProvider, new object[] { id, persistenceFactory });
return (T)ObjectFactory(serviceProvider, [id, persistenceFactory]);
}
}

14
backend/src/Squidex.Infrastructure/Json/System/PolymorphicConverter.cs

@ -10,6 +10,8 @@ using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using Squidex.Infrastructure.Reflection;
#pragma warning disable MA0084 // Local variables should not hide other symbols
namespace Squidex.Infrastructure.Json.System;
public sealed class PolymorphicConverter<T> : JsonConverter<T> where T : class
@ -38,8 +40,8 @@ public sealed class PolymorphicConverter<T> : JsonConverter<T> where T : class
{
if (typeRegistry.TryGetConfig(baseType, out var config) && config.TryGetName(typeInfo.Type, out var typeName))
{
var discriminiatorName = config.DiscriminatorProperty ?? Constants.DefaultDiscriminatorProperty;
var discriminatorField = typeInfo.CreateJsonPropertyInfo(typeof(string), discriminiatorName);
var discriminatorName = config.DiscriminatorProperty ?? Constants.DefaultDiscriminatorProperty;
var discriminatorField = typeInfo.CreateJsonPropertyInfo(typeof(string), discriminatorName);
discriminatorField.Get = x =>
{
@ -61,7 +63,7 @@ public sealed class PolymorphicConverter<T> : JsonConverter<T> where T : class
if (typeReader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
ThrowHelper.JsonSystemException($"Expected Object, got '{reader.TokenType}'");
}
while (typeReader.Read())
@ -73,7 +75,7 @@ public sealed class PolymorphicConverter<T> : JsonConverter<T> where T : class
if (typeReader.TokenType != JsonTokenType.String)
{
ThrowHelper.JsonException($"Expected string discriminator value, got '{reader.TokenType}'");
ThrowHelper.JsonSystemException($"Expected string discriminator value, got '{reader.TokenType}'");
return default!;
}
@ -92,7 +94,7 @@ public sealed class PolymorphicConverter<T> : JsonConverter<T> where T : class
}
}
ThrowHelper.JsonException($"Object has no discriminator '{discriminatorName}'.");
ThrowHelper.JsonSystemException($"Object has no discriminator '{discriminatorName}'.");
return default!;
}
@ -112,7 +114,7 @@ public sealed class PolymorphicConverter<T> : JsonConverter<T> where T : class
{
if (!typeRegistry.TryGetType<T>(name, out var type))
{
ThrowHelper.JsonException($"Object has invalid discriminator '{name}'.");
ThrowHelper.JsonSystemException($"Object has invalid discriminator '{name}'.");
return default!;
}

4
backend/src/Squidex.Infrastructure/Json/System/StringConverter.cs

@ -43,7 +43,7 @@ public sealed class StringConverter<T> : JsonConverter<T> where T : notnull
}
catch (Exception ex)
{
ThrowHelper.JsonException("Error while converting from string.", ex);
ThrowHelper.JsonSystemException("Error while converting from string.", ex);
return default;
}
@ -53,7 +53,7 @@ public sealed class StringConverter<T> : JsonConverter<T> where T : notnull
return JsonSerializer.Deserialize<T>(ref reader, optionsWithoutSelf);
default:
ThrowHelper.JsonException($"Expected string or object, got {reader.TokenType}.");
ThrowHelper.JsonSystemException($"Expected string or object, got {reader.TokenType}.");
return default;
}
}

2
backend/src/Squidex.Infrastructure/Migrations/Migrator.cs

@ -86,7 +86,7 @@ public sealed class Migrator
{
while (!await migrationStatus.TryLockAsync(ct))
{
log.LogInformation("Could not acquire lock to start migrating. Tryping again in {time}ms.", LockWaitMs);
log.LogInformation("Could not acquire lock to start migrating. Trying again in {time}ms.", LockWaitMs);
await Task.Delay(LockWaitMs, ct);
}
}

2
backend/src/Squidex.Infrastructure/Queries/Json/JsonFilterSurrogate.cs

@ -50,7 +50,7 @@ public sealed class JsonFilterSurrogate : ISurrogate<FilterNode<JsonValue>>
return new CompareFilter<JsonValue>(Path, Op ?? CompareOperator.Equals, Value);
}
ThrowHelper.JsonException(Errors.InvalidJsonStructure());
ThrowHelper.JsonSystemException(Errors.InvalidJsonStructure());
return default!;
}
}

20
backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -11,25 +11,25 @@
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MailKit" Version="4.7.0" />
<PackageReference Include="MailKit" Version="4.7.1.1" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="8.0.6" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="8.0.8" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageReference Include="Microsoft.OData.Core" Version="7.21.3" />
<PackageReference Include="Microsoft.OData.Core" Version="8.0.1" />
<PackageReference Include="NodaTime" Version="3.1.11" />
<PackageReference Include="OpenTelemetry.Api" Version="1.9.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets" Version="6.18.0" />
<PackageReference Include="Squidex.Caching" Version="6.18.0" />
<PackageReference Include="Squidex.Hosting.Abstractions" Version="6.18.0" />
<PackageReference Include="Squidex.Log" Version="6.18.0" />
<PackageReference Include="Squidex.Messaging" Version="6.18.0" />
<PackageReference Include="Squidex.Text" Version="6.18.0" />
<PackageReference Include="Squidex.Assets" Version="6.19.0" />
<PackageReference Include="Squidex.Caching" Version="6.19.0" />
<PackageReference Include="Squidex.Hosting.Abstractions" Version="6.19.0" />
<PackageReference Include="Squidex.Log" Version="6.19.0" />
<PackageReference Include="Squidex.Messaging" Version="6.19.0" />
<PackageReference Include="Squidex.Text" Version="6.19.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />

2
backend/src/Squidex.Infrastructure/States/StoreExtensions.cs

@ -14,6 +14,6 @@ public static class StoreExtensions
public static Task WriteEventAsync<T>(this IPersistence<T> persistence, Envelope<IEvent> @event,
CancellationToken ct = default)
{
return persistence.WriteEventsAsync(new[] { @event }, ct);
return persistence.WriteEventsAsync([@event], ct);
}
}

2
backend/src/Squidex.Infrastructure/Tasks/Scheduler.cs

@ -41,7 +41,7 @@ public sealed class Scheduler
if (pendingTasks >= 1)
{
// If we already in a tasks we just queue it with the semaphore.
ScheduleTasks(new[] { task }, default);
ScheduleTasks([task], default);
return;
}

5
backend/src/Squidex.Infrastructure/ThrowHelper.cs

@ -45,4 +45,9 @@ public static class ThrowHelper
{
throw new NotSupportedException(message);
}
public static void JsonSystemException(string? message = null, Exception? ex = null)
{
throw new System.Text.Json.JsonException(message, ex);
}
}

2
backend/src/Squidex.Infrastructure/Translations/MissingKeys.cs

@ -33,7 +33,7 @@ public sealed class MissingKeys
{
if (!missingTranslations.Add(key))
{
File.AppendAllLines(MissingFileName, new[] { key });
File.AppendAllLines(MissingFileName, [key]);
}
}
}

3
backend/src/Squidex.Shared/PermissionIds.cs

@ -103,6 +103,9 @@ public static class PermissionIds
// App Usage
public const string AppUsage = "squidex.apps.{app}.usage";
// App Expose Users
public const string AppPii = "squidex.apps.{app}.pii";
// App Comments
public const string AppComments = "squidex.apps.{app}.comments";
public const string AppCommentsRead = "squidex.apps.{app}.comments.read";

2
backend/src/Squidex.Shared/Squidex.Shared.csproj

@ -10,7 +10,7 @@
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

1
backend/src/Squidex.Web/ApiExceptionConverter.cs

@ -8,6 +8,7 @@
using System.Diagnostics;
using System.Security;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Squidex.Infrastructure;

2
backend/src/Squidex.Web/GraphQL/DynamicUserContextBuilder.cs

@ -19,7 +19,7 @@ public sealed class DynamicUserContextBuilder : IUserContextBuilder
public ValueTask<IDictionary<string, object?>?> BuildUserContextAsync(HttpContext context, object? payload)
{
var executionContext = (GraphQLExecutionContext)factory(context.RequestServices, new object[] { context.Context() });
var executionContext = (GraphQLExecutionContext)factory(context.RequestServices, [context.Context()]);
return new ValueTask<IDictionary<string, object?>?>(executionContext);
}

8
backend/src/Squidex.Web/Squidex.Web.csproj

@ -13,10 +13,10 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="GraphQL" Version="7.8.0" />
<PackageReference Include="GraphQL.SystemTextJson" Version="7.8.0" />
<PackageReference Include="GraphQL.Server.Transports.AspNetCore" Version="7.7.1" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="GraphQL" Version="8.0.2" />
<PackageReference Include="GraphQL.SystemTextJson" Version="8.0.2" />
<PackageReference Include="GraphQL.Server.Transports.AspNetCore" Version="8.0.2" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

2
backend/src/Squidex/Areas/Api/Config/OpenApi/SecurityProcessor.cs

@ -15,7 +15,7 @@ namespace Squidex.Areas.Api.Config.OpenApi;
public sealed class SecurityProcessor : SecurityDefinitionAppender
{
public SecurityProcessor(IUrlGenerator urlGenerator)
: base(Constants.SecurityDefinition, new[] { Constants.ScopeApi }, CreateOAuthSchema(urlGenerator))
: base(Constants.SecurityDefinition, [Constants.ScopeApi], CreateOAuthSchema(urlGenerator))
{
}

5
backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -452,6 +452,7 @@ public sealed class ContentsController : ApiController
/// <param name="app">The name of the app.</param>
/// <param name="schema">The name of the schema.</param>
/// <param name="id">The ID of the content item to update.</param>
/// <param name="request">The request parameters.</param>
/// <response code="200">Content updated.</response>
/// <response code="404">Content references, schema or app not found.</response>
/// <remarks>
@ -464,9 +465,9 @@ public sealed class ContentsController : ApiController
[AcceptHeader.Unpublished]
[AcceptHeader.Languages]
[ApiCosts(1)]
public async Task<IActionResult> PutContentDefaults(string app, string schema, DomainId id)
public async Task<IActionResult> PutContentDefaults(string app, string schema, DomainId id, EnrichContentDefaultsDto request)
{
var command = new EnrichContentDefaults { ContentId = id };
var command = request.ToCommand(id);
var response = await InvokeCommandAsync(command);

35
backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsSharedController.cs

@ -24,6 +24,13 @@ namespace Squidex.Areas.Api.Controllers.Contents;
[ApiExplorerSettings(GroupName = nameof(Contents))]
public sealed class ContentsSharedController : ApiController
{
private static readonly GraphQLHttpMiddlewareOptions GraphQLOptions = new GraphQLHttpMiddlewareOptions
{
DefaultResponseContentType = new MediaTypeHeaderValue("application/json"),
CsrfProtectionEnabled = false,
CsrfProtectionHeaders = []
};
private readonly IContentQueryService contentQuery;
private readonly IContentWorkflow contentWorkflow;
@ -54,12 +61,7 @@ public sealed class ContentsSharedController : ApiController
[IgnoreCacheFilter]
public IActionResult GetGraphQL(string app, GraphQLQueryDto request)
{
var options = new GraphQLHttpMiddlewareOptions
{
DefaultResponseContentType = new MediaTypeHeaderValue("application/json")
};
return new GraphQLExecutionActionResult<DummySchema>(options);
return new GraphQLExecutionActionResult<DummySchema>(GraphQLOptions);
}
/// <summary>
@ -80,12 +82,7 @@ public sealed class ContentsSharedController : ApiController
[IgnoreCacheFilter]
public IActionResult PostGraphQL(string app)
{
var options = new GraphQLHttpMiddlewareOptions
{
DefaultResponseContentType = new MediaTypeHeaderValue("application/json")
};
return new GraphQLExecutionActionResult<DummySchema>(options);
return new GraphQLExecutionActionResult<DummySchema>(GraphQLOptions);
}
/// <summary>
@ -106,12 +103,7 @@ public sealed class ContentsSharedController : ApiController
[IgnoreCacheFilter]
public IActionResult GetGraphQLBatch(string app, GraphQLQueryDto request)
{
var options = new GraphQLHttpMiddlewareOptions
{
DefaultResponseContentType = new MediaTypeHeaderValue("application/json")
};
return new GraphQLExecutionActionResult<DummySchema>(options);
return new GraphQLExecutionActionResult<DummySchema>(GraphQLOptions);
}
/// <summary>
@ -132,12 +124,7 @@ public sealed class ContentsSharedController : ApiController
[IgnoreCacheFilter]
public IActionResult PostGraphQLBatch(string app)
{
var options = new GraphQLHttpMiddlewareOptions
{
DefaultResponseContentType = new MediaTypeHeaderValue("application/json")
};
return new GraphQLExecutionActionResult<DummySchema>(options);
return new GraphQLExecutionActionResult<DummySchema>(GraphQLOptions);
}
/// <summary>

2
backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationBuilder.cs

@ -151,7 +151,7 @@ internal sealed class OperationBuilder
{
new OpenApiSecurityRequirement
{
[Constants.SecurityDefinition] = new[] { fullId }
[Constants.SecurityDefinition] = [fullId]
}
};

5
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/BulkUpdateContentsDto.cs

@ -33,6 +33,11 @@ public sealed class BulkUpdateContentsDto
/// </summary>
public bool DoNotScript { get; set; } = true;
/// <summary>
/// True, to also enrich required fields. Default: false.
/// </summary>
public bool EnrichRequiredFields { get; set; }
/// <summary>
/// True to turn off validation for faster inserts. Default: false.
/// </summary>

8
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs

@ -40,14 +40,18 @@ public sealed class ContentsDto : Resource
if (schema != null)
{
await result.AssignStatusesAsync(workflow, schema);
await result.CreateStatusesAsync(workflow, schema);
await result.CreateLinksAsync(resources, workflow, schema);
}
else
{
result.Statuses = [];
}
return result;
}
private async Task AssignStatusesAsync(IContentWorkflow workflow, Schema schema)
private async Task CreateStatusesAsync(IContentWorkflow workflow, Schema schema)
{
var allStatuses = await workflow.GetAllAsync(schema);

29
backend/src/Squidex/Areas/Api/Controllers/Contents/Models/EnrichContentDefaultsDto.cs

@ -0,0 +1,29 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Microsoft.AspNetCore.Mvc;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Contents.Models;
public class EnrichContentDefaultsDto
{
/// <summary>
/// True, to also enrich required fields. Default: false.
/// </summary>
[FromQuery(Name = "enrichRequiredFields")]
public bool EnrichRequiredFields { get; set; }
public EnrichContentDefaults ToCommand(DomainId id)
{
var command = SimpleMapper.Map(this, new EnrichContentDefaults { ContentId = id });
return command;
}
}

8
backend/src/Squidex/Areas/Api/Controllers/UploadModel.cs

@ -23,14 +23,14 @@ public class UploadModel
/// <summary>
/// The alternative URL to download from.
/// </summary>
[FromForm(Name = "fileUrl")]
public string? FileUrl { get; set; }
[FromForm(Name = "url")]
public string? Url { get; set; }
/// <summary>
/// The file name if the URL is specified.
/// </summary>
[FromForm(Name = "fileName")]
public string? FileName { get; set; }
[FromForm(Name = "name")]
public string? Name { get; set; }
public Task<IAssetFile> ToFileAsync(HttpContext httpContext, App? app)
{

161
backend/src/Squidex/Areas/IdentityServer/Views/Account/Login.cshtml

@ -32,101 +32,110 @@
</div>
</div>
<form asp-controller="Account" asp-action="External" asp-route-returnurl="@Model.ReturnUrl" method="post">
@Html.AntiForgeryToken()
@foreach (var provider in Model.ExternalProviders)
<div>
@if (Model.HasExternalLogin)
{
var schema = provider.AuthenticationScheme.ToLowerInvariant();
<form asp-controller="Account" asp-action="External" asp-route-returnurl="@Model.ReturnUrl" method="post">
@Html.AntiForgeryToken()
<div class="form-group mb-2">
<button class="btn external-button btn-block btn btn-@schema" type="submit" name="provider" value="@provider.AuthenticationScheme">
<i class="icon-@schema external-icon"></i> @Html.Raw(T.Get("users.login.loginWith", new { action, provider = provider.DisplayName }))
</button>
</div>
@foreach (var provider in Model.ExternalProviders)
{
var schema = provider.AuthenticationScheme.ToLowerInvariant();
<div class="form-group mb-2">
<button class="btn external-button btn-block btn btn-@schema" type="submit" name="provider" value="@provider.AuthenticationScheme">
<i class="icon-@schema external-icon"></i> @Html.Raw(T.Get("users.login.loginWith", new { action, provider = provider.DisplayName }))
</button>
</div>
}
</form>
}
</form>
@if (Model.HasPasswordAuth)
{
<div class="profile-separator">
<div class="profile-separator-text">@T.Get("users.login.separator")</div>
</div>
if (Model.IsLogin)
{
@if (Model.RequestType == RequestType.Login)
{
<div asp-validation-summary="ModelOnly" class="form-alert form-alert-error"></div>
}
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl" method="post">
@Html.AntiForgeryToken()
<div class="form-group">
<div error-for="Email"></div>
<input asp-for="Email" type="email" class="form-control" placeholder="@T.Get("users.login.emailPlaceholder")" />
</div>
<div class="form-group">
<div error-for="Password"></div>
<input asp-for="Password" type="password" class="form-control" placeholder="@T.Get("users.login.passwordPlaceholder")" />
</div>
<button type="submit" data-testid="login-button" class="btn btn-block btn-primary">@action</button>
</form>
}
else
@if (Model.HasPasswordAuth)
{
<div class="profile-password-signup text-center">@T.Get("users.login.askAdmin")</div>
<div>
@if (Model.IsLogin)
{
@if (Model.RequestType == RequestType.Login)
{
<div asp-validation-summary="ModelOnly" class="form-alert form-alert-error"></div>
}
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl" method="post">
@Html.AntiForgeryToken()
<div class="form-group">
<div error-for="Email"></div>
<input asp-for="Email" type="email" class="form-control" placeholder="@T.Get("users.login.emailPlaceholder")" />
</div>
<div class="form-group">
<div error-for="Password"></div>
<input asp-for="Password" type="password" class="form-control" placeholder="@T.Get("users.login.passwordPlaceholder")" />
</div>
<button type="submit" data-testid="login-button" class="btn btn-block btn-primary">@action</button>
</form>
}
else
{
<div class="profile-password-signup text-center">@T.Get("users.login.askAdmin")</div>
}
</div>
}
}
@if (Model.HasCustomAuth && Model.IsLogin)
{
<div class="profile-separator">
<div class="profile-separator-text">@T.Get("users.login.separator")</div>
</div>
@if (Model.RequestType == RequestType.LoginCustom)
@if (Model.HasCustomAuth && Model.IsLogin)
{
<div asp-validation-summary="ModelOnly" class="form-alert form-alert-error"></div>
}
<div>
@if (Model.RequestType == RequestType.LoginCustom)
{
<div asp-validation-summary="ModelOnly" class="form-alert form-alert-error"></div>
}
<div class="mb-2">
<small class="text-muted form-text">@T.Get("users.login.custom")</small>
</div>
<div class="mb-2">
<small class="text-muted form-text">@T.Get("users.login.custom")</small>
</div>
<form asp-controller="Account" asp-action="LoginDynamic" asp-route-returnurl="@Model.ReturnUrl" method="post">
@Html.AntiForgeryToken()
<form asp-controller="Account" asp-action="LoginDynamic" asp-route-returnurl="@Model.ReturnUrl" method="post">
@Html.AntiForgeryToken()
<div class="form-group">
<div error-for="DynamicEmail"></div>
<input asp-for="DynamicEmail" type="email" class="form-control" placeholder="@T.Get("users.login.emailBusinessPlaceholder")" />
<div class="form-group">
<div error-for="DynamicEmail"></div>
<input asp-for="DynamicEmail" type="email" class="form-control" placeholder="@T.Get("users.login.emailBusinessPlaceholder")" />
</div>
<button type="submit" data-testid="dynamic-button" class="btn btn-block btn-primary">@action</button>
</form>
</div>
}
@if (Model.IsLogin)
{
<p class="profile-footer">
@T.Get("users.login.noAccountSignupQuestion")
<a asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">
@T.Get("users.login.noAccountSignupAction")
</a>
</p>
}
else
{
<p class="profile-footer">
@T.Get("users.login.noAccountLoginQuestion")
<button type="submit" data-testid="dynamic-button" class="btn btn-block btn-primary">@action</button>
</form>
}
@if (Model.IsLogin)
{
<p class="profile-footer">
@T.Get("users.login.noAccountSignupQuestion")
<a asp-controller="Account" asp-action="Signup" asp-route-returnurl="@Model.ReturnUrl">
@T.Get("users.login.noAccountSignupAction")
</a>
</p>
}
else
{
<p class="profile-footer">
@T.Get("users.login.noAccountLoginQuestion")
<a asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">
@T.Get("users.login.noAccountLoginAction")
</a>
</p>
}
<a asp-controller="Account" asp-action="Login" asp-route-returnurl="@Model.ReturnUrl">
@T.Get("users.login.noAccountLoginAction")
</a>
</p>
}
</div>
</div>

87
backend/src/Squidex/Areas/IdentityServer/Views/Setup/Setup.cshtml

@ -118,66 +118,67 @@
<div class="profile-section">
<h2 class="mb-3">@T.Get("setup.createUser.headline")</h2>
@if (Model.HasExternalLogin)
{
<div>
<small class="form-text text-muted mt-2 mb-2">@T.Get("setup.createUser.loginHint")</small>
<div>
@if (Model.HasExternalLogin)
{
<div>
<small class="form-text text-muted mt-2 mb-2">@T.Get("setup.createUser.loginHint")</small>
<div class="mt-3">
<a class="btn btn-primary force-white" asp-controller="Account" asp-action="Login">
@T.Get("setup.createUser.loginLink")
</a>
<div class="mt-3">
<a class="btn btn-primary force-white" asp-controller="Account" asp-action="Login">
@T.Get("setup.createUser.loginLink")
</a>
</div>
</div>
</div>
}
}
@if (Model.HasExternalLogin && Model.HasPasswordAuth)
{
<div class="profile-separator">
<div class="profile-separator-text">@T.Get("setup.createUser.separator")</div>
</div>
}
@if (Model.HasPasswordAuth)
{
<h3>@T.Get("setup.createUser.headlineCreate")</h3>
<div class="profile-separator-text">@T.Get("setup.createUser.separator")</div>
</div>
@if (!string.IsNullOrWhiteSpace(Model.ErrorMessage))
@if (Model.HasPasswordAuth)
{
<div class="form-alert form-alert-error">
<div>
<h3>@T.Get("setup.createUser.headlineCreate")</h3>
@if (!string.IsNullOrWhiteSpace(Model.ErrorMessage))
{
<div class="form-alert form-alert-error">
@Model.ErrorMessage
</div>
}
}
<form class="profile-form" asp-controller="Setup" asp-action="Setup" method="post">
@Html.AntiForgeryToken()
<form class="profile-form" asp-controller="Setup" asp-action="Setup" method="post">
@Html.AntiForgeryToken()
<div class="form-group">
<label asp-for="Email">@T.Get("common.email")</label>
<div class="form-group">
<label asp-for="Email">@T.Get("common.email")</label>
<div error-for="Email"></div>
<input asp-for="Email" type="text" class="form-control" />
</div>
<div error-for="Email"></div>
<input asp-for="Email" type="text" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Password">@T.Get("common.password")</label>
<div class="form-group">
<label asp-for="Password">@T.Get("common.password")</label>
<div error-for="Password"></div>
<input asp-for="Password" type="password" class="form-control" />
</div>
<div error-for="Password"></div>
<input asp-for="Password" type="password" class="form-control" />
</div>
<div class="form-group">
<label asp-for="PasswordConfirm">@T.Get("setup.createUser.confirmPassword")</label>
<div class="form-group">
<label asp-for="PasswordConfirm">@T.Get("setup.createUser.confirmPassword")</label>
<div error-for="PasswordConfirm"></div>
<input asp-for="PasswordConfirm" type="password" class="form-control" />
</div>
<div error-for="PasswordConfirm"></div>
<input asp-for="PasswordConfirm" type="password" class="form-control" />
</div>
<div class="form-group mb-0">
<button type="submit" class="btn btn-success">@T.Get("setup.createUser.button")</button>
<div class="form-group mb-0">
<button type="submit" class="btn btn-success">@T.Get("setup.createUser.button")</button>
</div>
</form>
</div>
</form>
}
}
</div>
@if (!Model.HasExternalLogin && !Model.HasPasswordAuth)
{

2
backend/src/Squidex/Config/Domain/EventSourcingServices.cs

@ -45,7 +45,7 @@ public static class EventSourcingServices
.As<IEventStore>();
services.AddHealthChecks()
.AddCheck<GetEventStoreHealthCheck>("EventStore", tags: new[] { "node" });
.AddCheck<GetEventStoreHealthCheck>("EventStore", tags: ["node"]);
}
});

4
backend/src/Squidex/Config/Domain/HealthCheckServices.cs

@ -18,7 +18,7 @@ public static class HealthCheckServices
"diagnostics:gc");
services.AddHealthChecks()
.AddCheck<GCHealthCheck>("GC", tags: new[] { "node" })
.AddCheck<EventConsumersHealthCheck>("EventConsumers", tags: new[] { "background" });
.AddCheck<GCHealthCheck>("GC", tags: ["node"])
.AddCheck<EventConsumersHealthCheck>("EventConsumers", tags: ["background"]);
}
}

5
backend/src/Squidex/Config/Domain/ResizeServices.cs

@ -15,11 +15,10 @@ public static class ResizeServices
public static void AddSquidexImageResizing(this IServiceCollection services, IConfiguration config)
{
var thumbnailGenerator = new CompositeThumbnailGenerator(
new IAssetThumbnailGenerator[]
{
[
new ImageSharpThumbnailGenerator(),
new ImageMagickThumbnailGenerator()
});
]);
var resizerUrl = config.GetValue<string>("assets:resizerUrl");

2
backend/src/Squidex/Config/Domain/StoreServices.cs

@ -123,7 +123,7 @@ public static class StoreServices
.As<IDistributedCache>();
services.AddHealthChecks()
.AddCheck<MongoHealthCheck>("MongoDB", tags: new[] { "node" });
.AddCheck<MongoHealthCheck>("MongoDB", tags: ["node"]);
services.AddSingletonAs<MongoRequestLogRepository>()
.As<IRequestLogRepository>();

1
backend/src/Squidex/Config/Domain/TelemetryServices.cs

@ -33,7 +33,6 @@ public static class TelemetryServices
builder.AddAspNetCoreInstrumentation();
builder.AddHttpClientInstrumentation();
builder.AddMongoDBInstrumentation();
var sampling = config.GetValue<double>("logging:otlp:sampling");

70
backend/src/Squidex/Squidex.csproj

@ -35,56 +35,56 @@
<ItemGroup>
<PackageReference Include="AspNet.Security.OAuth.GitHub" Version="8.1.0" />
<PackageReference Include="Google.Cloud.Trace.V2" Version="3.6.0" />
<PackageReference Include="GraphQL" Version="7.8.0" />
<PackageReference Include="GraphQL.MicrosoftDI" Version="7.8.0" />
<PackageReference Include="GraphQL.SystemTextJson" Version="7.8.0" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.159">
<PackageReference Include="GraphQL" Version="8.0.2" />
<PackageReference Include="GraphQL.MicrosoftDI" Version="8.0.2" />
<PackageReference Include="GraphQL.SystemTextJson" Version="8.0.2" />
<PackageReference Include="Meziantou.Analyzer" Version="2.0.163">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.6" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.8" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.RulesetToEditorconfigConverter" Version="3.3.3" />
<PackageReference Include="Microsoft.Data.Edm" Version="5.8.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="7.6.2" />
<PackageReference Include="Microsoft.OData.Core" Version="7.21.3" />
<PackageReference Include="MongoDB.Driver" Version="2.27.0" />
<PackageReference Include="MongoDB.Driver.Core.Extensions.OpenTelemetry" Version="1.0.0" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.0.2" />
<PackageReference Include="Microsoft.OData.Core" Version="8.0.1" />
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
<PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="1.5.0" />
<PackageReference Include="NetTopologySuite.IO.GeoJSON4STJ" Version="4.0.0" />
<PackageReference Include="NJsonSchema" Version="11.0.1" />
<PackageReference Include="NJsonSchema" Version="11.0.2" />
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.2.0" />
<PackageReference Include="NSwag.AspNetCore" Version="14.0.8" />
<PackageReference Include="NSwag.AspNetCore" Version="14.1.0" />
<PackageReference Include="OpenCover" Version="4.7.1221" PrivateAssets="all" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="ReportGenerator" Version="5.3.7" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets.Azure" Version="6.18.0" />
<PackageReference Include="Squidex.Assets.GoogleCloud" Version="6.18.0" />
<PackageReference Include="Squidex.Assets.FTP" Version="6.18.0" />
<PackageReference Include="Squidex.Assets.ImageMagick" Version="6.18.0" />
<PackageReference Include="Squidex.Assets.ImageSharp" Version="6.18.0" />
<PackageReference Include="Squidex.Assets.Mongo" Version="6.18.0" />
<PackageReference Include="Squidex.Assets.S3" Version="6.18.0" />
<PackageReference Include="Squidex.Assets.TusAdapter" Version="6.18.0" />
<PackageReference Include="Squidex.ClientLibrary" Version="19.2.0" />
<PackageReference Include="Squidex.Hosting" Version="6.18.0" />
<PackageReference Include="Squidex.Messaging.All" Version="6.18.0" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="6.18.0" />
<PackageReference Include="Squidex.OpenIddict.MongoDb" Version="5.1.0" />
<PackageReference Include="ReportGenerator" Version="5.3.8" PrivateAssets="all" />
<PackageReference Include="Squidex.Assets.Azure" Version="6.19.0" />
<PackageReference Include="Squidex.Assets.GoogleCloud" Version="6.19.0" />
<PackageReference Include="Squidex.Assets.FTP" Version="6.19.0" />
<PackageReference Include="Squidex.Assets.ImageMagick" Version="6.19.0" />
<PackageReference Include="Squidex.Assets.ImageSharp" Version="6.19.0" />
<PackageReference Include="Squidex.Assets.Mongo" Version="6.19.0" />
<PackageReference Include="Squidex.Assets.S3" Version="6.19.0" />
<PackageReference Include="Squidex.Assets.TusAdapter" Version="6.19.0" />
<PackageReference Include="Squidex.ClientLibrary" Version="19.7.0" />
<PackageReference Include="Squidex.Hosting" Version="6.19.0" />
<PackageReference Include="Squidex.Messaging.All" Version="6.19.0" />
<PackageReference Include="Squidex.Messaging.Subscriptions" Version="6.19.0" />
<PackageReference Include="Squidex.OpenIddict.MongoDb" Version="5.8.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="YDotNet" Version="0.4.0" />
<PackageReference Include="YDotNet.Native" Version="0.4.0" />
<PackageReference Include="YDotNet.Server" Version="0.4.0" />
<PackageReference Include="YDotNet.Server.MongoDB" Version="0.4.0" />
<PackageReference Include="YDotNet.Server.Redis" Version="0.4.0" />
<PackageReference Include="YDotNet.Server.WebSockets" Version="0.4.0" />
<PackageReference Include="YDotNet" Version="0.4.1" />
<PackageReference Include="YDotNet.Native" Version="0.4.1" />
<PackageReference Include="YDotNet.Server" Version="0.4.1" />
<PackageReference Include="YDotNet.Server.MongoDB" Version="0.4.1" />
<PackageReference Include="YDotNet.Server.Redis" Version="0.4.1" />
<PackageReference Include="YDotNet.Server.WebSockets" Version="0.4.1" />
</ItemGroup>
<PropertyGroup>

2
backend/src/Squidex/appsettings.json

@ -168,7 +168,7 @@
"hideDateTimeModeButton": false,
// Show the exposed values as information on the apps overview page.
"showInfo": false,
"showInfo": true,
// The number of content items for dropdown selector.
"referencesDropdownItemCount": 100

1
backend/src/Squidex/wwwroot/editor/squidex-editor.css

@ -1 +0,0 @@
.remirror-theme{width:100%}.remirror-theme{position:relative}.remirror-theme *{box-sizing:border-box}.remirror-theme .ProseMirror>p{margin-bottom:1rem!important}.remirror-theme .ProseMirror hr{border:0;border-top:1px solid #a7a9b4!important}.remirror-editor{min-height:300px!important;max-height:500px}.MuiStack-root>.MuiBox-root{margin-right:5px}.MuiBox-root>.MuiButtonBase-root{border-color:#dedfe3!important;border-radius:0}.MuiBox-root>.MuiButtonBase-root{border-left-width:0!important}.MuiBox-root>.MuiBox-root:first-child>.MuiButtonBase-root{border-left-width:1px!important}.MuiStack-root>.MuiBox-root>.MuiButtonBase-root:first-child{border-left-width:1px!important}.MuiButtonBase-root.Mui-selected:hover{background-color:#3284f4!important}.remirror-editor-wrapper{padding-top:0!important}.custom-icon path{fill:#0000008a}.remirror-theme div.ProseMirror{border:1px solid #dedfe3!important;border-radius:0!important;box-shadow:none!important}.MuiTooltip-popper>div{background-color:#1a2129;border-radius:0;font-size:85%;font-weight:400;padding:.5rem}.MuiMenu-paper{transform:none!important}.squidex-editor-disabled{pointer-events:none}.squidex-editor-menu{border:1px solid #dedfe3;border-bottom:0;padding:5px}.squidex-editor-menu fieldset{border-radius:0;border:0;display:flex;outline:0;padding:0}.squidex-editor-menu fieldset:disabled,.squidex-editor-menu fieldset.disabled{pointer-events:none}.squidex-editor-menu fieldset:disabled,.squidex-editor-menu fieldset.disabled{opacity:.5}.squidex-editor-counter{border:1px solid #dedfe3;border-top:0;font-size:85%;font-weight:400;opacity:.8;padding:4px 10px 4px 4px;text-align:right}.squidex-editor-image-view{border:1px solid #dedfe3;border-radius:0;display:inline-block;margin-top:15px;margin-bottom:15px;overflow:hidden}.squidex-editor-image-buttons{bottom:10px;left:10px;position:absolute}.squidex-editor-image-element{display:block;max-width:400px;max-height:400px}.squidex-editor-image-info{left:10px;top:10px;position:absolute;background-color:#3284f4;color:#fff;font-size:85%;font-weight:400;padding:2px 6px}.squidex-editor-main{position:relative}.squidex-editor-main .ace_editor{bottom:0;left:0;right:0;top:0;position:absolute;border:1px solid #dedfe3}.squidex-editor-content-link{align-items:center;border-radius:2px;border:1px solid #dedfe3;padding:10px;display:flex;flex-direction:row;flex-wrap:nowrap;margin-top:10px;margin-bottom:10px}.squidex-editor-content-schema{display:block;overflow-x:hidden;overflow-y:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;width:auto;border-right:1px solid #c2c4cc;color:#8b8f9d;flex-shrink:0;padding-left:10px;padding-right:10px;width:200px}.squidex-editor-content-name{display:block;overflow-x:hidden;overflow-y:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;min-width:0;width:auto;padding-left:10px;padding-right:0}.squidex-editor-html{margin-bottom:10px;margin-top:10px;position:relative}.squidex-editor-html-label{left:6px;top:0;position:absolute;color:#8b8f9d;font-size:85%;font-weight:400}.squidex-editor-html textarea{border:1px solid #dedfe3;font-family:monospace;font-size:inherit;height:100px;outline:none;overflow-x:hidden;overflow-y:auto;padding:30px 14px 14px;resize:vertical;width:100%}.squidex-editor-button{align-items:center;background-color:#fff;border-radius:0;border:1px solid #dedfe3;bottom:10px;display:inline-flex;font-size:85%;font-weight:400;margin-right:5px;padding:6px 12px}.squidex-editor-button:hover{background-color:#f5f5f5}.squidex-editor-input{border:1px solid #dedfe3;border-radius:0;box-sizing:border-box;height:30px;margin-left:0;margin-right:5px;outline:none;padding:6px 12px}.squidex-editor-input:active{border-color:#3284f4}.squidex-editor-floating{border:1px solid #dedfe3}.squidex-editor-floating .MuiBox-root{margin-right:0!important}.squidex-editor-floating .MuiButtonBase-root{height:30px}.squidex-editor-modal-wrapper,.squidex-editor-modal-backdrop{bottom:0;left:0;right:0;top:0;position:absolute}.squidex-editor-modal-backdrop{background-color:#00000003}.squidex-editor-modal-window{left:50%;top:50%;background-color:#fff;border:0;border-radius:.25rem;box-sizing:border-box;box-shadow:0 3px 16px #0003;margin-top:-50px;margin-left:-150px;position:absolute;width:350px}.squidex-editor-modal-body{display:flex;flex-direction:row;flex-grow:1;padding-top:0!important}.squidex-editor-modal-body,.squidex-editor-modal-title{padding:15px}.squidex-editor-modal-title{font-size:85%}.squidex-editor-modal-window input{flex-grow:1}:has(.remirror-language-select-positioner)~pre[class*=language-]{padding-top:2.5rem!important}.remirror-language-select-positioner{right:5px;top:5px;position:absolute}.b:before{content:"<a>";font-family:monospace;font-size:90%}.b:after{content:"</a>";font-family:monospace;font-size:90%}

531
backend/src/Squidex/wwwroot/editor/squidex-editor.js

File diff suppressed because one or more lines are too long

7
backend/src/Squidex/wwwroot/scripts/editor-context.html

@ -43,6 +43,13 @@
grow(element);
});
// Init is called once with a context that contains the app name, schema name and authentication information.
field.onContextChanged(function (context) {
element.innerHTML = JSON.stringify(context, null, 2);
grow(element);
});
</script>
</body>

6
backend/src/Squidex/wwwroot/scripts/editor-log.html

@ -78,7 +78,7 @@
appendLabel('Value of Form');
appendLine(`<${JSON.stringify(field.getFormValue(), 2)}>`);
appendLabel('Disabled: ');
appendLabel('Disabled');
appendLine(field.isDisabled());
console.log(text);
@ -124,6 +124,10 @@
logState('Field language changed');
});
field.onContextChanged(function () {
logState('Context changed');
});
field.onExpanded(function () {
logState('Expanded changed');
});

32
backend/src/Squidex/wwwroot/scripts/editor-sdk.js

@ -259,6 +259,7 @@ function SquidexWidget(options) {
*/
function SquidexFormField(options) {
var context;
var contextHandler;
var currentConfirm;
var currentPickAssets;
var currentPickContents;
@ -324,6 +325,12 @@ function SquidexFormField(options) {
}
}
function raiseContextChanged() {
if (contextHandler && context) {
contextHandler(context);
}
}
function raisedMoved() {
if (movedHandler && isNumber(index)) {
movedHandler(index);
@ -386,6 +393,10 @@ function SquidexFormField(options) {
context = event.data.context;
raiseInit();
} else if (type === 'contextChanged') {
context = event.data.context;
raiseContextChanged();
} else if (type === 'confirmResult') {
var correlationId = event.data.correlationId;
@ -631,7 +642,6 @@ function SquidexFormField(options) {
}
initHandler = callback;
raiseInit();
},
@ -661,10 +671,23 @@ function SquidexFormField(options) {
}
disabledHandler = callback;
raiseDisabled();
},
/**
* Register an function that is called whenever the context has been changed.
*
* @param {function} callback: The callback to invoke. Argument 1: New context.
*/
onContextChanged: function (callback) {
if (!isFunction(callback)) {
return;
}
contextHandler = callback;
raiseContextChanged();
},
/**
* Register an function that is called whenever the field language is changed.
*
@ -676,7 +699,6 @@ function SquidexFormField(options) {
}
languageHandler = callback;
raiseLanguageChanged();
},
@ -691,7 +713,6 @@ function SquidexFormField(options) {
}
valueHandler = callback;
raiseValueChanged();
},
@ -706,7 +727,6 @@ function SquidexFormField(options) {
}
formValueHandler = callback;
raiseFormValueChanged();
},
@ -721,7 +741,6 @@ function SquidexFormField(options) {
}
fullscreenHandler = callback;
raiseFullscreen();
},
@ -736,7 +755,6 @@ function SquidexFormField(options) {
}
expandedHandler = callback;
raiseExpanded();
},

2
backend/src/Squidex/wwwroot/scripts/embed-sdk.js

File diff suppressed because one or more lines are too long

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

Loading…
Cancel
Save