Browse Source

References editor and drag drop refactoring.

pull/65/head
Sebastian Stehle 9 years ago
parent
commit
a7c0b87d88
  1. 32
      src/Squidex.Read.MongoDb/Contents/MongoContentRepository.cs
  2. 11
      src/Squidex.Read.MongoDb/Contents/Visitors/FindExtensions.cs
  3. 4
      src/Squidex.Read/Contents/Repositories/IContentRepository.cs
  4. 17
      src/Squidex/Controllers/Api/Schemas/SchemasController.cs
  5. 42
      src/Squidex/Controllers/ContentApi/ContentsController.cs
  6. 12
      src/Squidex/Pipeline/CommandHandlers/EnrichWithSchemaIdHandler.cs
  7. 6
      src/Squidex/app/features/content/declarations.ts
  8. 26
      src/Squidex/app/features/content/module.ts
  9. 3
      src/Squidex/app/features/content/pages/content/content-field.component.html
  10. 15
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  11. 30
      src/Squidex/app/features/content/pages/contents/contents-page.component.html
  12. 22
      src/Squidex/app/features/content/pages/contents/contents-page.component.ts
  13. 6
      src/Squidex/app/features/content/shared/assets-editor.component.html
  14. 9
      src/Squidex/app/features/content/shared/assets-editor.component.scss
  15. 34
      src/Squidex/app/features/content/shared/assets-editor.component.ts
  16. 7
      src/Squidex/app/features/content/shared/content-item.component.html
  17. 0
      src/Squidex/app/features/content/shared/content-item.component.scss
  18. 3
      src/Squidex/app/features/content/shared/content-item.component.ts
  19. 31
      src/Squidex/app/features/content/shared/references-editor.component.html
  20. 63
      src/Squidex/app/features/content/shared/references-editor.component.scss
  21. 154
      src/Squidex/app/features/content/shared/references-editor.component.ts
  22. 3
      src/Squidex/app/features/schemas/module.ts
  23. 4
      src/Squidex/app/features/schemas/pages/schema/field.component.html
  24. 12
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts
  25. 5
      src/Squidex/app/features/schemas/pages/schema/types/references-ui.component.ts
  26. 12
      src/Squidex/app/features/schemas/pages/schema/types/references-validation.component.html
  27. 18
      src/Squidex/app/features/schemas/pages/schema/types/references-validation.component.ts
  28. 5
      src/Squidex/app/framework/angular/geolocation-editor.component.ts
  29. 4
      src/Squidex/app/framework/angular/parent-link.directive.ts
  30. 6
      src/Squidex/app/framework/angular/progress-bar.component.ts
  31. 48
      src/Squidex/app/framework/angular/router-utils.ts
  32. 0
      src/Squidex/app/framework/angular/sorted.directive.ts
  33. 11
      src/Squidex/app/framework/angular/template-wrapper.directive.ts
  34. 2
      src/Squidex/app/framework/declarations.ts
  35. 3
      src/Squidex/app/framework/module.ts
  36. 18
      src/Squidex/app/shared/components/history.component.ts
  37. 1
      src/Squidex/app/shared/declarations.ts
  38. 22
      src/Squidex/app/shared/guards/resolve-app-languages.guard.ts
  39. 40
      src/Squidex/app/shared/guards/resolve-content.guard.ts
  40. 33
      src/Squidex/app/shared/guards/resolve-published-schema.guard.ts
  41. 33
      src/Squidex/app/shared/guards/resolve-schema.guard.ts
  42. 22
      src/Squidex/app/shared/guards/resolve-user.guard.ts
  43. 3
      src/Squidex/app/shared/module.ts
  44. 6
      src/Squidex/app/shared/services/contents.service.ts
  45. 11
      src/Squidex/app/shared/services/schemas.service.ts
  46. 6
      src/Squidex/app/theme/icomoon/demo-files/demo.css
  47. 742
      src/Squidex/app/theme/icomoon/demo.html
  48. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.eot
  49. 4
      src/Squidex/app/theme/icomoon/fonts/icomoon.svg
  50. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.ttf
  51. BIN
      src/Squidex/app/theme/icomoon/fonts/icomoon.woff
  52. 1306
      src/Squidex/app/theme/icomoon/selection.json
  53. 97
      src/Squidex/app/theme/icomoon/style.css

32
src/Squidex.Read.MongoDb/Contents/MongoContentRepository.cs

@ -66,7 +66,7 @@ namespace Squidex.Read.MongoDb.Contents
this.modelBuilder = modelBuilder;
}
public async Task<IReadOnlyList<IContentEntity>> QueryAsync(Guid schemaId, bool nonPublished, string odataQuery, IAppEntity appEntity)
public async Task<IReadOnlyList<IContentEntity>> QueryAsync(Guid schemaId, bool nonPublished, HashSet<Guid> ids, string odataQuery, IAppEntity appEntity)
{
List<IContentEntity> result = null;
@ -81,7 +81,7 @@ namespace Squidex.Read.MongoDb.Contents
cursor =
collection
.Find(parser, schemaEntity.Id, schemaEntity.Schema, nonPublished)
.Find(parser, ids, schemaEntity.Id, schemaEntity.Schema, nonPublished)
.Take(parser)
.Skip(parser)
.Sort(parser, schemaEntity.Schema);
@ -112,19 +112,7 @@ namespace Squidex.Read.MongoDb.Contents
return result;
}
public async Task<bool> ExistsAsync(Guid appId, Guid schemaId, Guid contentId)
{
var result = false;
await ForAppIdAsync(appId, async collection =>
{
result = await collection.Find(x => x.Id == contentId && x.SchemaId == schemaId).CountAsync() == 1;
});
return result;
}
public async Task<long> CountAsync(Guid schemaId, bool nonPublished, string odataQuery, IAppEntity appEntity)
public async Task<long> CountAsync(Guid schemaId, bool nonPublished, HashSet<Guid> ids, string odataQuery, IAppEntity appEntity)
{
var result = 0L;
@ -137,7 +125,7 @@ namespace Squidex.Read.MongoDb.Contents
var parser = model.ParseQuery(odataQuery);
cursor = collection.Find(parser, schemaEntity.Id, schemaEntity.Schema, nonPublished);
cursor = collection.Find(parser, ids, schemaEntity.Id, schemaEntity.Schema, nonPublished);
}
catch (NotSupportedException)
{
@ -158,6 +146,18 @@ namespace Squidex.Read.MongoDb.Contents
return result;
}
public async Task<bool> ExistsAsync(Guid appId, Guid schemaId, Guid contentId)
{
var result = false;
await ForAppIdAsync(appId, async collection =>
{
result = await collection.Find(x => x.Id == contentId && x.SchemaId == schemaId).CountAsync() == 1;
});
return result;
}
public async Task<IContentEntity> FindContentAsync(Guid schemaId, Guid id, IAppEntity appEntity)
{
MongoContentEntity result = null;

11
src/Squidex.Read.MongoDb/Contents/Visitors/FindExtensions.cs

@ -59,14 +59,14 @@ namespace Squidex.Read.MongoDb.Contents.Visitors
return cursor;
}
public static IFindFluent<MongoContentEntity, MongoContentEntity> Find(this IMongoCollection<MongoContentEntity> cursor, ODataUriParser query, Guid schemaId, Schema schema, bool nonPublished)
public static IFindFluent<MongoContentEntity, MongoContentEntity> Find(this IMongoCollection<MongoContentEntity> cursor, ODataUriParser query, HashSet<Guid> ids, Guid schemaId, Schema schema, bool nonPublished)
{
var filter = BuildQuery(query, schemaId, schema, nonPublished);
var filter = BuildQuery(query, ids, schemaId, schema, nonPublished);
return cursor.Find(filter);
}
public static FilterDefinition<MongoContentEntity> BuildQuery(ODataUriParser query, Guid schemaId, Schema schema, bool nonPublished)
public static FilterDefinition<MongoContentEntity> BuildQuery(ODataUriParser query, HashSet<Guid> ids, Guid schemaId, Schema schema, bool nonPublished)
{
var filters = new List<FilterDefinition<MongoContentEntity>>
{
@ -78,6 +78,11 @@ namespace Squidex.Read.MongoDb.Contents.Visitors
filters.Add(Filter.Eq(x => x.IsPublished, true));
}
if (ids != null && ids.Count > 0)
{
Filter.In(x => x.Id, ids);
}
var filter = FilterBuilder.Build(query, schema);
if (filter != null)

4
src/Squidex.Read/Contents/Repositories/IContentRepository.cs

@ -15,9 +15,9 @@ namespace Squidex.Read.Contents.Repositories
{
public interface IContentRepository
{
Task<IReadOnlyList<IContentEntity>> QueryAsync(Guid schemaId, bool nonPublished, string odataQuery, IAppEntity appEntity);
Task<IReadOnlyList<IContentEntity>> QueryAsync(Guid schemaId, bool nonPublished, HashSet<Guid> ids, string odataQuery, IAppEntity appEntity);
Task<long> CountAsync(Guid schemaId, bool nonPublished, string odataQuery, IAppEntity appEntity);
Task<long> CountAsync(Guid schemaId, bool nonPublished, HashSet<Guid> ids, string odataQuery, IAppEntity appEntity);
Task<bool> ExistsAsync(Guid appId, Guid schemaId, Guid contentId);

17
src/Squidex/Controllers/Api/Schemas/SchemasController.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
@ -17,6 +18,7 @@ using Squidex.Controllers.Api.Schemas.Models;
using Squidex.Controllers.Api.Schemas.Models.Converters;
using Squidex.Core.Schemas;
using Squidex.Pipeline;
using Squidex.Read.Schemas;
using Squidex.Read.Schemas.Repositories;
using Squidex.Write.Schemas.Commands;
@ -50,7 +52,7 @@ namespace Squidex.Controllers.Api.Schemas
[HttpGet]
[Route("apps/{app}/schemas/")]
[ProducesResponseType(typeof(SchemaDto[]), 200)]
[ApiCosts(1)]
[ApiCosts(0)]
public async Task<IActionResult> GetSchemas(string app)
{
var schemas = await schemaRepository.QueryAllAsync(AppId);
@ -73,10 +75,19 @@ namespace Squidex.Controllers.Api.Schemas
[HttpGet]
[Route("apps/{app}/schemas/{name}/")]
[ProducesResponseType(typeof(SchemaDetailsDto[]), 200)]
[ApiCosts(1)]
[ApiCosts(0)]
public async Task<IActionResult> GetSchema(string app, string name)
{
var entity = await schemaRepository.FindSchemaAsync(AppId, name);
ISchemaEntity entity;
if (Guid.TryParse(name, out var id))
{
entity = await schemaRepository.FindSchemaAsync(id);
}
else
{
entity = await schemaRepository.FindSchemaAsync(AppId, name);
}
if (entity == null)
{

42
src/Squidex/Controllers/ContentApi/ContentsController.cs

@ -7,6 +7,7 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
@ -18,6 +19,7 @@ using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Reflection;
using Squidex.Pipeline;
using Squidex.Read.Contents.Repositories;
using Squidex.Read.Schemas;
using Squidex.Read.Schemas.Services;
using Squidex.Write.Contents.Commands;
@ -43,19 +45,27 @@ namespace Squidex.Controllers.ContentApi
[HttpGet]
[Route("content/{app}/{name}")]
[ApiCosts(2)]
public async Task<IActionResult> GetContents(string name, [FromQuery] bool nonPublished = false, [FromQuery] bool hidden = false)
public async Task<IActionResult> GetContents(string name, [FromQuery] bool nonPublished = false, [FromQuery] bool hidden = false, [FromQuery] string ids = null)
{
var schemaEntity = await schemas.FindSchemaByNameAsync(AppId, name);
var schemaEntity = await FindSchemaAsync(name);
if (schemaEntity == null)
var idsList = new HashSet<Guid>();
if (!string.IsNullOrWhiteSpace(ids))
{
return NotFound();
foreach (var id in ids.Split(','))
{
if (Guid.TryParse(id, out var guid))
{
idsList.Add(guid);
}
}
}
var query = Request.QueryString.ToString();
var taskForItems = contentRepository.QueryAsync(schemaEntity.Id, nonPublished, query, App);
var taskForCount = contentRepository.CountAsync(schemaEntity.Id, nonPublished, query, App);
var taskForItems = contentRepository.QueryAsync(schemaEntity.Id, nonPublished, idsList, query, App);
var taskForCount = contentRepository.CountAsync(schemaEntity.Id, nonPublished, idsList, query, App);
await Task.WhenAll(taskForItems, taskForCount);
@ -83,7 +93,7 @@ namespace Squidex.Controllers.ContentApi
[ApiCosts(1)]
public async Task<IActionResult> GetContent(string name, Guid id, bool hidden = false)
{
var schemaEntity = await schemas.FindSchemaByNameAsync(AppId, name);
var schemaEntity = await FindSchemaAsync(name);
if (schemaEntity == null)
{
@ -183,5 +193,21 @@ namespace Squidex.Controllers.ContentApi
return NoContent();
}
private async Task<ISchemaEntity> FindSchemaAsync(string name)
{
ISchemaEntity schemaEntity;
if (Guid.TryParse(name, out var schemaId))
{
schemaEntity = await schemas.FindSchemaByIdAsync(schemaId);
}
else
{
schemaEntity = await schemas.FindSchemaByNameAsync(AppId, name);
}
return schemaEntity;
}
}
}

12
src/Squidex/Pipeline/CommandHandlers/EnrichWithSchemaIdHandler.cs

@ -11,6 +11,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Read.Schemas;
using Squidex.Read.Schemas.Services;
using Squidex.Write;
using Squidex.Write.Schemas;
@ -41,7 +42,16 @@ namespace Squidex.Pipeline.CommandHandlers
{
var schemaName = routeValues["name"].ToString();
var schema = await schemas.FindSchemaByNameAsync(schemaCommand.AppId.Id, schemaName);
ISchemaEntity schema;
if (Guid.TryParse(schemaName, out var id))
{
schema = await schemas.FindSchemaByIdAsync(id);
}
else
{
schema = await schemas.FindSchemaByNameAsync(schemaCommand.AppId.Id, schemaName);
}
if (schema == null)
{

6
src/Squidex/app/features/content/declarations.ts

@ -7,6 +7,8 @@
export * from './pages/content/content-field.component';
export * from './pages/content/content-page.component';
export * from './pages/contents/content-item.component';
export * from './pages/contents/contents-page.component';
export * from './pages/schemas/schemas-page.component';
export * from './pages/schemas/schemas-page.component';
export * from './shared/assets-editor.component';
export * from './shared/content-item.component';
export * from './shared/references-editor.component';

26
src/Squidex/app/features/content/module.ts

@ -7,6 +7,7 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DndModule } from 'ng2-dnd';
import {
CanDeactivateGuard,
@ -19,10 +20,12 @@ import {
} from 'shared';
import {
AssetsEditorComponent,
ContentFieldComponent,
ContentPageComponent,
ContentItemComponent,
ContentsPageComponent,
ReferencesEditorComponent,
SchemasPageComponent
} from './declarations';
@ -49,6 +52,16 @@ const routes: Routes = [
{
path: 'assets',
loadChildren: './../assets/module#SqxFeatureAssetsModule'
},
{
path: 'references/:schemaName/:language',
component: ContentsPageComponent,
data: {
isReadOnly: true
},
resolve: {
schemaOverride: ResolvePublishedSchemaGuard
}
}
]
},
@ -67,6 +80,16 @@ const routes: Routes = [
channel: 'contents.{contentId}'
}
},
{
path: 'references/:schemaName/:language',
component: ContentsPageComponent,
data: {
isReadOnly: true
},
resolve: {
schema: ResolvePublishedSchemaGuard
}
},
{
path: 'assets',
loadChildren: './../assets/module#SqxFeatureAssetsModule'
@ -82,13 +105,16 @@ const routes: Routes = [
imports: [
SqxFrameworkModule,
SqxSharedModule,
DndModule,
RouterModule.forChild(routes)
],
declarations: [
AssetsEditorComponent,
ContentFieldComponent,
ContentItemComponent,
ContentPageComponent,
ContentsPageComponent,
ReferencesEditorComponent,
SchemasPageComponent
]
})

3
src/Squidex/app/features/content/pages/content/content-field.component.html

@ -92,6 +92,9 @@
<div *ngSwitchCase="'Assets'">
<sqx-assets-editor [formControlName]="partition"></sqx-assets-editor>
</div>
<div *ngSwitchCase="'References'">
<sqx-references-editor [formControlName]="partition" [languageCode]="partition" [schemaId]="field.properties.schemaId"></sqx-references-editor>
</div>
</div>
</div>
</div>

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

@ -20,6 +20,7 @@ import {
AppComponentBase,
AppLanguageDto,
AppsStoreService,
allDataFromRoute,
CanComponentDeactivate,
ContentDto,
ContentsService,
@ -73,6 +74,10 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
}
public ngOnInit() {
const routeData = allDataFromRoute(this.route);
this.languages = routeData['appLanguages'];
this.contentDeletedSubscription =
this.messageBus.of(ContentDeleted)
.subscribe(message => {
@ -81,15 +86,7 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
}
});
this.route.parent!.data.map(p => p['appLanguages'])
.subscribe((languages: AppLanguageDto[]) => {
this.languages = languages;
});
this.route.parent!.data.map(p => p['schema'])
.subscribe((schema: SchemaDetailsDto) => {
this.setupForm(schema);
});
this.setupForm(routeData['schema']);
this.route.data.map(p => p['content'])
.subscribe((content: ContentDto) => {

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

@ -1,6 +1,6 @@
<sqx-title message="{app} | {schema} | Contents" parameter1="app" parameter2="schema" value1="{{appName() | async}}" value2="{{schema.name}}"></sqx-title>
<sqx-panel panelWidth="60rem">
<sqx-panel [panelWidth]="isReadOnly ? '40rem' : '60rem'">
<div class="panel-header">
<div class="panel-title-row">
<div class="float-right">
@ -10,17 +10,17 @@
<sqx-shortcut keys="ctrl+shift+r" (trigger)="load(true)"></sqx-shortcut>
<sqx-shortcut keys="ctrl+shift+f" (trigger)="findInput.focus()"></sqx-shortcut>
<sqx-shortcut keys="ctrl+shift+g" (trigger)="newButton.click()"></sqx-shortcut>
<sqx-shortcut keys="ctrl+shift+g" (trigger)="newButton.click()" *ngIf="!isReadOnly"></sqx-shortcut>
<form class="form-inline" (ngSubmit)="search()">
<input class="form-control" #findInput [formControl]="contentsFilter" placeholder="Search for content" />
</form>
<span *ngIf="languages.length > 1">
<span *ngIf="!isReadOnly && languages.length > 1">
<sqx-language-selector class="languages-buttons" (selectedLanguageChanged)="selectLanguage($event)" [languages]="languages"></sqx-language-selector>
</span>
<button class="btn btn-success" #newButton routerLink="new" title="New Content (CTRL + SHIFT + G)">
<button *ngIf="!isReadOnly" class="btn btn-success" #newButton routerLink="new" title="New Content (CTRL + SHIFT + G)">
<i class="icon-plus"></i> New
</button>
</div>
@ -38,9 +38,9 @@
<table class="table table-items table-fixed" *ngIf="contentItems">
<colgroup>
<col *ngFor="let field of contentFields" [style.width]="columnWidth + '%'" />
<col style="width: 190px" />
<col style="width: 80px" />
<col style="width: 80px" />
<col style="width: 180px" />
<col style="width: 50px" />
<col style="width: 80px" *ngIf="!isReadOnly" />
</colgroup>
<thead>
@ -54,15 +54,15 @@
<th>
By
</th>
<th>
<th *ngIf="!isReadOnly">
Options
</th>
</tr>
</thead>
<tbody>
<tbody *ngIf="!isReadOnly">
<ng-template ngFor let-content [ngForOf]="contentItems">
<tr [routerLink]="[content.id]" routerLinkActive="active" class="content"
<tr [routerLink]="[content.id]" routerLinkActive="active" class="content"
[sqxContent]="content"
[language]="languageSelected"
[fields]="contentFields"
@ -73,6 +73,16 @@
<tr class="spacer"></tr>
</ng-template>
</tbody>
<tbody *ngIf="isReadOnly">
<ng-template ngFor let-content [ngForOf]="contentItems">
<tr [sqxContent]="content" isReadOnly="true" dnd-draggable [dragData]="dropData(content)"
[language]="languageSelected"
[fields]="contentFields"
[schema]="schema"></tr>
<tr class="spacer"></tr>
</ng-template>
</tbody>
</table>
<div class="clearfix" *ngIf="contentsPager.numberOfItems > 0">

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

@ -17,6 +17,7 @@ import {
} from './../messages';
import {
allDataFromRoute,
AppComponentBase,
AppLanguageDto,
AppsStoreService,
@ -52,6 +53,9 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
public languages: AppLanguageDto[] = [];
public languageSelected: AppLanguageDto;
public languageParameter: string;
public isReadOnly = false;
public columnWidth: number;
@ -70,6 +74,10 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
}
public ngOnInit() {
const routeData = allDataFromRoute(this.route);
this.languages = routeData['appLanguages'];
this.contentCreatedSubscription =
this.messageBus.of(ContentCreated)
.subscribe(message => {
@ -83,18 +91,20 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
this.updateContents(message.id, undefined, message.data, message.version);
});
this.route.data.map(p => p['appLanguages'])
.subscribe((languages: AppLanguageDto[]) => {
this.languages = languages;
this.route.params.map(p => <string> p['language'])
.subscribe(language => {
this.languageSelected = this.languages.find(l => l.iso2Code === language) || this.languages.find(l => l.isMaster) || this.languages[0];
});
this.route.data.map(p => p['schema'])
this.route.data.map(p => p['schemaOverride'] || p['schema'])
.subscribe(schema => {
this.schema = schema;
this.reset();
this.load();
});
this.isReadOnly = routeData['isReadOnly'];
}
public search() {
@ -175,6 +185,10 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
});
}
public dropData(content: ContentDto) {
return { content, schemaId: this.schema.id };
}
public goNext() {
this.contentsPager = this.contentsPager.goNext();

6
src/Squidex/app/shared/components/assets-editor.component.html → src/Squidex/app/features/content/shared/assets-editor.component.html

@ -1,7 +1,7 @@
<div class="assets-container">
<div class="assets-container" [class.disabled]="isDisabled">
<div class="row">
<div class="col-4 drop-area-container">
<div class="drop-area" dnd-droppable (onDropSuccess)="onAssetDropped($event.dragData)" (sqxFileDrop)="addFiles($event)">
<div class="col-4 drop-area-container" *ngIf="!isDisabled">
<div class="drop-area" dnd-droppable (onDropSuccess)="onAssetDropped($event.dragData)" [allowDrop]="canDrop()" (sqxFileDrop)="addFiles($event)" routerLink="assets">
Drop files or assets here to add them.
</div>
</div>

9
src/Squidex/app/shared/components/assets-editor.component.scss → src/Squidex/app/features/content/shared/assets-editor.component.scss

@ -1,6 +1,10 @@
@import '_vars';
@import '_mixins';
.disabled {
pointer-events: none;
}
.assets {
&-container {
& {
@ -27,6 +31,10 @@
color: darken($color-border, 30%);
}
&:hover {
text-decoration: underline;
}
&-container {
padding-bottom: 1rem;
}
@ -37,6 +45,7 @@
border-color: darken($color-border, 10%);
cursor: copy;
color: darken($color-border, 40%);
text-decoration: none;
}
}

34
src/Squidex/app/shared/components/assets-editor.component.ts → src/Squidex/app/features/content/shared/assets-editor.component.ts

@ -8,12 +8,11 @@
// tslint:disable:prefer-for-of
import { Component, forwardRef, OnDestroy, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subscription } from 'rxjs';
import { AppComponentBase } from './app.component-base';
import {
AppComponentBase,
AppsStoreService,
AssetDto,
AssetsService,
@ -21,7 +20,7 @@ import {
ImmutableArray,
MessageBus,
NotificationService
} from './../declarations-base';
} from 'shared';
const NOOP = () => { /* NOOP */ };
@ -37,13 +36,12 @@ export const SQX_ASSETS_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
})
export class AssetsEditorComponent extends AppComponentBase implements ControlValueAccessor, OnDestroy, OnInit {
private assetUpdatedSubscription: Subscription;
private changeCallback: (value: any) => void = NOOP;
private touchedCallback: () => void = NOOP;
public newAssets = ImmutableArray.empty<File>();
public oldAssets = ImmutableArray.empty<AssetDto>();
private changeCallback: (value: any) => void = NOOP;
private touchedCallback: () => void = NOOP;
public isDisabled = false;
constructor(apps: AppsStoreService, notifications: NotificationService,
@ -102,6 +100,14 @@ export class AssetsEditorComponent extends AppComponentBase implements ControlVa
}
}
public canDrop() {
const component = this;
return (dragData: any) => {
return dragData instanceof AssetDto && !component.oldAssets.find(a => a.id === dragData.id);
};
}
public onAssetDropped(asset: AssetDto) {
if (asset) {
this.oldAssets = this.oldAssets.pushFront(asset);
@ -110,15 +116,17 @@ export class AssetsEditorComponent extends AppComponentBase implements ControlVa
}
}
public onAssetLoaded(file: File, asset: AssetDto) {
this.newAssets = this.newAssets.remove(file);
this.oldAssets = this.oldAssets.pushFront(asset);
public onAssetRemoving(asset: AssetDto) {
if (asset) {
this.oldAssets = this.oldAssets.remove(asset);
this.updateValue();
this.updateValue();
}
}
public onAssetRemoving(asset: AssetDto) {
this.oldAssets = this.oldAssets.remove(asset);
public onAssetLoaded(file: File, asset: AssetDto) {
this.newAssets = this.newAssets.remove(file);
this.oldAssets = this.oldAssets.pushFront(asset);
this.updateValue();
}

7
src/Squidex/app/features/content/pages/contents/content-item.component.html → src/Squidex/app/features/content/shared/content-item.component.html

@ -11,7 +11,7 @@
<td>
<img class="user-picture" [attr.title]="content.lastModifiedBy | userNameRef" [attr.src]="content.lastModifiedBy | userPictureRef" />
</td>
<td>
<td *ngIf="!isReadOnly">
<div class="dropdown dropdown-options" *ngIf="content">
<button type="button" class="btn btn-link btn-decent" (click)="dropdown.toggle(); $event.stopPropagation()" [class.active]="dropdown.isOpen | async" #optionsButton>
<i class="icon-dots"></i>
@ -28,4 +28,9 @@
</a>
</div>
</div>
</td>
<td *ngIf="isReadOnly">
<button type="button" class="btn btn-link btn-danger" (click)="deleting.emit(); $event.stopPropagation()">
<i class="icon-bin2"></i>
</button>
</td>

0
src/Squidex/app/features/content/pages/contents/content-item.component.scss → src/Squidex/app/features/content/shared/content-item.component.scss

3
src/Squidex/app/features/content/pages/contents/content-item.component.ts → src/Squidex/app/features/content/shared/content-item.component.ts

@ -49,6 +49,9 @@ export class ContentItemComponent extends AppComponentBase implements OnInit, On
@Input()
public schema: SchemaDto;
@Input()
public isReadOnly = false;
@Input('sqxContent')
public content: ContentDto;

31
src/Squidex/app/features/content/shared/references-editor.component.html

@ -0,0 +1,31 @@
<div class="references-container" [class.disabled]="isDisabled">
<div class="drop-area-container" *ngIf="schema && !isDisabled">
<div class="drop-area" dnd-droppable (onDropSuccess)="onContentDropped($event.dragData.content)" [allowDrop]="canDrop()" [routerLink]="['references', schemaId, languageCode]">
Drop content here to add them.
</div>
</div>
<div class="invalid" *ngIf="isInvalidSchema">
Schema not found or not configured yet.
</div>
<table class="table table-items table-fixed" [class.disabled]="isDisabled" *ngIf="contentItems && contentItems.length > 0">
<colgroup>
<col *ngFor="let field of contentFields" [style.width]="columnWidth + '%'" />
<col style="width: 180px" />
<col style="width: 50px" />
<col style="width: 80px" />
</colgroup>
<tbody dnd-sortable-container [sortableData]="contentItems.mutableValues">
<ng-template ngFor let-content let-i="index" [ngForOf]="contentItems">
<tr [sqxContent]="content" isReadOnly="true" dnd-sortable [sortableIndex]="i" (sorted)="onContentsSorted($event)"
[language]="languageSelected"
[fields]="contentFields"
[schema]="schema"
(deleting)="onContentRemoving(content)"></tr>
<tr class="spacer"></tr>
</ng-template>
</tbody>
</table>
</div>

63
src/Squidex/app/features/content/shared/references-editor.component.scss

@ -0,0 +1,63 @@
@import '_vars';
@import '_mixins';
.disabled {
pointer-events: none;
}
.references {
&-container {
& {
background: $color-background;
overflow-x: hidden;
overflow-y: scroll;
padding: 1rem;
}
}
}
.invalid {
padding: 2rem;
font-size: 1.2rem;
font-weight: normal;
text-align: center;
color: darken($color-border, 30%);
}
.drop-area {
& {
@include transition(border-color .4s ease);
@include border-radius;
border: 2px dashed $color-border;
font-size: 1.2rem;
font-weight: normal;
text-align: center;
padding: 2rem;
cursor: pointer;
color: darken($color-border, 30%);
}
&:hover {
text-decoration: underline;
}
&.drag,
&.dnd-drag-over,
&.dnd-drag-enter {
border-color: darken($color-border, 10%);
cursor: copy;
color: darken($color-border, 40%);
text-decoration: none;
}
}
.table {
& {
margin-bottom: -.3rem;
margin-top: 1rem;
}
&.disabled {
margin-top: 0;
}
}

154
src/Squidex/app/features/content/shared/references-editor.component.ts

@ -0,0 +1,154 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
// tslint:disable:prefer-for-of
import { Component, forwardRef, Input } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import {
AppComponentBase,
AppsStoreService,
ContentDto,
ContentsService,
FieldDto,
ImmutableArray,
NotificationService,
SchemaDetailsDto,
SchemasService
} from 'shared';
const NOOP = () => { /* NOOP */ };
export const SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ReferencesEditorComponent), multi: true
};
@Component({
selector: 'sqx-references-editor',
styleUrls: ['./references-editor.component.scss'],
templateUrl: './references-editor.component.html',
providers: [SQX_REFERENCES_EDITOR_CONTROL_VALUE_ACCESSOR]
})
export class ReferencesEditorComponent extends AppComponentBase implements ControlValueAccessor {
private changeCallback: (value: any) => void = NOOP;
private touchedCallback: () => void = NOOP;
@Input()
public schemaId: string;
@Input()
public languageCode: string;
public schema: SchemaDetailsDto;
public contentItems = ImmutableArray.empty<ContentDto>();
public contentFields: FieldDto[];
public columnWidth: number;
public isDisabled = false;
public isInvalidSchema = false;
constructor(apps: AppsStoreService, notifications: NotificationService,
private readonly contentsService: ContentsService,
private readonly schemasService: SchemasService
) {
super(notifications, apps);
}
public ngOnInit() {
this.appNameOnce()
.switchMap(app => this.schemasService.getSchema(app, this.schemaId))
.subscribe(dto => {
this.schema = dto;
this.loadFields();
}, error => {
this.isInvalidSchema = true;
});
}
public writeValue(value: any) {
this.contentItems = ImmutableArray.empty<ContentDto>();
if (value && value.length > 0) {
const contentIds: string[] = value;
this.appNameOnce()
.switchMap(app => this.contentsService.getContents(app, this.schemaId, 10000, 0, undefined, contentIds))
.subscribe(dtos => {
this.contentItems = ImmutableArray.of(dtos.items);
});
}
}
public setDisabledState(isDisabled: boolean): void {
this.isDisabled = isDisabled;
}
public registerOnChange(fn: any) {
this.changeCallback = fn;
}
public registerOnTouched(fn: any) {
this.touchedCallback = fn;
}
public canDrop() {
const component = this;
return (dragData: any) => {
return dragData.content instanceof ContentDto && dragData.schemaId === component.schemaId && !component.contentItems.find(c => c.id === dragData.content.id);
};
}
public onContentDropped(content: ContentDto) {
if (content) {
this.contentItems = this.contentItems.pushFront(content);
this.updateValue();
}
}
public onContentRemoving(content: ContentDto) {
if (content) {
this.contentItems = this.contentItems.remove(content);
this.updateValue();
}
}
public onContentsSorted(contents: ContentDto[]) {
if (contents) {
this.contentItems = ImmutableArray.of(contents);
this.updateValue();
}
}
private updateValue() {
let ids: string[] | null = this.contentItems.values.map(x => x.id);
if (ids.length === 0) {
ids = null;
}
this.touchedCallback();
this.changeCallback(ids);
}
private loadFields() {
this.contentFields = this.schema.fields.filter(x => x.properties.isListField);
this.columnWidth = 100 / this.contentFields.length;
if (this.contentFields.length === 0 && this.schema.fields.length > 0) {
this.contentFields = [this.schema.fields[0]];
}
}
}

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

@ -17,8 +17,6 @@ import {
SqxSharedModule
} from 'shared';
import { SortedDirective } from './utils/sorted.directive';
import {
FieldComponent,
AssetsUIComponent,
@ -104,7 +102,6 @@ const routes: Routes = [
SchemaFormComponent,
SchemaPageComponent,
SchemasPageComponent,
SortedDirective,
StringUIComponent,
StringValidationComponent
]

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

@ -147,7 +147,7 @@
<sqx-assets-validation [editForm]="editForm" [properties]="field.properties"></sqx-assets-validation>
</div>
<div *ngSwitchCase="'References'">
<sqx-references-validation [editForm]="editForm" [properties]="field.properties"></sqx-references-validation>
<sqx-references-validation [editForm]="editForm" [properties]="field.properties" [schemas]="schemas"></sqx-references-validation>
</div>
</div>
</div>
@ -176,7 +176,7 @@
<sqx-assets-ui [editForm]="editForm" [properties]="field.properties"></sqx-assets-ui>
</div>
<div *ngSwitchCase="'References'">
<sqx-references-ui [editForm]="editForm" [properties]="field.properties" [schemas]="schemas"></sqx-references-ui>
<sqx-references-ui [editForm]="editForm" [properties]="field.properties"></sqx-references-ui>
</div>
</div>
</div>

12
src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts

@ -16,6 +16,7 @@ import {
createProperties,
fadeAnimation,
FieldDto,
fieldTypes,
HistoryChannelUpdated,
ImmutableArray,
MessageBus,
@ -41,16 +42,7 @@ import { SchemaDeleted, SchemaUpdated } from './../messages';
]
})
export class SchemaPageComponent extends AppComponentBase implements OnInit {
public fieldTypes: string[] = [
'Assets',
'Boolean',
'DateTime',
'Geolocation',
'Json',
'Number',
'References',
'String'
];
public fieldTypes = fieldTypes;
public schemaExport: any;
public schemaName: string;

5
src/Squidex/app/features/schemas/pages/schema/types/references-ui.component.ts

@ -8,7 +8,7 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { ReferencesFieldPropertiesDto, SchemaDto } from 'shared';
import { ReferencesFieldPropertiesDto } from 'shared';
@Component({
selector: 'sqx-references-ui',
@ -22,7 +22,4 @@ export class ReferencesUIComponent {
@Input()
public properties: ReferencesFieldPropertiesDto;
@Input()
public schems: SchemaDto[];
}

12
src/Squidex/app/features/schemas/pages/schema/types/references-validation.component.html

@ -1,4 +1,13 @@
<div [formGroup]="editForm">
<div class="form-group row">
<label for="field-placeholder" class="col col-3 col-form-label">Schema</label>
<div class="col col-6">
<select class="form-control" formControlName="schemaId">
<option *ngFor="let schema of schemas" [ngValue]="schema.id">{{schema.name}}</option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col col-3 col-form-checkbox-label" for="field-required">Required</label>
@ -6,4 +15,5 @@
<input type="checkbox" class="form-check-input" id="field-required" formControlName="isRequired" />
</div>
</div>
</div>
</div>

18
src/Squidex/app/features/schemas/pages/schema/types/references-validation.component.ts

@ -5,10 +5,10 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { ReferencesFieldPropertiesDto } from 'shared';
import { ReferencesFieldPropertiesDto, SchemaDto } from 'shared';
@Component({
selector: 'sqx-references-validation',
@ -16,10 +16,20 @@ import { ReferencesFieldPropertiesDto } from 'shared';
templateUrl: 'references-validation.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ReferencesValidationComponent {
export class ReferencesValidationComponent implements OnInit {
@Input()
public editForm: FormGroup;
@Input()
public properties: ReferencesFieldPropertiesDto;
@Input()
public schemas: SchemaDto[];
public ngOnInit() {
this.editForm.setControl('schemaId',
new FormControl(this.properties.schemaId, [
Validators.required
]));
}
}

5
src/Squidex/app/framework/angular/geolocation-editor.component.ts

@ -73,6 +73,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
if (isDisabled) {
if (this.map) {
this.map.zoomControl.disable();
this.map._handlers.forEach((handler: any) => {
handler.disable();
});
@ -86,6 +87,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
} else {
if (this.map) {
this.map.zoomControl.enable();
this.map._handlers.forEach((handler: any) => {
handler.enable();
});
@ -129,7 +131,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
}).addTo(this.map);
this.map.on('click', (event: any) => {
if (!this.marker) {
if (!this.marker && !this.isDisabled) {
const latlng = event.latlng.wrap();
this.value = { latitude: latlng.lat, longitude: latlng.lng };
@ -142,6 +144,7 @@ export class GeolocationEditorComponent implements ControlValueAccessor, AfterVi
if (this.isDisabled) {
this.map.zoomControl.disable();
this.map._handlers.forEach((handler: any) => {
handler.disable();
});

4
src/Squidex/app/framework/angular/parent-link.directive.ts

@ -28,7 +28,9 @@ export class ParentLinkDirective implements OnInit, OnDestroy {
}
public ngOnDestroy() {
this.urlSubscription.unsubscribe();
if (this.urlSubscription) {
this.urlSubscription.unsubscribe();
}
}
public ngOnInit() {

6
src/Squidex/app/framework/angular/progress-bar.component.ts

@ -5,7 +5,7 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { Component, ElementRef, Input, OnChanges, OnInit, Renderer } from '@angular/core';
import { Component, ElementRef, Input, OnChanges, OnInit, Renderer, SimpleChanges } from '@angular/core';
const ProgressBar = require('progressbar.js');
@ -59,8 +59,8 @@ export class ProgressBarComponent implements OnChanges, OnInit {
this.updateValue();
}
public ngOnChanges() {
if (this.progressBar) {
public ngOnChanges(changes: SimpleChanges) {
if (this.progressBar && changes.value) {
this.updateValue();
}
}

48
src/Squidex/app/framework/angular/router-utils.ts

@ -0,0 +1,48 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { ActivatedRoute, ActivatedRouteSnapshot, Data, Params } from '@angular/router';
export function allDataFromRoute(route: ActivatedRoute): Data {
return allData(route.snapshot);
}
export function allData(route: ActivatedRouteSnapshot): Data {
let result: { [key: string]: any } = { };
while (route) {
for (let key in route.data) {
if (route.data.hasOwnProperty(key) && !result[key]) {
result[key] = route.data[key];
}
}
route = route.parent;
}
return result;
}
export function allParametersFromRoute(route: ActivatedRoute): Params {
return allParameters(route.snapshot);
}
export function allParameters(route: ActivatedRouteSnapshot): Params {
let result: { [key: string]: any } = { };
while (route) {
for (let key of route.paramMap.keys) {
if (!result[key]) {
result[key] = route.paramMap.get(key);
}
}
route = route.parent;
}
return result;
}

0
src/Squidex/app/features/schemas/utils/sorted.directive.ts → src/Squidex/app/framework/angular/sorted.directive.ts

11
src/Squidex/app/framework/angular/template-wrapper.directive.ts

@ -5,7 +5,7 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { Directive, Input, OnDestroy, OnInit, OnChanges, TemplateRef, ViewContainerRef, EmbeddedViewRef } from '@angular/core';
import { Directive, EmbeddedViewRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[sqxTemplateWrapper]'
@ -34,10 +34,13 @@ export class TemplateWrapper implements OnInit, OnDestroy, OnChanges {
});
}
public ngOnChanges() {
public ngOnChanges(changes: SimpleChanges) {
if (this.view) {
this.view.context.$implicit = this.item;
this.view.context.index = this.index;
if (changes.item) {
this.view.context.$implicit = this.item;
} else if (changes.index) {
this.view.context.index = this.index;
}
}
}

2
src/Squidex/app/framework/declarations.ts

@ -34,9 +34,11 @@ export * from './angular/parent-link.directive';
export * from './angular/popup-link.directive';
export * from './angular/progress-bar.component';
export * from './angular/rich-editor.component';
export * from './angular/router-utils';
export * from './angular/scroll-active.directive';
export * from './angular/shortcut.component';
export * from './angular/slider.component';
export * from './angular/sorted.directive';
export * from './angular/stars.component';
export * from './angular/tag-editor.component';
export * from './angular/template-wrapper.directive';

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

@ -55,6 +55,7 @@ import {
ShortDatePipe,
ShortTimePipe,
SliderComponent,
SortedDirective,
StarsComponent,
TagEditorComponent,
TemplateWrapper,
@ -108,6 +109,7 @@ import {
ShortDatePipe,
ShortTimePipe,
SliderComponent,
SortedDirective,
StarsComponent,
TagEditorComponent,
TemplateWrapper,
@ -151,6 +153,7 @@ import {
ShortDatePipe,
ShortTimePipe,
SliderComponent,
SortedDirective,
StarsComponent,
TagEditorComponent,
TemplateWrapper,

18
src/Squidex/app/shared/components/history.component.ts

@ -12,6 +12,7 @@ import { Observable } from 'rxjs';
import { AppComponentBase } from './app.component-base';
import {
allParametersFromRoute,
AppsStoreService,
HistoryChannelUpdated,
HistoryEventDto,
@ -30,18 +31,21 @@ const REPLACEMENT_TEMP = '$TEMP$';
})
export class HistoryComponent extends AppComponentBase {
public get channel(): string {
let result = this.route.snapshot.data['channel'];
let params = this.route.parent!.snapshot.params;
let channelPath = this.route.snapshot.data['channel'];
for (let key in params) {
if (params.hasOwnProperty(key)) {
const value = params[key];
if (channelPath) {
let params = allParametersFromRoute(this.route);
result = result.replace('{' + key + '}', value);
for (let key in params) {
if (params.hasOwnProperty(key)) {
const value = params[key];
channelPath = channelPath.replace('{' + key + '}', value);
}
}
}
return result;
return channelPath;
}
public events: Observable<HistoryEventDto[]> =

1
src/Squidex/app/shared/declarations.ts

@ -8,7 +8,6 @@
export * from './components/app.component-base';
export * from './components/app-form.component';
export * from './components/asset.component';
export * from './components/assets-editor.component';
export * from './components/component-base';
export * from './components/help.component';
export * from './components/history.component';

22
src/Squidex/app/shared/guards/resolve-app-languages.guard.ts

@ -8,6 +8,8 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { allParameters } from 'framework';
import { AppLanguageDto, AppLanguagesService } from './../services/app-languages.service';
@Injectable()
@ -19,7 +21,9 @@ export class ResolveAppLanguagesGuard implements Resolve<AppLanguageDto[]> {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<AppLanguageDto[]> {
const appName = this.findParameter(route, 'appName');
const params = allParameters(route);
const appName = params['appName'];
if (!appName) {
throw 'Route must contain app name.';
@ -43,20 +47,4 @@ export class ResolveAppLanguagesGuard implements Resolve<AppLanguageDto[]> {
return result;
}
private findParameter(route: ActivatedRouteSnapshot, name: string): string | null {
let result: string | null = null;
while (route) {
result = route.params[name];
if (result || !route.parent) {
break;
}
route = route.parent;
}
return result;
}
}

40
src/Squidex/app/shared/guards/resolve-content.guard.ts

@ -8,6 +8,8 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { allParameters } from 'framework';
import { ContentDto, ContentsService } from './../services/contents.service';
@Injectable()
@ -19,12 +21,24 @@ export class ResolveContentGuard implements Resolve<ContentDto> {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<ContentDto> {
const appName = this.findParameter(route, 'appName');
const schemaName = this.findParameter(route, 'schemaName');
const contentId = this.findParameter(route, 'contentId');
const params = allParameters(route);
const appName = params['appName'];
if (!appName) {
throw 'Route must contain app name.';
}
const schemaName = params['schemaName'];
if (!schemaName) {
throw 'Route must contain schema name.';
}
const contentId = params['contentId'];
if (!appName || !schemaName || !contentId) {
throw 'Route must contain app and schema name and id.';
if (!contentId) {
throw 'Route must contain content id.';
}
const result =
@ -45,20 +59,4 @@ export class ResolveContentGuard implements Resolve<ContentDto> {
return result;
}
private findParameter(route: ActivatedRouteSnapshot, name: string): string | null {
let result: string | null = null;
while (route) {
result = route.params[name];
if (result || !route.parent) {
break;
}
route = route.parent;
}
return result;
}
}

33
src/Squidex/app/shared/guards/resolve-published-schema.guard.ts

@ -8,6 +8,8 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { allParameters } from 'framework';
import { SchemaDetailsDto, SchemasService } from './../services/schemas.service';
@Injectable()
@ -19,11 +21,18 @@ export class ResolvePublishedSchemaGuard implements Resolve<SchemaDetailsDto> {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<SchemaDetailsDto> {
const appName = this.findParameter(route, 'appName');
const schemaName = this.findParameter(route, 'schemaName');
const params = allParameters(route);
const appName = params['appName'];
if (!appName || !schemaName) {
throw 'Route must contain app and schema name.';
if (!appName) {
throw 'Route must contain app name.';
}
const schemaName = params['schemaName'];
if (!schemaName) {
throw 'Route must contain schema name.';
}
const result =
@ -44,20 +53,4 @@ export class ResolvePublishedSchemaGuard implements Resolve<SchemaDetailsDto> {
return result;
}
private findParameter(route: ActivatedRouteSnapshot, name: string): string | null {
let result: string | null = null;
while (route) {
result = route.params[name];
if (result || !route.parent) {
break;
}
route = route.parent;
}
return result;
}
}

33
src/Squidex/app/shared/guards/resolve-schema.guard.ts

@ -8,6 +8,8 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { allParameters } from 'framework';
import { SchemaDetailsDto, SchemasService } from './../services/schemas.service';
@Injectable()
@ -19,11 +21,18 @@ export class ResolveSchemaGuard implements Resolve<SchemaDetailsDto> {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<SchemaDetailsDto> {
const appName = this.findParameter(route, 'appName');
const schemaName = this.findParameter(route, 'schemaName');
const params = allParameters(route);
const appName = params['appName'];
if (!appName || !schemaName) {
throw 'Route must contain app and schema name.';
if (!appName) {
throw 'Route must contain app name.';
}
const schemaName = params['schemaName'];
if (!schemaName) {
throw 'Route must contain schema name.';
}
const result =
@ -44,20 +53,4 @@ export class ResolveSchemaGuard implements Resolve<SchemaDetailsDto> {
return result;
}
private findParameter(route: ActivatedRouteSnapshot, name: string): string | null {
let result: string | null = null;
while (route) {
result = route.params[name];
if (result || !route.parent) {
break;
}
route = route.parent;
}
return result;
}
}

22
src/Squidex/app/shared/guards/resolve-user.guard.ts

@ -8,6 +8,8 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { allParameters } from 'framework';
import { UserDto, UserManagementService } from './../services/users.service';
@Injectable()
@ -19,7 +21,9 @@ export class ResolveUserGuard implements Resolve<UserDto> {
}
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<UserDto> {
const userId = this.findParameter(route, 'userId');
const params = allParameters(route);
const userId = params['userId'];
if (!userId) {
throw 'Route must contain user id.';
@ -43,20 +47,4 @@ export class ResolveUserGuard implements Resolve<UserDto> {
return result;
}
private findParameter(route: ActivatedRouteSnapshot, name: string): string | null {
let result: string | null = null;
while (route) {
result = route.params[name];
if (result || !route.parent) {
break;
}
route = route.parent;
}
return result;
}
}

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

@ -20,7 +20,6 @@ import {
AppsService,
AppMustExistGuard,
AssetComponent,
AssetsEditorComponent,
AssetsService,
AuthService,
ContentsService,
@ -64,7 +63,6 @@ import {
declarations: [
AppFormComponent,
AssetComponent,
AssetsEditorComponent,
HelpComponent,
HistoryComponent,
LanguageSelectorComponent,
@ -80,7 +78,6 @@ import {
exports: [
AppFormComponent,
AssetComponent,
AssetsEditorComponent,
HelpComponent,
HistoryComponent,
LanguageSelectorComponent,

6
src/Squidex/app/shared/services/contents.service.ts

@ -48,7 +48,7 @@ export class ContentsService {
) {
}
public getContents(appName: string, schemaName: string, take: number, skip: number, query?: string): Observable<ContentsDto> {
public getContents(appName: string, schemaName: string, take: number, skip: number, query?: string, ids?: string[]): Observable<ContentsDto> {
let fullQuery = query ? query.trim() : '';
if (fullQuery.length > 0) {
@ -69,6 +69,10 @@ export class ContentsService {
fullQuery += `&$skip=${skip}`;
}
if (ids && ids.length > 0) {
fullQuery += `&ids=${ids.join(',')}`;
}
const url = this.apiUrl.buildUrl(`/api/content/${appName}/${schemaName}?nonPublished=true&hidden=true${fullQuery}`);
return this.authService.authGet(url)

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

@ -19,6 +19,17 @@ import {
import { AuthService } from './auth.service';
export const fieldTypes: string[] = [
'Assets',
'Boolean',
'DateTime',
'Geolocation',
'Json',
'Number',
'References',
'String'
];
export function createProperties(fieldType: string, values: Object | null = null): FieldPropertiesDto {
let properties: FieldPropertiesDto;

6
src/Squidex/app/theme/icomoon/demo-files/demo.css

@ -147,16 +147,16 @@ p {
font-size: 16px;
}
.fs1 {
font-size: 28px;
font-size: 32px;
}
.fs2 {
font-size: 32px;
}
.fs3 {
font-size: 32px;
font-size: 24px;
}
.fs4 {
font-size: 24px;
font-size: 28px;
}
.fs5 {
font-size: 20px;

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

File diff suppressed because it is too large

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

Binary file not shown.

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

@ -16,7 +16,7 @@
<glyph unicode="&#xe906;" glyph-name="check-circle" d="M512 857.6c-226.202 0-409.6-183.398-409.6-409.6s183.398-409.6 409.6-409.6c226.202 0 409.6 183.398 409.6 409.6s-183.398 409.6-409.6 409.6zM512 806.4c197.632 0 358.4-160.819 358.4-358.4s-160.768-358.4-358.4-358.4c-197.632 0-358.4 160.819-358.4 358.4s160.768 358.4 358.4 358.4zM691.9 627c-12.893-0.002-25.782-4.882-35.5-14.6l-222.2-221.9-67.7 67.5c-19.19 19.294-51.085 19.215-70.3 0-19.15-19.15-19.15-51.050 0-70.2 0.198-0.2 26.198-26.681 52-53 12.95-13.209 25.761-26.372 35.2-36 4.719-4.814 8.607-8.755 11.2-11.4 1.296-1.322 2.293-2.281 2.9-2.9 0.279-0.282 0.488-0.486 0.6-0.6 0.001-0.001 7.591 7.429 14.6 14.3l-14.5-14.4 0.2-0.2v-0.1c19.43-19.327 51.57-19.327 71 0v0.1l258.1 257.6c19.546 19.447 19.521 51.885-0.1 71.3-9.731 9.679-22.607 14.502-35.5 14.5z" />
<glyph unicode="&#xe907;" glyph-name="check-circle-filled" d="M512-64c-282.778 0-512 229.222-512 512s229.222 512 512 512 512-229.222 512-512-229.222-512-512-512zM855.808 689.408c-19.2 19.2-50.278 19.2-69.478 0l-376.73-376.73-171.878 171.93c-19.2 19.2-50.278 19.2-69.478 0s-19.2-50.278 0-69.478c0 0 201.523-205.261 204.8-208.486 9.984-10.138 23.347-14.643 36.557-14.080 13.21-0.563 26.573 3.942 36.608 14.029 3.277 3.226 409.6 413.286 409.6 413.286 19.2 19.2 19.2 50.33 0 69.53z" />
<glyph unicode="&#xe908;" glyph-name="close" d="M601.024 448l276.736-276.736c24.512-24.576 24.512-64.384 0-89.024-24.64-24.576-64.384-24.576-89.024 0l-276.736 276.736-276.736-276.736c-24.512-24.576-64.384-24.576-89.024 0-24.512 24.64-24.512 64.448 0 89.024l276.736 276.736-276.736 276.736c-24.512 24.576-24.512 64.384 0 89.024 24.64 24.576 64.512 24.576 89.024 0l276.736-276.736 276.736 276.736c24.64 24.576 64.384 24.576 89.024 0 24.512-24.64 24.512-64.448 0-89.024l-276.736-276.736z" />
<glyph unicode="&#xe909;" glyph-name="content" d="M409.6 524.8h-153.6v-51.2h153.6v51.2zM409.6 627.2h-153.6v-51.2h153.6v51.2zM256 268.8h409.6v51.2h-409.6v-51.2zM409.6 729.6h-153.6v-51.2h153.6v51.2zM870.4 780.8h-51.2v51.2c0 28.262-22.938 51.2-51.2 51.2h-614.4c-28.262 0-51.2-22.938-51.2-51.2v-665.6c0-28.262 22.938-51.2 51.2-51.2h51.2v-51.2c0-28.262 22.938-51.2 51.2-51.2h614.4c28.262 0 51.2 22.938 51.2 51.2v665.6c0 28.262-22.938 51.2-51.2 51.2zM179.2 166.4c-14.157 0-25.6 11.443-25.6 25.6v614.4c0 14.131 11.443 25.6 25.6 25.6h563.2c14.157 0 25.6-11.469 25.6-25.6v-614.4c0-14.157-11.443-25.6-25.6-25.6h-563.2zM870.4 89.6c0-14.157-11.443-25.6-25.6-25.6h-563.2c-14.157 0-25.6 11.443-25.6 25.6v25.6h512c28.262 0 51.2 22.938 51.2 51.2v563.2h25.6c14.157 0 25.6-11.469 25.6-25.6v-614.4zM614.4 729.6h-102.4c-28.262 0-51.2-22.938-51.2-51.2v-153.6c0-28.262 22.938-51.2 51.2-51.2h102.4c28.262 0 51.2 22.938 51.2 51.2v153.6c0 28.262-22.938 51.2-51.2 51.2zM614.4 524.8h-102.4v153.6h102.4v-153.6zM256 371.2h409.6v51.2h-409.6v-51.2z" />
<glyph unicode="&#xe909;" glyph-name="content, type-References" d="M409.6 524.8h-153.6v-51.2h153.6v51.2zM409.6 627.2h-153.6v-51.2h153.6v51.2zM256 268.8h409.6v51.2h-409.6v-51.2zM409.6 729.6h-153.6v-51.2h153.6v51.2zM870.4 780.8h-51.2v51.2c0 28.262-22.938 51.2-51.2 51.2h-614.4c-28.262 0-51.2-22.938-51.2-51.2v-665.6c0-28.262 22.938-51.2 51.2-51.2h51.2v-51.2c0-28.262 22.938-51.2 51.2-51.2h614.4c28.262 0 51.2 22.938 51.2 51.2v665.6c0 28.262-22.938 51.2-51.2 51.2zM179.2 166.4c-14.157 0-25.6 11.443-25.6 25.6v614.4c0 14.131 11.443 25.6 25.6 25.6h563.2c14.157 0 25.6-11.469 25.6-25.6v-614.4c0-14.157-11.443-25.6-25.6-25.6h-563.2zM870.4 89.6c0-14.157-11.443-25.6-25.6-25.6h-563.2c-14.157 0-25.6 11.443-25.6 25.6v25.6h512c28.262 0 51.2 22.938 51.2 51.2v563.2h25.6c14.157 0 25.6-11.469 25.6-25.6v-614.4zM614.4 729.6h-102.4c-28.262 0-51.2-22.938-51.2-51.2v-153.6c0-28.262 22.938-51.2 51.2-51.2h102.4c28.262 0 51.2 22.938 51.2 51.2v153.6c0 28.262-22.938 51.2-51.2 51.2zM614.4 524.8h-102.4v153.6h102.4v-153.6zM256 371.2h409.6v51.2h-409.6v-51.2z" />
<glyph unicode="&#xe90a;" glyph-name="control-Checkbox" d="M793.6 115.2c0-14.157-11.443-25.6-25.6-25.6h-665.6c-14.131 0-25.6 11.443-25.6 25.6v665.6c0 14.157 11.469 25.6 25.6 25.6h665.6c14.157 0 25.6-11.443 25.6-25.6v-102.4h51.2v128c0 28.262-22.938 51.2-51.2 51.2h-716.8c-28.262 0-51.2-22.938-51.2-51.2v-716.8c0-28.262 22.938-51.2 51.2-51.2h716.8c28.262 0 51.2 22.938 51.2 51.2v281.6h-51.2v-256zM991.078 722.253c-9.958 9.958-26.035 9.958-35.968 0l-391.91-391.91-238.31 238.31c-9.958 9.958-26.061 9.958-35.942 0-9.958-9.907-9.958-26.010 0-35.942l254.874-254.874c0.461-0.538 0.614-1.203 1.126-1.69 5.043-5.018 11.674-7.475 18.278-7.373 6.605-0.102 13.235 2.355 18.278 7.373 0.512 0.512 0.666 1.178 1.126 1.69l408.448 408.474c9.933 9.933 9.933 26.035 0 35.942z" />
<glyph unicode="&#xe90b;" glyph-name="control-Dropdown" d="M51.2 960c-28.262 0-51.2-22.938-51.2-51.2v-281.6c0-28.262 22.938-51.2 51.2-51.2h921.6c28.262 0 51.2 22.938 51.2 51.2v281.6c0 28.262-22.938 51.2-51.2 51.2h-921.6zM76.8 908.8h512v-281.6h-512c-14.157 0-25.6 11.443-25.6 25.6v230.4c0 14.157 11.443 25.6 25.6 25.6zM640 908.8h307.2c14.157 0 25.6-11.443 25.6-25.6v-230.4c0-14.157-11.443-25.6-25.6-25.6h-307.2v281.6zM716.8 806.4c-0.41-0.358 89.139-102.938 89.6-102.4 0.512 0 89.6 95.36 89.6 102.4 0-0.384-172.16 0-179.2 0zM128 524.8c-42.394 0-76.8-34.406-76.8-76.8s34.406-76.8 76.8-76.8c42.394 0 76.8 34.406 76.8 76.8s-34.406 76.8-76.8 76.8zM128 473.6c14.157 0 25.6-11.443 25.6-25.6s-11.443-25.6-25.6-25.6c-14.157 0-25.6 11.443-25.6 25.6s11.443 25.6 25.6 25.6zM307.2 473.6c-14.157 0-25.6-11.443-25.6-25.6s11.443-25.6 25.6-25.6h640c14.157 0 25.6 11.443 25.6 25.6s-11.443 25.6-25.6 25.6h-640zM128 320c-42.394 0-76.8-34.381-76.8-76.8s34.406-76.8 76.8-76.8c42.394 0 76.8 34.381 76.8 76.8s-34.406 76.8-76.8 76.8zM128 268.8c14.157 0 25.6-11.443 25.6-25.6s-11.443-25.6-25.6-25.6c-14.157 0-25.6 11.443-25.6 25.6s11.443 25.6 25.6 25.6zM307.2 268.8c-14.157 0-25.6-11.443-25.6-25.6s11.443-25.6 25.6-25.6h640c14.157 0 25.6 11.443 25.6 25.6s-11.443 25.6-25.6 25.6h-640zM128 115.2c-42.394 0-76.8-34.381-76.8-76.8s34.406-76.8 76.8-76.8c42.394 0 76.8 34.381 76.8 76.8s-34.406 76.8-76.8 76.8zM128 64c14.157 0 25.6-11.443 25.6-25.6s-11.443-25.6-25.6-25.6c-14.157 0-25.6 11.443-25.6 25.6s11.443 25.6 25.6 25.6zM307.2 64c-14.157 0-25.6-11.443-25.6-25.6s11.443-25.6 25.6-25.6h640c14.157 0 25.6 11.443 25.6 25.6s-11.443 25.6-25.6 25.6h-640z" />
<glyph unicode="&#xe90c;" glyph-name="control-Input" d="M512 960c-14.157 0-25.6-11.443-25.6-25.6s11.443-25.6 25.6-25.6h128v-870.4h-128c-14.157 0-25.6-11.443-25.6-25.6s11.443-25.6 25.6-25.6h307.2c14.157 0 25.6 11.443 25.6 25.6s-11.443 25.6-25.6 25.6h-128v870.4h128c14.157 0 25.6 11.443 25.6 25.6s-11.443 25.6-25.6 25.6h-307.2zM51.2 755.2c-28.262 0-51.2-22.938-51.2-51.2v-460.8c0-28.262 22.938-51.2 51.2-51.2h537.6v51.2h-512c-14.131 0-25.6 11.443-25.6 25.6v409.6c0 14.157 11.469 25.6 25.6 25.6h512v51.2h-537.6zM742.4 755.2v-51.2h204.8c14.157 0 25.6-11.443 25.6-25.6v-409.6c0-14.157-11.443-25.6-25.6-25.6h-204.8v-51.2h230.4c28.262 0 51.2 22.938 51.2 51.2v460.8c0 28.262-22.938 51.2-51.2 51.2h-230.4zM285.9 653c-0.589-0.051-1.161-0.048-1.75-0.15-8.243-0.051-16.396-4.474-20.85-13.050l-132.55-306.25c-6.656-12.749-2.866-28.981 8.5-36.2 11.341-7.219 25.97-2.749 32.6 10l27.65 63.85h170.5c0.512 0 0.914 0.224 1.4 0.25l27.45-64.050c6.63-12.749 21.136-17.269 32.4-10.050s15.005 23.451 8.4 36.2l-131.3 306.25c-4.454 8.576-12.432 12.973-20.65 13.050-0.614 0.102-1.211 0.099-1.8 0.15zM285.9 570.85l63.65-148.45h-127.9l64.25 148.45z" />
@ -76,6 +76,4 @@
<glyph unicode="&#xe942;" glyph-name="checkmark" d="M864 832l-480-480-224 224-160-160 384-384 640 640z" />
<glyph unicode="&#xe943;" glyph-name="elapsed" d="M512.002 766.788v65.212h128v64c0 35.346-28.654 64-64.002 64h-191.998c-35.346 0-64-28.654-64-64v-64h128v-65.212c-214.798-16.338-384-195.802-384-414.788 0-229.75 186.25-416 416-416s416 186.25 416 416c0 218.984-169.202 398.448-384 414.788zM706.276 125.726c-60.442-60.44-140.798-93.726-226.274-93.726s-165.834 33.286-226.274 93.726c-60.44 60.44-93.726 140.8-93.726 226.274s33.286 165.834 93.726 226.274c58.040 58.038 134.448 91.018 216.114 93.548l-21.678-314.020c-1.86-26.29 12.464-37.802 31.836-37.802s33.698 11.512 31.836 37.802l-21.676 314.022c81.666-2.532 158.076-35.512 216.116-93.55 60.44-60.44 93.726-140.8 93.726-226.274s-33.286-165.834-93.726-226.274z" />
<glyph unicode="&#xe944;" glyph-name="timeout" d="M512 832c-247.424 0-448-200.576-448-448s200.576-448 448-448 448 200.576 448 448-200.576 448-448 448zM512 24c-198.824 0-360 161.178-360 360 0 198.824 161.176 360 360 360 198.822 0 360-161.176 360-360 0-198.822-161.178-360-360-360zM934.784 672.826c16.042 28.052 25.216 60.542 25.216 95.174 0 106.040-85.96 192-192 192-61.818 0-116.802-29.222-151.92-74.596 131.884-27.236 245.206-105.198 318.704-212.578v0zM407.92 885.404c-35.116 45.374-90.102 74.596-151.92 74.596-106.040 0-192-85.96-192-192 0-34.632 9.174-67.122 25.216-95.174 73.5 107.38 186.822 185.342 318.704 212.578zM512 384v256h-64v-320h256v64z" />
<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" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 58 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.

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

File diff suppressed because it is too large

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

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?b0qz8m');
src: url('fonts/icomoon.eot?b0qz8m#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?b0qz8m') format('truetype'),
url('fonts/icomoon.woff?b0qz8m') format('woff'),
url('fonts/icomoon.svg?b0qz8m#icomoon') format('svg');
src: url('fonts/icomoon.eot?3aocbn');
src: url('fonts/icomoon.eot?3aocbn#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?3aocbn') format('truetype'),
url('fonts/icomoon.woff?3aocbn') format('woff'),
url('fonts/icomoon.svg?3aocbn#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
@ -24,51 +24,6 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-grid:before {
content: "\f00a";
}
.icon-list:before {
content: "\f0c9";
}
.icon-bug:before {
content: "\e93d";
}
.icon-control-Markdown:before {
content: "\e938";
}
.icon-control-Date:before {
content: "\e936";
}
.icon-control-DateTime:before {
content: "\e937";
}
.icon-angle-right:before {
content: "\e931";
}
.icon-user-o:before {
content: "\e932";
}
.icon-caret-right:before {
content: "\e929";
}
.icon-caret-left:before {
content: "\e92a";
}
.icon-caret-up:before {
content: "\e92b";
}
.icon-caret-down:before {
content: "\e92c";
}
.icon-angle-up:before {
content: "\e903";
}
.icon-angle-down:before {
content: "\e900";
}
.icon-angle-left:before {
content: "\e901";
}
.icon-elapsed:before {
content: "\e943";
}
@ -138,6 +93,9 @@
.icon-content:before {
content: "\e909";
}
.icon-type-References:before {
content: "\e909";
}
.icon-control-Checkbox:before {
content: "\e90a";
}
@ -255,6 +213,45 @@
.icon-control-RichText:before {
content: "\e939";
}
.icon-bug:before {
content: "\e93d";
}
.icon-control-Markdown:before {
content: "\e938";
}
.icon-control-Date:before {
content: "\e936";
}
.icon-control-DateTime:before {
content: "\e937";
}
.icon-angle-right:before {
content: "\e931";
}
.icon-user-o:before {
content: "\e932";
}
.icon-caret-right:before {
content: "\e929";
}
.icon-caret-left:before {
content: "\e92a";
}
.icon-caret-up:before {
content: "\e92b";
}
.icon-caret-down:before {
content: "\e92c";
}
.icon-angle-up:before {
content: "\e903";
}
.icon-angle-down:before {
content: "\e900";
}
.icon-angle-left:before {
content: "\e901";
}
.icon-info:before {
content: "\e93c";
}

Loading…
Cancel
Save