Browse Source

UI improved.

pull/297/head
Sebastian 8 years ago
parent
commit
3cc0908b10
  1. 23
      src/Squidex.Domain.Apps.Core.Model/Schemas/FieldExtensions.cs
  2. 20
      src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs
  3. 61
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs
  4. 2
      src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs
  5. 5
      src/Squidex/Areas/Api/Controllers/Schemas/Models/NestedFieldDto.cs
  6. 72
      src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs
  7. 3
      src/Squidex/app/app.module.ts
  8. 10
      src/Squidex/app/features/schemas/pages/schema/field-wizard.component.html
  9. 16
      src/Squidex/app/features/schemas/pages/schema/field-wizard.component.ts
  10. 64
      src/Squidex/app/features/schemas/pages/schema/field.component.html
  11. 39
      src/Squidex/app/features/schemas/pages/schema/field.component.scss
  12. 17
      src/Squidex/app/features/schemas/pages/schema/field.component.ts
  13. 2
      src/Squidex/app/shared/components/help.component.html
  14. 2
      src/Squidex/app/shared/components/history.component.html
  15. 39
      src/Squidex/app/shared/services/schemas.service.spec.ts
  16. 46
      src/Squidex/app/shared/services/schemas.service.ts
  17. 14
      src/Squidex/app/shared/state/schemas.state.spec.ts
  18. 24
      src/Squidex/app/shared/state/schemas.state.ts
  19. 18
      src/Squidex/app/theme/icomoon/demo.html
  20. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.eot
  21. 1
      src/Squidex/app/theme/icomoon/fonts/icomoon.svg
  22. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.ttf
  23. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.woff
  24. 21
      src/Squidex/app/theme/icomoon/icons/type-Array.svg
  25. 398
      src/Squidex/app/theme/icomoon/selection.json
  26. 13
      src/Squidex/app/theme/icomoon/style.css
  27. 12753
      src/Squidex/package-lock.json
  28. 1
      src/Squidex/package.json
  29. 55
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs
  30. 62
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs

23
src/Squidex.Domain.Apps.Core.Model/Schemas/FieldExtensions.cs

@ -11,11 +11,6 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
public static class FieldExtensions
{
public static Schema LockField(this Schema schema, long fieldId)
{
return schema.UpdateField(fieldId, f => f.Lock());
}
public static Schema ReorderFields(this Schema schema, List<long> ids, long? parentId = null)
{
if (parentId != null)
@ -52,6 +47,24 @@ namespace Squidex.Domain.Apps.Core.Schemas
return schema.DeleteField(fieldId);
}
public static Schema LockField(this Schema schema, long fieldId, long? parentId = null)
{
if (parentId != null)
{
return schema.UpdateField(parentId.Value, f =>
{
if (f is ArrayField arrayField)
{
return arrayField.UpdateField(fieldId, n => n.Lock());
}
return f;
});
}
return schema.UpdateField(fieldId, f => f.Lock());
}
public static Schema HideField(this Schema schema, long fieldId, long? parentId = null)
{
if (parentId != null)

20
src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs

@ -16,6 +16,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
private readonly string fieldName;
private bool isDisabled;
private bool isHidden;
private bool isLocked;
public long Id
{
@ -27,6 +28,11 @@ namespace Squidex.Domain.Apps.Core.Schemas
get { return fieldName; }
}
public bool IsLocked
{
get { return isLocked; }
}
public bool IsHidden
{
get { return isHidden; }
@ -37,11 +43,6 @@ namespace Squidex.Domain.Apps.Core.Schemas
get { return isDisabled; }
}
public bool IsLocked
{
get { return false; }
}
public abstract FieldProperties RawProperties { get; }
protected NestedField(long id, string name)
@ -53,6 +54,15 @@ namespace Squidex.Domain.Apps.Core.Schemas
fieldName = name;
}
[Pure]
public NestedField Lock()
{
return Clone(clone =>
{
clone.isLocked = true;
});
}
[Pure]
public NestedField Hide()
{

61
src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs

@ -20,11 +20,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
Validate.It(() => "Cannot add a new field.", error =>
{
if (command.ParentFieldId == null && !command.Partitioning.IsValidPartitioning())
{
error(new ValidationError("Partitioning is not valid.", nameof(command.Partitioning)));
}
if (!command.Name.IsPropertyName())
{
error(new ValidationError("Name must be a valid property name.", nameof(command.Name)));
@ -44,9 +39,30 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
}
}
if (schema.FieldsByName.ContainsKey(command.Name))
if (command.ParentFieldId.HasValue)
{
var parentId = command.ParentFieldId.Value;
if (!schema.FieldsById.TryGetValue(parentId, out var rootField) || !(rootField is IArrayField arrayField))
{
throw new DomainObjectNotFoundException(parentId.ToString(), "Fields", typeof(Schema));
}
if (arrayField.FieldsByName.ContainsKey(command.Name))
{
error(new ValidationError($"There is already a field with name '{command.Name}'", nameof(command.Name)));
}
}
else
{
error(new ValidationError($"There is already a field with name '{command.Name}'", nameof(command.Name)));
if (command.ParentFieldId == null && !command.Partitioning.IsValidPartitioning())
{
error(new ValidationError("Partitioning is not valid.", nameof(command.Partitioning)));
}
if (schema.FieldsByName.ContainsKey(command.Name))
{
error(new ValidationError($"There is already a field with name '{command.Name}'", nameof(command.Name)));
}
}
});
}
@ -80,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
});
}
public static void CanDelete(Schema schema, DeleteField command)
public static void CanHide(Schema schema, HideField command)
{
Guard.NotNull(command, nameof(command));
@ -90,46 +106,53 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
throw new DomainException("Schema field is locked.");
}
if (field.IsHidden)
{
throw new DomainException("Schema field is already hidden.");
}
}
public static void CanHide(Schema schema, HideField command)
public static void CanDisable(Schema schema, DisableField command)
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId);
if (field.IsHidden)
if (field.IsDisabled)
{
throw new DomainException("Schema field is already hidden.");
throw new DomainException("Schema field is already disabled.");
}
}
public static void CanShow(Schema schema, ShowField command)
public static void CanDelete(Schema schema, DeleteField command)
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId);
if (!field.IsHidden)
if (field.IsLocked)
{
throw new DomainException("Schema field is already visible.");
throw new DomainException("Schema field is locked.");
}
}
public static void CanDisable(Schema schema, DisableField command)
public static void CanShow(Schema schema, ShowField command)
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId);
if (field.IsDisabled)
if (!field.IsHidden)
{
throw new DomainException("Schema field is already disabled.");
throw new DomainException("Schema field is already visible.");
}
}
public static void CanEnable(Schema schema, EnableField command)
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId);
if (!field.IsDisabled)
@ -142,7 +165,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId, null);
var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId);
if (field.IsLocked)
{
@ -154,7 +177,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{
if (parentId.HasValue)
{
if (!schema.FieldsById.TryGetValue(parentId.Value, out var rootField) || !(rootField is ArrayField arrayField))
if (!schema.FieldsById.TryGetValue(parentId.Value, out var rootField) || !(rootField is IArrayField arrayField))
{
throw new DomainObjectNotFoundException(parentId.ToString(), "Fields", typeof(Schema));
}

2
src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs

@ -188,7 +188,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State
protected void On(FieldLocked @event, FieldRegistry registry)
{
SchemaDef = SchemaDef.LockField(@event.FieldId.Id);
SchemaDef = SchemaDef.LockField(@event.FieldId.Id, @event.ParentFieldId?.Id);
}
protected void On(FieldDisabled @event, FieldRegistry registry)

5
src/Squidex/Areas/Api/Controllers/Schemas/Models/NestedFieldDto.cs

@ -28,6 +28,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// </summary>
public bool IsHidden { get; set; }
/// <summary>
/// Defines if the field is locked.
/// </summary>
public bool IsLocked { get; set; }
/// <summary>
/// Defines if the field is disabled.
/// </summary>

72
src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs

@ -69,7 +69,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// 201 => Schema field created.
/// 400 => Schema field properties not valid.
/// 409 => Schema field name already in use.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
[HttpPost]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/")]
@ -119,7 +119,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema fields reorderd.
/// 400 => Schema field ids do not cover the fields of the schema.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/ordering/")]
@ -142,7 +142,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field updated.
/// 400 => Schema field properties not valid or field is locked.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/")]
@ -167,7 +167,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field updated.
/// 400 => Schema field properties not valid or field is locked.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")]
@ -190,10 +190,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field shown.
/// 400 => Schema field already locked.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
/// <remarks>
/// A hidden field is not part of the API response, but can still be edited in the portal.
/// A locked field cannot be updated or deleted.
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/lock/")]
@ -206,6 +206,32 @@ namespace Squidex.Areas.Api.Controllers.Schemas
return NoContent();
}
/// <summary>
/// Lock a nested schema field.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="name">The name of the schema.</param>
/// <param name="parentId">The parent field id.</param>
/// <param name="id">The id of the field to lock.</param>
/// <returns>
/// 204 => Schema field hidden.
/// 400 => Schema field already hidden.
/// 404 => Field, schema, or app not found.
/// </returns>
/// <remarks>
/// A locked field cannot be edited or deleted.
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/lock/")]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiCosts(1)]
public async Task<IActionResult> LockNestedField(string app, string name, long parentId, long id)
{
await CommandBus.PublishAsync(new LockField { ParentFieldId = parentId, FieldId = id });
return NoContent();
}
/// <summary>
/// Hide a schema field.
/// </summary>
@ -215,10 +241,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field hidden.
/// 400 => Schema field already hidden.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
/// <remarks>
/// A locked field cannot be edited or deleted.
/// A hidden field is not part of the API response, but can still be edited in the portal.
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/hide/")]
@ -244,7 +270,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// 404 => Field, schema, or app not found.
/// </returns>
/// <remarks>
/// A locked field cannot be edited or deleted.
/// A hidden field is not part of the API response, but can still be edited in the portal.
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/hide/")]
@ -266,7 +292,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field shown.
/// 400 => Schema field already visible.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
/// <remarks>
/// A hidden field is not part of the API response, but can still be edited in the portal.
@ -292,7 +318,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field shown.
/// 400 => Schema field already visible.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
/// <remarks>
/// A hidden field is not part of the API response, but can still be edited in the portal.
@ -317,11 +343,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field enabled.
/// 400 => Schema field already enabled.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
/// <remarks>
/// A disabled field cannot not be edited in the squidex portal anymore,
/// but will be part of the API response.
/// A disabled field cannot not be edited in the squidex portal anymore, but will be part of the API response.
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/enable/")]
@ -344,11 +369,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field enabled.
/// 400 => Schema field already enabled.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
/// <remarks>
/// A disabled field cannot not be edited in the squidex portal anymore,
/// but will be part of the API response.
/// A disabled field cannot not be edited in the squidex portal anymore, but will be part of the API response.
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/enable/")]
@ -370,11 +394,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field disabled.
/// 400 => Schema field already disabled.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
/// <remarks>
/// A disabled field cannot not be edited in the squidex portal anymore,
/// but will be part of the API response.
/// A disabled field cannot not be edited in the squidex portal anymore, but will be part of the API response.
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/disable/")]
@ -397,11 +420,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field disabled.
/// 400 => Schema field already disabled.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
/// <remarks>
/// A disabled field cannot not be edited in the squidex portal anymore,
/// but will be part of the API response.
/// A disabled field cannot not be edited in the squidex portal anymore, but will be part of the API response.
/// </remarks>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/disable/")]
@ -423,7 +445,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field deleted.
/// 400 => Field is locked.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
[HttpDelete]
[Route("apps/{app}/schemas/{name}/fields/{id:long}/")]
@ -445,7 +467,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// <returns>
/// 204 => Schema field deleted.
/// 400 => Field is locked.
/// 404 => Field, schema or app not found.
/// 404 => Schema, field or app not found.
/// </returns>
[HttpDelete]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")]

3
src/Squidex/app/app.module.ts

@ -12,7 +12,8 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router';
import { DndModule } from 'ng2-dnd';
import { DragulaModule } from 'ng2-dragula';
import { AppComponent } from './app.component';

10
src/Squidex/app/features/schemas/pages/schema/field-wizard.component.html

@ -1,7 +1,13 @@
<form [formGroup]="addFieldForm.form" (ngSubmit)="addField(false)">
<sqx-modal-dialog (closed)="complete()" large="true">
<ng-container title>
Create Field
<ng-container *ngIf="parent; else noParent">
Add Nested Field
</ng-container>
<ng-template #noParent>
Add Field
</ng-template>
</ng-container>
<ng-container content>
@ -36,7 +42,7 @@
<input type="text" class="form-control" formControlName="name" maxlength="40" #nameInput placeholder="Enter field name" sqxFocusOnInit />
</div>
<div class="form-group">
<div class="form-group" *ngIf="!parent">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="isLocalizable" formControlName="isLocalizable" />
<label class="form-check-label" for="isLocalizable">

16
src/Squidex/app/features/schemas/pages/schema/field-wizard.component.ts

@ -5,12 +5,13 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import {
AddFieldForm,
fieldTypes,
RootFieldDto,
SchemaDetailsDto,
SchemasState,
Types
@ -21,13 +22,16 @@ import {
styleUrls: ['./field-wizard.component.scss'],
templateUrl: './field-wizard.component.html'
})
export class FieldWizardComponent {
export class FieldWizardComponent implements OnInit {
@ViewChild('nameInput')
public nameInput: ElementRef;
@Input()
public schema: SchemaDetailsDto;
@Input()
public parent: RootFieldDto;
@Output()
public completed = new EventEmitter();
@ -41,6 +45,12 @@ export class FieldWizardComponent {
) {
}
public ngOnInit() {
if (this.parent) {
this.fieldTypes = this.fieldTypes.filter(x => x.type !== 'Array');
}
}
public complete() {
this.completed.emit();
}
@ -49,7 +59,7 @@ export class FieldWizardComponent {
const value = this.addFieldForm.submit();
if (value) {
this.schemasState.addField(this.schema, value)
this.schemasState.addField(this.schema, value, this.parent)
.subscribe(dto => {
this.addFieldForm.submitCompleted({ type: fieldTypes[0].type });

64
src/Squidex/app/features/schemas/pages/schema/field.component.html

@ -6,7 +6,7 @@
<i class="field-icon icon-type-{{field.properties.fieldType}}"></i>
<span [class.field-hidden]="field.isHidden" [attr.title]="field.isHidden ? 'Hidden Field' : 'Visible Field'">{{field.displayName}}</span>
<span class="field-partitioning" *ngIf="field.isLocalizable">localizable</span>
<span class="field-partitioning" *ngIf="field['isLocalizable']">localizable</span>
</span>
</div>
<div class="col col-tags">
@ -27,36 +27,38 @@
<i class="icon-dots"></i>
</button>
<div class="dropdown-menu" *sqxModalView="dropdown" [sqxModalTarget]="optionsButton" @fade>
<a class="dropdown-item" (click)="enableField()" *ngIf="field.isDisabled" [class.disabled]="field.isLocked">
<a class="dropdown-item" (click)="enableField()" *ngIf="field.isDisabled">
Enable in UI
</a>
<a class="dropdown-item" (click)="disableField()" *ngIf="!field.isDisabled" [class.disabled]="field.isLocked">
<a class="dropdown-item" (click)="disableField()" *ngIf="!field.isDisabled">
Disable in UI
</a>
<a class="dropdown-item" (click)="hideField()" *ngIf="!field.isHidden" [class.disabled]="field.isLocked">
<a class="dropdown-item" (click)="hideField()" *ngIf="!field.isHidden && !field.isLocked">
Hide in API
</a>
<a class="dropdown-item" (click)="showField()" *ngIf="field.isHidden" [class.disabled]="field.isLocked">
<a class="dropdown-item" (click)="showField()" *ngIf="field.isHidden && !field.isLocked">
Show in API
</a>
<div class="dropdown-divider"></div>
<ng-container *ngIf="!field.isLocked">
<div class="dropdown-divider"></div>
<a class="dropdown-item" *ngIf="!field.isLocked"
(sqxConfirmClick)="lockField()"
confirmTitle="Lock field"
confirmText="Do you really want to lock the field? Lock fields cannot be deleted or changed.">
Lock and prevent changes
</a>
<a class="dropdown-item"
(sqxConfirmClick)="lockField()"
confirmTitle="Lock field"
confirmText="Do you really want to lock the field? Locked fields cannot be deleted or changed anymore.">
Lock and prevent changes
</a>
<div class="dropdown-divider"></div>
<div class="dropdown-divider"></div>
<a class="dropdown-item dropdown-item-delete" [class.disabled]="field.isLocked"
(sqxConfirmClick)="deleteField()"
confirmTitle="Delete field"
confirmText="Do you really want to delete the field?">
Delete
</a>
<a class="dropdown-item dropdown-item-delete"
(sqxConfirmClick)="deleteField()"
confirmTitle="Delete field"
confirmText="Do you really want to delete the field?">
Delete
</a>
</ng-container>
</div>
</div>
</div>
@ -98,4 +100,28 @@
</div>
</form>
</div>
<div class="nested-fields" *ngIf="field.properties.fieldType === 'Array'">
<span class="nested-field-line-v"></span>
<div class="nested-field" *ngFor="let nested of field['nested']; let i = index;" dnd-sortable [sortableIndex]="i" (sqxSorted)="sortFields($event)">
<span class="nested-field-line-h"></span>
<sqx-field [field]="nested" [schema]="schema" [parent]="field" [patterns]="patterns"></sqx-field>
</div>
<div class="nested-field nested-field-add">
<span class="nested-field-line-h"></span>
<button class="btn btn-success btn-sm" (click)="addFieldDialog.show()">
<i class="icon icon-plus"></i> Add Nested Field
</button>
</div>
<ng-container *sqxModalView="addFieldDialog;onRoot:true;closeAuto:false">
<sqx-field-wizard [schema]="schema" [parent]="field"
(completed)="addFieldDialog.hide()">
</sqx-field-wizard>
</ng-container>
</div>
</div>

39
src/Squidex/app/features/schemas/pages/schema/field.component.scss

@ -2,6 +2,9 @@
@import '_mixins';
$field-header: #e7ebef;
$field-line: #c7cfd7;
$padding: 1rem;
.table-items-row-details {
& {
@ -24,6 +27,38 @@ $field-header: #e7ebef;
}
}
.nested-fields {
padding: $padding;
padding-left: 2 * $padding;
position: relative;
border: 0;
background: $color-border;
}
.nested-field {
& {
position: relative;
}
&-add {
border: 2px dashed $field-line;
position: relative;
padding: 1rem;
}
&-line-v {
@include absolute($padding, auto, 3 * $padding + .25rem, $padding);
border-left: 2px dashed $field-line;
width: 2px;
}
&-line-h {
@include absolute(-2px, auto, 50%, -$padding);
border-bottom: 2px dashed $field-line;
width: $padding - .25rem;
}
}
.field {
&-icon {
margin-right: 1rem;
@ -53,3 +88,7 @@ $field-header: #e7ebef;
max-width: 6rem;
}
}
.dnd-sortable-drag {
border: 0;
}

17
src/Squidex/app/features/schemas/pages/schema/field.component.ts

@ -16,10 +16,10 @@ import {
fadeAnimation,
ImmutableArray,
ModalView,
NestedFieldDto,
RootFieldDto,
SchemaDetailsDto,
SchemasState,
Types,
UpdateFieldDto
} from '@app/shared';
@ -38,6 +38,9 @@ export class FieldComponent implements OnInit {
@Input()
public schema: SchemaDetailsDto;
@Input()
public parent: RootFieldDto;
@Input()
public patterns: ImmutableArray<AppPatternDto>;
@ -48,6 +51,8 @@ export class FieldComponent implements OnInit {
public editForm: EditFieldForm;
public addFieldDialog = new ModalView();
constructor(
private readonly formBuilder: FormBuilder,
private readonly schemasState: SchemasState
@ -58,7 +63,7 @@ export class FieldComponent implements OnInit {
this.editForm = new EditFieldForm(this.formBuilder);
this.editForm.load(this.field.properties);
if (Types.is(this.field, RootFieldDto) && this.field.isLocked) {
if (this.field.isLocked) {
this.editForm.form.disable();
}
}
@ -96,10 +101,12 @@ export class FieldComponent implements OnInit {
this.schemasState.hideField(this.schema, this.field).onErrorResumeNext().subscribe();
}
public sortFields(fields: NestedFieldDto[]) {
this.schemasState.sortFields(this.schema, fields, <any>this.field).subscribe();
}
public lockField() {
if (Types.is(this.field, RootFieldDto)) {
this.schemasState.lockField(this.schema, this.field).onErrorResumeNext().subscribe();
}
this.schemasState.lockField(this.schema, this.field).onErrorResumeNext().subscribe();
}
public save() {

2
src/Squidex/app/shared/components/help.component.html

@ -1,4 +1,4 @@
<sqx-panel desiredWidth="16rem" isBlank="true">
<sqx-panel desiredWidth="16rem" isBlank="true" [isLazyLoaded]="false">
<ng-container title>
Help
</ng-container>

2
src/Squidex/app/shared/components/history.component.html

@ -1,4 +1,4 @@
<sqx-panel desiredWidth="16rem" isBlank="true">
<sqx-panel desiredWidth="16rem" isBlank="true" [isLazyLoaded]="false">
<ng-container title>
Activity
</ng-container>

39
src/Squidex/app/shared/services/schemas.service.spec.ts

@ -516,6 +516,32 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to lock field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.lockField('my-app', 'my-schema', 1, undefined, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/lock');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make put request to lock nested field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.lockField('my-app', 'my-schema', 1, 13, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/13/nested/1/lock');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
it('should make put request to enable field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
@ -658,17 +684,4 @@ describe('SchemasService', () => {
req.flush({});
}));
it('should make put request to lock field',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
schemasService.lockField('my-app', 'my-schema', 1, version).subscribe();
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields/1/lock');
expect(req.request.method).toEqual('PUT');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({});
}));
});

46
src/Squidex/app/shared/services/schemas.service.ts

@ -52,6 +52,9 @@ export const fieldTypes = [
}, {
type: 'Tags',
description: 'Special format for tags.'
}, {
type: 'Array',
description: 'List of embedded objects.'
}
];
@ -118,6 +121,8 @@ export class SchemaDto extends Model {
public readonly version: Version
) {
super();
this.onCreated();
}
public onCreated() {
@ -148,14 +153,16 @@ export class SchemaDetailsDto extends SchemaDto {
public onCreated() {
super.onCreated();
this.listFields = this.fields.filter(x => x.properties.isListField);
if (this.fields) {
this.listFields = this.fields.filter(x => x.properties.isListField);
if (this.listFields.length === 0 && this.fields.length > 0) {
this.listFields = [this.fields[0]];
}
if (this.listFields.length === 0 && this.fields.length > 0) {
this.listFields = [this.fields[0]];
}
if (this.listFields.length === 0) {
this.listFields = [<any>{ properties: {} }];
if (this.listFields.length === 0) {
this.listFields = [<any>{ properties: {} }];
}
}
}
@ -171,7 +178,10 @@ export class FieldDto extends Model {
constructor(
public readonly fieldId: number,
public readonly name: string,
public readonly properties: FieldPropertiesDto
public readonly properties: FieldPropertiesDto,
public readonly isLocked: boolean = false,
public readonly isHidden: boolean = false,
public readonly isDisabled: boolean = false
) {
super();
}
@ -203,12 +213,12 @@ export class RootFieldDto extends FieldDto {
constructor(fieldId: number, name: string, properties: FieldPropertiesDto,
public readonly partitioning: string,
public readonly isHidden: boolean = false,
public readonly isDisabled: boolean = false,
public readonly isLocked: boolean = false,
isLocked: boolean = false,
isHidden: boolean = false,
isDisabled: boolean = false,
public readonly nested: NestedFieldDto[] = []
) {
super(fieldId, name, properties);
super(fieldId, name, properties, isLocked, isHidden, isDisabled);
this.onCreated();
}
@ -221,10 +231,11 @@ export class RootFieldDto extends FieldDto {
export class NestedFieldDto extends FieldDto {
constructor(fieldId: number, name: string, properties: FieldPropertiesDto,
public readonly parentId: number,
public readonly isHidden: boolean = false,
public readonly isDisabled: boolean = false
isLocked: boolean = false,
isHidden: boolean = false,
isDisabled: boolean = false
) {
super(fieldId, name, properties);
super(fieldId, name, properties, isLocked, isHidden, isDisabled);
this.onCreated();
}
@ -830,6 +841,7 @@ export class SchemasService {
nestedItem.name,
nestedPropertiesDto,
item.fieldId,
nestedItem.isLocked,
nestedItem.isHidden,
nestedItem.isDisabled);
});
@ -840,9 +852,9 @@ export class SchemasService {
item.name,
propertiesDto,
item.partitioning,
item.isLocked,
item.isHidden,
item.isDisabled,
item.isLocked,
nested || []);
});
@ -1001,8 +1013,8 @@ export class SchemasService {
.pretifyError('Failed to update field. Please reload.');
}
public lockField(appName: string, schemaName: string, fieldId: number, version: Version): Observable<Versioned<any>> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/${fieldId}/lock`);
public lockField(appName: string, schemaName: string, fieldId: number, parentId: number | undefined, version: Version): Observable<Versioned<any>> {
const url = this.buildUrl(appName, schemaName, parentId, `/${fieldId}/lock`);
return HTTP.putVersioned(this.http, url, {}, version)
.do(() => {

14
src/Squidex/app/shared/state/schemas.state.spec.ts

@ -458,7 +458,7 @@ describe('SchemasState', () => {
});
it('should mark field locked and update user info when field locked', () => {
schemasService.setup(x => x.lockField(app, schema.name, field1.fieldId, version))
schemasService.setup(x => x.lockField(app, schema.name, field1.fieldId, undefined, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.lockField(schema, field1, modified).subscribe();
@ -469,6 +469,18 @@ describe('SchemasState', () => {
expectToBeModified(schema_1);
});
it('should mark field locked and update user info when nested field locked', () => {
schemasService.setup(x => x.lockField(app, schema.name, nested1.fieldId, 2, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));
schemasState.lockField(schema, nested1, modified).subscribe();
const schema_1 = <SchemaDetailsDto>schemasState.snapshot.schemas.at(1);
expect(schema_1.fields[1].nested[0].isLocked).toBeTruthy();
expectToBeModified(schema_1);
});
it('should unmark field hidden and update user info when field shown', () => {
schemasService.setup(x => x.showField(app, schema.name, field2.fieldId, undefined, version))
.returns(() => Observable.of(new Versioned<any>(newVersion, {})));

24
src/Squidex/app/shared/state/schemas.state.ts

@ -325,14 +325,6 @@ export class SchemasState extends State<Snapshot> {
.notify(this.dialogs);
}
public lockField(schema: SchemaDetailsDto, field: RootFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.lockField(this.appName, schema.name, field.fieldId, schema.version)
.do(dto => {
this.replaceSchema(updateField(schema, setLocked(field, true), this.user, dto.version, now));
})
.notify(this.dialogs);
}
public addField(schema: SchemaDetailsDto, request: AddFieldDto, parent?: RootFieldDto, now?: DateTime): Observable<FieldDto> {
return this.schemasService.postField(this.appName, schema.name, request, pid(parent), schema.version)
.do(dto => {
@ -356,6 +348,14 @@ export class SchemasState extends State<Snapshot> {
.notify(this.dialogs);
}
public lockField(schema: SchemaDetailsDto, field: AnyFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.lockField(this.appName, schema.name, field.fieldId, pidof(field), schema.version)
.do(dto => {
this.replaceField(schema, setLocked(field, true), dto.version, now);
})
.notify(this.dialogs);
}
public enableField(schema: SchemaDetailsDto, field: AnyFieldDto, now?: DateTime): Observable<any> {
return this.schemasService.enableField(this.appName, schema.name, field.fieldId, pidof(field), schema.version)
.do(dto => {
@ -556,14 +556,14 @@ const replaceNested = (parent: RootFieldDto, nested: NestedFieldDto[]) =>
const removeNested = (parent: RootFieldDto, nested: NestedFieldDto) =>
parent.with({ nested: parent.nested.filter(f => f.fieldId !== nested.fieldId) });
const setLocked = (field: RootFieldDto, isLocked: boolean) =>
field.with({ isLocked });
const setLocked = <T extends FieldDto>(field: T, isLocked: boolean) =>
<T>field.with({ isLocked });
const setHidden = <T extends FieldDto>(field: T, isHidden: boolean) =>
<T>field.with(<any>{ isHidden });
<T>field.with({ isHidden });
const setDisabled = <T extends FieldDto>(field: T, isDisabled: boolean) =>
<T>field.with(<any>{ isDisabled });
<T>field.with({ isDisabled });
const update = <T extends FieldDto>(field: T, properties: FieldPropertiesDto) =>
<T>field.with({ properties });

18
src/Squidex/app/theme/icomoon/demo.html

@ -9,10 +9,26 @@
<link rel="stylesheet" href="style.css"></head>
<body>
<div class="bgc1 clearfix">
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs:&nbsp;89)</small></h1>
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs:&nbsp;90)</small></h1>
</div>
<div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: 14</h1>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-type-Array">
</span>
<span class="mls"> icon-type-Array</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e956" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe956;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-exclamation">

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.eot

Binary file not shown.

1
src/Squidex/app/theme/icomoon/fonts/icomoon.svg

@ -93,6 +93,7 @@
<glyph unicode="&#xe953;" glyph-name="spinner" d="M192 448c0 12.18 0.704 24.196 2.030 36.022l-184.98 60.104c-5.916-31.14-9.050-63.264-9.050-96.126 0-147.23 62.166-279.922 161.654-373.324l114.284 157.296c-52.124 56.926-83.938 132.758-83.938 216.028zM832 448c0-83.268-31.812-159.102-83.938-216.028l114.284-157.296c99.488 93.402 161.654 226.094 161.654 373.324 0 32.862-3.132 64.986-9.048 96.126l-184.98-60.104c1.324-11.828 2.028-23.842 2.028-36.022zM576 761.592c91.934-18.662 169.544-76.742 214.45-155.826l184.978 60.102c-73.196 155.42-222.24 268.060-399.428 290.156v-194.432zM233.55 605.768c44.906 79.084 122.516 137.164 214.45 155.826v194.43c-177.188-22.096-326.23-134.736-399.426-290.154l184.976-60.102zM644.556 156.672c-40.39-18.408-85.272-28.672-132.556-28.672s-92.166 10.264-132.554 28.67l-114.292-157.31c73.206-40.366 157.336-63.36 246.846-63.36s173.64 22.994 246.848 63.36l-114.292 157.312z" />
<glyph unicode="&#xe954;" glyph-name="hour-glass" d="M728.992 448c137.754 87.334 231.008 255.208 231.008 448 0 21.676-1.192 43.034-3.478 64h-889.042c-2.29-20.968-3.48-42.326-3.48-64 0-192.792 93.254-360.666 231.006-448-137.752-87.334-231.006-255.208-231.006-448 0-21.676 1.19-43.034 3.478-64h889.042c2.288 20.966 3.478 42.324 3.478 64 0.002 192.792-93.252 360.666-231.006 448zM160 0c0 186.912 80.162 345.414 224 397.708v100.586c-143.838 52.29-224 210.792-224 397.706v0h704c0-186.914-80.162-345.416-224-397.706v-100.586c143.838-52.294 224-210.796 224-397.708h-704zM619.626 290.406c-71.654 40.644-75.608 93.368-75.626 125.366v64.228c0 31.994 3.804 84.914 75.744 125.664 38.504 22.364 71.808 56.348 97.048 98.336h-409.582c25.266-42.032 58.612-76.042 97.166-98.406 71.654-40.644 75.606-93.366 75.626-125.366v-64.228c0-31.992-3.804-84.914-75.744-125.664-72.622-42.18-126.738-125.684-143.090-226.336h501.67c-16.364 100.708-70.53 184.248-143.212 226.406z" />
<glyph unicode="&#xe955;" glyph-name="exclamation" horiz-adv-x="366" d="M292.571 237.714v-128c0-20-16.571-36.571-36.571-36.571h-146.286c-20 0-36.571 16.571-36.571 36.571v128c0 20 16.571 36.571 36.571 36.571h146.286c20 0 36.571-16.571 36.571-36.571zM309.714 841.143l-16-438.857c-0.571-20-17.714-36.571-37.714-36.571h-146.286c-20 0-37.143 16.571-37.714 36.571l-16 438.857c-0.571 20 15.429 36.571 35.429 36.571h182.857c20 0 36-16.571 35.429-36.571z" />
<glyph unicode="&#xe956;" glyph-name="type-Array" d="M832 682.057h-657.92c-15.36 0-25.6 10.24-25.6 25.6s10.24 25.6 25.6 25.6h657.92c15.36 0 25.6-10.24 25.6-25.6s-10.24-25.6-25.6-25.6zM832 497.737h-409.6c-15.36 0-25.6 10.24-25.6 25.6s10.24 25.6 25.6 25.6h409.6c15.36 0 25.6-10.24 25.6-25.6s-10.24-25.6-25.6-25.6zM832 308.297h-409.6c-15.36 0-25.6 10.24-25.6 25.6s10.24 25.6 25.6 25.6h409.6c15.36 0 25.6-10.24 25.6-25.6s-10.24-25.6-25.6-25.6zM832 118.857h-409.6c-15.36 0-25.6 10.24-25.6 25.6s10.24 25.6 25.6 25.6h409.6c15.36 0 25.6-10.24 25.6-25.6s-10.24-25.6-25.6-25.6z" />
<glyph unicode="&#xe9ca;" glyph-name="earth" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512-0.002c-62.958 0-122.872 13.012-177.23 36.452l233.148 262.29c5.206 5.858 8.082 13.422 8.082 21.26v96c0 17.674-14.326 32-32 32-112.99 0-232.204 117.462-233.374 118.626-6 6.002-14.14 9.374-22.626 9.374h-128c-17.672 0-32-14.328-32-32v-192c0-12.122 6.848-23.202 17.69-28.622l110.31-55.156v-187.886c-116.052 80.956-192 215.432-192 367.664 0 68.714 15.49 133.806 43.138 192h116.862c8.488 0 16.626 3.372 22.628 9.372l128 128c6 6.002 9.372 14.14 9.372 22.628v77.412c40.562 12.074 83.518 18.588 128 18.588 70.406 0 137.004-16.26 196.282-45.2-4.144-3.502-8.176-7.164-12.046-11.036-36.266-36.264-56.236-84.478-56.236-135.764s19.97-99.5 56.236-135.764c36.434-36.432 85.218-56.264 135.634-56.26 3.166 0 6.342 0.080 9.518 0.236 13.814-51.802 38.752-186.656-8.404-372.334-0.444-1.744-0.696-3.488-0.842-5.224-81.324-83.080-194.7-134.656-320.142-134.656z" />
<glyph unicode="&#xf00a;" glyph-name="grid" d="M292.571 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM292.571 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 237.714v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM658.286 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 530.286v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857zM1024 822.857v-109.714c0-30.286-24.571-54.857-54.857-54.857h-182.857c-30.286 0-54.857 24.571-54.857 54.857v109.714c0 30.286 24.571 54.857 54.857 54.857h182.857c30.286 0 54.857-24.571 54.857-54.857z" />
<glyph unicode="&#xf0c9;" glyph-name="list" horiz-adv-x="878" d="M877.714 182.857v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 475.428v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571zM877.714 768v-73.143c0-20-16.571-36.571-36.571-36.571h-804.571c-20 0-36.571 16.571-36.571 36.571v73.143c0 20 16.571 36.571 36.571 36.571h804.571c20 0 36.571-16.571 36.571-36.571z" />

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.ttf

Binary file not shown.

BIN
src/Squidex/app/theme/icomoon/fonts/icomoon.woff

Binary file not shown.

21
src/Squidex/app/theme/icomoon/icons/type-Array.svg

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="Layer_1" inkscape:version="0.91 r13725" sodipodi:docname="json.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 40 40"
style="enable-background:new 0 0 40 40;" xml:space="preserve">
<sodipodi:namedview bordercolor="#666666" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview9" inkscape:current-layer="Layer_1" inkscape:cx="20" inkscape:cy="20" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="742" inkscape:window-x="2052" inkscape:window-y="328" inkscape:zoom="5.9" objecttolerance="10" pagecolor="#ffffff" showgrid="false">
</sodipodi:namedview>
<g>
<path d="M32.5,10.5H6.8c-0.6,0-1-0.4-1-1s0.4-1,1-1h25.7c0.6,0,1,0.4,1,1S33.1,10.5,32.5,10.5z"/>
</g>
<g>
<path d="M32.5,17.7h-16c-0.6,0-1-0.4-1-1s0.4-1,1-1h16c0.6,0,1,0.4,1,1S33.1,17.7,32.5,17.7z"/>
</g>
<g>
<path d="M32.5,25.1h-16c-0.6,0-1-0.4-1-1s0.4-1,1-1h16c0.6,0,1,0.4,1,1S33.1,25.1,32.5,25.1z"/>
</g>
<g>
<path d="M32.5,32.5h-16c-0.6,0-1-0.4-1-1s0.4-1,1-1h16c0.6,0,1,0.4,1,1S33.1,32.5,32.5,32.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

398
src/Squidex/app/theme/icomoon/selection.json

File diff suppressed because it is too large

13
src/Squidex/app/theme/icomoon/style.css

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?ajcf5j');
src: url('fonts/icomoon.eot?ajcf5j#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?ajcf5j') format('truetype'),
url('fonts/icomoon.woff?ajcf5j') format('woff'),
url('fonts/icomoon.svg?ajcf5j#icomoon') format('svg');
src: url('fonts/icomoon.eot?ebsjd4');
src: url('fonts/icomoon.eot?ebsjd4#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?ebsjd4') format('truetype'),
url('fonts/icomoon.woff?ebsjd4') format('woff'),
url('fonts/icomoon.svg?ebsjd4#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
@ -24,6 +24,9 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-type-Array:before {
content: "\e956";
}
.icon-exclamation:before {
content: "\e955";
}

12753
src/Squidex/package-lock.json

File diff suppressed because it is too large

1
src/Squidex/package.json

@ -33,6 +33,7 @@
"moment": "2.20.1",
"mousetrap": "1.6.1",
"ng2-dnd": "5.0.2",
"ng2-dragula": "^1.5.0",
"oidc-client": "1.4.1",
"pikaday": "1.7.0",
"progressbar.js": "1.0.1",

55
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaFieldTests.cs

@ -57,25 +57,26 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
public static IEnumerable<object[]> InvalidStates = new[]
{
new object[] { A<EnableField>(GuardSchemaField.CanEnable), S(s => s) },
new object[] { A<DisableField>(GuardSchemaField.CanDisable), S(s => s.DisableField(1)) },
new object[] { A<EnableField>(GuardSchemaField.CanEnable), S(s => s) },
new object[] { A<HideField>(GuardSchemaField.CanHide), S(s => s.HideField(1)) },
new object[] { A<LockField>(GuardSchemaField.CanLock), S(s => s.LockField(1)) },
new object[] { A<ShowField>(GuardSchemaField.CanShow), S(s => s.LockField(1)) }
new object[] { A<ShowField>(GuardSchemaField.CanShow), S(s => s.LockField(1)) },
new object[] { A<LockField>(GuardSchemaField.CanLock), S(s => s.LockField(1)) }
};
public static IEnumerable<object[]> InvalidNestedStates = new[]
{
new object[] { A<EnableField>(GuardSchemaField.CanEnable), S(s => s) },
new object[] { A<DisableField>(GuardSchemaField.CanDisable), S(s => s.DisableField(301, 3)) },
new object[] { A<EnableField>(GuardSchemaField.CanEnable), S(s => s) },
new object[] { A<HideField>(GuardSchemaField.CanHide), S(s => s.HideField(301, 3)) },
new object[] { A<ShowField>(GuardSchemaField.CanShow), S(s => s) }
new object[] { A<ShowField>(GuardSchemaField.CanShow), S(s => s) },
new object[] { A<LockField>(GuardSchemaField.CanLock), S(s => s.LockField(301, 3)) }
};
public static IEnumerable<object[]> ValidStates = new[]
{
new object[] { A<EnableField>(GuardSchemaField.CanEnable), S(s => s.DisableField(1)) },
new object[] { A<DisableField>(GuardSchemaField.CanDisable), S(s => s) },
new object[] { A<EnableField>(GuardSchemaField.CanEnable), S(s => s.DisableField(1)) },
new object[] { A<HideField>(GuardSchemaField.CanHide), S(s => s) },
new object[] { A<ShowField>(GuardSchemaField.CanShow), S(s => s.HideField(1)) }
};
@ -161,6 +162,16 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
Assert.Throws<DomainException>(() => GuardSchemaField.CanDelete(schema_1, command));
}
[Fact]
public void CanHide_should_throw_exception_if_locked()
{
var command = new HideField { FieldId = 1 };
var schema_1 = schema_0.UpdateField(1, f => f.Lock());
Assert.Throws<DomainException>(() => GuardSchemaField.CanHide(schema_1, command));
}
[Fact]
public void CanDelete_should_not_throw_exception_if_not_locked()
{
@ -172,7 +183,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
[Fact]
public void CanUpdate_should_throw_exception_if_locked()
{
var command = new UpdateField { FieldId = 1, Properties = new StringFieldProperties() };
var command = new UpdateField { FieldId = 1, Properties = validProperties };
var schema_1 = schema_0.UpdateField(1, f => f.Lock());
@ -182,7 +193,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
[Fact]
public void CanUpdate_should_not_throw_exception_if_not_locked()
{
var command = new UpdateField { FieldId = 1, Properties = new StringFieldProperties() };
var command = new UpdateField { FieldId = 1, Properties = validProperties };
GuardSchemaField.CanUpdate(schema_0, command);
}
@ -206,7 +217,15 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
[Fact]
public void CanAdd_should_throw_exception_if_field_already_exists()
{
var command = new AddField { Name = "field1", Properties = new StringFieldProperties() };
var command = new AddField { Name = "field1", Properties = validProperties };
Assert.Throws<ValidationException>(() => GuardSchemaField.CanAdd(schema_0, command));
}
[Fact]
public void CanAdd_should_throw_exception_if_nested_field_already_exists()
{
var command = new AddField { Name = "field301", Properties = validProperties, ParentFieldId = 3 };
Assert.Throws<ValidationException>(() => GuardSchemaField.CanAdd(schema_0, command));
}
@ -243,10 +262,26 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
Assert.Throws<ValidationException>(() => GuardSchemaField.CanAdd(schema_0, command));
}
[Fact]
public void CanAdd_should_throw_exception_if_parent_not_exists()
{
var command = new AddField { Name = "field302", Properties = validProperties, ParentFieldId = 99 };
Assert.Throws<DomainObjectNotFoundException>(() => GuardSchemaField.CanAdd(schema_0, command));
}
[Fact]
public void CanAdd_should_not_throw_exception_if_field_not_exists()
{
var command = new AddField { Name = "field4", Properties = new StringFieldProperties() };
var command = new AddField { Name = "field4", Properties = validProperties };
GuardSchemaField.CanAdd(schema_0, command);
}
[Fact]
public void CanAdd_should_not_throw_exception_if_field_exists_in_root()
{
var command = new AddField { Name = "field1", Properties = validProperties, ParentFieldId = 3 };
GuardSchemaField.CanAdd(schema_0, command);
}

62
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs

@ -243,26 +243,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
);
}
[Fact]
public async Task LockField_should_create_events_and_update_state()
{
var command = new LockField { FieldId = 1 };
await ExecuteCreateAsync();
await ExecuteAddFieldAsync(fieldName);
var result = await sut.ExecuteAsync(CreateCommand(command));
result.ShouldBeEquivalent(new EntitySavedResult(2));
Assert.False(GetField(1).IsDisabled);
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new FieldLocked { FieldId = fieldId })
);
}
[Fact]
public async Task Reorder_should_create_events_and_update_state()
{
@ -384,6 +364,48 @@ namespace Squidex.Domain.Apps.Entities.Schemas
);
}
[Fact]
public async Task LockField_should_create_events_and_update_state()
{
var command = new LockField { FieldId = 1 };
await ExecuteCreateAsync();
await ExecuteAddFieldAsync(fieldName);
var result = await sut.ExecuteAsync(CreateCommand(command));
result.ShouldBeEquivalent(new EntitySavedResult(2));
Assert.False(GetField(1).IsDisabled);
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new FieldLocked { FieldId = fieldId })
);
}
[Fact]
public async Task LockField_should_create_events_and_update_state_for_array()
{
var command = new LockField { ParentFieldId = 1, FieldId = 2 };
await ExecuteCreateAsync();
await ExecuteAddArrayFieldAsync();
await ExecuteAddFieldAsync(fieldName, 1);
var result = await sut.ExecuteAsync(CreateCommand(command));
result.ShouldBeEquivalent(new EntitySavedResult(3));
Assert.False(GetField(1).IsLocked);
Assert.True(GetNestedField(1, 2).IsLocked);
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new FieldLocked { ParentFieldId = arrayId, FieldId = nestedId })
);
}
[Fact]
public async Task HideField_should_create_events_and_update_state()
{

Loading…
Cancel
Save