Browse Source

Merge branch 'release/4.x'

pull/590/head
Sebastian 5 years ago
parent
commit
4ba329e98d
  1. 50
      backend/extensions/Squidex.Extensions/APM/ApplicationInsights/ApplicationInsightsPlugin.cs
  2. 36
      backend/extensions/Squidex.Extensions/APM/ApplicationInsights/RoleNameTelemetryInitializer.cs
  3. 1
      backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj
  4. 82
      backend/i18n/frontend_nl.json
  5. 4
      backend/i18n/source/backend__ignore.json
  6. 8
      backend/i18n/source/backend_nl.json
  7. 4
      backend/i18n/source/frontend__ignore.json
  8. 43
      backend/i18n/source/frontend_nl.json
  9. 12
      backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleQueryService.cs
  10. 26
      backend/src/Squidex.Shared/Texts.nl.resx
  11. 12
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesIndexTests.cs
  12. 19
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleQueryServiceTests.cs
  13. 16
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs
  14. 16
      frontend/app/shared/components/assets/asset.component.html
  15. 12
      frontend/app/shared/components/forms/markdown-editor.component.ts
  16. 10
      frontend/app/shared/components/forms/rich-editor.component.ts
  17. 10
      frontend/app/shared/services/assets.service.ts
  18. 4
      frontend/app/shared/state/assets.forms.spec.ts

50
backend/extensions/Squidex.Extensions/APM/ApplicationInsights/ApplicationInsightsPlugin.cs

@ -0,0 +1,50 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Plugins;
namespace Squidex.Extensions.APM.ApplicationInsights
{
public sealed class ApplicationInsightsPlugin : IPlugin, IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
var client = builder.ApplicationServices.GetRequiredService<TelemetryClient>();
Profiler.SpanStarted += session =>
{
session.Listen(client.StartOperation<RequestTelemetry>(session.Key));
};
next(builder);
};
}
public void ConfigureServices(IServiceCollection services, IConfiguration config)
{
var isEnabled = config.GetValue<bool>("logging:applicationInsights");
if (isEnabled)
{
services.AddSingleton<IStartupFilter>(this);
services.AddApplicationInsightsTelemetry();
services.AddSingleton<ITelemetryInitializer, RoleNameTelemetryInitializer>();
}
}
}
}

36
backend/extensions/Squidex.Extensions/APM/ApplicationInsights/RoleNameTelemetryInitializer.cs

@ -0,0 +1,36 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.Configuration;
namespace Squidex.Extensions.APM.ApplicationInsights
{
public sealed class RoleNameTelemetryInitializer : ITelemetryInitializer
{
private readonly string roleName;
public RoleNameTelemetryInitializer(IConfiguration configuration)
{
roleName = configuration.GetValue<string>("logging:roleName");
if (string.IsNullOrWhiteSpace(roleName))
{
roleName = "Squidex";
}
}
public void Initialize(ITelemetry telemetry)
{
if (string.IsNullOrEmpty(telemetry.Context.Cloud.RoleName))
{
telemetry.Context.Cloud.RoleName = roleName;
}
}
}
}

1
backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj

@ -15,6 +15,7 @@
<PackageReference Include="CoreTweet" Version="1.0.0.483" /> <PackageReference Include="CoreTweet" Version="1.0.0.483" />
<PackageReference Include="Datadog.Trace" Version="1.19.2" /> <PackageReference Include="Datadog.Trace" Version="1.19.2" />
<PackageReference Include="Elasticsearch.Net" Version="7.9.0" /> <PackageReference Include="Elasticsearch.Net" Version="7.9.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.15.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.7" /> <PackageReference Include="Microsoft.Extensions.Http" Version="3.1.7" />
<PackageReference Include="Microsoft.OData.Core" Version="7.7.1" /> <PackageReference Include="Microsoft.OData.Core" Version="7.7.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />

82
backend/i18n/frontend_nl.json

@ -58,8 +58,8 @@
"assets.deleteFolderConfirmTitle": "Map verwijderen", "assets.deleteFolderConfirmTitle": "Map verwijderen",
"assets.deleteMetadataConfirmText": "Wil je deze metadata echt verwijderen?", "assets.deleteMetadataConfirmText": "Wil je deze metadata echt verwijderen?",
"assets.deleteMetadataConfirmTitle": "Metadata verwijderen", "assets.deleteMetadataConfirmTitle": "Metadata verwijderen",
"assets.deleteReferrerConfirmText": "The asset is referenced by a content item.\n\nDo you really want to delete the asset?", "assets.deleteReferrerConfirmText": "Er wordt naar het item verwezen door een contentitem.\n\nWil je het item echt verwijderen?",
"assets.deleteReferrerConfirmTitle": "Delete asset", "assets.deleteReferrerConfirmTitle": "Verwijder bestand",
"assets.downloadVersion": "Download deze versie", "assets.downloadVersion": "Download deze versie",
"assets.dropToUpdate": "Zet neer om te updaten", "assets.dropToUpdate": "Zet neer om te updaten",
"assets.duplicateFile": "Asset is al geüpload.", "assets.duplicateFile": "Asset is al geüpload.",
@ -277,18 +277,18 @@
"common.preview": "Preview", "common.preview": "Preview",
"common.product": "Squidex Headless CMS", "common.product": "Squidex Headless CMS",
"common.project": "Project", "common.project": "Project",
"common.queryOperators.contains": "contains", "common.queryOperators.contains": "bevat",
"common.queryOperators.empty": "is empty", "common.queryOperators.empty": "is leeg",
"common.queryOperators.endsWith": "ends with", "common.queryOperators.endsWith": "eindigt op",
"common.queryOperators.eq": "is equals to", "common.queryOperators.eq": "is gelijk aan",
"common.queryOperators.ge": "is greater than or equals to", "common.queryOperators.ge": "is groter dan of gelijk aan",
"common.queryOperators.gt": "is greater than", "common.queryOperators.gt": "is groter dan",
"common.queryOperators.le": "is less than pr equals to", "common.queryOperators.le": "is kleiner dan of is gelijk aan",
"common.queryOperators.lt": "is less than", "common.queryOperators.lt": "is kleiner dan",
"common.queryOperators.ne": "is not equals to", "common.queryOperators.ne": "is niet gelijk aan",
"common.queryOperators.startsWith": "starts with", "common.queryOperators.startsWith": "begint met",
"common.refresh": "Vernieuwen", "common.refresh": "Vernieuwen",
"common.remember": "Remember my decision", "common.remember": "Onthoud mijn keuze",
"common.rename": "Hernoemen", "common.rename": "Hernoemen",
"common.requiredHint": "verplicht", "common.requiredHint": "verplicht",
"common.reset": "Reset", "common.reset": "Reset",
@ -304,7 +304,7 @@
"common.searchResults": "Zoekresultaten", "common.searchResults": "Zoekresultaten",
"common.separateByLine": "Scheiden op regel", "common.separateByLine": "Scheiden op regel",
"common.settings": "Instellingen", "common.settings": "Instellingen",
"common.sidebar": "Sidebar Extension", "common.sidebar": "Zijbalk Uitbreiding",
"common.sidebarTour": "De zijbalknavigatie bevat nuttige contextspecifieke links. Hier kun je de geschiedenis bekijken hoe dit schema in de loop van de tijd is veranderd.", "common.sidebarTour": "De zijbalknavigatie bevat nuttige contextspecifieke links. Hier kun je de geschiedenis bekijken hoe dit schema in de loop van de tijd is veranderd.",
"common.slug": "Slug", "common.slug": "Slug",
"common.stars.max": "Mag niet meer dan 15 sterren hebben", "common.stars.max": "Mag niet meer dan 15 sterren hebben",
@ -357,15 +357,15 @@
"contents.deleteConfirmTitle": "Inhoud verwijderen", "contents.deleteConfirmTitle": "Inhoud verwijderen",
"contents.deleteFailed": "Verwijderen van inhoud is mislukt. Laad opnieuw.", "contents.deleteFailed": "Verwijderen van inhoud is mislukt. Laad opnieuw.",
"contents.deleteManyConfirmText": "Weet je zeker dat je de geselecteerde inhoudsitems wilt verwijderen?", "contents.deleteManyConfirmText": "Weet je zeker dat je de geselecteerde inhoudsitems wilt verwijderen?",
"contents.deleteReferrerConfirmText": "The content is referenced by another content item.\n\nDo you really want to delete the content?", "contents.deleteReferrerConfirmText": "Er wordt naar de inhoud verwezen door een ander inhoudsitem. \n \n Wil je de inhoud echt verwijderen?",
"contents.deleteReferrerConfirmTitle": "Delete content", "contents.deleteReferrerConfirmTitle": "Inhoud verwijderen",
"contents.deleteVersionConfirmText": "Wil je deze versie echt verwijderen?", "contents.deleteVersionConfirmText": "Wil je deze versie echt verwijderen?",
"contents.deleteVersionFailed": "Verwijderen van versie is mislukt. Laad opnieuw.", "contents.deleteVersionFailed": "Verwijderen van versie is mislukt. Laad opnieuw.",
"contents.draftNew": "Nieuw concept", "contents.draftNew": "Nieuw concept",
"contents.draftStatus": "Nieuwe versie", "contents.draftStatus": "Nieuwe versie",
"contents.editPageTitle": "Inhoud bewerken", "contents.editPageTitle": "Inhoud bewerken",
"contents.editTitle": "Inhoud bewerken", "contents.editTitle": "Inhoud bewerken",
"contents.invariantFieldDescription": "The '{fieldName}' field of the content item.", "contents.invariantFieldDescription": "Het veld '{fieldName}' van het inhoudsitem.",
"contents.languageModeAll": "Alle talen", "contents.languageModeAll": "Alle talen",
"contents.languageModeSingle": "Enkele taal", "contents.languageModeSingle": "Enkele taal",
"contents.lastModifiedByFieldDescription": "De gebruiker die het inhoudsitem de laatste keer heeft gewijzigd.", "contents.lastModifiedByFieldDescription": "De gebruiker die het inhoudsitem de laatste keer heeft gewijzigd.",
@ -376,7 +376,7 @@
"contents.loadDataFailed": "Laden van gegevens is mislukt. Laad opnieuw.", "contents.loadDataFailed": "Laden van gegevens is mislukt. Laad opnieuw.",
"contents.loadFailed": "Laden van inhoud is mislukt. Laad opnieuw.", "contents.loadFailed": "Laden van inhoud is mislukt. Laad opnieuw.",
"contents.loadVersionFailed": "Versie van een nieuwe versie is mislukt. Laad opnieuw.", "contents.loadVersionFailed": "Versie van een nieuwe versie is mislukt. Laad opnieuw.",
"contents.localizedFieldDescription": "The '{fieldName}' field of the content item (localized).", "contents.localizedFieldDescription": "Het veld '{fieldName}' van het inhoudsitem (gelokaliseerd).",
"contents.newStatusFieldDescription": "De nieuwe status van het item.", "contents.newStatusFieldDescription": "De nieuwe status van het item.",
"contents.noReference": "- Geen referentie -", "contents.noReference": "- Geen referentie -",
"contents.pendingChangesTextToChange": "Je hebt niet-opgeslagen wijzigingen. \n \n Wanneer je de status wijzigt, raak je ze kwijt. \n \n **Wil je toch doorgaan?**", "contents.pendingChangesTextToChange": "Je hebt niet-opgeslagen wijzigingen. \n \n Wanneer je de status wijzigt, raak je ze kwijt. \n \n **Wil je toch doorgaan?**",
@ -403,16 +403,16 @@
"contents.statusQueries": "Statusquery's", "contents.statusQueries": "Statusquery's",
"contents.stockPhotoEmpty": "Niets geselecteerd", "contents.stockPhotoEmpty": "Niets geselecteerd",
"contents.stockPhotoSearch": "Zoeken naar foto's op Unsplash", "contents.stockPhotoSearch": "Zoeken naar foto's op Unsplash",
"contents.tableHeaders.created": "Created", "contents.tableHeaders.created": "Gemaakt",
"contents.tableHeaders.createdBy": "Created By", "contents.tableHeaders.createdBy": "Gemaakt door",
"contents.tableHeaders.createdByShort": "By", "contents.tableHeaders.createdByShort": "Door",
"contents.tableHeaders.id": "Id", "contents.tableHeaders.id": "Id",
"contents.tableHeaders.lastModified": "Updated", "contents.tableHeaders.lastModified": "Bijgewerkt",
"contents.tableHeaders.lastModifiedBy": "Updated By", "contents.tableHeaders.lastModifiedBy": "Bijgewerkt door",
"contents.tableHeaders.lastModifiedByShort": "By", "contents.tableHeaders.lastModifiedByShort": "Door",
"contents.tableHeaders.nextStatus": "Next Status", "contents.tableHeaders.nextStatus": "Volgende status",
"contents.tableHeaders.status": "Status", "contents.tableHeaders.status": "Status",
"contents.tableHeaders.version": "Version", "contents.tableHeaders.version": "Versie",
"contents.unsavedChangesText": "Je hebt niet-opgeslagen wijzigingen. Wil je ze nu laden?", "contents.unsavedChangesText": "Je hebt niet-opgeslagen wijzigingen. Wil je ze nu laden?",
"contents.unsavedChangesTitle": "Niet-opgeslagen wijzigingen", "contents.unsavedChangesTitle": "Niet-opgeslagen wijzigingen",
"contents.updated": "Inhoud succesvol bijgewerkt.", "contents.updated": "Inhoud succesvol bijgewerkt.",
@ -487,7 +487,7 @@
"dashboard.trafficSummaryCard": "API Verkeer Samenvatting", "dashboard.trafficSummaryCard": "API Verkeer Samenvatting",
"dashboard.welcomeText": "Welkom bij **{app}** dashboard.", "dashboard.welcomeText": "Welkom bij **{app}** dashboard.",
"dashboard.welcomeTitle": "Hallo {user}", "dashboard.welcomeTitle": "Hallo {user}",
"eventConsumers.count": "Count", "eventConsumers.count": "Tellen",
"eventConsumers.loadFailed": "Kan gebeurtenisgebruikers niet laden. Laad opnieuw.", "eventConsumers.loadFailed": "Kan gebeurtenisgebruikers niet laden. Laad opnieuw.",
"eventConsumers.pageTitle": "Evenementconsumenten", "eventConsumers.pageTitle": "Evenementconsumenten",
"eventConsumers.position": "Positie", "eventConsumers.position": "Positie",
@ -556,16 +556,16 @@
"roles.deleteConfirmTitle": "Wil je de rol echt verwijderen?", "roles.deleteConfirmTitle": "Wil je de rol echt verwijderen?",
"roles.loadFailed": "Laden van rollen is mislukt. Laad opnieuw.", "roles.loadFailed": "Laden van rollen is mislukt. Laad opnieuw.",
"roles.loadPermissionsFailed": "Kan machtigingen niet laden. Laad opnieuw.", "roles.loadPermissionsFailed": "Kan machtigingen niet laden. Laad opnieuw.",
"roles.permissions": "Permissions", "roles.permissions": "Rechten",
"roles.permissionsDescription": "Permissions restrict the allowed operations and queries at API level and are a security feature.", "roles.permissionsDescription": "Machtigingen beperken de toegestane bewerkingen en zoekopdrachten op API-niveau en zijn een beveiligingsfunctie.",
"roles.permissionsPlaceholder": "Start typing to search for permissions", "roles.permissionsPlaceholder": "Begin met typen om naar rechten te zoeken",
"roles.properties": "Properties", "roles.properties": "Eigenschappen",
"roles.properties.hideAPI": "Hide API", "roles.properties.hideAPI": "API verbergen",
"roles.properties.hideAssets": "Hide Assets", "roles.properties.hideAssets": "Assets verbergen",
"roles.properties.hideContents": "Hide {schema} Contents", "roles.properties.hideContents": "Verberg {schema} inhoud",
"roles.properties.hideSchemas": "Hide Schemas", "roles.properties.hideSchemas": "Verberg schema's",
"roles.properties.hideSettings": "Hide Settings", "roles.properties.hideSettings": "Verberg instellingen",
"roles.propertiesDescription": "Properties describe the behavior of the Management UI, but do not provide security for the API.", "roles.propertiesDescription": " Eigenschappen beschrijven het gedrag van de beheerinterface, maar bieden geen beveiliging voor de API. ",
"roles.refreshTooltip": "Ververs rollen (CTRL + SHIFT + R)", "roles.refreshTooltip": "Ververs rollen (CTRL + SHIFT + R)",
"roles.reloaded": "Rollen opnieuw geladen.", "roles.reloaded": "Rollen opnieuw geladen.",
"roles.revokeFailed": "Kan rol niet intrekken. Laad opnieuw.", "roles.revokeFailed": "Kan rol niet intrekken. Laad opnieuw.",
@ -628,10 +628,10 @@
"schemas.addNestedField": "Voeg genest veld toe", "schemas.addNestedField": "Voeg genest veld toe",
"schemas.changeCategoryFailed": "Kan categorie niet wijzigen. Laad opnieuw.", "schemas.changeCategoryFailed": "Kan categorie niet wijzigen. Laad opnieuw.",
"schemas.clone": "Clone Schema", "schemas.clone": "Clone Schema",
"schemas.contentSidebarUrl": "Content Sidebar Extension", "schemas.contentSidebarUrl": "Inhoud zijbalk uitbreiding",
"schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.", "schemas.contentSidebarUrlHint": "URL naar de plug-in voor de zijbalk in de detailweergave.",
"schemas.contentsSidebarUrl": "Contents Sidebar Extension", "schemas.contentsSidebarUrl": "Inhoud zijbalk uitbreiding",
"schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.", "schemas.contentsSidebarUrlHint": "URL naar de plug-in voor de zijbalk in de lijstweergave.",
"schemas.contextMenuTour": "Open het contextmenu om het schema te verwijderen of om scripts te maken voor wijzigingen in de inhoud.", "schemas.contextMenuTour": "Open het contextmenu om het schema te verwijderen of om scripts te maken voor wijzigingen in de inhoud.",
"schemas.create": "Schema maken", "schemas.create": "Schema maken",
"schemas.createCategory": "Nieuwe categorie maken ...", "schemas.createCategory": "Nieuwe categorie maken ...",

4
backend/i18n/source/backend__ignore.json

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

8
backend/i18n/source/backend_nl.json

@ -1,7 +1,12 @@
{ {
"annotations_AbsoluteUrl": "Het veld {name|lower} moet een absolute URL zijn.", "annotations_AbsoluteUrl": "Het veld {name|lower} moet een absolute URL zijn.",
"annotations_Compare": "Het veld {name|lower} moet hetzelfde zijn als {other|lower}.", "annotations_Compare": "Het veld {name|lower} moet hetzelfde zijn als {other|lower}.",
"annotations_EmailAddress": "Het veld {name|lower} is geen geldig e-mailadres.",
"annotations_Range": "Het veld {name|lower} moet tussen {min} en {max} zijn.",
"annotations_RegularExpression": "Het veld {name|lower} is niet.",
"annotations_Required": "Het veld {name|lower} is verplicht.", "annotations_Required": "Het veld {name|lower} is verplicht.",
"annotations_StringLength": "Het veld {name|lower} moet een string zijn met een maximale lengte van {max}.",
"annotations_StringLengthMinimum": "Het veld {name|lower} moet een string zijn met een minimum lengte van {min} en een maximum lengte van {max}.",
"apps.alreadyArchieved": "App is al gearchiveerd.", "apps.alreadyArchieved": "App is al gearchiveerd.",
"apps.clients.idAlreadyExists": "Er bestaat al een client met dezelfde id.", "apps.clients.idAlreadyExists": "Er bestaat al een client met dezelfde id.",
"apps.contributors.cannotChangeYourself": "Je kunt jouw eigen rol niet wijzigen.", "apps.contributors.cannotChangeYourself": "Je kunt jouw eigen rol niet wijzigen.",
@ -28,6 +33,7 @@
"assets.folderNotFound": "Assetmap bestaat niet.", "assets.folderNotFound": "Assetmap bestaat niet.",
"assets.folderRecursion": "Kan map niet toevoegen aan zijn eigen kind.", "assets.folderRecursion": "Kan map niet toevoegen aan zijn eigen kind.",
"assets.maxSizeReached": "Je hebt jouw maximale assetgrootte bereikt.", "assets.maxSizeReached": "Je hebt jouw maximale assetgrootte bereikt.",
"assets.referenced": "Er wordt naar dit bestand verwezen door een contentitem en kan niet worden verwijderd.",
"backups.alreadyRunning": "Er wordt al een ander back-upproces uitgevoerd.", "backups.alreadyRunning": "Er wordt al een ander back-upproces uitgevoerd.",
"backups.maxReached": "Je kunt niet meer dan {max} backups hebben.", "backups.maxReached": "Je kunt niet meer dan {max} backups hebben.",
"backups.restoreRunning": "Er wordt al een herstelbewerking uitgevoerd.", "backups.restoreRunning": "Er wordt al een herstelbewerking uitgevoerd.",
@ -131,6 +137,7 @@
"contents.invalidNumber": "Ongeldig json-type, verwacht aantal.", "contents.invalidNumber": "Ongeldig json-type, verwacht aantal.",
"contents.invalidString": "Ongeldig json-type, verwachte string.", "contents.invalidString": "Ongeldig json-type, verwachte string.",
"contents.listReferences": "{count} referentie (s)", "contents.listReferences": "{count} referentie (s)",
"contents.referenced": "Er wordt naar de inhoud verwezen door een andere item en kan niet worden verwijderd.",
"contents.singletonNotChangeable": "Singleton-inhoud kan niet worden bijgewerkt.", "contents.singletonNotChangeable": "Singleton-inhoud kan niet worden bijgewerkt.",
"contents.singletonNotCreatable": "Singleton-inhoud kan niet worden gemaakt.", "contents.singletonNotCreatable": "Singleton-inhoud kan niet worden gemaakt.",
"contents.singletonNotDeletable": "Singleton-inhoud kan niet worden verwijderd.", "contents.singletonNotDeletable": "Singleton-inhoud kan niet worden verwijderd.",
@ -142,6 +149,7 @@
"contents.validation.characterCount": "Moet exact {count} teken (s) bevatten.", "contents.validation.characterCount": "Moet exact {count} teken (s) bevatten.",
"contents.validation.charactersBetween": "Moet tussen {min} en {max} teken (s) bevatten.", "contents.validation.charactersBetween": "Moet tussen {min} en {max} teken (s) bevatten.",
"contents.validation.duplicates": "Mag geen dubbele waarden bevatten.", "contents.validation.duplicates": "Mag geen dubbele waarden bevatten.",
"contents.validation.error": "Validatie mislukt met interne fout.",
"contents.validation.exactValue": "Moet exact {waarde} zijn.", "contents.validation.exactValue": "Moet exact {waarde} zijn.",
"contents.validation.extension": "Moet een toegestane extensie zijn.", "contents.validation.extension": "Moet een toegestane extensie zijn.",
"contents.validation.image": "Geen afbeelding.", "contents.validation.image": "Geen afbeelding.",

4
backend/i18n/source/frontend__ignore.json

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

43
backend/i18n/source/frontend_nl.json

@ -58,6 +58,8 @@
"assets.deleteFolderConfirmTitle": "Map verwijderen", "assets.deleteFolderConfirmTitle": "Map verwijderen",
"assets.deleteMetadataConfirmText": "Wil je deze metadata echt verwijderen?", "assets.deleteMetadataConfirmText": "Wil je deze metadata echt verwijderen?",
"assets.deleteMetadataConfirmTitle": "Metadata verwijderen", "assets.deleteMetadataConfirmTitle": "Metadata verwijderen",
"assets.deleteReferrerConfirmText": "Er wordt naar het item verwezen door een contentitem. \n \n Wil je het item echt verwijderen?",
"assets.deleteReferrerConfirmTitle": "Verwijder bestand",
"assets.downloadVersion": "Download deze versie", "assets.downloadVersion": "Download deze versie",
"assets.dropToUpdate": "Zet neer om te updaten", "assets.dropToUpdate": "Zet neer om te updaten",
"assets.duplicateFile": "Asset is al geüpload.", "assets.duplicateFile": "Asset is al geüpload.",
@ -275,7 +277,18 @@
"common.preview": "Preview", "common.preview": "Preview",
"common.product": "Squidex Headless CMS", "common.product": "Squidex Headless CMS",
"common.project": "Project", "common.project": "Project",
"common.queryOperators.contains": "bevat",
"common.queryOperators.empty": "is leeg",
"common.queryOperators.endsWith": "eindigt op",
"common.queryOperators.eq": "is gelijk aan",
"common.queryOperators.ge": "is groter dan of gelijk aan",
"common.queryOperators.gt": "is groter dan",
"common.queryOperators.le": "is kleiner dan of is gelijk aan",
"common.queryOperators.lt": "is kleiner dan",
"common.queryOperators.ne": "is niet gelijk aan",
"common.queryOperators.startsWith": "begint met",
"common.refresh": "Vernieuwen", "common.refresh": "Vernieuwen",
"common.remember": "Onthoud mijn keuze",
"common.rename": "Hernoemen", "common.rename": "Hernoemen",
"common.requiredHint": "verplicht", "common.requiredHint": "verplicht",
"common.reset": "Reset", "common.reset": "Reset",
@ -291,6 +304,7 @@
"common.searchResults": "Zoekresultaten", "common.searchResults": "Zoekresultaten",
"common.separateByLine": "Scheiden op regel", "common.separateByLine": "Scheiden op regel",
"common.settings": "Instellingen", "common.settings": "Instellingen",
"common.sidebar": "Zijbalk Uitbreiding",
"common.sidebarTour": "De zijbalknavigatie bevat nuttige contextspecifieke links. Hier kun je de geschiedenis bekijken hoe dit schema in de loop van de tijd is veranderd.", "common.sidebarTour": "De zijbalknavigatie bevat nuttige contextspecifieke links. Hier kun je de geschiedenis bekijken hoe dit schema in de loop van de tijd is veranderd.",
"common.slug": "Slug", "common.slug": "Slug",
"common.stars.max": "Mag niet meer dan 15 sterren hebben", "common.stars.max": "Mag niet meer dan 15 sterren hebben",
@ -343,12 +357,15 @@
"contents.deleteConfirmTitle": "Inhoud verwijderen", "contents.deleteConfirmTitle": "Inhoud verwijderen",
"contents.deleteFailed": "Verwijderen van inhoud is mislukt. Laad opnieuw.", "contents.deleteFailed": "Verwijderen van inhoud is mislukt. Laad opnieuw.",
"contents.deleteManyConfirmText": "Weet je zeker dat je de geselecteerde inhoudsitems wilt verwijderen?", "contents.deleteManyConfirmText": "Weet je zeker dat je de geselecteerde inhoudsitems wilt verwijderen?",
"contents.deleteReferrerConfirmText": "Er wordt naar de inhoud verwezen door een ander inhoudsitem. \n \n Wil je de inhoud echt verwijderen?",
"contents.deleteReferrerConfirmTitle": "Inhoud verwijderen",
"contents.deleteVersionConfirmText": "Wil je deze versie echt verwijderen?", "contents.deleteVersionConfirmText": "Wil je deze versie echt verwijderen?",
"contents.deleteVersionFailed": "Verwijderen van versie is mislukt. Laad opnieuw.", "contents.deleteVersionFailed": "Verwijderen van versie is mislukt. Laad opnieuw.",
"contents.draftNew": "Nieuw concept", "contents.draftNew": "Nieuw concept",
"contents.draftStatus": "Nieuwe versie", "contents.draftStatus": "Nieuwe versie",
"contents.editPageTitle": "Inhoud bewerken", "contents.editPageTitle": "Inhoud bewerken",
"contents.editTitle": "Inhoud bewerken", "contents.editTitle": "Inhoud bewerken",
"contents.invariantFieldDescription": "Het veld '{fieldName}' van het inhoudsitem.",
"contents.languageModeAll": "Alle talen", "contents.languageModeAll": "Alle talen",
"contents.languageModeSingle": "Enkele taal", "contents.languageModeSingle": "Enkele taal",
"contents.lastModifiedByFieldDescription": "De gebruiker die het inhoudsitem de laatste keer heeft gewijzigd.", "contents.lastModifiedByFieldDescription": "De gebruiker die het inhoudsitem de laatste keer heeft gewijzigd.",
@ -359,6 +376,7 @@
"contents.loadDataFailed": "Laden van gegevens is mislukt. Laad opnieuw.", "contents.loadDataFailed": "Laden van gegevens is mislukt. Laad opnieuw.",
"contents.loadFailed": "Laden van inhoud is mislukt. Laad opnieuw.", "contents.loadFailed": "Laden van inhoud is mislukt. Laad opnieuw.",
"contents.loadVersionFailed": "Versie van een nieuwe versie is mislukt. Laad opnieuw.", "contents.loadVersionFailed": "Versie van een nieuwe versie is mislukt. Laad opnieuw.",
"contents.localizedFieldDescription": "Het veld '{fieldName}' van het inhoudsitem (gelokaliseerd).",
"contents.newStatusFieldDescription": "De nieuwe status van het item.", "contents.newStatusFieldDescription": "De nieuwe status van het item.",
"contents.noReference": "- Geen referentie -", "contents.noReference": "- Geen referentie -",
"contents.pendingChangesTextToChange": "Je hebt niet-opgeslagen wijzigingen. \n \n Wanneer je de status wijzigt, raak je ze kwijt. \n \n **Wil je toch doorgaan?**", "contents.pendingChangesTextToChange": "Je hebt niet-opgeslagen wijzigingen. \n \n Wanneer je de status wijzigt, raak je ze kwijt. \n \n **Wil je toch doorgaan?**",
@ -385,6 +403,16 @@
"contents.statusQueries": "Statusquery's", "contents.statusQueries": "Statusquery's",
"contents.stockPhotoEmpty": "Niets geselecteerd", "contents.stockPhotoEmpty": "Niets geselecteerd",
"contents.stockPhotoSearch": "Zoeken naar foto's op Unsplash", "contents.stockPhotoSearch": "Zoeken naar foto's op Unsplash",
"contents.tableHeaders.created": "Gemaakt",
"contents.tableHeaders.createdBy": "Gemaakt door",
"contents.tableHeaders.createdByShort": "Door",
"contents.tableHeaders.id": "Id",
"contents.tableHeaders.lastModified": "Bijgewerkt",
"contents.tableHeaders.lastModifiedBy": "Bijgewerkt door",
"contents.tableHeaders.lastModifiedByShort": "Door",
"contents.tableHeaders.nextStatus": "Volgende status",
"contents.tableHeaders.status": "Status",
"contents.tableHeaders.version": "Versie",
"contents.unsavedChangesText": "Je hebt niet-opgeslagen wijzigingen. Wil je ze nu laden?", "contents.unsavedChangesText": "Je hebt niet-opgeslagen wijzigingen. Wil je ze nu laden?",
"contents.unsavedChangesTitle": "Niet-opgeslagen wijzigingen", "contents.unsavedChangesTitle": "Niet-opgeslagen wijzigingen",
"contents.updated": "Inhoud succesvol bijgewerkt.", "contents.updated": "Inhoud succesvol bijgewerkt.",
@ -459,6 +487,7 @@
"dashboard.trafficSummaryCard": "API Verkeer Samenvatting", "dashboard.trafficSummaryCard": "API Verkeer Samenvatting",
"dashboard.welcomeText": "Welkom bij **{app}** dashboard.", "dashboard.welcomeText": "Welkom bij **{app}** dashboard.",
"dashboard.welcomeTitle": "Hallo {user}", "dashboard.welcomeTitle": "Hallo {user}",
"eventConsumers.count": "Tellen",
"eventConsumers.loadFailed": "Kan gebeurtenisgebruikers niet laden. Laad opnieuw.", "eventConsumers.loadFailed": "Kan gebeurtenisgebruikers niet laden. Laad opnieuw.",
"eventConsumers.pageTitle": "Evenementconsumenten", "eventConsumers.pageTitle": "Evenementconsumenten",
"eventConsumers.position": "Positie", "eventConsumers.position": "Positie",
@ -527,6 +556,16 @@
"roles.deleteConfirmTitle": "Wil je de rol echt verwijderen?", "roles.deleteConfirmTitle": "Wil je de rol echt verwijderen?",
"roles.loadFailed": "Laden van rollen is mislukt. Laad opnieuw.", "roles.loadFailed": "Laden van rollen is mislukt. Laad opnieuw.",
"roles.loadPermissionsFailed": "Kan machtigingen niet laden. Laad opnieuw.", "roles.loadPermissionsFailed": "Kan machtigingen niet laden. Laad opnieuw.",
"roles.permissions": "Rechten",
"roles.permissionsDescription": "Machtigingen beperken de toegestane bewerkingen en zoekopdrachten op API-niveau en zijn een beveiligingsfunctie.",
"roles.permissionsPlaceholder": "Begin met typen om naar rechten te zoeken",
"roles.properties": "Eigenschappen",
"roles.properties.hideAPI": "API verbergen",
"roles.properties.hideAssets": "Assets verbergen",
"roles.properties.hideContents": "Verberg {schema} inhoud",
"roles.properties.hideSchemas": "Verberg schema's",
"roles.properties.hideSettings": "Verberg instellingen",
"roles.propertiesDescription": " Eigenschappen beschrijven het gedrag van de beheerinterface, maar bieden geen beveiliging voor de API. ",
"roles.refreshTooltip": "Ververs rollen (CTRL + SHIFT + R)", "roles.refreshTooltip": "Ververs rollen (CTRL + SHIFT + R)",
"roles.reloaded": "Rollen opnieuw geladen.", "roles.reloaded": "Rollen opnieuw geladen.",
"roles.revokeFailed": "Kan rol niet intrekken. Laad opnieuw.", "roles.revokeFailed": "Kan rol niet intrekken. Laad opnieuw.",
@ -589,6 +628,10 @@
"schemas.addNestedField": "Voeg genest veld toe", "schemas.addNestedField": "Voeg genest veld toe",
"schemas.changeCategoryFailed": "Kan categorie niet wijzigen. Laad opnieuw.", "schemas.changeCategoryFailed": "Kan categorie niet wijzigen. Laad opnieuw.",
"schemas.clone": "Clone Schema", "schemas.clone": "Clone Schema",
"schemas.contentSidebarUrl": "Inhoud zijbalk uitbreiding",
"schemas.contentSidebarUrlHint": "URL naar de plug-in voor de zijbalk in de detailweergave.",
"schemas.contentsSidebarUrl": "Inhoud zijbalk uitbreiding",
"schemas.contentsSidebarUrlHint": "URL naar de plug-in voor de zijbalk in de lijstweergave.",
"schemas.contextMenuTour": "Open het contextmenu om het schema te verwijderen of om scripts te maken voor wijzigingen in de inhoud.", "schemas.contextMenuTour": "Open het contextmenu om het schema te verwijderen of om scripts te maken voor wijzigingen in de inhoud.",
"schemas.create": "Schema maken", "schemas.create": "Schema maken",
"schemas.createCategory": "Nieuwe categorie maken ...", "schemas.createCategory": "Nieuwe categorie maken ...",

12
backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleQueryService.cs

@ -14,6 +14,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Queries
{ {
public sealed class RuleQueryService : IRuleQueryService public sealed class RuleQueryService : IRuleQueryService
{ {
private static readonly List<IEnrichedRuleEntity> EmptyResults = new List<IEnrichedRuleEntity>();
private readonly IRulesIndex rulesIndex; private readonly IRulesIndex rulesIndex;
private readonly IRuleEnricher ruleEnricher; private readonly IRuleEnricher ruleEnricher;
@ -30,9 +31,16 @@ namespace Squidex.Domain.Apps.Entities.Rules.Queries
{ {
var rules = await rulesIndex.GetRulesAsync(context.App.Id); var rules = await rulesIndex.GetRulesAsync(context.App.Id);
var enriched = await ruleEnricher.EnrichAsync(rules, context); rules.RemoveAll(x => x.IsDeleted);
return enriched; if (rules.Count > 0)
{
var enriched = await ruleEnricher.EnrichAsync(rules, context);
return enriched;
}
return EmptyResults;
} }
} }
} }

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

@ -71,22 +71,22 @@
<value>Het veld {0} moet hetzelfde zijn als {1}.</value> <value>Het veld {0} moet hetzelfde zijn als {1}.</value>
</data> </data>
<data name="annotations_EmailAddress" xml:space="preserve"> <data name="annotations_EmailAddress" xml:space="preserve">
<value>The field {name|lower} is not a valid email address.</value> <value>Het veld {name|lower} is geen geldig e-mailadres.</value>
</data> </data>
<data name="dotnet_annotations_EmailAddress" xml:space="preserve"> <data name="dotnet_annotations_EmailAddress" xml:space="preserve">
<value>The field {0} is not a valid email address.</value> <value>Het veld {0} is geen geldig e-mailadres.</value>
</data> </data>
<data name="annotations_Range" xml:space="preserve"> <data name="annotations_Range" xml:space="preserve">
<value>The field {name|lower} must be between {min} and {max}.</value> <value>Het veld {name|lower} moet tussen {min} en {max} zijn.</value>
</data> </data>
<data name="dotnet_annotations_Range" xml:space="preserve"> <data name="dotnet_annotations_Range" xml:space="preserve">
<value>The field {0} must be between {1} and {2}.</value> <value>Het veld {0} moet tussen {1} en {2} zijn.</value>
</data> </data>
<data name="annotations_RegularExpression" xml:space="preserve"> <data name="annotations_RegularExpression" xml:space="preserve">
<value>The field {name|lower} is not.</value> <value>Het veld {name|lower} is niet.</value>
</data> </data>
<data name="dotnet_annotations_RegularExpression" xml:space="preserve"> <data name="dotnet_annotations_RegularExpression" xml:space="preserve">
<value>The field {0} is not.</value> <value>Het veld {0} is niet.</value>
</data> </data>
<data name="annotations_Required" xml:space="preserve"> <data name="annotations_Required" xml:space="preserve">
<value>Het veld {name|lower} is verplicht.</value> <value>Het veld {name|lower} is verplicht.</value>
@ -95,16 +95,16 @@
<value>Het veld {0} is verplicht.</value> <value>Het veld {0} is verplicht.</value>
</data> </data>
<data name="annotations_StringLength" xml:space="preserve"> <data name="annotations_StringLength" xml:space="preserve">
<value>The field {name|lower} must be a string with a maximum length of {max}.</value> <value>Het veld {name|lower} moet een string zijn met een maximale lengte van {max}.</value>
</data> </data>
<data name="dotnet_annotations_StringLength" xml:space="preserve"> <data name="dotnet_annotations_StringLength" xml:space="preserve">
<value>The field {0} must be a string with a maximum length of {1}.</value> <value>Het veld {0} moet een string zijn met een maximale lengte van {1}.</value>
</data> </data>
<data name="annotations_StringLengthMinimum" xml:space="preserve"> <data name="annotations_StringLengthMinimum" xml:space="preserve">
<value>The field {name|lower} must be a string with a minimum length of {min} and a maximum length of {max}.</value> <value>Het veld {name|lower} moet een string zijn met een minimum lengte van {min} en een maximum lengte van {max}.</value>
</data> </data>
<data name="dotnet_annotations_StringLengthMinimum" xml:space="preserve"> <data name="dotnet_annotations_StringLengthMinimum" xml:space="preserve">
<value>The field {0} must be a string with a minimum length of {1} and a maximum length of {2}.</value> <value>Het veld {0} moet een string zijn met een minimum lengte van {1} en een maximum lengte van {2}.</value>
</data> </data>
<data name="apps.alreadyArchieved" xml:space="preserve"> <data name="apps.alreadyArchieved" xml:space="preserve">
<value>App is al gearchiveerd.</value> <value>App is al gearchiveerd.</value>
@ -185,7 +185,7 @@
<value>Je hebt jouw maximale assetgrootte bereikt.</value> <value>Je hebt jouw maximale assetgrootte bereikt.</value>
</data> </data>
<data name="assets.referenced" xml:space="preserve"> <data name="assets.referenced" xml:space="preserve">
<value>Assets is referenced by a content and cannot be deleted.</value> <value>Er wordt naar dit bestand verwezen door een contentitem en kan niet worden verwijderd.</value>
</data> </data>
<data name="backups.alreadyRunning" xml:space="preserve"> <data name="backups.alreadyRunning" xml:space="preserve">
<value>Er wordt al een ander back-upproces uitgevoerd.</value> <value>Er wordt al een ander back-upproces uitgevoerd.</value>
@ -497,7 +497,7 @@
<value>{count} referentie (s)</value> <value>{count} referentie (s)</value>
</data> </data>
<data name="contents.referenced" xml:space="preserve"> <data name="contents.referenced" xml:space="preserve">
<value>Content is referenced by another content and cannot be deleted.</value> <value>Er wordt naar de inhoud verwezen door een andere item en kan niet worden verwijderd.</value>
</data> </data>
<data name="contents.singletonNotChangeable" xml:space="preserve"> <data name="contents.singletonNotChangeable" xml:space="preserve">
<value>Singleton-inhoud kan niet worden bijgewerkt.</value> <value>Singleton-inhoud kan niet worden bijgewerkt.</value>
@ -533,7 +533,7 @@
<value>Mag geen dubbele waarden bevatten.</value> <value>Mag geen dubbele waarden bevatten.</value>
</data> </data>
<data name="contents.validation.error" xml:space="preserve"> <data name="contents.validation.error" xml:space="preserve">
<value>Validation failed with internal error.</value> <value>Validatie mislukt met interne fout.</value>
</data> </data>
<data name="contents.validation.exactValue" xml:space="preserve"> <data name="contents.validation.exactValue" xml:space="preserve">
<value>Moet exact {waarde} zijn.</value> <value>Moet exact {waarde} zijn.</value>

12
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesIndexTests.cs

@ -47,7 +47,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes
} }
[Fact] [Fact]
public async Task Should_return_empty_rule_if_rule_not_created() public async Task Should_return_empty_rules_if_rule_not_created()
{ {
var rule = SetupRule(-1); var rule = SetupRule(-1);
@ -60,16 +60,16 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes
} }
[Fact] [Fact]
public async Task Should_return_empty_rule_if_rule_deleted() public async Task Should_return_rule_if_deleted()
{ {
var rule = SetupRule(-1); var rule = SetupRule(0, true);
A.CallTo(() => index.GetIdsAsync()) A.CallTo(() => index.GetIdsAsync())
.Returns(new List<DomainId> { rule.Id }); .Returns(new List<DomainId> { rule.Id });
var actual = await sut.GetRulesAsync(appId.Id); var actual = await sut.GetRulesAsync(appId.Id);
Assert.Empty(actual); Assert.Same(actual[0], rule);
} }
[Fact] [Fact]
@ -117,11 +117,11 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes
.MustHaveHappened(); .MustHaveHappened();
} }
private IRuleEntity SetupRule(long version) private IRuleEntity SetupRule(long version, bool isDeleted = false)
{ {
var ruleId = DomainId.NewGuid(); var ruleId = DomainId.NewGuid();
var ruleEntity = new RuleEntity { Id = ruleId, AppId = appId, Version = version }; var ruleEntity = new RuleEntity { Id = ruleId, AppId = appId, Version = version, IsDeleted = isDeleted };
var ruleGrain = A.Fake<IRuleGrain>(); var ruleGrain = A.Fake<IRuleGrain>();
A.CallTo(() => ruleGrain.GetStateAsync()) A.CallTo(() => ruleGrain.GetStateAsync())

19
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleQueryServiceTests.cs

@ -53,5 +53,24 @@ namespace Squidex.Domain.Apps.Entities.Rules.Queries
Assert.Same(enriched, result); Assert.Same(enriched, result);
} }
[Fact]
public async Task Should_not_get_deleted_rules()
{
var original = new List<IRuleEntity>
{
new RuleEntity { IsDeleted = true }
};
A.CallTo(() => rulesIndex.GetRulesAsync(appId.Id))
.Returns(original);
var result = await sut.QueryAsync(requestContext);
Assert.Empty(result);
A.CallTo(() => ruleEnricher.EnrichAsync(A<IEnumerable<IRuleEntity>>._, requestContext))
.MustNotHaveHappened();
}
} }
} }

16
backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasIndexTests.cs

@ -141,7 +141,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
} }
[Fact] [Fact]
public async Task Should_return_empty_schema_if_schema_not_created() public async Task Should_return_empty_schemas_if_schema_not_created()
{ {
var schema = SetupSchema(EtagVersion.NotFound); var schema = SetupSchema(EtagVersion.NotFound);
@ -154,16 +154,16 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
} }
[Fact] [Fact]
public async Task Should_return_empty_schema_if_schema_deleted() public async Task Should_return_schema_if_deleted()
{ {
var schema = SetupSchema(); var schema = SetupSchema(0, true);
A.CallTo(() => index.GetIdAsync(schema.SchemaDef.Name)) A.CallTo(() => index.GetIdsAsync())
.Returns(schema.Id); .Returns(new List<Guid> { schema.Id });
var actual = await sut.GetSchemasAsync(appId.Id); var actual = await sut.GetSchemasAsync(appId.Id);
Assert.Empty(actual); Assert.Same(actual[0], schema);
} }
[Fact] [Fact]
@ -275,7 +275,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
return new CreateSchema { SchemaId = schemaId.Id, Name = name, AppId = appId }; return new CreateSchema { SchemaId = schemaId.Id, Name = name, AppId = appId };
} }
private ISchemaEntity SetupSchema(long version = 0) private ISchemaEntity SetupSchema(long version = 0, bool isDeleted = false)
{ {
var schemaEntity = A.Fake<ISchemaEntity>(); var schemaEntity = A.Fake<ISchemaEntity>();
@ -287,6 +287,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
.Returns(appId); .Returns(appId);
A.CallTo(() => schemaEntity.Version) A.CallTo(() => schemaEntity.Version)
.Returns(version); .Returns(version);
A.CallTo(() => schemaEntity.IsDeleted)
.Returns(isDeleted);
var schemaGrain = A.Fake<ISchemaGrain>(); var schemaGrain = A.Fake<ISchemaGrain>();

16
frontend/app/shared/components/assets/asset.component.html

@ -1,5 +1,10 @@
<ng-container *ngIf="!isListView; else listTemplate"> <ng-container *ngIf="!isListView; else listTemplate">
<div class="card" (click)="select.emit()" [class.selectable]="isSelectable" [class.border-primary]="isSelected" (sqxDropFile)="updateFile($event)" [sqxDropDisabled]="!asset || !asset.canUpload" [sqxDropNoPaste]="true"> <div class="card" (click)="select.emit()"
[class.selectable]="isSelectable"
[class.border-primary]="isSelected"
(sqxDropFile)="updateFile($event)"
[sqxDropDisabled]="!asset || !asset.canUpload"
[sqxDropNoPaste]="true">
<div class="card-body"> <div class="card-body">
<div class="file-preview" *ngIf="asset && progress === 0"> <div class="file-preview" *ngIf="asset && progress === 0">
<span class="file-type" *ngIf="asset.fileType"> <span class="file-type" *ngIf="asset.fileType">
@ -93,7 +98,11 @@
</ng-container> </ng-container>
<ng-template #listTemplate> <ng-template #listTemplate>
<div class="table-items-row" (click)="select.emit()" [class.selectable]="isSelectable" (sqxDropFile)="updateFile($event)" [sqxDropDisabled]="!asset || !asset.canUpload" [sqxDropNoPaste]="true"> <div class="table-items-row" (click)="select.emit()"
[class.selectable]="isSelectable"
(sqxDropFile)="updateFile($event)"
[sqxDropDisabled]="!asset || !asset.canUpload"
[sqxDropNoPaste]="true">
<div class="left-border" [class.hidden]="!isSelectable" [class.selected]="isSelected"></div> <div class="left-border" [class.hidden]="!isSelectable" [class.selected]="isSelected"></div>
<ng-container *ngIf="asset && progress === 0"> <ng-container *ngIf="asset && progress === 0">
@ -166,7 +175,8 @@
<ng-container *ngIf="asset"> <ng-container *ngIf="asset">
<ng-container *sqxModal="editDialog"> <ng-container *sqxModal="editDialog">
<sqx-asset-dialog [allTags]="allTags" [asset]="asset" (changed)="setAsset($event)" (complete)="editDialog.hide()"> <sqx-asset-dialog [allTags]="allTags"
[asset]="asset" (changed)="setAsset($event)" (complete)="editDialog.hide()">
</sqx-asset-dialog> </sqx-asset-dialog>
</ng-container> </ng-container>
</ng-container> </ng-container>

12
frontend/app/shared/components/forms/markdown-editor.component.ts

@ -208,15 +208,17 @@ export class MarkdownEditorComponent extends StatefulControlComponent<State, str
let content = ''; let content = '';
for (const asset of assets) { for (const asset of assets) {
const name = asset.fileNameWithoutExtension;
switch (asset.type) { switch (asset.type) {
case 'Image': case 'Image':
content += `![${asset.fileName}](${asset.fullUrl(this.apiUrl)} '${asset.fileName}')`; content += `![${name}](${asset.fullUrl(this.apiUrl)} '${name}')`;
break; break;
case 'Video': case 'Video':
content += `[${asset.fileName}](${asset.fullUrl(this.apiUrl)}')`; content += `[${name}](${asset.fullUrl(this.apiUrl)}')`;
break; break;
default: default:
content += `[${asset.fileName}](${asset.fullUrl(this.apiUrl)}')`; content += `[${name}](${asset.fullUrl(this.apiUrl)}')`;
break; break;
} }
} }
@ -265,7 +267,9 @@ export class MarkdownEditorComponent extends StatefulControlComponent<State, str
this.assetUploader.uploadFile(file) this.assetUploader.uploadFile(file)
.subscribe(asset => { .subscribe(asset => {
if (Types.is(asset, AssetDto)) { if (Types.is(asset, AssetDto)) {
replaceText(`![${asset.fileName}](${asset.fullUrl(this.apiUrl)} '${asset.fileName}')`); const name = asset.fileNameWithoutExtension;
replaceText(`![${name}](${asset.fullUrl(this.apiUrl)} '${name}')`);
} }
}, error => { }, error => {
if (!Types.is(error, UploadCanceled)) { if (!Types.is(error, UploadCanceled)) {

10
frontend/app/shared/components/forms/rich-editor.component.ts

@ -221,15 +221,17 @@ export class RichEditorComponent extends StatefulControlComponent<undefined, str
let content = ''; let content = '';
for (const asset of assets) { for (const asset of assets) {
const name = asset.fileNameWithoutExtension;
switch (asset.type) { switch (asset.type) {
case 'Image': case 'Image':
content += `<img src="${asset.fullUrl(this.apiUrl)}" alt="${asset.fileName}" />`; content += `<img src="${asset.fullUrl(this.apiUrl)}" alt="${name}" />`;
break; break;
case 'Video': case 'Video':
content += `<video src="${asset.fullUrl(this.apiUrl)}" />`; content += `<video src="${asset.fullUrl(this.apiUrl)}" />`;
break; break;
default: default:
content += `<a href="${asset.fullUrl(this.apiUrl)}" alt="${asset.fileName}">${asset.fileName}</a>`; content += `<a href="${asset.fullUrl(this.apiUrl)}" alt="${name}">${name}</a>`;
break; break;
} }
} }
@ -261,10 +263,12 @@ export class RichEditorComponent extends StatefulControlComponent<undefined, str
this.assetUploader.uploadFile(file) this.assetUploader.uploadFile(file)
.subscribe(asset => { .subscribe(asset => {
if (Types.is(asset, AssetDto)) { if (Types.is(asset, AssetDto)) {
const name = asset.fileNameWithoutExtension;
if (asset.type === 'Video') { if (asset.type === 'Video') {
replaceText(`<video src="${asset.fullUrl(this.apiUrl)}" />`); replaceText(`<video src="${asset.fullUrl(this.apiUrl)}" />`);
} else { } else {
replaceText(`<img src="${asset.fullUrl(this.apiUrl)}" alt="${asset.fileName}" />`); replaceText(`<img src="${asset.fullUrl(this.apiUrl)}" alt="${name}" />`);
} }
} }
}, error => { }, error => {

10
frontend/app/shared/services/assets.service.ts

@ -34,6 +34,16 @@ export class AssetDto {
public readonly canUpload: boolean; public readonly canUpload: boolean;
public readonly canMove: boolean; public readonly canMove: boolean;
public get fileNameWithoutExtension() {
const index = this.fileName.lastIndexOf('.');
if (index > 0) {
return this.fileName.substr(0, index);
} else {
return this.fileName;
}
}
public get isDuplicate() { public get isDuplicate() {
return this._meta && this._meta['isDuplicate'] === 'true'; return this._meta && this._meta['isDuplicate'] === 'true';
} }

4
frontend/app/shared/state/assets.forms.spec.ts

@ -31,7 +31,7 @@ describe('AnnotateAssetForm', () => {
form = new AnnotateAssetForm(new FormBuilder()); form = new AnnotateAssetForm(new FormBuilder());
}); });
it('shoulde remov extension when loading asset file name', () => { it('shoulde remove extension when loading asset file name', () => {
form.load(asset); form.load(asset);
const slug = form.form.get('fileName')!.value; const slug = form.form.get('fileName')!.value;
@ -41,6 +41,7 @@ describe('AnnotateAssetForm', () => {
it('should create slug from file name', () => { it('should create slug from file name', () => {
form.load(asset); form.load(asset);
form.generateSlug({} as any); form.generateSlug({} as any);
const slug = form.form.get('slug')!.value; const slug = form.form.get('slug')!.value;
@ -50,6 +51,7 @@ describe('AnnotateAssetForm', () => {
it('should create slug from file name and append extension', () => { it('should create slug from file name and append extension', () => {
form.form.get('fileName')!.setValue('My New File'); form.form.get('fileName')!.setValue('My New File');
form.generateSlug(asset); form.generateSlug(asset);
const slug = form.form.get('slug')!.value; const slug = form.form.get('slug')!.value;

Loading…
Cancel
Save