Browse Source

Resolve (#499)

* Fix the API docs.
* Improve resolving of assets.
pull/501/head
Sebastian Stehle 6 years ago
committed by GitHub
parent
commit
d77eeeec9c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs
  2. 8
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs
  3. 2
      backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaExtensions.cs
  4. 20
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs
  5. 11
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs
  6. 9
      backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs
  7. 25
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ResolveAssets.cs
  8. 4
      backend/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs
  9. 2
      backend/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs
  10. 2
      backend/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs
  11. 2
      backend/src/Squidex/Areas/Api/Controllers/Ping/PingController.cs
  12. 4
      backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs
  13. 13
      backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs
  14. 16
      backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs
  15. 19
      backend/src/Squidex/Areas/Api/Views/Shared/Docs.cshtml
  16. 19
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs
  17. 72
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs
  18. 0
      frontend/answer.md
  19. 4
      frontend/app-config/webpack.config.js
  20. 8
      frontend/app/features/schemas/pages/schema/fields/types/assets-ui.component.html
  21. 4
      frontend/app/features/schemas/pages/schema/fields/types/assets-ui.component.ts
  22. 2
      frontend/app/shared/services/schemas.types.ts
  23. 20
      frontend/app/shared/state/contents.forms.spec.ts
  24. 10
      frontend/app/shared/state/contents.forms.ts
  25. 192
      frontend/package-lock.json
  26. 46
      frontend/package.json
  27. 1
      frontend/tsconfig.json

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

@ -31,7 +31,7 @@ namespace Squidex.Extensions.Actions.Webhook
[DataType(DataType.Text)] [DataType(DataType.Text)]
public string SharedSecret { get; set; } public string SharedSecret { get; set; }
[Display(Name = "Payload", Description = "The optional custom request payload.")] [Display(Name = "Payload (Optional)", Description = "Leave it empty to use the full event as payload.")]
[DataType(DataType.MultilineText)] [DataType(DataType.MultilineText)]
[Formattable] [Formattable]
public string Payload { get; set; } public string Payload { get; set; }

8
backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs

@ -36,7 +36,13 @@ namespace Squidex.Domain.Apps.Core.Schemas
public bool AllowDuplicates { get; set; } public bool AllowDuplicates { get; set; }
public bool ResolveImage { get; set; } public bool ResolveFirst { get; set; }
public bool ResolveImage
{
get => ResolveFirst;
set => ResolveFirst = value;
}
public ReadOnlyCollection<string>? AllowedExtensions { get; set; } public ReadOnlyCollection<string>? AllowedExtensions { get; set; }

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

@ -108,7 +108,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
public static IEnumerable<IField<AssetsFieldProperties>> ResolvingAssets(this Schema schema) public static IEnumerable<IField<AssetsFieldProperties>> ResolvingAssets(this Schema schema)
{ {
return schema.Fields.OfType<IField<AssetsFieldProperties>>() return schema.Fields.OfType<IField<AssetsFieldProperties>>()
.Where(x => x.Properties.ResolveImage); .Where(x => x.Properties.ResolveFirst);
} }
} }
} }

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

@ -17,40 +17,40 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{ {
public static class ContentReferencesExtensions public static class ContentReferencesExtensions
{ {
public static HashSet<Guid> GetReferencedIds(this NamedContentData source, Schema schema) public static HashSet<Guid> GetReferencedIds(this NamedContentData source, Schema schema, int referencesPerField = int.MaxValue)
{ {
Guard.NotNull(schema); Guard.NotNull(schema);
var extractor = new ReferencesExtractor(new HashSet<Guid>()); var extractor = new ReferencesExtractor(new HashSet<Guid>(), referencesPerField);
AddReferencedIds(source, schema.Fields, extractor); AddReferencedIds(source, schema.Fields, extractor);
return extractor.Result; return extractor.Result;
} }
public static void AddReferencedIds(this NamedContentData source, Schema schema, HashSet<Guid> result) public static void AddReferencedIds(this NamedContentData source, Schema schema, HashSet<Guid> result, int referencesPerField = int.MaxValue)
{ {
Guard.NotNull(schema); Guard.NotNull(schema);
var extractor = new ReferencesExtractor(result); var extractor = new ReferencesExtractor(result, referencesPerField);
AddReferencedIds(source, schema.Fields, extractor); AddReferencedIds(source, schema.Fields, extractor);
} }
public static void AddReferencedIds(this NamedContentData source, IEnumerable<IField> fields, HashSet<Guid> result) public static void AddReferencedIds(this NamedContentData source, IEnumerable<IField> fields, HashSet<Guid> result, int referencesPerField = int.MaxValue)
{ {
Guard.NotNull(fields); Guard.NotNull(fields);
var extractor = new ReferencesExtractor(result); var extractor = new ReferencesExtractor(result, referencesPerField);
AddReferencedIds(source, fields, extractor); AddReferencedIds(source, fields, extractor);
} }
public static void AddReferencedIds(this NamedContentData source, IField field, HashSet<Guid> result) public static void AddReferencedIds(this NamedContentData source, IField field, HashSet<Guid> result, int referencesPerField = int.MaxValue)
{ {
Guard.NotNull(field); Guard.NotNull(field);
var extractor = new ReferencesExtractor(result); var extractor = new ReferencesExtractor(result, referencesPerField);
AddReferencedIds(source, field, extractor); AddReferencedIds(source, field, extractor);
} }
@ -76,13 +76,13 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
} }
} }
public static HashSet<Guid> GetReferencedIds(this IField field, IJsonValue? value) public static HashSet<Guid> GetReferencedIds(this IField field, IJsonValue? value, int referencesPerField = int.MaxValue)
{ {
var result = new HashSet<Guid>(); var result = new HashSet<Guid>();
if (value != null) if (value != null)
{ {
var extractor = new ReferencesExtractor(result); var extractor = new ReferencesExtractor(result, referencesPerField);
extractor.SetValue(value); extractor.SetValue(value);

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

@ -13,8 +13,10 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
{ {
public static class ReferencesExtensions public static class ReferencesExtensions
{ {
public static void AddIds(this IJsonValue? value, HashSet<Guid> result) public static void AddIds(this IJsonValue? value, HashSet<Guid> result, int take)
{ {
var added = 0;
if (value is JsonArray array) if (value is JsonArray array)
{ {
foreach (var id in array) foreach (var id in array)
@ -22,6 +24,13 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
if (id.Type == JsonValueType.String && Guid.TryParse(id.ToString(), out var guid)) if (id.Type == JsonValueType.String && Guid.TryParse(id.ToString(), out var guid))
{ {
result.Add(guid); result.Add(guid);
added++;
if (added >= take)
{
break;
}
} }
} }
} }

9
backend/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs

@ -17,6 +17,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
internal sealed class ReferencesExtractor : IFieldVisitor<None> internal sealed class ReferencesExtractor : IFieldVisitor<None>
{ {
private readonly HashSet<Guid> result; private readonly HashSet<Guid> result;
private readonly int take;
private IJsonValue? value; private IJsonValue? value;
public HashSet<Guid> Result public HashSet<Guid> Result
@ -24,11 +25,13 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
get { return result; } get { return result; }
} }
public ReferencesExtractor(HashSet<Guid> result) public ReferencesExtractor(HashSet<Guid> result, int take)
{ {
Guard.NotNull(result); Guard.NotNull(result);
this.result = result; this.result = result;
this.take = take;
} }
public void SetValue(IJsonValue? newValue) public void SetValue(IJsonValue? newValue)
@ -59,14 +62,14 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
public None Visit(IField<AssetsFieldProperties> field) public None Visit(IField<AssetsFieldProperties> field)
{ {
value.AddIds(result); value.AddIds(result, take);
return None.Value; return None.Value;
} }
public None Visit(IField<ReferencesFieldProperties> field) public None Visit(IField<ReferencesFieldProperties> field)
{ {
value.AddIds(result); value.AddIds(result, take);
return None.Value; return None.Value;
} }

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

@ -82,19 +82,30 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
{ {
foreach (var (partitionKey, partitionValue) in fieldData) foreach (var (partitionKey, partitionValue) in fieldData)
{ {
var referencedImage = var referencedAsset =
field.GetReferencedIds(partitionValue) field.GetReferencedIds(partitionValue)
.Select(x => assets[x]) .Select(x => assets[x])
.SelectMany(x => x) .SelectMany(x => x)
.FirstOrDefault(x => x.Type == AssetType.Image); .FirstOrDefault();
if (referencedImage != null) if (referencedAsset != null)
{ {
var url = urlGenerator.AssetContent(Guid.Parse(referencedImage.Id.ToString())); IJsonValue array;
requestCache.AddDependency(referencedImage.Id, referencedImage.Version); if (referencedAsset.Type == AssetType.Image)
{
var url = urlGenerator.AssetContent(Guid.Parse(referencedAsset.Id.ToString()));
fieldReference.AddJsonValue(partitionKey, JsonValue.Create(url)); array = JsonValue.Array(url, referencedAsset.FileName);
}
else
{
array = JsonValue.Array(referencedAsset.FileName);
}
requestCache.AddDependency(referencedAsset.Id, referencedAsset.Version);
fieldReference.AddJsonValue(partitionKey, array);
} }
} }
} }
@ -118,7 +129,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps
{ {
foreach (var content in contents) foreach (var content in contents)
{ {
content.Data.AddReferencedIds(schema.SchemaDef.ResolvingAssets(), ids); content.Data.AddReferencedIds(schema.SchemaDef.ResolvingAssets(), ids, 1);
} }
} }

4
backend/src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs

@ -82,7 +82,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
} }
/// <summary> /// <summary>
/// Update an existing app pattern. /// Update an app pattern.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="id">The id of the pattern to be updated.</param> /// <param name="id">The id of the pattern to be updated.</param>
@ -107,7 +107,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
} }
/// <summary> /// <summary>
/// Delete an existing app pattern. /// Delete an app pattern.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="id">The id of the pattern to be deleted.</param> /// <param name="id">The id of the pattern to be deleted.</param>

2
backend/src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs

@ -107,7 +107,7 @@ namespace Squidex.Areas.Api.Controllers.Apps
} }
/// <summary> /// <summary>
/// Update an existing app role. /// Update an app role.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="name">The name of the role to be updated.</param> /// <param name="name">The name of the role to be updated.</param>

2
backend/src/Squidex/Areas/Api/Controllers/History/HistoryController.cs

@ -31,7 +31,7 @@ namespace Squidex.Areas.Api.Controllers.History
} }
/// <summary> /// <summary>
/// Get the events from the history. /// Get historical events.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="channel">The name of the channel.</param> /// <param name="channel">The name of the channel.</param>

2
backend/src/Squidex/Areas/Api/Controllers/Ping/PingController.cs

@ -27,7 +27,7 @@ namespace Squidex.Areas.Api.Controllers.Ping
} }
/// <summary> /// <summary>
/// Get general info status of the API. /// Get API information.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// 200 => Infos returned. /// 200 => Infos returned.

4
backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs

@ -46,7 +46,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
} }
/// <summary> /// <summary>
/// Get the supported rule actions. /// Get supported rule actions.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// 200 => Rule actions returned. /// 200 => Rule actions returned.
@ -291,7 +291,7 @@ namespace Squidex.Areas.Api.Controllers.Rules
} }
/// <summary> /// <summary>
/// Cancels the event and retries. /// Cancels an event.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="id">The event to enqueue.</param> /// <param name="id">The event to enqueue.</param>

13
backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
@ -68,10 +69,20 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
/// </summary> /// </summary>
public bool MustBeImage { get; set; } public bool MustBeImage { get; set; }
/// <summary>
/// True to resolve first asset in the content list.
/// </summary>
public bool ResolveFirst { get; set; }
/// <summary> /// <summary>
/// True to resolve first image in the content list. /// True to resolve first image in the content list.
/// </summary> /// </summary>
public bool ResolveImage { get; set; } [Obsolete("Use ResolveFirst now")]
public bool ResolveImage
{
get => ResolveFirst;
set => ResolveFirst = value;
}
/// <summary> /// <summary>
/// The allowed file extensions. /// The allowed file extensions.

16
backend/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs

@ -54,7 +54,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
} }
/// <summary> /// <summary>
/// Add a nested schema field. /// Add a nested field.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param> /// <param name="name">The name of the schema.</param>
@ -183,7 +183,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
} }
/// <summary> /// <summary>
/// Update a nested schema field. /// Update a nested field.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param> /// <param name="name">The name of the schema.</param>
@ -237,7 +237,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
} }
/// <summary> /// <summary>
/// Lock a nested schema field. /// Lock a nested field.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param> /// <param name="name">The name of the schema.</param>
@ -292,7 +292,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
} }
/// <summary> /// <summary>
/// Hide a nested schema field. /// Hide a nested field.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param> /// <param name="name">The name of the schema.</param>
@ -347,7 +347,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
} }
/// <summary> /// <summary>
/// Show a nested schema field. /// Show a nested field.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param> /// <param name="name">The name of the schema.</param>
@ -402,7 +402,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
} }
/// <summary> /// <summary>
/// Enable a nested schema field. /// Enable a nested field.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param> /// <param name="name">The name of the schema.</param>
@ -457,7 +457,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
} }
/// <summary> /// <summary>
/// Disable nested a schema field. /// Disable a nested field.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param> /// <param name="name">The name of the schema.</param>
@ -510,7 +510,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
} }
/// <summary> /// <summary>
/// Delete a nested schema field. /// Delete a nested field.
/// </summary> /// </summary>
/// <param name="app">The name of the app.</param> /// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param> /// <param name="name">The name of the schema.</param>

19
backend/src/Squidex/Areas/Api/Views/Shared/Docs.cshtml

@ -14,12 +14,26 @@
<style> <style>
body { margin: 0; padding: 0; } body { margin: 0; padding: 0; }
.menu-content {
position: fixed !important;
}
.api-content {
margin-left: 260px;
}
@@media print, screen and (max-width: 50rem) {
.api-content {
margin-left: 0;
}
}
</style> </style>
</head> </head>
<body> <body>
<div id="redoc-container"></div> <div id="redoc-container"></div>
<script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.22/bundles/redoc.standalone.js"></script> <script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.23/bundles/redoc.standalone.js"></script>
<script> <script>
Redoc.init('@Url.Content(Model.Specification)', { Redoc.init('@Url.Content(Model.Specification)', {
theme: { theme: {
@ -28,7 +42,8 @@
main: '#3f83df' main: '#3f83df'
} }
} }
} },
nativeScrollbars: true
}, document.getElementById('redoc-container')) }, document.getElementById('redoc-container'))
</script> </script>
</body> </body>

19
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs

@ -53,6 +53,25 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds
Assert.Equal(new[] { id1, id2 }, ids); Assert.Equal(new[] { id1, id2 }, ids);
} }
[Fact]
public void Should_get_limited_ids_from_name_data()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var input =
new NamedContentData()
.AddField("assets",
new ContentFieldData()
.AddJsonValue(JsonValue.Array(id1.ToString(), id2.ToString())));
var ids = new HashSet<Guid>();
input.AddReferencedIds(schema, ids, 1);
Assert.Equal(new[] { id1 }, ids);
}
[Fact] [Fact]
public void Should_cleanup_deleted_ids() public void Should_cleanup_deleted_ids()
{ {

72
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs

@ -43,13 +43,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
new Schema(schemaId.Name) new Schema(schemaId.Name)
.AddAssets(1, "asset1", Partitioning.Invariant, new AssetsFieldProperties .AddAssets(1, "asset1", Partitioning.Invariant, new AssetsFieldProperties
{ {
ResolveImage = true, ResolveFirst = true,
MinItems = 2, MinItems = 2,
MaxItems = 3 MaxItems = 3
}) })
.AddAssets(2, "asset2", Partitioning.Language, new AssetsFieldProperties .AddAssets(2, "asset2", Partitioning.Language, new AssetsFieldProperties
{ {
ResolveImage = true, ResolveFirst = true,
MinItems = 1, MinItems = 1,
MaxItems = 1 MaxItems = 1
}) })
@ -76,51 +76,48 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
[Fact] [Fact]
public async Task Should_add_assets_id_and_versions_as_dependency() public async Task Should_add_assets_id_and_versions_as_dependency()
{ {
var image1 = CreateAsset(Guid.NewGuid(), 1, AssetType.Image); var document1 = CreateAsset(Guid.NewGuid(), 3, AssetType.Unknown, "Document1.docx");
var image2 = CreateAsset(Guid.NewGuid(), 2, AssetType.Image); var document2 = CreateAsset(Guid.NewGuid(), 4, AssetType.Unknown, "Document2.docx");
var document1 = CreateAsset(Guid.NewGuid(), 3, AssetType.Unknown);
var document2 = CreateAsset(Guid.NewGuid(), 4, AssetType.Unknown);
var contents = new[] var contents = new[]
{ {
CreateContent( CreateContent(
new[] { document1.Id, image1.Id }, new[] { document1.Id },
new[] { document1.Id }), new[] { document1.Id }),
CreateContent( CreateContent(
new[] { document1.Id }, new[] { document2.Id },
new[] { document2.Id, image2.Id }) new[] { document2.Id }),
}; };
A.CallTo(() => assetQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichAsset()), null, A<Q>.That.Matches(x => x.Ids.Count == 4))) A.CallTo(() => assetQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichAsset()), null, A<Q>.That.Matches(x => x.Ids.Count == 2)))
.Returns(ResultList.CreateFrom(4, image1, image2, document1, document2)); .Returns(ResultList.CreateFrom(4, document1, document2));
await sut.EnrichAsync(requestContext, contents, schemaProvider); await sut.EnrichAsync(requestContext, contents, schemaProvider);
A.CallTo(() => requestCache.AddDependency(image1.Id, image1.Version)) A.CallTo(() => requestCache.AddDependency(document1.Id, document1.Version))
.MustHaveHappened(); .MustHaveHappened();
A.CallTo(() => requestCache.AddDependency(image2.Id, image2.Version)) A.CallTo(() => requestCache.AddDependency(document2.Id, document2.Version))
.MustHaveHappened(); .MustHaveHappened();
} }
[Fact] [Fact]
public async Task Should_enrich_with_asset_urls() public async Task Should_enrich_with_asset_urls()
{ {
var image1 = CreateAsset(Guid.NewGuid(), 1, AssetType.Image); var image1 = CreateAsset(Guid.NewGuid(), 1, AssetType.Image, "Image1.png");
var image2 = CreateAsset(Guid.NewGuid(), 2, AssetType.Image); var image2 = CreateAsset(Guid.NewGuid(), 2, AssetType.Image, "Image2.png");
var document1 = CreateAsset(Guid.NewGuid(), 3, AssetType.Unknown); var document1 = CreateAsset(Guid.NewGuid(), 3, AssetType.Unknown, "Document1.png");
var document2 = CreateAsset(Guid.NewGuid(), 4, AssetType.Unknown); var document2 = CreateAsset(Guid.NewGuid(), 4, AssetType.Unknown, "Document2.png");
var contents = new[] var contents = new[]
{ {
CreateContent( CreateContent(
new[] { document1.Id, image1.Id }, new[] { image1.Id },
new[] { document1.Id }), new[] { image2.Id, image1.Id }),
CreateContent( CreateContent(
new[] { document1.Id }, new[] { document1.Id },
new[] { document2.Id, image2.Id }) new[] { document2.Id, document1.Id })
}; };
A.CallTo(() => assetQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichAsset()), null, A<Q>.That.Matches(x => x.Ids.Count == 4))) A.CallTo(() => assetQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichAsset()), null, A<Q>.That.Matches(x => x.Ids.Count == 4)))
@ -132,18 +129,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
new NamedContentData() new NamedContentData()
.AddField("asset1", .AddField("asset1",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", $"url/to/{image1.Id}")) .AddValue("iv", JsonValue.Array($"url/to/{image1.Id}", image1.FileName)))
.AddField("asset2", .AddField("asset2",
new ContentFieldData()), new ContentFieldData()
.AddValue("en", JsonValue.Array($"url/to/{image2.Id}", image2.FileName))),
contents[0].ReferenceData); contents[0].ReferenceData);
Assert.Equal( Assert.Equal(
new NamedContentData() new NamedContentData()
.AddField("asset1", .AddField("asset1",
new ContentFieldData()) new ContentFieldData()
.AddValue("iv", JsonValue.Array(document1.FileName)))
.AddField("asset2", .AddField("asset2",
new ContentFieldData() new ContentFieldData()
.AddValue("en", $"url/to/{image2.Id}")), .AddValue("en", JsonValue.Array(document2.FileName))),
contents[1].ReferenceData); contents[1].ReferenceData);
} }
@ -199,6 +198,25 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
.MustNotHaveHappened(); .MustNotHaveHappened();
} }
[Fact]
public async Task Should_only_query_first_assets()
{
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var contents = new[]
{
CreateContent(new[] { id1, id2 }, new Guid[0])
};
await sut.EnrichAsync(requestContext, contents, schemaProvider);
Assert.NotNull(contents[0].ReferenceData);
A.CallTo(() => assetQuery.QueryAsync(A<Context>.That.Matches(x => !x.ShouldEnrichAsset()), null, A<Q>.That.Matches(x => x.Ids.Count == 1)))
.MustHaveHappened();
}
private ContentEntity CreateContent(Guid[] assets1, Guid[] assets2) private ContentEntity CreateContent(Guid[] assets1, Guid[] assets2)
{ {
return new ContentEntity return new ContentEntity
@ -215,9 +233,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries
}; };
} }
private static IEnrichedAssetEntity CreateAsset(Guid id, int version, AssetType type) private static IEnrichedAssetEntity CreateAsset(Guid id, int version, AssetType type, string fileName)
{ {
return new AssetEntity { Id = id, Type = type, Version = version }; return new AssetEntity { Id = id, Type = type, Version = version, FileName = fileName };
} }
} }
} }

0
frontend/answer.md

4
frontend/app-config/webpack.config.js

@ -40,7 +40,7 @@ module.exports = function (env) {
const isTests = env && env.target === 'tests'; const isTests = env && env.target === 'tests';
const isTestCoverage = env && env.coverage; const isTestCoverage = env && env.coverage;
const isAnalyzing = isProduction && env.analyze; const isAnalyzing = isProduction && env.analyze;
const isAot = isProduction; const isAot = !isDevServer;
const configFile = isTests ? 'tsconfig.spec.json' : 'tsconfig.app.json'; const configFile = isTests ? 'tsconfig.spec.json' : 'tsconfig.app.json';
@ -296,8 +296,8 @@ module.exports = function (env) {
new plugins.NgToolsWebpack.AngularCompilerPlugin({ new plugins.NgToolsWebpack.AngularCompilerPlugin({
directTemplateLoading: true, directTemplateLoading: true,
entryModule: 'app/app.module#AppModule', entryModule: 'app/app.module#AppModule',
sourceMap: !isProduction,
skipCodeGeneration: !isAot, skipCodeGeneration: !isAot,
sourceMap: !isProduction,
tsConfigPath: configFile tsConfigPath: configFile
}) })
); );

8
frontend/app/features/schemas/pages/schema/fields/types/assets-ui.component.html

@ -3,14 +3,14 @@
<div class="form-group row"> <div class="form-group row">
<div class="col-9 offset-3"> <div class="col-9 offset-3">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="{{field.fieldId}}_fieldResolveImage" formControlName="resolveImage" /> <input class="form-check-input" type="checkbox" id="{{field.fieldId}}_fieldResolveFirst" formControlName="resolveFirst" />
<label class="form-check-label" for="{{field.fieldId}}_fieldResolveImage"> <label class="form-check-label" for="{{field.fieldId}}_fieldResolveFirst">
Resolve image Resolve first asset
</label> </label>
</div> </div>
<sqx-form-hint> <sqx-form-hint>
Show the first referenced image in the content list. Show the first referenced asset in the content list.
</sqx-form-hint> </sqx-form-hint>
</div> </div>
</div> </div>

4
frontend/app/features/schemas/pages/schema/fields/types/assets-ui.component.ts

@ -26,7 +26,7 @@ export class AssetsUIComponent implements OnInit {
public properties: AssetsFieldPropertiesDto; public properties: AssetsFieldPropertiesDto;
public ngOnInit() { public ngOnInit() {
this.editForm.setControl('resolveImage', this.editForm.setControl('resolveFirst',
new FormControl(this.properties.resolveImage)); new FormControl(this.properties.resolveFirst));
} }
} }

2
frontend/app/shared/services/schemas.types.ts

@ -173,8 +173,8 @@ export class AssetsFieldPropertiesDto extends FieldPropertiesDto {
public readonly fieldType = 'Assets'; public readonly fieldType = 'Assets';
public readonly allowDuplicates?: boolean; public readonly allowDuplicates?: boolean;
public readonly resolveImage: boolean;
public readonly allowedExtensions?: ReadonlyArray<string>; public readonly allowedExtensions?: ReadonlyArray<string>;
public readonly resolveFirst: boolean;
public readonly aspectHeight?: number; public readonly aspectHeight?: number;
public readonly aspectWidth?: number; public readonly aspectWidth?: number;
public readonly maxHeight?: number; public readonly maxHeight?: number;

20
frontend/app/shared/state/contents.forms.spec.ts

@ -460,18 +460,32 @@ describe('GetContentValue', () => {
const fieldLocalized = createField({ properties: createProperties('Number') }); const fieldLocalized = createField({ properties: createProperties('Number') });
const fieldAssets = createField({ properties: createProperties('Assets') }); const fieldAssets = createField({ properties: createProperties('Assets') });
it('should resolve image url field from references value', () => { it('should resolve image url and filename from referenced asset', () => {
const content: any = { const content: any = {
referenceData: { referenceData: {
field1: { field1: {
en: '13' en: ['url/to/13', 'file13']
} }
} }
}; };
const result = getContentValue(content, language, fieldAssets); const result = getContentValue(content, language, fieldAssets);
expect(result).toEqual({ value: '13', formatted: new HtmlValue('<img src="13?width=50&height=50" />') }); expect(result).toEqual({ value: ['url/to/13', 'file13'], formatted: new HtmlValue('<img src="url/to/13?width=50&height=50" /> file13') });
});
it('should resolve filename from referenced asset', () => {
const content: any = {
referenceData: {
field1: {
en: ['file13']
}
}
};
const result = getContentValue(content, language, fieldAssets);
expect(result).toEqual({ value: ['file13'], formatted: 'file13' });
}); });
it('should not image url if not found', () => { it('should not image url if not found', () => {

10
frontend/app/shared/state/contents.forms.ts

@ -83,15 +83,19 @@ export function getContentValue(content: ContentDto, language: LanguageDto, fiel
if (Types.isObject(fieldValue)) { if (Types.isObject(fieldValue)) {
value = fieldValue[language.iso2Code]; value = fieldValue[language.iso2Code];
} else if (Types.isString(fieldValue)) { } else {
value = fieldValue; value = fieldValue;
} }
let formatted: FieldValue = value!; let formatted: FieldValue = value!;
if (value) { if (value) {
if (Types.isString(value) && isAssets) { if (isAssets && Types.isArray(value)) {
formatted = new HtmlValue(`<img src="${value}?width=50&height=50" />`); if (value.length === 2) {
formatted = new HtmlValue(`<img src="${value[0]}?width=50&height=50" /> ${value[1]}`);
} else if (value.length === 1) {
formatted = value[0];
}
} }
} else { } else {
value = formatted = '- No Value -'; value = formatted = '- No Value -';

192
frontend/package-lock.json

@ -5,9 +5,9 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@angular-devkit/build-optimizer": { "@angular-devkit/build-optimizer": {
"version": "0.900.3", "version": "0.900.5",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.900.3.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.900.5.tgz",
"integrity": "sha512-VLAWtAXpOzOoYUJrN6sT90UdIdvrVIipkzGz7nfI1kscDvxUFwVZnsNNHtFinaY2SfZAunHhYQOA/B9FJ8WPdQ==", "integrity": "sha512-BdmvD58DnAAf6/o/fRzU2l+2g4IwuIJf8x/rd9AGWd7fHrcwgJDhB9rYetB7JqYR8uOWk+AFElDpvNOj8YUy0w==",
"dev": true, "dev": true,
"requires": { "requires": {
"loader-utils": "1.2.3", "loader-utils": "1.2.3",
@ -49,6 +49,12 @@
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true "dev": true
}, },
"tslib": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
"dev": true
},
"typescript": { "typescript": {
"version": "3.6.4", "version": "3.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
@ -76,9 +82,9 @@
} }
}, },
"@angular-devkit/core": { "@angular-devkit/core": {
"version": "9.0.3", "version": "9.0.5",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-9.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-9.0.5.tgz",
"integrity": "sha512-3+abmv9K9d+BVgUAolYgoOqlGAA2Jb1pWo2biapSDG6KjUZHUCJdnsKigLtLorCdv0SrjTp56FFplkcqKsFQgA==", "integrity": "sha512-9TPQPzfSRbV5wVEnfo1d1CS+oVXROfE7VnBRuRMilFnNhuc29wX3zvBQRTreDVyxJetLBEb9sRlcKYGaJzpKPw==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "6.10.2", "ajv": "6.10.2",
@ -148,33 +154,33 @@
} }
}, },
"@angular/animations": { "@angular/animations": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-9.0.6.tgz",
"integrity": "sha512-WGs4Jxw5sr8GCpxMcwEVuZnDIkdNp9qtmuI2j13v/XAaMjvJ7jssCj9+JG5uI8joCi7PFVAWokPT1DdPwWb13Q==" "integrity": "sha512-LNtzUrrjqLTlZyhuAEV0sdEV0yi52Ih/p+ozCr/ivhTSSemcPbniTBbJlFZO4NJ2BuS2iEXkXwZs3mm8Fvx5Sg=="
}, },
"@angular/cdk": { "@angular/cdk": {
"version": "9.1.1", "version": "9.1.2",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.1.1.tgz", "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-9.1.2.tgz",
"integrity": "sha512-yzssAqbllGYgX+WeSYLjmEWtXVG5UPZwA0+dPlh+g85nG7b70DVRVYBi8PJySydsfPX/JMherFUU9h0QOWhhZw==", "integrity": "sha512-x5niyE0iYrbVtLYjJFw2MoS+OoSbJn6y/G2pNScviDwyjBBgqRh4YgUox2kMhdPumkvuh+eA6blZoE9qpvSo2w==",
"requires": { "requires": {
"parse5": "^5.0.0" "parse5": "^5.0.0"
} }
}, },
"@angular/common": { "@angular/common": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/common/-/common-9.0.6.tgz",
"integrity": "sha512-AwZKYK5M/3762woK+3290JnBdlBvZXqxX5vVze6wk23IiBlwIV+l79+Lyfjo/4s031kibq47taaZdC7qkkBkNA==" "integrity": "sha512-z+c+zmoZTOQ2fT2sFQpHhpUbIYtjerxYmdOVpukprZCuv9WT2SGJfu4QVGSkeqejYnMp6VtXMdQ1CeAQojj0sw=="
}, },
"@angular/compiler": { "@angular/compiler": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.6.tgz",
"integrity": "sha512-TeyhRGefTOtA9N3udMrvheafoXcz/dvTTdZLcieeZQxm1SSeaQDUQ/rUH6QTOiHVNMtjOCrZ9J5rk1A4mPYuag==", "integrity": "sha512-jGTGNs8l3zwTnVEQH2v3HwWVvpz0bQY7B6rPkfHNP2bVwrhz7L6fYyJY1HtWM0S95b09NuSwianhabnEzQeTfQ==",
"dev": true "dev": true
}, },
"@angular/compiler-cli": { "@angular/compiler-cli": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-9.0.6.tgz",
"integrity": "sha512-lFlasm8UBApTq4/MkxnYrRAMfpOvvg3YYBEMibuEGlaJjW/Xd1JcisUuFiooCxCIKF5phyORHmxjywGPhHqQgQ==", "integrity": "sha512-chzlImvinNigQ9JzehC7BRxct62OGkkru6jIMg3J2gr1r+sQlOn2ybvADloYkKnEP5hu2Izr2aSmEfMm4xobvg==",
"dev": true, "dev": true,
"requires": { "requires": {
"canonical-path": "1.0.0", "canonical-path": "1.0.0",
@ -200,38 +206,38 @@
} }
}, },
"@angular/core": { "@angular/core": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.6.tgz",
"integrity": "sha512-7VznrYjaAIzeq/zQ7v6tbeoOI7JJKgChKwG7s8jRoEpENu+w2pRlRdyQul88iJLsXgObR+/TfBNm/K+G4cqAFw==" "integrity": "sha512-egpVGqqI+L1QQFn9ziHIElXb0bCzY1l8vzyQGfm2KnxHpmx2TJp2uaaHh5LRcqYR7TLeGMpqmzhRxir6Up7AAQ=="
}, },
"@angular/forms": { "@angular/forms": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-9.0.6.tgz",
"integrity": "sha512-579PXAfT92J4mghjWKiZ3Zj3xee4h3RP70YHSlsfbi94MONvryWDrnXxvUZ0zJJCVnEJQ7x+nGEp3wwWqR12Jw==" "integrity": "sha512-mxUEqQny3scxQM/21QLKgtq5EcOm1Tn5cU3rStY1L8J6Mg+Rd2Rz4SY0WXQpaRKPj+WNd+PDgdGiRs3cAjfLFQ=="
}, },
"@angular/platform-browser": { "@angular/platform-browser": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.0.6.tgz",
"integrity": "sha512-24QGcQXthYXB/wT8okJjxqss/JOk4A6O1/Fmva79k0AvwtYkl2tikcyEc5T3xZtjoi8g32AN9nbZAobtkxlqTA==" "integrity": "sha512-CA7dW+j1mVh3OUo3C2vIn05NxNgrDPK4vpfRIwBIn1gErpnIXCa2vgnRzn3H9zKizKt0iuwSIukEnWG280Q0xg=="
}, },
"@angular/platform-browser-dynamic": { "@angular/platform-browser-dynamic": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.0.6.tgz",
"integrity": "sha512-NRfsAwbgxOvEcpqlERDAG0wap5xJa0wKwnudTCnyvf4B0D6kLkT1Idjqv22NDW5rfM2oDWaZ/qpgpDnAo6/ZBQ==" "integrity": "sha512-Z0/qHciqbR+c2fwGxrkr77tQkEKhZpAPljGva/VNoS3Ms1OikqZB9Ev7xmZOM9656khPBU38m3aLsTXAAnQ4YA=="
}, },
"@angular/platform-server": { "@angular/platform-server": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-9.0.6.tgz",
"integrity": "sha512-5iEugPj0oZgw6JHS5s8m4WejCnEoNeWgttzsCQuyCaVmIOQGCbTdqSsxD+AgBO7A5lrzxYQOgil/XCM/up5Smw==", "integrity": "sha512-v+as/mgdKTJXj/+FU7C57Jsc3P/yzJ64XPOu4N8DDh0TRZrEOunXSqH97Wyn9z7dIe4YdRR2MU9CZkiGvoeKnw==",
"requires": { "requires": {
"domino": "^2.1.2", "domino": "^2.1.2",
"xhr2": "^0.1.4" "xhr2": "^0.1.4"
} }
}, },
"@angular/router": { "@angular/router": {
"version": "9.0.5", "version": "9.0.6",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-9.0.5.tgz", "resolved": "https://registry.npmjs.org/@angular/router/-/router-9.0.6.tgz",
"integrity": "sha512-Sz3DQUxlzAk9aZ9eVtZRh6xF5SMg/Gb3rc5I7dL1M+mycSNoFJ4HPTXleZkKM69mMkKQ5fEtza4x26MSlF+O9w==" "integrity": "sha512-Ki1uk3jWPsoFh27SnyXatPSFK3ghF25pjiwWw9/inPvlS/HshSWgS2FbYf49LD5xVFF3Ni2Z5GRKxSEqxL8vQw=="
}, },
"@babel/code-frame": { "@babel/code-frame": {
"version": "7.8.3", "version": "7.8.3",
@ -409,12 +415,12 @@
"dev": true "dev": true
}, },
"@ngtools/webpack": { "@ngtools/webpack": {
"version": "9.0.3", "version": "9.0.5",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-9.0.3.tgz", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-9.0.5.tgz",
"integrity": "sha512-pMIXfq1IJLbvwmkPonGs7nrpuBCXrlZTf9A4OYsMBZcfU8JMn0pRdx7G2+bC9Q/f+uSw2uvPSv76xJXLBOntmA==", "integrity": "sha512-xe0rGpme04MNRK/PpPOx8cza9k8F/XuAOmxC3Tk4dIgigqIzYsP6v6N/At8vPRDrf88X4ZyR94lL5RrUYf/KNQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@angular-devkit/core": "9.0.3", "@angular-devkit/core": "9.0.5",
"enhanced-resolve": "4.1.1", "enhanced-resolve": "4.1.1",
"rxjs": "6.5.3", "rxjs": "6.5.3",
"webpack-sources": "1.4.3" "webpack-sources": "1.4.3"
@ -507,9 +513,9 @@
"dev": true "dev": true
}, },
"@types/marked": { "@types/marked": {
"version": "0.7.2", "version": "0.7.3",
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.7.2.tgz", "resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.7.3.tgz",
"integrity": "sha512-A3EDyNaq6OCcpaOia2HQ/tu2QYt8DKuj4ExP21VU3cU3HTo2FLslvbqa2T1vux910RHvuSVqpwKnnykSFcRWOA==", "integrity": "sha512-WXdEKuT3azHxLTThd5dwnpLt2Q9QiC8iKj09KZRtVqro3pX8hhY+GbD8FZOae6SBBEJ22yKJn3c7ejL0aucAcA==",
"dev": true "dev": true
}, },
"@types/mersenne-twister": { "@types/mersenne-twister": {
@ -810,9 +816,9 @@
} }
}, },
"acorn-walk": { "acorn-walk": {
"version": "6.2.0", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz",
"integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==",
"dev": true "dev": true
}, },
"after": { "after": {
@ -1760,14 +1766,14 @@
} }
}, },
"browserslist": { "browserslist": {
"version": "4.8.7", "version": "4.9.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.7.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz",
"integrity": "sha512-gFOnZNYBHrEyUML0xr5NJ6edFaaKbTFX9S9kQHlYfCP0Rit/boRIz4G+Avq6/4haEKJXdGGUnoolx+5MWW2BoA==", "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==",
"dev": true, "dev": true,
"requires": { "requires": {
"caniuse-lite": "^1.0.30001027", "caniuse-lite": "^1.0.30001030",
"electron-to-chromium": "^1.3.349", "electron-to-chromium": "^1.3.363",
"node-releases": "^1.1.49" "node-releases": "^1.1.50"
} }
}, },
"buffer": { "buffer": {
@ -2013,9 +2019,9 @@
} }
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001028", "version": "1.0.30001033",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001028.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001033.tgz",
"integrity": "sha512-Vnrq+XMSHpT7E+LWoIYhs3Sne8h9lx9YJV3acH3THNCwU/9zV93/ta4xVfzTtnqd3rvnuVpVjE3DFqf56tr3aQ==", "integrity": "sha512-8Ibzxee6ibc5q88cM1usPsMpJOG5CTq0s/dKOmlekPbDGKt+UrnOOTPSjQz3kVo6yL7N4SB5xd+FGLHQmbzh6A==",
"dev": true "dev": true
}, },
"canonical-path": { "canonical-path": {
@ -3561,9 +3567,9 @@
"dev": true "dev": true
}, },
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.3.355", "version": "1.3.375",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.355.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.375.tgz",
"integrity": "sha512-zKO/wS+2ChI/jz9WAo647xSW8t2RmgRLFdbUb/77cORkUTargO+SCj4ctTHjBn2VeNFrsLgDT7IuDVrd3F8mLQ==", "integrity": "sha512-zmaFnYVBtfpF8bGRYxgPeVAlXB7N3On8rjBE2ROc6wOpTPpzRWaiHo6KkbJMvlH07CH33uks/TEb6kuMMn8q6A==",
"dev": true "dev": true
}, },
"elliptic": { "elliptic": {
@ -7245,9 +7251,9 @@
} }
}, },
"marked": { "marked": {
"version": "0.7.0", "version": "0.8.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.0.tgz",
"integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" "integrity": "sha512-MyUe+T/Pw4TZufHkzAfDj6HarCBWia2y27/bhuYkTaiUnfDYFnCP3KUN+9oM7Wi6JA2rymtVYbQu3spE0GCmxQ=="
}, },
"md5.js": { "md5.js": {
"version": "1.3.5", "version": "1.3.5",
@ -7783,9 +7789,9 @@
} }
}, },
"node-releases": { "node-releases": {
"version": "1.1.49", "version": "1.1.51",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.49.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.51.tgz",
"integrity": "sha512-xH8t0LS0disN0mtRCh+eByxFPie+msJUBL/lJDBuap53QGiYPa9joh83K4pCZgWJ+2L4b9h88vCVdXQ60NO2bg==", "integrity": "sha512-1eQEs6HFYY1kMXQPOLzCf7HdjReErmvn85tZESMczdCNVWP3Y7URYLBAyYynuI7yef1zj4HN5q+oB2x67QU0lw==",
"dev": true, "dev": true,
"requires": { "requires": {
"semver": "^6.3.0" "semver": "^6.3.0"
@ -11171,9 +11177,9 @@
"dev": true "dev": true
}, },
"slugify": { "slugify": {
"version": "1.3.6", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.3.6.tgz", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.0.tgz",
"integrity": "sha512-wA9XS475ZmGNlEnYYLPReSfuz/c3VQsEMoU43mi6OnKMCdbnFXd4/Yg7J0lBv8jkPolacMpOrWEaoYxuE1+hoQ==" "integrity": "sha512-FtLNsMGBSRB/0JOE2A0fxlqjI6fJsgHGS13iTuVT28kViI4JjUiNqp/vyis0ZXYcMnpR3fzGNkv+6vRlI2GwdQ=="
}, },
"snapdragon": { "snapdragon": {
"version": "0.8.2", "version": "0.8.2",
@ -12469,9 +12475,9 @@
} }
}, },
"tslib": { "tslib": {
"version": "1.10.0", "version": "1.11.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="
}, },
"tslint": { "tslint": {
"version": "6.0.0", "version": "6.0.0",
@ -12605,9 +12611,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "3.7.5", "version": "3.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
"dev": true "dev": true
}, },
"uc.micro": { "uc.micro": {
@ -13682,9 +13688,9 @@
} }
}, },
"webpack": { "webpack": {
"version": "4.41.6", "version": "4.42.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.6.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz",
"integrity": "sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA==", "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.8.5",
@ -13713,15 +13719,15 @@
}, },
"dependencies": { "dependencies": {
"acorn": { "acorn": {
"version": "6.4.0", "version": "6.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
"integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
"dev": true "dev": true
}, },
"ajv": { "ajv": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
"integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
"dev": true, "dev": true,
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
@ -14024,13 +14030,13 @@
} }
}, },
"webpack-bundle-analyzer": { "webpack-bundle-analyzer": {
"version": "3.6.0", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.0.tgz", "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.1.tgz",
"integrity": "sha512-orUfvVYEfBMDXgEKAKVvab5iQ2wXneIEorGNsyuOyVYpjYrI7CUOhhXNDd3huMwQ3vNNWWlGP+hzflMFYNzi2g==", "integrity": "sha512-Nfd8HDwfSx1xBwC+P8QMGvHAOITxNBSvu/J/mCJvOwv+G4VWkU7zir9SSenTtyCi0LnVtmsc7G5SZo1uV+bxRw==",
"dev": true, "dev": true,
"requires": { "requires": {
"acorn": "^6.0.7", "acorn": "^7.1.1",
"acorn-walk": "^6.1.1", "acorn-walk": "^7.1.1",
"bfj": "^6.1.1", "bfj": "^6.1.1",
"chalk": "^2.4.1", "chalk": "^2.4.1",
"commander": "^2.18.0", "commander": "^2.18.0",
@ -14045,9 +14051,9 @@
}, },
"dependencies": { "dependencies": {
"acorn": { "acorn": {
"version": "6.4.0", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
"integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
"dev": true "dev": true
}, },
"commander": { "commander": {

46
frontend/package.json

@ -16,15 +16,15 @@
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points" "postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "9.0.5", "@angular/animations": "9.0.6",
"@angular/cdk": "9.1.1", "@angular/cdk": "9.1.2",
"@angular/common": "9.0.5", "@angular/common": "9.0.6",
"@angular/core": "9.0.5", "@angular/core": "9.0.6",
"@angular/forms": "9.0.5", "@angular/forms": "9.0.6",
"@angular/platform-browser": "9.0.5", "@angular/platform-browser": "9.0.6",
"@angular/platform-browser-dynamic": "9.0.5", "@angular/platform-browser-dynamic": "9.0.6",
"@angular/platform-server": "9.0.5", "@angular/platform-server": "9.0.6",
"@angular/router": "9.0.5", "@angular/router": "9.0.6",
"angular-mentions": "^1.1.3", "angular-mentions": "^1.1.3",
"angular2-chartjs": "0.5.1", "angular2-chartjs": "0.5.1",
"babel-polyfill": "6.26.0", "babel-polyfill": "6.26.0",
@ -34,7 +34,7 @@
"graphiql": "0.17.5", "graphiql": "0.17.5",
"graphql": "14.6.0", "graphql": "14.6.0",
"image-focus": "^1.1.0", "image-focus": "^1.1.0",
"marked": "0.7.0", "marked": "0.8.0",
"mersenne-twister": "1.1.0", "mersenne-twister": "1.1.0",
"moment": "2.24.0", "moment": "2.24.0",
"mousetrap": "1.6.5", "mousetrap": "1.6.5",
@ -45,26 +45,26 @@
"react": "16.10.2", "react": "16.10.2",
"react-dom": "16.10.2", "react-dom": "16.10.2",
"rxjs": "6.5.4", "rxjs": "6.5.4",
"slugify": "1.3.6", "slugify": "1.4.0",
"tslib": "1.10.0", "tslib": "1.11.1",
"zone.js": "0.10.2" "zone.js": "0.10.2"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-optimizer": "^0.900.3", "@angular-devkit/build-optimizer": "0.900.5",
"@angular/compiler": "9.0.5", "@angular/compiler": "9.0.6",
"@angular/compiler-cli": "9.0.5", "@angular/compiler-cli": "9.0.6",
"@ngtools/webpack": "9.0.3", "@ngtools/webpack": "9.0.5",
"@types/core-js": "2.5.3", "@types/core-js": "2.5.3",
"@types/jasmine": "3.5.5", "@types/jasmine": "3.5.9",
"@types/marked": "0.7.2", "@types/marked": "0.7.3",
"@types/mersenne-twister": "1.1.2", "@types/mersenne-twister": "1.1.2",
"@types/mousetrap": "1.6", "@types/mousetrap": "1.6",
"@types/node": "13.7.4", "@types/node": "13.7.4",
"@types/react": "16.9.21", "@types/react": "16.9.21",
"@types/react-dom": "16.9.5", "@types/react-dom": "16.9.5",
"@types/tinymce": "^4.5.24", "@types/tinymce": "^4.5.24",
"browserslist": "4.8.7", "browserslist": "4.9.1",
"caniuse-lite": "1.0.30001028", "caniuse-lite": "1.0.30001033",
"circular-dependency-plugin": "5.2.0", "circular-dependency-plugin": "5.2.0",
"codelyzer": "5.2.1", "codelyzer": "5.2.1",
"css-loader": "3.4.2", "css-loader": "3.4.2",
@ -108,10 +108,10 @@
"tslint-immutable": "6.0.1", "tslint-immutable": "6.0.1",
"tslint-webpack-plugin": "2.1.0", "tslint-webpack-plugin": "2.1.0",
"typemoq": "2.1.0", "typemoq": "2.1.0",
"typescript": "3.7.5", "typescript": "3.8.3",
"underscore": "1.9.2", "underscore": "1.9.2",
"webpack": "4.41.6", "webpack": "4.42.0",
"webpack-bundle-analyzer": "^3.6.0", "webpack-bundle-analyzer": "3.6.1",
"webpack-cli": "3.3.11", "webpack-cli": "3.3.11",
"webpack-dev-server": "3.10.3" "webpack-dev-server": "3.10.3"
} }

1
frontend/tsconfig.json

@ -28,6 +28,7 @@
}, },
}, },
"angularCompilerOptions": { "angularCompilerOptions": {
"disableTypeScriptVersionCheck": true,
"fullTemplateTypeCheck": true, "fullTemplateTypeCheck": true,
"strictInjectionParameters": true, "strictInjectionParameters": true,
"strictTemplate": true "strictTemplate": true

Loading…
Cancel
Save