Browse Source

Disable button for array editor when max item reached.

pull/581/head
Sebastian 5 years ago
parent
commit
a7d6654b9b
  1. 91
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Extensions/EventFluidExtensions.cs
  2. 21
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Extensions/EventJintExtension.cs
  3. 23
      backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/PredefinedPatternsFormatter.cs
  4. 2
      backend/src/Squidex.Domain.Apps.Core.Operations/IUrlGenerator.cs
  5. 5
      backend/src/Squidex.Web/Services/UrlGenerator.cs
  6. 76
      backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs
  7. 5
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/TestData/FakeUrlGenerator.cs
  8. 18
      frontend/app/features/content/shared/forms/array-editor.component.html
  9. 24
      frontend/app/features/content/shared/forms/array-editor.component.ts

91
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Extensions/EventFluidExtensions.cs

@ -11,6 +11,7 @@ using Fluid.Values;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Templates;
using Squidex.Infrastructure;
using Squidex.Text;
namespace Squidex.Domain.Apps.Core.HandleRules.Extensions
{
@ -29,20 +30,33 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Extensions
{
TemplateContext.GlobalFilters.AddFilter("contentUrl", ContentUrl);
TemplateContext.GlobalFilters.AddFilter("assetContentUrl", AssetContentUrl);
TemplateContext.GlobalFilters.AddFilter("assetContentAppUrl", AssetContentAppUrl);
TemplateContext.GlobalFilters.AddFilter("assetContentSlugUrl", AssetContentSlugUrl);
}
private FluidValue ContentUrl(FluidValue input, FilterArguments arguments, TemplateContext context)
{
if (input is ObjectValue objectValue)
var value = input.ToObjectValue();
switch (value)
{
if (context.GetValue("event")?.ToObjectValue() is EnrichedContentEvent contentEvent)
case Guid guid when guid != Guid.Empty:
{
if (objectValue.ToObjectValue() is Guid guid && guid != Guid.Empty)
if (context.GetValue("event")?.ToObjectValue() is EnrichedContentEvent contentEvent)
{
var result = urlGenerator.ContentUI(contentEvent.AppId, contentEvent.SchemaId, guid);
return new StringValue(result);
}
break;
}
case EnrichedContentEvent contentEvent:
{
var result = urlGenerator.ContentUI(contentEvent.AppId, contentEvent.SchemaId, contentEvent.Id);
return new StringValue(result);
}
}
@ -51,12 +65,79 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Extensions
private FluidValue AssetContentUrl(FluidValue input, FilterArguments arguments, TemplateContext context)
{
if (input is ObjectValue objectValue)
var value = input.ToObjectValue();
switch (value)
{
if (objectValue.ToObjectValue() is Guid guid && guid != Guid.Empty)
case Guid guid when guid != Guid.Empty:
{
var result = urlGenerator.AssetContent(guid);
return new StringValue(result);
}
case EnrichedAssetEvent assetEvent:
{
var result = urlGenerator.AssetContent(assetEvent.Id);
return new StringValue(result);
}
}
return NilValue.Empty;
}
private FluidValue AssetContentAppUrl(FluidValue input, FilterArguments arguments, TemplateContext context)
{
var value = input.ToObjectValue();
switch (value)
{
case Guid guid when guid != Guid.Empty:
{
if (context.GetValue("event")?.ToObjectValue() is EnrichedAssetEvent assetEvent)
{
var result = urlGenerator.AssetContent(assetEvent.AppId, guid.ToString());
return new StringValue(result);
}
break;
}
case EnrichedAssetEvent assetEvent:
{
var result = urlGenerator.AssetContent(assetEvent.AppId, assetEvent.Id.ToString());
return new StringValue(result);
}
}
return NilValue.Empty;
}
private FluidValue AssetContentSlugUrl(FluidValue input, FilterArguments arguments, TemplateContext context)
{
var value = input.ToObjectValue();
switch (value)
{
case string s:
{
if (context.GetValue("event")?.ToObjectValue() is EnrichedAssetEvent assetEvent)
{
var result = urlGenerator.AssetContent(assetEvent.AppId, s.Slugify());
return new StringValue(result);
}
break;
}
case EnrichedAssetEvent assetEvent:
{
var result = urlGenerator.AssetContent(assetEvent.AppId, assetEvent.FileName.Slugify());
return new StringValue(result);
}
}

21
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Extensions/EventJintExtension.cs

@ -9,6 +9,7 @@ using Jint.Native;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Infrastructure;
using Squidex.Text;
namespace Squidex.Domain.Apps.Core.HandleRules.Extensions
{
@ -55,6 +56,26 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Extensions
return JsValue.Null;
}));
context.Engine.SetValue("assetContentAppUrl", new EventDelegate(() =>
{
if (context.TryGetValue("event", out var temp) && temp is EnrichedAssetEvent assetEvent)
{
return urlGenerator.AssetContent(assetEvent.AppId, assetEvent.Id.ToString());
}
return JsValue.Null;
}));
context.Engine.SetValue("assetContentSlugUrl", new EventDelegate(() =>
{
if (context.TryGetValue("event", out var temp) && temp is EnrichedAssetEvent assetEvent)
{
return urlGenerator.AssetContent(assetEvent.AppId, assetEvent.FileName.Slugify());
}
return JsValue.Null;
}));
}
}
}

23
backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/PredefinedPatternsFormatter.cs

@ -11,6 +11,7 @@ using System.Globalization;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Infrastructure;
using Squidex.Shared.Users;
using Squidex.Text;
namespace Squidex.Domain.Apps.Core.HandleRules
{
@ -28,6 +29,8 @@ namespace Squidex.Domain.Apps.Core.HandleRules
AddPattern("APP_ID", AppId);
AddPattern("APP_NAME", AppName);
AddPattern("ASSET_CONTENT_URL", AssetContentUrl);
AddPattern("ASSET_CONTENT_APP_URL", AssetContentAppUrl);
AddPattern("ASSET_CONTENT_SLUG_URL", AssetContentSlugUrl);
AddPattern("CONTENT_ACTION", ContentAction);
AddPattern("CONTENT_URL", ContentUrl);
AddPattern("MENTIONED_ID", MentionedId);
@ -124,6 +127,26 @@ namespace Squidex.Domain.Apps.Core.HandleRules
return null;
}
private string? AssetContentAppUrl(EnrichedEvent @event)
{
if (@event is EnrichedAssetEvent assetEvent)
{
return urlGenerator.AssetContent(assetEvent.AppId, assetEvent.Id.ToString());
}
return null;
}
private string? AssetContentSlugUrl(EnrichedEvent @event)
{
if (@event is EnrichedAssetEvent assetEvent)
{
return urlGenerator.AssetContent(assetEvent.AppId, assetEvent.FileName.Slugify());
}
return null;
}
private string? ContentUrl(EnrichedEvent @event)
{
if (@event is EnrichedContentEvent contentEvent)

2
backend/src/Squidex.Domain.Apps.Core.Operations/IUrlGenerator.cs

@ -27,6 +27,8 @@ namespace Squidex.Domain.Apps.Core
string AssetContent(Guid assetId);
string AssetContent(NamedId<Guid> appId, string idOrSlug);
string BackupsUI(NamedId<Guid> appId);
string ClientsUI(NamedId<Guid> appId);

5
backend/src/Squidex.Web/Services/UrlGenerator.cs

@ -53,6 +53,11 @@ namespace Squidex.Web.Services
return urlsOptions.BuildUrl($"api/assets/{assetId}");
}
public string AssetContent(NamedId<Guid> appId, string idOrSlug)
{
return urlsOptions.BuildUrl($"assets/{appId.Name}/{idOrSlug}");
}
public string? AssetSource(Guid assetId, long fileVersion)
{
return assetFileStore.GeneratePublicUrl(assetId, fileVersion);

76
backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs

@ -70,6 +70,12 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => urlGenerator.AssetContent(assetId))
.Returns("asset-content-url");
A.CallTo(() => urlGenerator.AssetContent(appId, assetId.ToString()))
.Returns("asset-content-app-url");
A.CallTo(() => urlGenerator.AssetContent(appId, "file-name"))
.Returns("asset-content-slug-url");
A.CallTo(() => user.Id)
.Returns("user123");
@ -301,6 +307,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
"Download at ${assetContentUrl()}",
"Download at {{event.id | assetContentUrl}}"
)]
[InlineData("Liquid(Download at {{event | assetContentUrl}})")]
public async Task Should_format_asset_content_url_from_event(string script)
{
var @event = new EnrichedAssetEvent { Id = assetId };
@ -317,6 +324,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
"Download at ${assetContentUrl()}",
"Download at {{event.id | assetContentUrl | default: 'null'}}"
)]
[InlineData("Liquid(Download at {{event | assetContentUrl | default: 'null'}})")]
public async Task Should_return_null_when_asset_content_url_not_found(string script)
{
var @event = new EnrichedContentEvent();
@ -326,6 +334,74 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
Assert.Equal("Download at null", result);
}
[Theory]
[Expressions(
"Download at $ASSET_CONTENT_APP_URL",
null,
"Download at ${assetContentAppUrl()}",
"Download at {{event.id | assetContentAppUrl | default: 'null'}}"
)]
[InlineData("Liquid(Download at {{event | assetContentAppUrl | default: 'null'}})")]
public async Task Should_format_asset_content_app_url_from_event(string script)
{
var @event = new EnrichedAssetEvent { AppId = appId, Id = assetId, FileName = "File Name" };
var result = await sut.FormatAsync(script, @event);
Assert.Equal("Download at asset-content-app-url", result);
}
[Theory]
[Expressions(
"Download at $ASSET_CONTENT_APP_URL",
null,
"Download at ${assetContentAppUrl()}",
"Download at {{event.id | assetContentAppUrl | default: 'null'}}"
)]
[InlineData("Liquid(Download at {{event | assetContentAppUrl | default: 'null'}})")]
public async Task Should_return_null_when_asset_content_app_url_not_found(string script)
{
var @event = new EnrichedContentEvent();
var result = await sut.FormatAsync(script, @event);
Assert.Equal("Download at null", result);
}
[Theory]
[Expressions(
"Download at $ASSET_CONTENT_SLUG_URL",
null,
"Download at ${assetContentSlugUrl()}",
"Download at {{event.fileName | assetContentSlugUrl | default: 'null'}}"
)]
[InlineData("Liquid(Download at {{event | assetContentSlugUrl | default: 'null'}})")]
public async Task Should_format_asset_content_slug_url_from_event(string script)
{
var @event = new EnrichedAssetEvent { AppId = appId, Id = assetId, FileName = "File Name" };
var result = await sut.FormatAsync(script, @event);
Assert.Equal("Download at asset-content-slug-url", result);
}
[Theory]
[Expressions(
"Download at $ASSET_CONTENT_SLUG_URL",
null,
"Download at ${assetContentSlugUrl()}",
"Download at {{event.id | assetContentSlugUrl | default: 'null'}}"
)]
[InlineData("Liquid(Download at {{event | assetContentSlugUrl | default: 'null'}})")]
public async Task Should_return_null_when_asset_content_slug_url_not_found(string script)
{
var @event = new EnrichedContentEvent();
var result = await sut.FormatAsync(script, @event);
Assert.Equal("Download at null", result);
}
[Theory]
[Expressions(
"Go to $CONTENT_URL",

5
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/TestData/FakeUrlGenerator.cs

@ -31,6 +31,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.TestData
return $"assets/{assetId}";
}
public string AssetContent(NamedId<Guid> appId, string idOrSlug)
{
return $"assets/{appId.Name}/{idOrSlug}";
}
public string ContentUI(NamedId<Guid> appId, NamedId<Guid> schemaId, Guid contentId)
{
return $"contents/{schemaId.Name}/{contentId}";

18
frontend/app/features/content/shared/forms/array-editor.component.html

@ -1,4 +1,5 @@
<div class="array-container" *ngIf="formModel.items.length > 0"
<ng-container *ngIf="hasFields; else noFields">
<div class="array-container" *ngIf="formModel.items.length > 0"
cdkDropList
[cdkDropListDisabled]="false"
[cdkDropListData]="formModel.items"
@ -19,11 +20,11 @@
<i cdkDragHandle class="icon-drag2"></i>
</sqx-array-item>
</div>
</div>
</div>
<div class="row">
<div class="row">
<div class="col">
<button type="button" class="btn btn-success" [disabled]="field.nested.length === 0 || formModel.form.disabled" (click)="itemAdd(undefined)">
<button type="button" class="btn btn-success" [disabled]="!canAdd || formModel.form.disabled" (click)="itemAdd(undefined)">
{{ 'contents.arrayAddItem' | sqxTranslate }}
</button>
</div>
@ -36,8 +37,11 @@
<i class="icon-minus-square"></i>
</button>
</div>
</div>
</div>
</ng-container>
<small class="text-muted ml-2" *ngIf="field.nested.length === 0">
<ng-template #noFields>
<small class="text-muted">
{{ 'contents.arrayNoFields' | sqxTranslate }}
</small>
</small>
</ng-template>

24
frontend/app/features/content/shared/forms/array-editor.component.ts

@ -6,8 +6,8 @@
*/
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { ChangeDetectionStrategy, Component, Input, QueryList, ViewChildren } from '@angular/core';
import { AppLanguageDto, EditContentForm, FieldArrayForm, FieldArrayItemForm, sorted } from '@app/shared';
import { ChangeDetectionStrategy, Component, Input, OnChanges, QueryList, SimpleChanges, ViewChildren } from '@angular/core';
import { AppLanguageDto, ArrayFieldPropertiesDto, EditContentForm, FieldArrayForm, FieldArrayItemForm, sorted } from '@app/shared';
import { ArrayItemComponent } from './array-item.component';
@Component({
@ -16,7 +16,7 @@ import { ArrayItemComponent } from './array-item.component';
templateUrl: './array-editor.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ArrayEditorComponent {
export class ArrayEditorComponent implements OnChanges {
@Input()
public form: EditContentForm;
@ -38,10 +38,28 @@ export class ArrayEditorComponent {
@ViewChildren(ArrayItemComponent)
public children: QueryList<ArrayItemComponent>;
public maxItems: number;
public get field() {
return this.formModel.field;
}
public get hasFields() {
return this.field.nested.length > 0;
}
public get canAdd() {
return this.formModel.items.length < this.maxItems;
}
public ngOnChanges(changes: SimpleChanges) {
if (changes['formModel']) {
const properties = this.field.properties as ArrayFieldPropertiesDto;
this.maxItems = properties.maxItems || Number.MAX_VALUE;
}
}
public itemRemove(index: number) {
this.formModel.removeItemAt(index);
}

Loading…
Cancel
Save