Browse Source

Merge pull request #304 from Squidex/feature_singleton

Singleton Content Types.
pull/305/head
Sebastian Stehle 8 years ago
committed by GitHub
parent
commit
a38db3a62a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs
  2. 5
      src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs
  3. 1
      src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs
  4. 23
      src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs
  5. 42
      src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs
  6. 2
      src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs
  7. 2
      src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs
  8. 5
      src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs
  9. 2
      src/Squidex.Domain.Apps.Events/Schemas/SchemaCreated.cs
  10. 4
      src/Squidex.Infrastructure/States/Store.cs
  11. 5
      src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs
  12. 5
      src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs
  13. 5
      src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs
  14. 3
      src/Squidex/Config/Domain/EntitiesServices.cs
  15. 82
      src/Squidex/app/features/content/pages/content/content-page.component.html
  16. 46
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html
  17. 62
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.scss
  18. 2
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts
  19. 26
      src/Squidex/app/shared/guards/schema-must-exist-published.guard.spec.ts
  20. 10
      src/Squidex/app/shared/guards/schema-must-exist-published.guard.ts
  21. 13
      src/Squidex/app/shared/services/schemas.service.spec.ts
  22. 11
      src/Squidex/app/shared/services/schemas.service.ts
  23. 2
      src/Squidex/app/shared/state/contents.forms.spec.ts
  24. 16
      src/Squidex/app/shared/state/contents.state.ts
  25. 1
      src/Squidex/app/shared/state/schemas.forms.ts
  26. 8
      src/Squidex/app/shared/state/schemas.state.spec.ts
  27. 34
      src/Squidex/app/theme/icomoon/demo.html
  28. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.eot
  29. 2
      src/Squidex/app/theme/icomoon/fonts/icomoon.svg
  30. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.ttf
  31. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.woff
  32. 95
      src/Squidex/app/theme/icomoon/icons/multiple-content.svg
  33. 69
      src/Squidex/app/theme/icomoon/icons/single-content.svg
  34. 261
      src/Squidex/app/theme/icomoon/selection.json
  35. 16
      src/Squidex/app/theme/icomoon/style.css
  36. 66
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/Guard/GuardContentTests.cs
  37. 60
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/SingletonCommandMiddlewareTests.cs
  38. 5
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs

24
src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs

@ -61,17 +61,17 @@ namespace Squidex.Domain.Apps.Entities.Contents
case CreateContent createContent:
return CreateReturnAsync(createContent, async c =>
{
GuardContent.CanCreate(c);
var ctx = await CreateContext(c.AppId.Id, c.SchemaId.Id, () => "Failed to create content.");
var operationContext = await CreateContext(c.AppId.Id, c.SchemaId.Id, () => "Failed to create content.");
GuardContent.CanCreate(ctx.Schema, c);
await operationContext.ExecuteScriptAndTransformAsync(x => x.ScriptCreate, "Create", c, c.Data, null);
await operationContext.EnrichAsync(c.Data);
await operationContext.ValidateAsync(c.Data);
await ctx.ExecuteScriptAndTransformAsync(x => x.ScriptCreate, "Create", c, c.Data, null);
await ctx.EnrichAsync(c.Data);
await ctx.ValidateAsync(c.Data);
if (c.Publish)
{
await operationContext.ExecuteScriptAsync(x => x.ScriptChange, "Published", c, c.Data, null);
await ctx.ExecuteScriptAsync(x => x.ScriptChange, "Published", c, c.Data, null);
}
Create(c);
@ -100,7 +100,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
try
{
GuardContent.CanChangeContentStatus(Snapshot.IsPending, Snapshot.Status, c);
var ctx = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, () => "Failed to change content.");
GuardContent.CanChangeContentStatus(ctx.Schema, Snapshot.IsPending, Snapshot.Status, c);
if (c.DueTime.HasValue)
{
@ -114,8 +116,6 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
else
{
var ctx = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, () => "Failed to change content.");
await ctx.ExecuteScriptAsync(x => x.ScriptChange, c.Status, c, Snapshot.Data);
ChangeStatus(c);
@ -138,11 +138,11 @@ namespace Squidex.Domain.Apps.Entities.Contents
case DeleteContent deleteContent:
return UpdateAsync(deleteContent, async c =>
{
GuardContent.CanDelete(c);
var ctx = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, () => "Failed to delete content.");
var operationContext = await CreateContext(Snapshot.AppId.Id, Snapshot.SchemaId.Id, () => "Failed to delete content.");
GuardContent.CanDelete(ctx.Schema, c);
await operationContext.ExecuteScriptAsync(x => x.ScriptDelete, "Delete", c, Snapshot.Data);
await ctx.ExecuteScriptAsync(x => x.ScriptDelete, "Delete", c, Snapshot.Data);
Delete(c);
});

5
src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs

@ -31,6 +31,11 @@ namespace Squidex.Domain.Apps.Entities.Contents
private IAppEntity appEntity;
private Func<string> message;
public ISchemaEntity Schema
{
get { return schemaEntity; }
}
public static async Task<ContentOperationContext> CreateAsync(
Guid appId,
Guid schemaId,

1
src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs

@ -63,7 +63,6 @@ namespace Squidex.Domain.Apps.Entities.Contents
public async Task<IContentEntity> FindContentAsync(QueryContext context, Guid id, long version = -1)
{
Guard.NotNull(context, nameof(context));
Guard.NotEmpty(id, nameof(id));
var schema = await GetSchemaAsync(context);

23
src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs

@ -5,16 +5,18 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents.Guards
{
public static class GuardContent
{
public static void CanCreate(CreateContent command)
public static void CanCreate(ISchemaEntity schema, CreateContent command)
{
Guard.NotNull(command, nameof(command));
@ -22,6 +24,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
{
ValidateData(command, e);
});
if (schema.IsSingleton && command.ContentId != Guid.Empty)
{
throw new DomainException("Singleton content cannot be created.");
}
}
public static void CanUpdate(UpdateContent command)
@ -54,10 +61,15 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
}
}
public static void CanChangeContentStatus(bool isPending, Status status, ChangeContentStatus command)
public static void CanChangeContentStatus(ISchemaEntity schema, bool isPending, Status status, ChangeContentStatus command)
{
Guard.NotNull(command, nameof(command));
if (schema.IsSingleton && command.Status != Status.Published)
{
throw new DomainException("Singleton content archived or unpublished.");
}
Validate.It(() => "Cannot change status.", e =>
{
if (!StatusFlow.Exists(command.Status))
@ -86,9 +98,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
});
}
public static void CanDelete(DeleteContent command)
public static void CanDelete(ISchemaEntity schema, DeleteContent command)
{
Guard.NotNull(command, nameof(command));
if (schema.IsSingleton)
{
throw new DomainException("Singleton content cannot be deleted.");
}
}
private static void ValidateData(ContentDataCommand command, AddValidation e)

42
src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs

@ -0,0 +1,42 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class SingletonCommandMiddleware : ICommandMiddleware
{
public async Task HandleAsync(CommandContext context, Func<Task> next)
{
await next();
if (context.IsCompleted &&
context.Command is CreateSchema createSchema &&
createSchema.Singleton)
{
var schemaId = new NamedId<Guid>(createSchema.SchemaId, createSchema.Name);
var data = new NamedContentData();
var contentId = Guid.Empty;
var content = new CreateContent { Data = data, ContentId = contentId, SchemaId = schemaId, Publish = true };
SimpleMapper.Map(context.Command, content);
await context.CommandBus.PublishAsync(content);
}
}
}
}

2
src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs

@ -22,6 +22,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands
public SchemaProperties Properties { get; set; }
public bool Singleton { get; set; }
public bool Publish { get; set; }
public CreateSchema()

2
src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs

@ -23,6 +23,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas
string Category { get; }
bool IsSingleton { get; }
bool IsPublished { get; }
bool IsDeleted { get; }

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

@ -37,6 +37,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State
[JsonProperty]
public bool IsDeleted { get; set; }
[JsonProperty]
public bool IsSingleton { get; set; }
[JsonProperty]
public string ScriptQuery { get; set; }
@ -65,6 +68,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State
{
Name = @event.Name;
IsSingleton = @event.Singleton;
var schema = new Schema(@event.Name);
if (@event.Properties != null)

2
src/Squidex.Domain.Apps.Events/Schemas/SchemaCreated.cs

@ -20,6 +20,8 @@ namespace Squidex.Domain.Apps.Events.Schemas
public SchemaProperties Properties { get; set; }
public bool Singleton { get; set; }
public bool Publish { get; set; }
}
}

4
src/Squidex.Infrastructure/States/Store.cs

@ -42,7 +42,7 @@ namespace Squidex.Infrastructure.States
public IPersistence WithEventSourcing(Type owner, TKey key, Func<Envelope<IEvent>, Task> applyEvent)
{
Guard.NotDefault(key, nameof(key));
Guard.NotNull(key, nameof(key));
var snapshotStore = (ISnapshotStore<object, TKey>)services.GetService(typeof(ISnapshotStore<object, TKey>));
@ -51,7 +51,7 @@ namespace Squidex.Infrastructure.States
private IPersistence<TState> CreatePersistence<TState>(Type owner, TKey key, PersistenceMode mode, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
{
Guard.NotDefault(key, nameof(key));
Guard.NotNull(key, nameof(key));
var snapshotStore = (ISnapshotStore<TState, TKey>)services.GetService(typeof(ISnapshotStore<TState, TKey>));

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

@ -32,6 +32,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// </summary>
public List<CreateSchemaFieldDto> Fields { get; set; }
/// <summary>
/// Set to true to allow a single content item only.
/// </summary>
public bool Singleton { get; set; }
/// <summary>
/// Set it to true to autopublish the schema.
/// </summary>

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

@ -36,6 +36,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// </summary>
public string Category { get; set; }
/// <summary>
/// Indicates if the schema is a singleton.
/// </summary>
public bool IsSingleton { get; set; }
/// <summary>
/// Indicates if the schema is published.
/// </summary>

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

@ -39,6 +39,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
[Required]
public SchemaPropertiesDto Properties { get; set; }
/// <summary>
/// Indicates if the schema is a singleton.
/// </summary>
public bool IsSingleton { get; set; }
/// <summary>
/// Indicates if the schema is published.
/// </summary>

3
src/Squidex/Config/Domain/EntitiesServices.cs

@ -128,6 +128,9 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<SchemasByAppIndexCommandMiddleware>()
.As<ICommandMiddleware>();
services.AddSingletonAs<SingletonCommandMiddleware>()
.As<ICommandMiddleware>();
services.AddSingletonAs<CreateBlogCommandMiddleware>()
.As<ICommandMiddleware>();

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

@ -3,7 +3,7 @@
<form [formGroup]="contentForm.form" (ngSubmit)="saveAndPublish()">
<sqx-panel desiredWidth="*" [showSidebar]="content">
<ng-container title>
<a class="btn btn-link" (click)="back()">
<a class="btn btn-link" (click)="back()" *ngIf="!schema.isSingleton">
<i class="icon-angle-left"></i>
</a>
@ -32,7 +32,8 @@
</ng-container>
<ng-template #notNew>
<div class="dropdown dropdown-options ml-1" *ngIf="content">
<button type="button" class="btn btn-outline-secondary" (click)="dropdown.toggle()" [class.active]="dropdown.isOpen | async" #optionsButton>
<button type="button" class="btn btn-outline-secondary" (click)="dropdown.toggle()" [disabled]="schema.isSingleton && !content.isPending"
[class.active]="dropdown.isOpen | async" #optionsButton>
<sqx-content-status
[status]="content.status"
[scheduledTo]="content.scheduleJob?.status"
@ -42,42 +43,47 @@
</sqx-content-status>
</button>
<div class="dropdown-menu" *sqxModalView="dropdown" [sqxModalTarget]="optionsButton" @fade>
<ng-container *ngIf="content.isPending">
<a class="dropdown-item" (click)="discardChanges()">
Discard changes
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" (click)="publishChanges()">
Publish changes
</a>
</ng-container>
<a class="dropdown-item" (click)="publish()" *ngIf="content.status === 'Draft'">
Publish
</a>
<a class="dropdown-item" (click)="unpublish()" *ngIf="content.status === 'Published'">
Unpublish
</a>
<a class="dropdown-item" (click)="archive()" *ngIf="content.status !== 'Archived'">
Archive
</a>
<a class="dropdown-item" (click)="restore()" *ngIf="content.status === 'Archived'">
Restore
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item dropdown-item-delete"
(sqxConfirmClick)="delete()"
confirmTitle="Delete content"
confirmText="Do you really want to delete the content?">
Delete
</a>
</div>
</div>
<ng-container *ngIf="content.isPending || !schema.isSingleton">
<div class="dropdown-menu" *sqxModalView="dropdown" [sqxModalTarget]="optionsButton" @fade>
<ng-container *ngIf="content.isPending">
<a class="dropdown-item" (click)="discardChanges()">
Discard changes
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" (click)="publishChanges()">
Publish changes
</a>
</ng-container>
<ng-container *ngIf="!schema.isSingleton">
<a class="dropdown-item" (click)="publish()" *ngIf="content.status === 'Draft'">
Publish
</a>
<a class="dropdown-item" (click)="unpublish()" *ngIf="content.status === 'Published'">
Unpublish
</a>
<a class="dropdown-item" (click)="archive()" *ngIf="content.status !== 'Archived'">
Archive
</a>
<a class="dropdown-item" (click)="restore()" *ngIf="content.status === 'Archived'">
Restore
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item dropdown-item-delete"
(sqxConfirmClick)="delete()"
confirmTitle="Delete content"
confirmText="Do you really want to delete the content?">
Delete
</a>
</ng-container>
</div>
</ng-container>
</div>
<ng-container *ngIf="content.status !== 'Archived'">
<button type="button" class="btn btn-secondary ml-1" (click)="saveAsProposal()" *ngIf="content.status === 'Published'">

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

@ -12,7 +12,7 @@
<ng-container content>
<sqx-form-error [error]="createForm.error | async"></sqx-form-error>
<div class="form-group">
<div class="form-group name-group">
<label for="schemaName">Name</label>
<sqx-control-errors for="name" submitOnly="true" [submitted]="createForm.submitted | async"></sqx-control-errors>
@ -28,10 +28,50 @@
</div>
<div class="form-group">
<a class="btn btn-sm btn-link" (click)="toggleImport()" [class.hidden]="showImport">
<div class="row no-gutters">
<div class="col-6 type">
<label>
<input type="radio" class="radio-input" formControlName="singleton" [value]="false" />
<div class="row no-gutters">
<div class="col col-auto">
<div class="type-icon" [class.active]="createForm.form.controls['singleton'].value !== true">
<i class="icon-multiple-content"></i>
</div>
</div>
<div class="col-lg">
<div class="type-title">Multiple contents</div>
<div class="type-text text-muted">Blog posts, texts, pages...</div>
</div>
</div>
</label>
</div>
<div class="col-6 type">
<label>
<input type="radio" class="radio-input" formControlName="singleton" [value]="true" />
<div class="row no-gutters">
<div class="col col-auto">
<div class="type-icon" [class.active]="createForm.form.controls['singleton'].value === true">
<i class="icon-single-content"></i>
</div>
</div>
<div class="col-lg">
<div class="type-title">Single content</div>
<div class="type-text text-muted">Settings, single content types</div>
</div>
</div>
</label>
</div>
</div>
</div>
<div class="form-group">
<a class="btn btn-sm btn-link force" (click)="toggleImport()" [class.hidden]="showImport">
Import schema
</a>
<a class="btn btn-sm btn-link" (click)="toggleImport()" [class.hidden]="!showImport">
<a class="btn btn-sm btn-link force" (click)="toggleImport()" [class.hidden]="!showImport">
Hide
</a>

62
src/Squidex/app/features/schemas/pages/schemas/schema-form.component.scss

@ -1,6 +1,8 @@
@import '_vars';
@import '_mixins';
$icon-size: 4.5rem;
.btn-link {
margin-top: -1rem;
margin-left: -.5rem;
@ -10,4 +12,64 @@
height: 15rem !important;
margin-bottom: .5rem;
margin-top: 0;
}
.name-group {
margin-bottom: 1.5rem;
}
.type {
& {
margin-bottom: .5rem;
}
&-title {
font-weight: bold;
}
&-text {
font-size: .9rem;
}
&-icon {
& {
@include border-radius;
height: $icon-size;
color: $color-theme-blue;
cursor: pointer;
border: 1px solid $color-border;
background: transparent;
margin-right: .5rem;
line-height: $icon-size;
font-size: 1.75rem;
font-weight: normal;
text-align: center;
width: $icon-size;
}
.radio-input {
display: none;
}
&.active {
& {
@include box-shadow(0, 0, 10px, .5);
background: $color-theme-blue;
border-color: $color-theme-blue;
color: $color-dark-foreground;
}
&:hover {
color: $color-dark-foreground;
}
}
&:hover {
border-color: $color-border-dark;
}
}
.radio-input {
display: none;
}
}

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

@ -67,7 +67,7 @@ export class SchemaFormComponent implements OnInit {
const value = this.createForm.submit();
if (value) {
const schemaDto = Object.assign(value.import || {}, { name: value.name });
const schemaDto = Object.assign(value.import || {}, { name: value.name, singleton: value.singleton });
this.schemasState.create(schemaDto)
.subscribe(dto => {

26
src/Squidex/app/shared/guards/schema-must-exist-published.guard.spec.ts

@ -5,10 +5,12 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Router } from '@angular/router';
import { Router, RouterStateSnapshot } from '@angular/router';
import { of } from 'rxjs';
import { IMock, Mock, Times } from 'typemoq';
import { MathHelper } from '@app/framework';
import { SchemaDetailsDto } from './../services/schemas.service';
import { SchemasState } from './../state/schemas.state';
import { SchemaMustExistPublishedGuard } from './schema-must-exist-published.guard';
@ -21,6 +23,7 @@ describe('SchemaMustExistPublishedGuard', () => {
};
let schemasState: IMock<SchemasState>;
let state: RouterStateSnapshot = <any>{ url: 'current-url' };
let router: IMock<Router>;
let schemaGuard: SchemaMustExistPublishedGuard;
@ -36,7 +39,7 @@ describe('SchemaMustExistPublishedGuard', () => {
let result: boolean;
schemaGuard.canActivate(route).subscribe(x => {
schemaGuard.canActivate(route, state).subscribe(x => {
result = x;
}).unsubscribe();
@ -51,7 +54,7 @@ describe('SchemaMustExistPublishedGuard', () => {
let result: boolean;
schemaGuard.canActivate(route).subscribe(x => {
schemaGuard.canActivate(route, state).subscribe(x => {
result = x;
}).unsubscribe();
@ -66,7 +69,7 @@ describe('SchemaMustExistPublishedGuard', () => {
let result: boolean;
schemaGuard.canActivate(route).subscribe(x => {
schemaGuard.canActivate(route, state).subscribe(x => {
result = x;
}).unsubscribe();
@ -74,4 +77,19 @@ describe('SchemaMustExistPublishedGuard', () => {
router.verify(x => x.navigate(['/404']), Times.once());
});
it('should redirect to content when singleton', () => {
schemasState.setup(x => x.select('123'))
.returns(() => of(<SchemaDetailsDto>{ isSingleton: true }));
let result: boolean;
schemaGuard.canActivate(route, state).subscribe(x => {
result = x;
}).unsubscribe();
expect(result!).toBeFalsy();
router.verify(x => x.navigate([state.url, MathHelper.EMPTY_GUID]), Times.once());
});
});

10
src/Squidex/app/shared/guards/schema-must-exist-published.guard.ts

@ -6,11 +6,11 @@
*/
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { allParams } from '@app/framework';
import { allParams, MathHelper } from '@app/framework';
import { SchemasState } from './../state/schemas.state';
@ -22,7 +22,7 @@ export class SchemaMustExistPublishedGuard implements CanActivate {
) {
}
public canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
const schemaName = allParams(route)['schemaName'];
const result =
@ -31,6 +31,10 @@ export class SchemaMustExistPublishedGuard implements CanActivate {
if (!dto || !dto.isPublished) {
this.router.navigate(['/404']);
}
if (dto && dto.isSingleton && state.url.indexOf(MathHelper.EMPTY_GUID) < 0) {
this.router.navigate([state.url, MathHelper.EMPTY_GUID]);
}
}),
map(s => s !== null && s.isPublished));

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

@ -78,6 +78,7 @@ describe('SchemasService', () => {
label: 'label1',
hints: 'hints1'
},
isSingleton: true,
isPublished: true,
created: '2016-12-12T10:10',
createdBy: 'Created1',
@ -94,6 +95,7 @@ describe('SchemasService', () => {
label: 'label2',
hints: 'hints2'
},
isSingleton: true,
isPublished: true,
created: '2016-10-12T10:10',
createdBy: 'Created2',
@ -106,11 +108,11 @@ describe('SchemasService', () => {
expect(schemas!).toEqual(
[
new SchemaDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true,
new SchemaDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true, true,
DateTime.parseISO_UTC('2016-12-12T10:10'), 'Created1',
DateTime.parseISO_UTC('2017-12-12T10:10'), 'LastModifiedBy1',
new Version('11')),
new SchemaDto('id2', 'name2', 'category2', new SchemaPropertiesDto('label2', 'hints2'), true,
new SchemaDto('id2', 'name2', 'category2', new SchemaPropertiesDto('label2', 'hints2'), true, true,
DateTime.parseISO_UTC('2016-10-12T10:10'), 'Created2',
DateTime.parseISO_UTC('2017-10-12T10:10'), 'LastModifiedBy2',
new Version('22'))
@ -135,6 +137,7 @@ describe('SchemasService', () => {
id: 'id1',
name: 'name1',
category: 'category1',
isSingleton: true,
isPublished: true,
created: '2016-12-12T10:10',
createdBy: 'Created1',
@ -290,7 +293,7 @@ describe('SchemasService', () => {
});
expect(schema!).toEqual(
new SchemaDetailsDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true,
new SchemaDetailsDto('id1', 'name1', 'category1', new SchemaPropertiesDto('label1', 'hints1'), true, true,
DateTime.parseISO_UTC('2016-12-12T10:10'), 'Created1',
DateTime.parseISO_UTC('2017-12-12T10:10'), 'LastModifiedBy1',
new Version('2'),
@ -319,7 +322,7 @@ describe('SchemasService', () => {
it('should make post request to create schema',
inject([SchemasService, HttpTestingController], (schemasService: SchemasService, httpMock: HttpTestingController) => {
const dto = new CreateSchemaDto('name');
const dto = new CreateSchemaDto('name', undefined, undefined, true);
let schema: SchemaDetailsDto;
@ -341,7 +344,7 @@ describe('SchemasService', () => {
});
expect(schema!).toEqual(
new SchemaDetailsDto('1', dto.name, '', new SchemaPropertiesDto(), false, now, user, now, user, new Version('2'), []));
new SchemaDetailsDto('1', dto.name, '', new SchemaPropertiesDto(), true, false, now, user, now, user, new Version('2'), []));
}));
it('should make put request to update schema',

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

@ -34,6 +34,7 @@ export class SchemaDto extends Model {
public readonly name: string,
public readonly category: string,
public readonly properties: SchemaPropertiesDto,
public readonly isSingleton: boolean,
public readonly isPublished: boolean,
public readonly created: DateTime,
public readonly createdBy: string,
@ -53,7 +54,7 @@ export class SchemaDetailsDto extends SchemaDto {
public listFields: RootFieldDto[];
public listFieldsEditable: RootFieldDto[];
constructor(id: string, name: string, category: string, properties: SchemaPropertiesDto, isPublished: boolean, created: DateTime, createdBy: string, lastModified: DateTime, lastModifiedBy: string, version: Version,
constructor(id: string, name: string, category: string, properties: SchemaPropertiesDto, isSingleton: boolean, isPublished: boolean, created: DateTime, createdBy: string, lastModified: DateTime, lastModifiedBy: string, version: Version,
public readonly fields: RootFieldDto[],
public readonly scriptQuery?: string,
public readonly scriptCreate?: string,
@ -61,7 +62,7 @@ export class SchemaDetailsDto extends SchemaDto {
public readonly scriptDelete?: string,
public readonly scriptChange?: string
) {
super(id, name, category, properties, isPublished, created, createdBy, lastModified, lastModifiedBy, version);
super(id, name, category, properties, isSingleton, isPublished, created, createdBy, lastModified, lastModifiedBy, version);
this.onCloned();
}
@ -177,7 +178,8 @@ export class CreateSchemaDto {
constructor(
public readonly name: string,
public readonly fields?: RootFieldDto[],
public readonly properties?: SchemaPropertiesDto
public readonly properties?: SchemaPropertiesDto,
public readonly isSingleton?: boolean
) {
}
}
@ -240,6 +242,7 @@ export class SchemasService {
item.id,
item.name,
item.category, properties,
item.isSingleton,
item.isPublished,
DateTime.parseISO_UTC(item.created), item.createdBy,
DateTime.parseISO_UTC(item.lastModified), item.lastModifiedBy,
@ -300,6 +303,7 @@ export class SchemasService {
body.name,
body.category,
properties,
body.isSingleton,
body.isPublished,
DateTime.parseISO_UTC(body.created), body.createdBy,
DateTime.parseISO_UTC(body.lastModified), body.lastModifiedBy,
@ -328,6 +332,7 @@ export class SchemasService {
dto.name,
'',
dto.properties || new SchemaPropertiesDto(),
dto.isSingleton === true,
false,
now, user,
now, user,

2
src/Squidex/app/shared/state/contents.forms.spec.ts

@ -371,7 +371,7 @@ describe('StringField', () => {
});
function createSchema(properties: SchemaPropertiesDto, index = 1, fields: RootFieldDto[]) {
return new SchemaDetailsDto('id' + index, 'schema' + index, '', properties, true, null!, null!, null!, null!, null!, fields);
return new SchemaDetailsDto('id' + index, 'schema' + index, '', properties, false, true, null!, null!, null!, null!, null!, fields);
}
function createField(properties: FieldPropertiesDto, index = 1, partitioning = 'languages') {

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

@ -213,7 +213,9 @@ export abstract class ContentsStateBase extends State<Snapshot> {
tap(dto => {
this.dialogs.notifyInfo('Content updated successfully.');
this.replaceContent(updateData(content, dto.payload, this.user, dto.version, now));
if (dto.version.value !== content.version.value) {
this.replaceContent(updateData(content, dto.payload, this.user, dto.version, now));
}
}),
notify(this.dialogs));
}
@ -223,7 +225,9 @@ export abstract class ContentsStateBase extends State<Snapshot> {
tap(dto => {
this.dialogs.notifyInfo('Content updated successfully.');
this.replaceContent(updateDataDraft(content, dto.payload, this.user, dto.version, now));
if (dto.version.value !== content.version.value) {
this.replaceContent(updateDataDraft(content, dto.payload, this.user, dto.version, now));
}
}),
notify(this.dialogs));
}
@ -233,7 +237,9 @@ export abstract class ContentsStateBase extends State<Snapshot> {
tap(dto => {
this.dialogs.notifyInfo('Content updated successfully.');
this.replaceContent(discardChanges(content, this.user, dto.version, now));
if (dto.version.value !== content.version.value) {
this.replaceContent(discardChanges(content, this.user, dto.version, now));
}
}),
notify(this.dialogs));
}
@ -243,7 +249,9 @@ export abstract class ContentsStateBase extends State<Snapshot> {
tap(dto => {
this.dialogs.notifyInfo('Content updated successfully.');
this.replaceContent(updateData(content, dto.payload, this.user, dto.version, now));
if (dto.version.value !== content.version.value) {
this.replaceContent(updateData(content, dto.payload, this.user, dto.version, now));
}
}),
notify(this.dialogs));
}

1
src/Squidex/app/shared/state/schemas.forms.ts

@ -36,6 +36,7 @@ export class CreateSchemaForm extends Form<FormGroup> {
ValidatorsEx.pattern('[a-z0-9]+(\-[a-z0-9]+)*', 'Name can contain lower case letters (a-z), numbers and dashes only (not at the end).')
]
],
singleton: false,
import: {}
}));
}

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

@ -41,8 +41,8 @@ describe('SchemasState', () => {
const newVersion = new Version('2');
const oldSchemas = [
new SchemaDto('id1', 'name1', 'category1', {}, false, creation, creator, creation, creator, version),
new SchemaDto('id2', 'name2', 'category2', {}, true , creation, creator, creation, creator, version)
new SchemaDto('id1', 'name1', 'category1', {}, false, false, creation, creator, creation, creator, version),
new SchemaDto('id2', 'name2', 'category2', {}, false, true , creation, creator, creation, creator, version)
];
const nested1 = new NestedFieldDto(3, '3', createProperties('Number'), 2);
@ -52,7 +52,7 @@ describe('SchemasState', () => {
const field2 = new RootFieldDto(2, '2', createProperties('Array'), 'invariant', true, true, true, [nested1, nested2]);
const schema =
new SchemaDetailsDto('id2', 'name2', 'category2', {}, true,
new SchemaDetailsDto('id2', 'name2', 'category2', {}, false, true,
creation, creator,
creation, creator,
version,
@ -284,7 +284,7 @@ describe('SchemasState', () => {
it('should add schema to snapshot when created', () => {
const request = new CreateSchemaDto('newName');
const result = new SchemaDetailsDto('id4', 'newName', '', {}, false, modified, modifier, modified, modifier, version, []);
const result = new SchemaDetailsDto('id4', 'newName', '', {}, false, false, modified, modifier, modified, modifier, version, []);
schemasService.setup(x => x.postSchema(app, request, modifier, modified))
.returns(() => of(result));

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

@ -9,10 +9,42 @@
<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;90)</small></h1>
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs:&nbsp;92)</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-single-content">
</span>
<span class="mls"> icon-single-content</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e958" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe958;" 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-multiple-content">
</span>
<span class="mls"> icon-multiple-content</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e957" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe957;" 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-type-Array">

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

Binary file not shown.

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

@ -94,6 +94,8 @@
<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="&#xe957;" glyph-name="multiple-content" horiz-adv-x="1029" d="M777.143 4.571h-525.714c-89.143 0-160 70.857-160 160v297.143c0 89.143 70.857 160 160 160h525.714c89.143 0 160-70.857 160-160v-297.143c0-89.143-70.857-160-160-160zM251.429 576c-64 0-114.286-50.286-114.286-114.286v-297.143c0-64 50.286-114.286 114.286-114.286h525.714c64 0 114.286 50.286 114.286 114.286v297.143c0 64-50.286 114.286-114.286 114.286h-525.714zM731.429 370.286h-457.143c-13.714 0-22.857 9.143-22.857 22.857s9.143 22.857 22.857 22.857h457.143c13.714 0 22.857-9.143 22.857-22.857s-9.143-22.857-22.857-22.857zM502.857 210.286h-228.571c-13.714 0-22.857 9.143-22.857 22.857s9.143 22.857 22.857 22.857h228.571c13.714 0 22.857-9.143 22.857-22.857s-9.143-22.857-22.857-22.857zM777.143 690.286h-525.714c-13.714 0-22.857 9.143-22.857 22.857s9.143 22.857 22.857 22.857h525.714c13.714 0 22.857-9.143 22.857-22.857s-9.143-22.857-22.857-22.857zM685.714 804.571h-342.857c-13.714 0-22.857 9.143-22.857 22.857s9.143 22.857 22.857 22.857h342.857c13.714 0 22.857-9.143 22.857-22.857s-9.143-22.857-22.857-22.857z" />
<glyph unicode="&#xe958;" glyph-name="single-content" horiz-adv-x="1029" d="M251.429 850.277c-87.896 0-160-72.104-160-160v-525.714c0-87.896 72.104-160 160-160h525.714c87.896 0 160 72.104 160 160v525.714c0 87.896-72.104 160-160 160zM251.429 804.562h525.714c62.961 0 114.286-51.325 114.286-114.286v-525.714c0-62.961-51.325-114.286-114.286-114.286h-525.714c-62.961 0-114.286 51.325-114.286 114.286v525.714c0 62.961 51.325 114.286 114.286 114.286zM251.429 644.562c-0.096 0.001-0.21 0.002-0.323 0.002-12.625 0-22.859-10.235-22.859-22.859s10.235-22.859 22.859-22.859c0.114 0 0.227 0.001 0.34 0.002h525.697c0.096-0.001 0.21-0.002 0.323-0.002 12.625 0 22.859 10.235 22.859 22.859s-10.235 22.859-22.859 22.859c-0.114 0-0.227-0.001-0.34-0.002h0.017zM251.429 507.419c-0.096 0.001-0.21 0.002-0.323 0.002-12.625 0-22.859-10.235-22.859-22.859s10.235-22.859 22.859-22.859c0.114 0 0.227 0.001 0.34 0.002h297.126c0.096-0.001 0.21-0.002 0.323-0.002 12.625 0 22.859 10.235 22.859 22.859s-10.235 22.859-22.859 22.859c-0.114 0-0.227-0.001-0.34-0.002h0.017zM251.429 370.277c-0.096 0.001-0.21 0.002-0.323 0.002-12.625 0-22.859-10.235-22.859-22.859s10.235-22.859 22.859-22.859c0.114 0 0.227 0.001 0.34 0.002h297.126c0.096-0.001 0.21-0.002 0.323-0.002 12.625 0 22.859 10.235 22.859 22.859s-10.235 22.859-22.859 22.859c-0.114 0-0.227-0.001-0.34-0.002h0.017z" />
<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: 78 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.

95
src/Squidex/app/theme/icomoon/icons/multiple-content.svg

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 45 44.8"
style="enable-background:new 0 0 45 44.8;"
xml:space="preserve"
sodipodi:docname="multiple-content.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata29"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs27" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1017"
id="namedview25"
showgrid="false"
inkscape:lockguides="true"
inkscape:zoom="10.535714"
inkscape:cx="4.7504213"
inkscape:cy="20.4951"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:#3389FF;}
</style>
<g
id="g6"
style="fill:#000000">
<path
class="st0"
d="M34,41.4H11c-3.9,0-7-3.1-7-7v-13c0-3.9,3.1-7,7-7h23c3.9,0,7,3.1,7,7v13C41,38.3,37.9,41.4,34,41.4z M11,16.4 c-2.8,0-5,2.2-5,5v13c0,2.8,2.2,5,5,5h23c2.8,0,5-2.2,5-5v-13c0-2.8-2.2-5-5-5H11z"
id="path4"
style="fill:#000000" />
</g>
<g
id="g10"
style="fill:#000000">
<path
class="st0"
d="M32,25.4H12c-0.6,0-1-0.4-1-1s0.4-1,1-1h20c0.6,0,1,0.4,1,1S32.6,25.4,32,25.4z"
id="path8"
style="fill:#000000" />
</g>
<g
id="g14"
style="fill:#000000">
<path
class="st0"
d="M22,32.4H12c-0.6,0-1-0.4-1-1s0.4-1,1-1h10c0.6,0,1,0.4,1,1S22.6,32.4,22,32.4z"
id="path12"
style="fill:#000000" />
</g>
<g
id="g18"
style="fill:#000000">
<path
class="st0"
d="M34,11.4H11c-0.6,0-1-0.4-1-1s0.4-1,1-1h23c0.6,0,1,0.4,1,1S34.6,11.4,34,11.4z"
id="path16"
style="fill:#000000" />
</g>
<g
id="g22"
style="fill:#000000">
<path
class="st0"
d="M30,6.4H15c-0.6,0-1-0.4-1-1s0.4-1,1-1h15c0.6,0,1,0.4,1,1S30.6,6.4,30,6.4z"
id="path20"
style="fill:#000000" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

69
src/Squidex/app/theme/icomoon/icons/single-content.svg

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 45 44.8"
style="enable-background:new 0 0 45 44.8;"
xml:space="preserve"
sodipodi:docname="single-content.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata17"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs15" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1017"
id="namedview13"
showgrid="false"
inkscape:zoom="14.89975"
inkscape:cx="-30.136175"
inkscape:cy="28.891426"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style2">
.st0{fill:none;stroke:#3389FF;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
</style>
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 11,4.4003906 c -3.8454545,0 -7,3.1545455 -7,7.0000004 v 23 c 0,3.845454 3.1545455,7 7,7 h 23 c 3.845455,0 7,-3.154546 7,-7 v -23 C 41,7.5549361 37.845455,4.4003906 34,4.4003906 Z m 0,2 h 23 c 2.754545,0 5,2.2454546 5,5.0000004 v 23 c 0,2.754545 -2.245455,5 -5,5 H 11 c -2.7545455,0 -5,-2.245455 -5,-5 v -23 C 6,8.6458452 8.2454545,6.4003906 11,6.4003906 Z"
id="path4"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 11,13.400391 a 1.0001,1.0001 0 1 0 0,2 h 23 a 1.0001,1.0001 0 1 0 0,-2 z"
id="line6"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 11,19.400391 a 1.0001,1.0001 0 1 0 0,2 h 13 a 1.0001,1.0001 0 1 0 0,-2 z"
id="line8"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 11,25.400391 a 1.0001,1.0001 0 1 0 0,2 h 13 a 1.0001,1.0001 0 1 0 0,-2 z"
id="line10"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

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

@ -1,6 +1,87 @@
{
"IcoMoonType": "selection",
"icons": [
{
"icon": {
"paths": [
"M251.429 100.58c-87.896 0-160 72.104-160 160v525.714c0 87.896 72.104 160 160 160h525.714c87.896 0 160-72.104 160-160v-525.714c0-87.896-72.104-160-160-160zM251.429 146.295h525.714c62.961 0 114.286 51.325 114.286 114.286v525.714c0 62.961-51.325 114.286-114.286 114.286h-525.714c-62.961 0-114.286-51.325-114.286-114.286v-525.714c0-62.961 51.325-114.286 114.286-114.286z",
"M251.429 306.295c-0.096-0.001-0.21-0.002-0.323-0.002-12.625 0-22.859 10.235-22.859 22.859s10.235 22.859 22.859 22.859c0.114 0 0.227-0.001 0.34-0.002l-0.017 0h525.714c0.096 0.001 0.21 0.002 0.323 0.002 12.625 0 22.859-10.235 22.859-22.859s-10.235-22.859-22.859-22.859c-0.114 0-0.227 0.001-0.34 0.002l0.017-0z",
"M251.429 443.438c-0.096-0.001-0.21-0.002-0.323-0.002-12.625 0-22.859 10.235-22.859 22.859s10.235 22.859 22.859 22.859c0.114 0 0.227-0.001 0.34-0.002l-0.017 0h297.143c0.096 0.001 0.21 0.002 0.323 0.002 12.625 0 22.859-10.235 22.859-22.859s-10.235-22.859-22.859-22.859c-0.114 0-0.227 0.001-0.34 0.002l0.017-0z",
"M251.429 580.58c-0.096-0.001-0.21-0.002-0.323-0.002-12.625 0-22.859 10.235-22.859 22.859s10.235 22.859 22.859 22.859c0.114 0 0.227-0.001 0.34-0.002l-0.017 0h297.143c0.096 0.001 0.21 0.002 0.323 0.002 12.625 0 22.859-10.235 22.859-22.859s-10.235-22.859-22.859-22.859c-0.114 0-0.227 0.001-0.34 0.002l0.017-0z"
],
"attrs": [
{},
{},
{},
{}
],
"width": 1029,
"isMulticolor": false,
"isMulticolor2": false,
"grid": 14,
"tags": [
"single-content"
]
},
"attrs": [
{},
{},
{},
{}
],
"properties": {
"order": 112,
"id": 214,
"name": "single-content",
"prevSize": 28,
"code": 59736
},
"setIdx": 0,
"setId": 1,
"iconIdx": 0
},
{
"icon": {
"paths": [
"M777.143 946.286h-525.714c-89.143 0-160-70.857-160-160v-297.143c0-89.143 70.857-160 160-160h525.714c89.143 0 160 70.857 160 160v297.143c0 89.143-70.857 160-160 160zM251.429 374.857c-64 0-114.286 50.286-114.286 114.286v297.143c0 64 50.286 114.286 114.286 114.286h525.714c64 0 114.286-50.286 114.286-114.286v-297.143c0-64-50.286-114.286-114.286-114.286h-525.714z",
"M731.429 580.571h-457.143c-13.714 0-22.857-9.143-22.857-22.857s9.143-22.857 22.857-22.857h457.143c13.714 0 22.857 9.143 22.857 22.857s-9.143 22.857-22.857 22.857z",
"M502.857 740.571h-228.571c-13.714 0-22.857-9.143-22.857-22.857s9.143-22.857 22.857-22.857h228.571c13.714 0 22.857 9.143 22.857 22.857s-9.143 22.857-22.857 22.857z",
"M777.143 260.571h-525.714c-13.714 0-22.857-9.143-22.857-22.857s9.143-22.857 22.857-22.857h525.714c13.714 0 22.857 9.143 22.857 22.857s-9.143 22.857-22.857 22.857z",
"M685.714 146.286h-342.857c-13.714 0-22.857-9.143-22.857-22.857s9.143-22.857 22.857-22.857h342.857c13.714 0 22.857 9.143 22.857 22.857s-9.143 22.857-22.857 22.857z"
],
"attrs": [
{},
{},
{},
{},
{}
],
"width": 1029,
"isMulticolor": false,
"isMulticolor2": false,
"grid": 14,
"tags": [
"multiple-content"
]
},
"attrs": [
{},
{},
{},
{},
{}
],
"properties": {
"order": 113,
"id": 213,
"name": "multiple-content",
"prevSize": 28,
"code": 59735
},
"setIdx": 0,
"setId": 1,
"iconIdx": 1
},
{
"icon": {
"paths": [
@ -37,7 +118,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 0
"iconIdx": 3
},
{
"icon": {
@ -67,7 +148,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 1
"iconIdx": 4
},
{
"icon": {
@ -97,7 +178,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 2
"iconIdx": 5
},
{
"icon": {
@ -127,7 +208,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 3
"iconIdx": 6
},
{
"icon": {
@ -156,7 +237,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 4
"iconIdx": 7
},
{
"icon": {
@ -188,7 +269,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 5
"iconIdx": 8
},
{
"icon": {
@ -220,7 +301,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 6
"iconIdx": 9
},
{
"icon": {
@ -250,7 +331,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 7
"iconIdx": 10
},
{
"icon": {
@ -280,7 +361,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 8
"iconIdx": 11
},
{
"icon": {
@ -310,7 +391,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 9
"iconIdx": 12
},
{
"icon": {
@ -340,7 +421,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 10
"iconIdx": 13
},
{
"icon": {
@ -381,7 +462,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 11
"iconIdx": 14
},
{
"icon": {
@ -416,7 +497,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 12
"iconIdx": 15
},
{
"icon": {
@ -446,7 +527,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 13
"iconIdx": 16
},
{
"icon": {
@ -476,7 +557,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 14
"iconIdx": 17
},
{
"icon": {
@ -506,7 +587,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 15
"iconIdx": 18
},
{
"icon": {
@ -536,7 +617,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 16
"iconIdx": 19
},
{
"icon": {
@ -566,7 +647,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 17
"iconIdx": 20
},
{
"icon": {
@ -604,7 +685,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 18
"iconIdx": 21
},
{
"icon": {
@ -633,7 +714,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 19
"iconIdx": 22
},
{
"icon": {
@ -662,7 +743,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 20
"iconIdx": 23
},
{
"icon": {
@ -691,7 +772,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 21
"iconIdx": 24
},
{
"icon": {
@ -718,7 +799,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 22
"iconIdx": 25
},
{
"icon": {
@ -747,7 +828,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 23
"iconIdx": 26
},
{
"icon": {
@ -776,7 +857,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 24
"iconIdx": 27
},
{
"icon": {
@ -826,7 +907,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 25
"iconIdx": 28
},
{
"icon": {
@ -858,7 +939,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 26
"iconIdx": 29
},
{
"icon": {
@ -891,7 +972,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 27
"iconIdx": 30
},
{
"icon": {
@ -922,7 +1003,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 28
"iconIdx": 31
},
{
"icon": {
@ -959,7 +1040,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 29
"iconIdx": 32
},
{
"icon": {
@ -992,7 +1073,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 30
"iconIdx": 33
},
{
"icon": {
@ -1025,7 +1106,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 31
"iconIdx": 34
},
{
"icon": {
@ -1055,7 +1136,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 32
"iconIdx": 35
},
{
"icon": {
@ -1087,7 +1168,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 33
"iconIdx": 36
},
{
"icon": {
@ -1118,7 +1199,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 34
"iconIdx": 37
},
{
"icon": {
@ -1148,7 +1229,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 35
"iconIdx": 38
},
{
"icon": {
@ -1178,7 +1259,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 36
"iconIdx": 39
},
{
"icon": {
@ -1217,7 +1298,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 37
"iconIdx": 40
},
{
"icon": {
@ -1252,7 +1333,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 38
"iconIdx": 41
},
{
"icon": {
@ -1286,7 +1367,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 39
"iconIdx": 42
},
{
"icon": {
@ -1316,7 +1397,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 40
"iconIdx": 43
},
{
"icon": {
@ -1345,7 +1426,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 41
"iconIdx": 44
},
{
"icon": {
@ -1375,7 +1456,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 42
"iconIdx": 45
},
{
"icon": {
@ -1407,7 +1488,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 43
"iconIdx": 46
},
{
"icon": {
@ -1436,7 +1517,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 44
"iconIdx": 47
},
{
"icon": {
@ -1474,7 +1555,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 45
"iconIdx": 48
},
{
"icon": {
@ -1503,7 +1584,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 46
"iconIdx": 49
},
{
"icon": {
@ -1532,7 +1613,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 47
"iconIdx": 50
},
{
"icon": {
@ -1561,7 +1642,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 48
"iconIdx": 51
},
{
"icon": {
@ -1590,7 +1671,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 49
"iconIdx": 52
},
{
"icon": {
@ -1619,7 +1700,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 50
"iconIdx": 53
},
{
"icon": {
@ -1648,7 +1729,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 51
"iconIdx": 54
},
{
"icon": {
@ -1677,7 +1758,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 52
"iconIdx": 55
},
{
"icon": {
@ -1706,7 +1787,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 53
"iconIdx": 56
},
{
"icon": {
@ -1735,7 +1816,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 54
"iconIdx": 57
},
{
"icon": {
@ -1764,7 +1845,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 55
"iconIdx": 58
},
{
"icon": {
@ -1793,7 +1874,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 56
"iconIdx": 59
},
{
"icon": {
@ -1822,7 +1903,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 57
"iconIdx": 60
},
{
"icon": {
@ -1851,7 +1932,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 58
"iconIdx": 61
},
{
"icon": {
@ -1886,7 +1967,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 59
"iconIdx": 62
},
{
"icon": {
@ -1915,7 +1996,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 60
"iconIdx": 63
},
{
"icon": {
@ -1944,7 +2025,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 61
"iconIdx": 64
},
{
"icon": {
@ -1973,7 +2054,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 62
"iconIdx": 65
},
{
"icon": {
@ -2002,7 +2083,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 63
"iconIdx": 66
},
{
"icon": {
@ -2031,7 +2112,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 64
"iconIdx": 67
},
{
"icon": {
@ -2060,7 +2141,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 65
"iconIdx": 68
},
{
"icon": {
@ -2089,7 +2170,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 66
"iconIdx": 69
},
{
"icon": {
@ -2119,7 +2200,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 67
"iconIdx": 70
},
{
"icon": {
@ -2148,7 +2229,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 68
"iconIdx": 71
},
{
"icon": {
@ -2177,7 +2258,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 69
"iconIdx": 72
},
{
"icon": {
@ -2206,7 +2287,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 70
"iconIdx": 73
},
{
"icon": {
@ -2235,7 +2316,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 71
"iconIdx": 74
},
{
"icon": {
@ -2264,7 +2345,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 72
"iconIdx": 75
},
{
"icon": {
@ -2293,7 +2374,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 73
"iconIdx": 76
},
{
"icon": {
@ -2322,7 +2403,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 74
"iconIdx": 77
},
{
"icon": {
@ -2360,7 +2441,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 75
"iconIdx": 78
},
{
"icon": {
@ -2389,7 +2470,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 76
"iconIdx": 79
},
{
"icon": {
@ -2418,7 +2499,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 77
"iconIdx": 80
},
{
"icon": {
@ -2447,7 +2528,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 78
"iconIdx": 81
},
{
"icon": {
@ -2476,7 +2557,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 79
"iconIdx": 82
},
{
"icon": {
@ -2505,7 +2586,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 80
"iconIdx": 83
},
{
"icon": {
@ -2534,7 +2615,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 81
"iconIdx": 84
},
{
"icon": {
@ -2563,7 +2644,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 82
"iconIdx": 85
},
{
"icon": {
@ -2592,7 +2673,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 83
"iconIdx": 86
},
{
"icon": {
@ -2625,7 +2706,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 84
"iconIdx": 87
},
{
"icon": {
@ -2657,7 +2738,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 85
"iconIdx": 88
},
{
"icon": {
@ -2687,7 +2768,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 86
"iconIdx": 89
},
{
"icon": {
@ -2716,7 +2797,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 87
"iconIdx": 90
},
{
"icon": {
@ -2745,7 +2826,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 88
"iconIdx": 91
},
{
"icon": {
@ -2774,7 +2855,7 @@
},
"setIdx": 0,
"setId": 1,
"iconIdx": 89
"iconIdx": 92
}
],
"height": 1024,

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

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
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');
src: url('fonts/icomoon.eot?vrpp1i');
src: url('fonts/icomoon.eot?vrpp1i#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?vrpp1i') format('truetype'),
url('fonts/icomoon.woff?vrpp1i') format('woff'),
url('fonts/icomoon.svg?vrpp1i#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
@ -24,6 +24,12 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-single-content:before {
content: "\e958";
}
.icon-multiple-content:before {
content: "\e957";
}
.icon-type-Array:before {
content: "\e956";
}

66
tests/Squidex.Domain.Apps.Entities.Tests/Contents/Guard/GuardContentTests.cs

@ -5,10 +5,13 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using FakeItEasy;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.Guards;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
using Xunit;
@ -17,6 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
{
public class GuardContentTests
{
private readonly ISchemaEntity schema = A.Fake<ISchemaEntity>();
private readonly Instant dueTimeInPast = SystemClock.Instance.GetCurrentInstant().Minus(Duration.FromHours(1));
[Fact]
@ -24,16 +28,36 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
{
var command = new CreateContent();
ValidationAssert.Throws(() => GuardContent.CanCreate(command),
ValidationAssert.Throws(() => GuardContent.CanCreate(schema, command),
new ValidationError("Data is required.", "Data"));
}
[Fact]
public void CanCreate_should_throw_exception_if_singleton()
{
A.CallTo(() => schema.IsSingleton).Returns(true);
var command = new CreateContent { Data = new NamedContentData() };
Assert.Throws<DomainException>(() => GuardContent.CanCreate(schema, command));
}
[Fact]
public void CanCreate_should_not_throw_exception_if_singleton_and_id_empty()
{
A.CallTo(() => schema.IsSingleton).Returns(true);
var command = new CreateContent { Data = new NamedContentData(), ContentId = Guid.Empty };
GuardContent.CanCreate(schema, command);
}
[Fact]
public void CanCreate_should_not_throw_exception_if_data_is_not_null()
{
var command = new CreateContent { Data = new NamedContentData() };
GuardContent.CanCreate(command);
GuardContent.CanCreate(schema, command);
}
[Fact]
@ -75,7 +99,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
{
var command = new ChangeContentStatus { Status = (Status)10 };
ValidationAssert.Throws(() => GuardContent.CanChangeContentStatus(false, Status.Archived, command),
ValidationAssert.Throws(() => GuardContent.CanChangeContentStatus(schema, false, Status.Archived, command),
new ValidationError("Status is not valid.", "Status"));
}
@ -84,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
{
var command = new ChangeContentStatus { Status = Status.Published };
ValidationAssert.Throws(() => GuardContent.CanChangeContentStatus(false, Status.Archived, command),
ValidationAssert.Throws(() => GuardContent.CanChangeContentStatus(schema, false, Status.Archived, command),
new ValidationError("Cannot change status from Archived to Published.", "Status"));
}
@ -93,7 +117,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
{
var command = new ChangeContentStatus { Status = Status.Published, DueTime = dueTimeInPast };
ValidationAssert.Throws(() => GuardContent.CanChangeContentStatus(false, Status.Draft, command),
ValidationAssert.Throws(() => GuardContent.CanChangeContentStatus(schema, false, Status.Draft, command),
new ValidationError("Due time must be in the future.", "DueTime"));
}
@ -102,16 +126,28 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
{
var command = new ChangeContentStatus { Status = Status.Published };
ValidationAssert.Throws(() => GuardContent.CanChangeContentStatus(false, Status.Published, command),
ValidationAssert.Throws(() => GuardContent.CanChangeContentStatus(schema, false, Status.Published, command),
new ValidationError("Content has no changes to publish.", "Status"));
}
[Fact]
public void CanChangeContentStatus_should_throw_exception_if_singleton()
{
A.CallTo(() => schema.IsSingleton).Returns(true);
var command = new ChangeContentStatus { Status = Status.Draft };
Assert.Throws<DomainException>(() => GuardContent.CanChangeContentStatus(schema, false, Status.Published, command));
}
[Fact]
public void CanChangeContentStatus_should_not_throw_exception_if_publishing_with_pending_changes()
{
A.CallTo(() => schema.IsSingleton).Returns(true);
var command = new ChangeContentStatus { Status = Status.Published };
GuardContent.CanChangeContentStatus(true, Status.Published, command);
GuardContent.CanChangeContentStatus(schema, true, Status.Published, command);
}
[Fact]
@ -119,7 +155,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
{
var command = new ChangeContentStatus { Status = Status.Published };
GuardContent.CanChangeContentStatus(false, Status.Draft, command);
GuardContent.CanChangeContentStatus(schema, false, Status.Draft, command);
}
[Fact]
@ -139,11 +175,21 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
}
[Fact]
public void CanPatch_should_not_throw_exception()
public void CanDelete_should_throw_exception_if_singleton()
{
A.CallTo(() => schema.IsSingleton).Returns(true);
var command = new DeleteContent();
Assert.Throws<DomainException>(() => GuardContent.CanDelete(schema, command));
}
[Fact]
public void CanDelete_should_not_throw_exception()
{
var command = new DeleteContent();
GuardContent.CanDelete(command);
GuardContent.CanDelete(schema, command);
}
}
}

60
tests/Squidex.Domain.Apps.Entities.Tests/Contents/SingletonCommandMiddlewareTests.cs

@ -0,0 +1,60 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Commands;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class SingletonCommandMiddlewareTests
{
private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly SingletonCommandMiddleware sut = new SingletonCommandMiddleware();
[Fact]
public async Task Should_create_content_when_singleton_schema_is_created()
{
var context =
new CommandContext(new CreateSchema { Singleton = true, Name = "my-schema" }, commandBus)
.Complete();
await sut.HandleAsync(context);
A.CallTo(() => commandBus.PublishAsync(A<ICommand>.That.Matches(x => x is CreateContent)))
.MustHaveHappened();
}
[Fact]
public async Task Should_not_create_content_when_non_singleton_schema_is_created()
{
var context =
new CommandContext(new CreateSchema { Singleton = false }, commandBus)
.Complete();
await sut.HandleAsync(context);
A.CallTo(() => commandBus.PublishAsync(A<ICommand>.Ignored))
.MustNotHaveHappened();
}
[Fact]
public async Task Should_not_create_content_when_singleton_schema_not_created()
{
var context =
new CommandContext(new CreateSchema { Singleton = true }, commandBus);
await sut.HandleAsync(context);
A.CallTo(() => commandBus.PublishAsync(A<ICommand>.Ignored))
.MustNotHaveHappened();
}
}
}

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

@ -62,7 +62,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
var properties = new SchemaProperties();
var command = new CreateSchema { Name = SchemaName, SchemaId = SchemaId, Properties = properties };
var command = new CreateSchema { Name = SchemaName, SchemaId = SchemaId, Properties = properties, Singleton = true };
var result = await sut.ExecuteAsync(CreateCommand(command));
@ -72,10 +72,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas
Assert.Equal(SchemaName, sut.Snapshot.Name);
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name);
Assert.True(sut.Snapshot.IsSingleton);
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new SchemaCreated { Name = SchemaName, Properties = properties })
CreateEvent(new SchemaCreated { Name = SchemaName, Properties = properties, Singleton = true })
);
}

Loading…
Cancel
Save