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="Datadog.Trace" Version="1.19.2" />
<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.OData.Core" Version="7.7.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />

82
backend/i18n/frontend_nl.json

@ -58,8 +58,8 @@
"assets.deleteFolderConfirmTitle": "Map verwijderen",
"assets.deleteMetadataConfirmText": "Wil je deze metadata echt 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.deleteReferrerConfirmTitle": "Delete asset",
"assets.deleteReferrerConfirmText": "Er wordt naar het item verwezen door een contentitem.\n\nWil je het item echt verwijderen?",
"assets.deleteReferrerConfirmTitle": "Verwijder bestand",
"assets.downloadVersion": "Download deze versie",
"assets.dropToUpdate": "Zet neer om te updaten",
"assets.duplicateFile": "Asset is al geüpload.",
@ -277,18 +277,18 @@
"common.preview": "Preview",
"common.product": "Squidex Headless CMS",
"common.project": "Project",
"common.queryOperators.contains": "contains",
"common.queryOperators.empty": "is empty",
"common.queryOperators.endsWith": "ends with",
"common.queryOperators.eq": "is equals to",
"common.queryOperators.ge": "is greater than or equals to",
"common.queryOperators.gt": "is greater than",
"common.queryOperators.le": "is less than pr equals to",
"common.queryOperators.lt": "is less than",
"common.queryOperators.ne": "is not equals to",
"common.queryOperators.startsWith": "starts with",
"common.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.remember": "Remember my decision",
"common.remember": "Onthoud mijn keuze",
"common.rename": "Hernoemen",
"common.requiredHint": "verplicht",
"common.reset": "Reset",
@ -304,7 +304,7 @@
"common.searchResults": "Zoekresultaten",
"common.separateByLine": "Scheiden op regel",
"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.slug": "Slug",
"common.stars.max": "Mag niet meer dan 15 sterren hebben",
@ -357,15 +357,15 @@
"contents.deleteConfirmTitle": "Inhoud verwijderen",
"contents.deleteFailed": "Verwijderen van inhoud is mislukt. Laad opnieuw.",
"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.deleteReferrerConfirmTitle": "Delete content",
"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.deleteVersionFailed": "Verwijderen van versie is mislukt. Laad opnieuw.",
"contents.draftNew": "Nieuw concept",
"contents.draftStatus": "Nieuwe versie",
"contents.editPageTitle": "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.languageModeSingle": "Enkele taal",
"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.loadFailed": "Laden van inhoud is mislukt. Laad opnieuw.",
"contents.loadVersionFailed": "Versie van een nieuwe versie is mislukt. Laad opnieuw.",
"contents.localizedFieldDescription": "The '{fieldName}' field of the content item (localized).",
"contents.localizedFieldDescription": "Het veld '{fieldName}' van het inhoudsitem (gelokaliseerd).",
"contents.newStatusFieldDescription": "De nieuwe status van het item.",
"contents.noReference": "- Geen referentie -",
"contents.pendingChangesTextToChange": "Je hebt niet-opgeslagen wijzigingen. \n \n Wanneer je de status wijzigt, raak je ze kwijt. \n \n **Wil je toch doorgaan?**",
@ -403,16 +403,16 @@
"contents.statusQueries": "Statusquery's",
"contents.stockPhotoEmpty": "Niets geselecteerd",
"contents.stockPhotoSearch": "Zoeken naar foto's op Unsplash",
"contents.tableHeaders.created": "Created",
"contents.tableHeaders.createdBy": "Created By",
"contents.tableHeaders.createdByShort": "By",
"contents.tableHeaders.created": "Gemaakt",
"contents.tableHeaders.createdBy": "Gemaakt door",
"contents.tableHeaders.createdByShort": "Door",
"contents.tableHeaders.id": "Id",
"contents.tableHeaders.lastModified": "Updated",
"contents.tableHeaders.lastModifiedBy": "Updated By",
"contents.tableHeaders.lastModifiedByShort": "By",
"contents.tableHeaders.nextStatus": "Next Status",
"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": "Version",
"contents.tableHeaders.version": "Versie",
"contents.unsavedChangesText": "Je hebt niet-opgeslagen wijzigingen. Wil je ze nu laden?",
"contents.unsavedChangesTitle": "Niet-opgeslagen wijzigingen",
"contents.updated": "Inhoud succesvol bijgewerkt.",
@ -487,7 +487,7 @@
"dashboard.trafficSummaryCard": "API Verkeer Samenvatting",
"dashboard.welcomeText": "Welkom bij **{app}** dashboard.",
"dashboard.welcomeTitle": "Hallo {user}",
"eventConsumers.count": "Count",
"eventConsumers.count": "Tellen",
"eventConsumers.loadFailed": "Kan gebeurtenisgebruikers niet laden. Laad opnieuw.",
"eventConsumers.pageTitle": "Evenementconsumenten",
"eventConsumers.position": "Positie",
@ -556,16 +556,16 @@
"roles.deleteConfirmTitle": "Wil je de rol echt verwijderen?",
"roles.loadFailed": "Laden van rollen is mislukt. Laad opnieuw.",
"roles.loadPermissionsFailed": "Kan machtigingen niet laden. Laad opnieuw.",
"roles.permissions": "Permissions",
"roles.permissionsDescription": "Permissions restrict the allowed operations and queries at API level and are a security feature.",
"roles.permissionsPlaceholder": "Start typing to search for permissions",
"roles.properties": "Properties",
"roles.properties.hideAPI": "Hide API",
"roles.properties.hideAssets": "Hide Assets",
"roles.properties.hideContents": "Hide {schema} Contents",
"roles.properties.hideSchemas": "Hide Schemas",
"roles.properties.hideSettings": "Hide Settings",
"roles.propertiesDescription": "Properties describe the behavior of the Management UI, but do not provide security for the API.",
"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.reloaded": "Rollen opnieuw geladen.",
"roles.revokeFailed": "Kan rol niet intrekken. Laad opnieuw.",
@ -628,10 +628,10 @@
"schemas.addNestedField": "Voeg genest veld toe",
"schemas.changeCategoryFailed": "Kan categorie niet wijzigen. Laad opnieuw.",
"schemas.clone": "Clone Schema",
"schemas.contentSidebarUrl": "Content Sidebar Extension",
"schemas.contentSidebarUrlHint": "URL to the plugin for the sidebar in the details view.",
"schemas.contentsSidebarUrl": "Contents Sidebar Extension",
"schemas.contentsSidebarUrlHint": "URL to the plugin for the sidebar in the list view.",
"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.create": "Schema 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/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": [

8
backend/i18n/source/backend_nl.json

@ -1,7 +1,12 @@
{
"annotations_AbsoluteUrl": "Het veld {name|lower} moet een absolute URL zijn.",
"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_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.clients.idAlreadyExists": "Er bestaat al een client met dezelfde id.",
"apps.contributors.cannotChangeYourself": "Je kunt jouw eigen rol niet wijzigen.",
@ -28,6 +33,7 @@
"assets.folderNotFound": "Assetmap bestaat niet.",
"assets.folderRecursion": "Kan map niet toevoegen aan zijn eigen kind.",
"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.maxReached": "Je kunt niet meer dan {max} backups hebben.",
"backups.restoreRunning": "Er wordt al een herstelbewerking uitgevoerd.",
@ -131,6 +137,7 @@
"contents.invalidNumber": "Ongeldig json-type, verwacht aantal.",
"contents.invalidString": "Ongeldig json-type, verwachte string.",
"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.singletonNotCreatable": "Singleton-inhoud kan niet worden gemaakt.",
"contents.singletonNotDeletable": "Singleton-inhoud kan niet worden verwijderd.",
@ -142,6 +149,7 @@
"contents.validation.characterCount": "Moet exact {count} teken (s) bevatten.",
"contents.validation.charactersBetween": "Moet tussen {min} en {max} teken (s) 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.extension": "Moet een toegestane extensie zijn.",
"contents.validation.image": "Geen afbeelding.",

4
backend/i18n/source/frontend__ignore.json

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

43
backend/i18n/source/frontend_nl.json

@ -58,6 +58,8 @@
"assets.deleteFolderConfirmTitle": "Map verwijderen",
"assets.deleteMetadataConfirmText": "Wil je deze metadata echt 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.dropToUpdate": "Zet neer om te updaten",
"assets.duplicateFile": "Asset is al geüpload.",
@ -275,7 +277,18 @@
"common.preview": "Preview",
"common.product": "Squidex Headless CMS",
"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.remember": "Onthoud mijn keuze",
"common.rename": "Hernoemen",
"common.requiredHint": "verplicht",
"common.reset": "Reset",
@ -291,6 +304,7 @@
"common.searchResults": "Zoekresultaten",
"common.separateByLine": "Scheiden op regel",
"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.slug": "Slug",
"common.stars.max": "Mag niet meer dan 15 sterren hebben",
@ -343,12 +357,15 @@
"contents.deleteConfirmTitle": "Inhoud verwijderen",
"contents.deleteFailed": "Verwijderen van inhoud is mislukt. Laad opnieuw.",
"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.deleteVersionFailed": "Verwijderen van versie is mislukt. Laad opnieuw.",
"contents.draftNew": "Nieuw concept",
"contents.draftStatus": "Nieuwe versie",
"contents.editPageTitle": "Inhoud bewerken",
"contents.editTitle": "Inhoud bewerken",
"contents.invariantFieldDescription": "Het veld '{fieldName}' van het inhoudsitem.",
"contents.languageModeAll": "Alle talen",
"contents.languageModeSingle": "Enkele taal",
"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.loadFailed": "Laden van inhoud 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.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?**",
@ -385,6 +403,16 @@
"contents.statusQueries": "Statusquery's",
"contents.stockPhotoEmpty": "Niets geselecteerd",
"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.unsavedChangesTitle": "Niet-opgeslagen wijzigingen",
"contents.updated": "Inhoud succesvol bijgewerkt.",
@ -459,6 +487,7 @@
"dashboard.trafficSummaryCard": "API Verkeer Samenvatting",
"dashboard.welcomeText": "Welkom bij **{app}** dashboard.",
"dashboard.welcomeTitle": "Hallo {user}",
"eventConsumers.count": "Tellen",
"eventConsumers.loadFailed": "Kan gebeurtenisgebruikers niet laden. Laad opnieuw.",
"eventConsumers.pageTitle": "Evenementconsumenten",
"eventConsumers.position": "Positie",
@ -527,6 +556,16 @@
"roles.deleteConfirmTitle": "Wil je de rol echt verwijderen?",
"roles.loadFailed": "Laden van rollen is mislukt. 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.reloaded": "Rollen opnieuw geladen.",
"roles.revokeFailed": "Kan rol niet intrekken. Laad opnieuw.",
@ -589,6 +628,10 @@
"schemas.addNestedField": "Voeg genest veld toe",
"schemas.changeCategoryFailed": "Kan categorie niet wijzigen. Laad opnieuw.",
"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.create": "Schema 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
{
private static readonly List<IEnrichedRuleEntity> EmptyResults = new List<IEnrichedRuleEntity>();
private readonly IRulesIndex rulesIndex;
private readonly IRuleEnricher ruleEnricher;
@ -30,9 +31,16 @@ namespace Squidex.Domain.Apps.Entities.Rules.Queries
{
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>
</data>
<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 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 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 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 name="annotations_RegularExpression" xml:space="preserve">
<value>The field {name|lower} is not.</value>
<value>Het veld {name|lower} is niet.</value>
</data>
<data name="dotnet_annotations_RegularExpression" xml:space="preserve">
<value>The field {0} is not.</value>
<value>Het veld {0} is niet.</value>
</data>
<data name="annotations_Required" xml:space="preserve">
<value>Het veld {name|lower} is verplicht.</value>
@ -95,16 +95,16 @@
<value>Het veld {0} is verplicht.</value>
</data>
<data name="annotations_StringLength" xml:space="preserve">
<value>The field {name|lower} must be a string with a maximum length of {max}.</value>
<value>Het veld {name|lower} moet een string zijn met een maximale lengte van {max}.</value>
</data>
<data name="dotnet_annotations_StringLength" xml:space="preserve">
<value>The field {0} must be a string with a maximum length of {1}.</value>
<value>Het veld {0} moet een string zijn met een maximale lengte van {1}.</value>
</data>
<data name="annotations_StringLengthMinimum" xml:space="preserve">
<value>The field {name|lower} must be a string with a minimum length of {min} and a maximum length of {max}.</value>
<value>Het veld {name|lower} moet een string zijn met een minimum lengte van {min} en een maximum lengte van {max}.</value>
</data>
<data name="dotnet_annotations_StringLengthMinimum" xml:space="preserve">
<value>The field {0} must be a string with a minimum length of {1} and a maximum length of {2}.</value>
<value>Het veld {0} moet een string zijn met een minimum lengte van {1} en een maximum lengte van {2}.</value>
</data>
<data name="apps.alreadyArchieved" xml:space="preserve">
<value>App is al gearchiveerd.</value>
@ -185,7 +185,7 @@
<value>Je hebt jouw maximale assetgrootte bereikt.</value>
</data>
<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 name="backups.alreadyRunning" xml:space="preserve">
<value>Er wordt al een ander back-upproces uitgevoerd.</value>
@ -497,7 +497,7 @@
<value>{count} referentie (s)</value>
</data>
<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 name="contents.singletonNotChangeable" xml:space="preserve">
<value>Singleton-inhoud kan niet worden bijgewerkt.</value>
@ -533,7 +533,7 @@
<value>Mag geen dubbele waarden bevatten.</value>
</data>
<data name="contents.validation.error" xml:space="preserve">
<value>Validation failed with internal error.</value>
<value>Validatie mislukt met interne fout.</value>
</data>
<data name="contents.validation.exactValue" xml:space="preserve">
<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]
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);
@ -60,16 +60,16 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes
}
[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())
.Returns(new List<DomainId> { rule.Id });
var actual = await sut.GetRulesAsync(appId.Id);
Assert.Empty(actual);
Assert.Same(actual[0], rule);
}
[Fact]
@ -117,11 +117,11 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes
.MustHaveHappened();
}
private IRuleEntity SetupRule(long version)
private IRuleEntity SetupRule(long version, bool isDeleted = false)
{
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>();
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);
}
[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]
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);
@ -154,16 +154,16 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
}
[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))
.Returns(schema.Id);
A.CallTo(() => index.GetIdsAsync())
.Returns(new List<Guid> { schema.Id });
var actual = await sut.GetSchemasAsync(appId.Id);
Assert.Empty(actual);
Assert.Same(actual[0], schema);
}
[Fact]
@ -275,7 +275,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
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>();
@ -287,6 +287,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes
.Returns(appId);
A.CallTo(() => schemaEntity.Version)
.Returns(version);
A.CallTo(() => schemaEntity.IsDeleted)
.Returns(isDeleted);
var schemaGrain = A.Fake<ISchemaGrain>();

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

@ -1,5 +1,10 @@
<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="file-preview" *ngIf="asset && progress === 0">
<span class="file-type" *ngIf="asset.fileType">
@ -93,7 +98,11 @@
</ng-container>
<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>
<ng-container *ngIf="asset && progress === 0">
@ -166,7 +175,8 @@
<ng-container *ngIf="asset">
<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>
</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 = '';
for (const asset of assets) {
const name = asset.fileNameWithoutExtension;
switch (asset.type) {
case 'Image':
content += `![${asset.fileName}](${asset.fullUrl(this.apiUrl)} '${asset.fileName}')`;
content += `![${name}](${asset.fullUrl(this.apiUrl)} '${name}')`;
break;
case 'Video':
content += `[${asset.fileName}](${asset.fullUrl(this.apiUrl)}')`;
content += `[${name}](${asset.fullUrl(this.apiUrl)}')`;
break;
default:
content += `[${asset.fileName}](${asset.fullUrl(this.apiUrl)}')`;
content += `[${name}](${asset.fullUrl(this.apiUrl)}')`;
break;
}
}
@ -265,7 +267,9 @@ export class MarkdownEditorComponent extends StatefulControlComponent<State, str
this.assetUploader.uploadFile(file)
.subscribe(asset => {
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 => {
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 = '';
for (const asset of assets) {
const name = asset.fileNameWithoutExtension;
switch (asset.type) {
case 'Image':
content += `<img src="${asset.fullUrl(this.apiUrl)}" alt="${asset.fileName}" />`;
content += `<img src="${asset.fullUrl(this.apiUrl)}" alt="${name}" />`;
break;
case 'Video':
content += `<video src="${asset.fullUrl(this.apiUrl)}" />`;
break;
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;
}
}
@ -261,10 +263,12 @@ export class RichEditorComponent extends StatefulControlComponent<undefined, str
this.assetUploader.uploadFile(file)
.subscribe(asset => {
if (Types.is(asset, AssetDto)) {
const name = asset.fileNameWithoutExtension;
if (asset.type === 'Video') {
replaceText(`<video src="${asset.fullUrl(this.apiUrl)}" />`);
} else {
replaceText(`<img src="${asset.fullUrl(this.apiUrl)}" alt="${asset.fileName}" />`);
replaceText(`<img src="${asset.fullUrl(this.apiUrl)}" alt="${name}" />`);
}
}
}, error => {

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

@ -34,6 +34,16 @@ export class AssetDto {
public readonly canUpload: 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() {
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());
});
it('shoulde remov extension when loading asset file name', () => {
it('shoulde remove extension when loading asset file name', () => {
form.load(asset);
const slug = form.form.get('fileName')!.value;
@ -41,6 +41,7 @@ describe('AnnotateAssetForm', () => {
it('should create slug from file name', () => {
form.load(asset);
form.generateSlug({} as any);
const slug = form.form.get('slug')!.value;
@ -50,6 +51,7 @@ describe('AnnotateAssetForm', () => {
it('should create slug from file name and append extension', () => {
form.form.get('fileName')!.setValue('My New File');
form.generateSlug(asset);
const slug = form.form.get('slug')!.value;

Loading…
Cancel
Save