Browse Source

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

# Conflicts:
#	src/Squidex/app/framework/angular/forms/form-error.component.ts
release/3.x
Sebastian 6 years ago
parent
commit
07f44f6014
  1. 2
      src/Squidex/app/features/content/pages/content/content-page.component.html
  2. 29
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  3. 2
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html
  4. 33
      src/Squidex/app/framework/angular/forms/form-error.component.ts
  5. 3
      src/Squidex/app/framework/angular/modals/dialog-renderer.component.html
  6. 6
      src/Squidex/app/shared/state/contents.state.ts
  7. 42
      src/Squidex/app/theme/_forms.scss
  8. 85
      tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/SchemaSynchronizerTests.cs

2
src/Squidex/app/features/content/pages/content/content-page.component.html

@ -93,6 +93,8 @@
<sqx-shortcut keys="ctrl+s" (trigger)="saveAndPublish()"></sqx-shortcut> <sqx-shortcut keys="ctrl+s" (trigger)="saveAndPublish()"></sqx-shortcut>
</ng-container> </ng-container>
</ng-template> </ng-template>
<sqx-form-error bubble="true" closeable="true" [error]="contentForm.error | async"></sqx-form-error>
</ng-container> </ng-container>
<ng-container content> <ng-container content>

29
src/Squidex/app/features/content/pages/content/content-page.component.ts

@ -156,12 +156,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
} }
public canDeactivate(): Observable<boolean> { public canDeactivate(): Observable<boolean> {
const observable = return this.checkPendingChanges('close current content view').pipe(
this.contentForm.hasChanged() ?
this.dialogs.confirm('Unsaved changes', 'You have unsaved changes, do you want to close the current content view and discard your changes?') :
of(true);
return observable.pipe(
tap(confirmed => { tap(confirmed => {
if (confirmed) { if (confirmed) {
this.autoSaveService.remove(this.autoSaveKey); this.autoSaveService.remove(this.autoSaveKey);
@ -225,7 +220,7 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
}); });
} }
} else { } else {
this.dialogs.notifyError('Content element not valid, please check the field with the red bar on the left in all languages (if localizable).'); this.contentForm.submitFailed('Content element not valid, please check the field with the red bar on the left in all languages (if localizable).');
} }
} }
@ -253,17 +248,29 @@ export class ContentPageComponent extends ResourceOwner implements CanComponentD
} }
public publishChanges() { public publishChanges() {
this.dueTimeSelector.selectDueTime('Publish').pipe( this.checkPendingChanges('publish your changes').pipe(
switchMap(d => this.contentsState.publishDraft(this.content, d)), onErrorResumeNext()) filter(x => !!x),
switchMap(_ => this.dueTimeSelector.selectDueTime(status)),
switchMap(d => this.contentsState.publishDraft(this.content, d)),
onErrorResumeNext())
.subscribe(); .subscribe();
} }
public changeStatus(status: string) { public changeStatus(status: string) {
this.dueTimeSelector.selectDueTime(status).pipe( this.checkPendingChanges('change the status').pipe(
switchMap(d => this.contentsState.changeStatus(this.content, status, d)), onErrorResumeNext()) filter(x => !!x),
switchMap(_ => this.dueTimeSelector.selectDueTime(status)),
switchMap(d => this.contentsState.changeStatus(this.content, status, d)),
onErrorResumeNext())
.subscribe(); .subscribe();
} }
private checkPendingChanges(action: string) {
return this.contentForm.hasChanged() ?
this.dialogs.confirm('Unsaved changes', `You have unsaved changes. When you ${action} you will loose them.<br />Do you want to continue anyway?`) :
of(true);
}
public showLatest() { public showLatest() {
this.loadVersion(null, false); this.loadVersion(null, false);
} }

2
src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html

@ -11,7 +11,7 @@
</ng-container> </ng-container>
<ng-container content> <ng-container content>
<sqx-form-error [error]="createForm.error | async"></sqx-form-error> <sqx-form-error closeable="true" [error]="createForm.error | async"></sqx-form-error>
<div class="form-group name-group"> <div class="form-group name-group">
<label for="schemaName">Name <small class="hint">(required)</small></label> <label for="schemaName">Name <small class="hint">(required)</small></label>

33
src/Squidex/app/framework/angular/forms/form-error.component.ts

@ -5,19 +5,44 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { ErrorDto } from '@app/framework/internal'; import { ErrorDto } from '@app/framework/internal';
@Component({ @Component({
selector: 'sqx-form-error', selector: 'sqx-form-error',
template: ` template: `
<ng-container *ngIf="error"> <ng-container *ngIf="show">
<div class="form-alert form-alert-error" [innerHTML]="error?.displayMessage"></div> <div [class.form-bubble]="bubble">
<div class="form-alert form-alert-error" [class.closeable]="closeable">
<a class="form-alert-close" (click)="close()">
<i class="icon-close"></i>
</a>
<div [innerHTML]="error?.displayMessage"></div>
</div>
</div>
</ng-container>`, </ng-container>`,
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class FormErrorComponent { export class FormErrorComponent implements OnChanges {
@Input() @Input()
public error?: ErrorDto | null; public error?: ErrorDto | null;
@Input()
public bubble = false;
@Input()
public closeable = false;
public show: boolean;
public ngOnChanges(changes: SimpleChanges) {
if (changes['error']) {
this.show = !!this.error;
}
}
public close() {
this.show = false;
}
} }

3
src/Squidex/app/framework/angular/modals/dialog-renderer.component.html

@ -1,6 +1,5 @@
<ng-content></ng-content> <ng-content></ng-content>
<ng-container *sqxModal="dialogView"> <ng-container *sqxModal="dialogView">
<sqx-modal-dialog showClose="false" (close)="cancel()"> <sqx-modal-dialog showClose="false" (close)="cancel()">
<ng-container title> <ng-container title>
@ -8,7 +7,7 @@
</ng-container> </ng-container>
<ng-container content> <ng-container content>
{{snapshot.dialogRequest?.text}} <span [innerHTML]="snapshot.dialogRequest?.text"></span>
</ng-container> </ng-container>
<ng-container footer> <ng-container footer>

6
src/Squidex/app/shared/state/contents.state.ts

@ -197,7 +197,7 @@ export abstract class ContentsStateBase extends State<Snapshot> {
return { ...s, contents, contentsPager }; return { ...s, contents, contentsPager };
}); });
}), }),
shareSubscribed(this.dialogs)); shareSubscribed(this.dialogs, {silent: true}));
} }
public changeManyStatus(contents: ReadonlyArray<ContentDto>, status: string, dueTime: string | null): Observable<any> { public changeManyStatus(contents: ReadonlyArray<ContentDto>, status: string, dueTime: string | null): Observable<any> {
@ -263,7 +263,7 @@ export abstract class ContentsStateBase extends State<Snapshot> {
this.replaceContent(updated, content.version); this.replaceContent(updated, content.version);
}), }),
shareSubscribed(this.dialogs)); shareSubscribed(this.dialogs, { silent: true }));
} }
public proposeDraft(content: ContentDto, request: any): Observable<ContentDto> { public proposeDraft(content: ContentDto, request: any): Observable<ContentDto> {
@ -273,7 +273,7 @@ export abstract class ContentsStateBase extends State<Snapshot> {
this.replaceContent(updated, content.version); this.replaceContent(updated, content.version);
}), }),
shareSubscribed(this.dialogs)); shareSubscribed(this.dialogs, { silent: true }));
} }
public discardDraft(content: ContentDto): Observable<ContentDto> { public discardDraft(content: ContentDto): Observable<ContentDto> {

42
src/Squidex/app/theme/_forms.scss

@ -77,6 +77,13 @@
font-size: .9rem; font-size: .9rem;
font-weight: normal; font-weight: normal;
padding: .5rem; padding: .5rem;
padding-right: 1.5rem;
}
&-close {
@include absolute(0, 0, auto, auto);
padding: .5rem;
display: none;
} }
&-error { &-error {
@ -87,11 +94,46 @@
background: $color-theme-green-dark; background: $color-theme-green-dark;
} }
&.closeable {
position: relative;
.form-alert-close {
display: inline-block;
}
}
ul { ul {
margin: 0; margin: 0;
} }
} }
.form-bubble {
& {
position: relative;
}
.form-alert {
@include absolute(.25rem, 0, auto, auto);
padding: 1rem;
min-width: 200px;
max-width: 400px;
font-size: 1rem;
font-weight: normal;
text-align: left;
z-index: 1000;
&::after {
@include absolute(-.75rem, .625rem, auto, auto);
content: '';
height: 0;
border-style: solid;
border-width: .4rem;
border-color: transparent transparent $color-theme-error;
width: 0;
}
}
}
// //
// Control Dropdown item // Control Dropdown item
// //

85
tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/SchemaSynchronizerTests.cs

@ -1,4 +1,5 @@
// ========================================================================== 
// ==========================================================================
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt) // Copyright (c) Squidex UG (haftungsbeschraenkt)
@ -33,8 +34,11 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
[Fact] [Fact]
public void Should_create_events_if_schema_deleted() public void Should_create_events_if_schema_deleted()
{ {
var sourceSchema = new Schema("source"); var sourceSchema =
var targetSchema = (Schema)null; new Schema("source");
var targetSchema =
(Schema)null;
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -46,8 +50,12 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
[Fact] [Fact]
public void Should_create_events_if_category_changed() public void Should_create_events_if_category_changed()
{ {
var sourceSchema = new Schema("source"); var sourceSchema =
var targetSchema = new Schema("target").ChangeCategory("Category"); new Schema("source");
var targetSchema =
new Schema("target")
.ChangeCategory("Category");
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -64,8 +72,11 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
Create = "<create-script>" Create = "<create-script>"
}; };
var sourceSchema = new Schema("source"); var sourceSchema =
var targetSchema = new Schema("target").ConfigureScripts(scripts); new Schema("source");
var targetSchema =
new Schema("target").ConfigureScripts(scripts);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -82,8 +93,12 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
["web"] = "Url" ["web"] = "Url"
}; };
var sourceSchema = new Schema("source"); var sourceSchema =
var targetSchema = new Schema("target").ConfigurePreviewUrls(previewUrls); new Schema("source");
var targetSchema =
new Schema("target")
.ConfigurePreviewUrls(previewUrls);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -95,8 +110,12 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
[Fact] [Fact]
public void Should_create_events_if_schema_published() public void Should_create_events_if_schema_published()
{ {
var sourceSchema = new Schema("source"); var sourceSchema =
var targetSchema = new Schema("target").Publish(); new Schema("source");
var targetSchema =
new Schema("target")
.Publish();
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -108,8 +127,12 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
[Fact] [Fact]
public void Should_create_events_if_schema_unpublished() public void Should_create_events_if_schema_unpublished()
{ {
var sourceSchema = new Schema("source").Publish(); var sourceSchema =
var targetSchema = new Schema("target"); new Schema("source")
.Publish();
var targetSchema =
new Schema("target");
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -207,7 +230,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
.AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f .AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f
.AddString(nestedId.Id, nestedId.Name)).LockField(nestedId.Id, arrayId.Id); .AddString(nestedId.Id, nestedId.Name))
.LockField(nestedId.Id, arrayId.Id);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -225,7 +249,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
.AddString(stringId.Id, stringId.Name, Partitioning.Invariant).LockField(stringId.Id); .AddString(stringId.Id, stringId.Name, Partitioning.Invariant)
.LockField(stringId.Id);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -245,7 +270,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
.AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f .AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f
.AddString(nestedId.Id, nestedId.Name)).HideField(nestedId.Id, arrayId.Id); .AddString(nestedId.Id, nestedId.Name))
.HideField(nestedId.Id, arrayId.Id);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -263,7 +289,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
.AddString(stringId.Id, stringId.Name, Partitioning.Invariant).HideField(stringId.Id); .AddString(stringId.Id, stringId.Name, Partitioning.Invariant)
.HideField(stringId.Id);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -278,7 +305,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var sourceSchema = var sourceSchema =
new Schema("source") new Schema("source")
.AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f .AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f
.AddString(nestedId.Id, nestedId.Name)).HideField(nestedId.Id, arrayId.Id); .AddString(nestedId.Id, nestedId.Name))
.HideField(nestedId.Id, arrayId.Id);
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
@ -297,7 +325,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
{ {
var sourceSchema = var sourceSchema =
new Schema("source") new Schema("source")
.AddString(stringId.Id, stringId.Name, Partitioning.Invariant).HideField(stringId.Id); .AddString(stringId.Id, stringId.Name, Partitioning.Invariant)
.HideField(stringId.Id);
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
@ -321,7 +350,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
.AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f .AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f
.AddString(nestedId.Id, nestedId.Name)).DisableField(nestedId.Id, arrayId.Id); .AddString(nestedId.Id, nestedId.Name))
.DisableField(nestedId.Id, arrayId.Id);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -339,7 +369,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
.AddString(stringId.Id, stringId.Name, Partitioning.Invariant).DisableField(stringId.Id); .AddString(stringId.Id, stringId.Name, Partitioning.Invariant)
.DisableField(stringId.Id);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -354,7 +385,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var sourceSchema = var sourceSchema =
new Schema("source") new Schema("source")
.AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f .AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f
.AddString(nestedId.Id, nestedId.Name)).DisableField(nestedId.Id, arrayId.Id); .AddString(nestedId.Id, nestedId.Name))
.DisableField(nestedId.Id, arrayId.Id);
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
@ -373,7 +405,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
{ {
var sourceSchema = var sourceSchema =
new Schema("source") new Schema("source")
.AddString(stringId.Id, stringId.Name, Partitioning.Invariant).DisableField(stringId.Id); .AddString(stringId.Id, stringId.Name, Partitioning.Invariant)
.DisableField(stringId.Id);
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
@ -394,7 +427,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
.AddString(stringId.Id, stringId.Name, Partitioning.Invariant).HideField(stringId.Id); .AddString(stringId.Id, stringId.Name, Partitioning.Invariant)
.HideField(stringId.Id);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);
@ -457,7 +491,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization
var targetSchema = var targetSchema =
new Schema("target") new Schema("target")
.AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f .AddArray(arrayId.Id, arrayId.Name, Partitioning.Invariant, f => f
.AddString(nestedId.Id, nestedId.Name)).HideField(nestedId.Id, arrayId.Id); .AddString(nestedId.Id, nestedId.Name))
.HideField(nestedId.Id, arrayId.Id);
var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator); var events = sourceSchema.Synchronize(targetSchema, jsonSerializer, idGenerator);

Loading…
Cancel
Save