Browse Source

Merge branch 'release/4.x'

# Conflicts:
#	backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs
pull/593/head
Sebastian 5 years ago
parent
commit
d165cc8485
  1. 11
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs
  2. 38
      backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonAppPattern.cs
  3. 4
      frontend/app/features/content/pages/contents/custom-view-editor.component.html
  4. 2
      frontend/app/features/content/pages/contents/custom-view-editor.component.scss
  5. 2
      frontend/app/features/content/shared/forms/array-editor.component.html
  6. 39
      frontend/app/features/settings/pages/workflows/workflow-step.component.html
  7. 2
      frontend/app/features/settings/pages/workflows/workflow-step.component.scss
  8. 16
      frontend/app/features/settings/pages/workflows/workflow-transition.component.html
  9. 5
      frontend/app/features/settings/pages/workflows/workflow.component.html
  10. 3
      frontend/app/features/settings/pages/workflows/workflows-page.component.html
  11. 105
      frontend/app/framework/angular/forms/file-drop.directive.ts

11
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs

@ -7,7 +7,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Newtonsoft;
@ -18,11 +17,11 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
{
protected override void WriteValue(JsonWriter writer, AppPatterns value, JsonSerializer serializer)
{
var json = new Dictionary<DomainId, JsonAppPattern>(value.Count);
var json = new Dictionary<DomainId, AppPattern>(value.Count);
foreach (var (key, appPattern) in value)
foreach (var (key, pattern) in value)
{
json.Add(key, new JsonAppPattern(appPattern));
json.Add(key, pattern);
}
serializer.Serialize(writer, json);
@ -30,9 +29,9 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
protected override AppPatterns ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<DomainId, JsonAppPattern>>(reader)!;
var json = serializer.Deserialize<Dictionary<DomainId, AppPattern>>(reader)!;
return new AppPatterns(json.ToDictionary(x => x.Key, x => x.Value.ToPattern()));
return new AppPatterns(json);
}
}
}

38
backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonAppPattern.cs

@ -1,38 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Apps.Json
{
public class JsonAppPattern
{
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public string Pattern { get; set; }
[JsonProperty]
public string? Message { get; set; }
public JsonAppPattern()
{
}
public JsonAppPattern(AppPattern pattern)
{
SimpleMapper.Map(pattern, this);
}
public AppPattern ToPattern()
{
return new AppPattern(Name, Pattern, Message);
}
}
}

4
frontend/app/features/content/pages/contents/custom-view-editor.component.html

@ -15,9 +15,9 @@
<i class="icon-drag2 drag-handle"></i>
<div class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" checked (click)="removeField(field)" id="field_{{field}}">
<input class="custom-control-input" type="checkbox" checked (click)="removeField(field)" id="field_{{field}}" [disabled]="!field">
<label class="custom-control-label" for="field_{{field}}">
{{field}}
{{field || '--'}}
</label>
</div>
</div>

2
frontend/app/features/content/pages/contents/custom-view-editor.component.scss

@ -13,7 +13,7 @@
visibility: hidden;
}
.form-check {
.custom-checkbox {
display: inline-block;
padding-left: 2rem;
padding-right: .5rem;

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

@ -24,7 +24,7 @@
<div class="row">
<div class="col">
<button type="button" class="btn btn-success" [disabled]="!canAdd || formModel.form.disabled" (click)="itemAdd(undefined)">
<button type="button" class="btn btn-outline-success" [disabled]="!canAdd || formModel.form.disabled" (click)="itemAdd(undefined)">
{{ 'contents.arrayAddItem' | sqxTranslate }}
</button>
</div>

39
frontend/app/features/settings/pages/workflows/workflow-step.component.html

@ -1,12 +1,19 @@
<div class="step">
<div class="row no-gutters step-header">
<div class="col-auto">
<button class="btn btn-initial mr-1" (click)="makeInitial.emit()" [class.enabled]="step.name !== workflow.initial && !step.isLocked" [class.active]="step.name === workflow.initial" [disabled]="step.name === workflow.initial || step.isLocked || disabled">
<button class="btn btn-initial mr-1" (click)="makeInitial.emit()"
[class.enabled]="step.name !== workflow.initial && !step.isLocked"
[class.active]="step.name === workflow.initial"
[disabled]="step.name === workflow.initial || step.isLocked || disabled">
<i class="icon-arrow-right text-decent" *ngIf="!step.isLocked"></i>
</button>
</div>
<div class="col-auto color pr-2">
<sqx-color-picker mode="Circle" [ngModelOptions]="onBlur" [ngModel]="step.color" (ngModelChange)="changeColor($event)" [disabled]="disabled">
<sqx-color-picker mode="Circle"
[disabled]="disabled"
[ngModelOptions]="onBlur"
[ngModel]="step.color"
(ngModelChange)="changeColor($event)" >
</sqx-color-picker>
</div>
<div class="col">
@ -26,7 +33,11 @@
</div>
<div class="step-inner">
<sqx-workflow-transition *ngFor="let transition of transitions; trackBy: trackByTransition" [transition]="transition" [disabled]="disabled" [roles]="roles" (remove)="transitionRemove.emit(transition)" (update)="changeTransition(transition, $event)">
<sqx-workflow-transition *ngFor="let transition of transitions; trackBy: trackByTransition" [transition]="transition"
[disabled]="disabled"
[roles]="roles"
(remove)="transitionRemove.emit(transition)"
(update)="changeTransition(transition, $event)">
</sqx-workflow-transition>
<div class="row transition no-gutters" *ngIf="openSteps.length > 0 && !disabled">
@ -47,11 +58,14 @@
</div>
</div>
<div class="row transition-prevent-updates no-gutters">
<div class="row transition-prevent-updates no-gutters align-items-center">
<div class="col col-arrow"></div>
<div class="col col-step">
<div class="custom-control custom-checkbox float-right">
<input class="custom-control-input transition-prevent-updates-checkbox" type="checkbox" id="preventUpdates_{{step.name}}" [disabled]="disabled" [ngModel]="step.noUpdate" (ngModelChange)="changeNoUpdate($event)">
<input class="custom-control-input transition-prevent-updates-checkbox" type="checkbox" id="preventUpdates_{{step.name}}"
[disabled]="disabled"
[ngModel]="step.noUpdate"
(ngModelChange)="changeNoUpdate($event)">
<label class="custom-control-label" for="preventUpdates_{{step.name}}">
{{ 'workflows.preventUpdates' | sqxTranslate }}
@ -63,13 +77,24 @@
<span class="text-decent">{{ 'workflows.syntax.when' | sqxTranslate }}</span>
</div>
<div class="col col-step-expression">
<input class="form-control" [class.dashed]="!step.noUpdateExpression" spellcheck="false" [disabled]="disabled" [ngModelOptions]="onBlur" [ngModel]="step.noUpdateExpression" (ngModelChange)="changeNoUpdateExpression($event)" placeholder="{{ 'workflows.syntax.expression' | sqxTranslate }}">
<input class="form-control" [class.dashed]="!step.noUpdateExpression" spellcheck="false"
[disabled]="disabled"
[ngModelOptions]="onBlur"
[ngModel]="step.noUpdateExpression"
(ngModelChange)="changeNoUpdateExpression($event)"
placeholder="{{ 'workflows.syntax.expression' | sqxTranslate }}">
</div>
<div class="col-auto col-label">
<span class="text-decent">{{ 'workflows.syntax.for' | sqxTranslate }}</span>
</div>
<div class="col col-roles">
<sqx-tag-editor [allowDuplicates]="false" [dashed]="true" [disabled]="disabled" (ngModelChange)="changeNoUpdateRoles($event)" [ngModel]="step.noUpdateRoles" [ngModelOptions]="onBlur" [singleLine]="true" [suggestions]="roles" placeholder="{{ 'common.role' | sqxTranslate }}">
<sqx-tag-editor [allowDuplicates]="false" [dashed]="true"
[disabled]="disabled"
[ngModelOptions]="onBlur"
[ngModel]="step.noUpdateRoles"
(ngModelChange)="changeNoUpdateRoles($event)"
[singleLine]="true"
[suggestions]="roles" placeholder="{{ 'common.role' | sqxTranslate }}">
</sqx-tag-editor>
</div>
<div class="col col-button"></div>

2
frontend/app/features/settings/pages/workflows/workflow-step.component.scss

@ -84,9 +84,9 @@
.transition-prevent-updates {
& {
line-height: 2.5rem;
margin-bottom: 1rem;
margin-top: .25rem;
min-height: 2.5rem;
}
&-to {

16
frontend/app/features/settings/pages/workflows/workflow-transition.component.html

@ -11,13 +11,25 @@
<span class="text-decent">{{ 'workflows.syntax.when' | sqxTranslate }}</span>
</div>
<div class="col">
<input class="form-control" [class.dashed]="!transition.expression" spellcheck="false" [disabled]="disabled" [ngModelOptions]="onBlur" [ngModel]="transition.expression" (ngModelChange)="changeExpression($event)" placeholder="{{ 'workflows.syntax.expression' | sqxTranslate }}">
<input class="form-control" [class.dashed]="!transition.expression" spellcheck="false"
[disabled]="disabled"
[ngModelOptions]="onBlur"
[ngModel]="transition.expression"
(ngModelChange)="changeExpression($event)"
placeholder="{{ 'workflows.syntax.expression' | sqxTranslate }}">
</div>
<div class="col-auto col-label">
<span class="text-decent">{{ 'workflows.syntax.for' | sqxTranslate }}</span>
</div>
<div class="col col-roles">
<sqx-tag-editor [allowDuplicates]="false" [dashed]="true" [disabled]="disabled" (ngModelChange)="changeRole($event)" [ngModel]="transition.roles" [ngModelOptions]="onBlur" [singleLine]="true" [suggestions]="roles" placeholder="{{ 'common.role' | sqxTranslate }}">
<sqx-tag-editor [allowDuplicates]="false"
[dashed]="true"
[disabled]="disabled"
[ngModelOptions]="onBlur"
[ngModel]="transition.roles"
(ngModelChange)="changeRole($event)"
[singleLine]="true"
[suggestions]="roles" placeholder="{{ 'common.role' | sqxTranslate }}">
</sqx-tag-editor>
</div>

5
frontend/app/features/settings/pages/workflows/workflow.component.html

@ -59,7 +59,10 @@
<label class="col-form-label" for="{{workflow.id}}_name">{{ 'common.name' | sqxTranslate }}</label>
<div class="col">
<input class="form-control" id="{{workflow.id}}_name" [ngModelOptions]="onBlur" [ngModel]="workflow.name" (ngModelChange)="rename($event)">
<input class="form-control" id="{{workflow.id}}_name"
[ngModelOptions]="onBlur"
[ngModel]="workflow.name"
(ngModelChange)="rename($event)">
<sqx-form-hint>
{{ 'workflows.workflowNameHint' | sqxTranslate }}

3
frontend/app/features/settings/pages/workflows/workflows-page.component.html

@ -34,7 +34,8 @@
{{ 'workflows.empty' | sqxTranslate }}
</div>
<sqx-workflow *ngFor="let workflow of workflows; trackBy: trackByWorkflow" [workflow]="workflow" [roles]="roles" [schemasSource]="schemasSource">
<sqx-workflow *ngFor="let workflow of workflows; trackBy: trackByWorkflow"
[workflow]="workflow" [roles]="roles" [schemasSource]="schemasSource">
</sqx-workflow>
<sqx-workflow-add-form *ngIf="workflowsState.canCreate | async"></sqx-workflow-add-form>

105
frontend/app/framework/angular/forms/file-drop.directive.ts

@ -6,6 +6,7 @@
*/
// tslint:disable: prefer-for-of
// tslint:disable: readonly-array
import { Directive, ElementRef, EventEmitter, HostListener, Input, Output, Renderer2 } from '@angular/core';
import { Types } from '@app/framework/internal';
@ -45,18 +46,16 @@ export class FileDropDirective {
}
@HostListener('paste', ['$event'])
public onPaste(event: ClipboardEvent) {
if (this.noPaste) {
return;
}
public async onPaste(event: ClipboardEvent) {
if (!this.noPaste) {
this.stopEvent(event);
const files = this.getAllowedFiles(event.clipboardData);
const files = await this.getAllowedFiles(event.clipboardData);
if (files && !this.disabled) {
this.drop.emit(files);
}
this.stopEvent(event);
}
}
@HostListener('dragend', ['$event'])
@ -88,16 +87,15 @@ export class FileDropDirective {
}
@HostListener('drop', ['$event'])
public onDrop(event: DragDropEvent) {
public async onDrop(event: DragDropEvent) {
if (hasFiles(event.dataTransfer)) {
const files = this.getAllowedFiles(event.dataTransfer);
this.stopDrag(event);
const files = await this.getAllowedFiles(event.dataTransfer);
if (files && !this.disabled) {
this.drop.emit(files);
}
this.dragEnd(0);
this.stopEvent(event);
}
}
@ -106,6 +104,11 @@ export class FileDropDirective {
event.stopPropagation();
}
private stopDrag(event: DragDropEvent) {
this.dragEnd(0);
this.stopEvent(event);
}
private dragStart() {
this.dragCounter++;
@ -122,31 +125,21 @@ export class FileDropDirective {
}
}
private getAllowedFiles(dataTransfer: DataTransfer | null) {
private async getAllowedFiles(dataTransfer: DataTransfer | null) {
if (!dataTransfer || !hasFiles(dataTransfer)) {
return null;
}
const files: File[] = [];
for (let i = 0; i < dataTransfer.files.length; i++) {
const file = dataTransfer.files.item(i);
let files: File[] = [];
if (file && this.isAllowedFile(file)) {
files.push(file);
}
}
if (files.length === 0) {
for (let i = 0; i < dataTransfer.items.length; i++) {
const file = dataTransfer.items[i].getAsFile();
const item = dataTransfer.items[i];
if (file && this.isAllowedFile(file)) {
files.push(file);
}
}
await transferFileTree(item, files);
}
files = files.filter(f => this.isAllowedFile(f));
return files.length > 0 ? files : null;
}
@ -203,6 +196,62 @@ function hasFiles(dataTransfer: DataTransfer): boolean {
}
}
async function transferWebkitTree(item: any, files: File[]) {
if (item.isFile) {
const file = await getFilePromise(item);
files.push(file);
} else if (item.isDirectory) {
const entries = await getFilesPromise(item);
for (const entry of entries) {
await transferWebkitTree(entry, files);
}
}
}
async function transferFileTree(item: DataTransferItem, files: File[]) {
if (Types.isFunction(item['webkitGetAsEntry'])) {
const webkitEntry = item.webkitGetAsEntry();
if (webkitEntry) {
await transferWebkitTree(webkitEntry, files);
return;
}
}
if (Types.isFunction(item['getAsFile'])) {
const fileItem = item.getAsFile();
if (fileItem) {
files.push(fileItem);
}
}
}
function getFilesPromise(item: any): Promise<ReadonlyArray<any>> {
return new Promise((resolve, reject) => {
try {
const reader = item.createReader();
reader.readEntries(resolve);
} catch (ex) {
reject(ex);
}
});
}
function getFilePromise(item: any): Promise<File> {
return new Promise((resolve, reject) => {
try {
item.file(resolve);
} catch (ex) {
reject(ex);
}
});
}
interface DragDropEvent extends MouseEvent {
readonly dataTransfer: DataTransfer;
}
Loading…
Cancel
Save