Browse Source

More consistent UI code.

pull/95/head
Sebastian Stehle 9 years ago
parent
commit
f1f551cbb7
  1. 5
      src/Squidex/Controllers/Api/Webhooks/Models/WebhookCreatedDto.cs
  2. 2
      src/Squidex/Controllers/Api/Webhooks/WebhooksController.cs
  3. 6
      src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html
  4. 23
      src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts
  5. 22
      src/Squidex/app/features/administration/pages/messages.ts
  6. 25
      src/Squidex/app/features/administration/pages/users/messages.ts
  7. 49
      src/Squidex/app/features/administration/pages/users/user-page.component.ts
  8. 4
      src/Squidex/app/features/administration/pages/users/users-page.component.html
  9. 29
      src/Squidex/app/features/administration/pages/users/users-page.component.ts
  10. 5
      src/Squidex/app/features/assets/pages/assets-page.component.ts
  11. 31
      src/Squidex/app/features/content/pages/content/content-page.component.ts
  12. 47
      src/Squidex/app/features/content/pages/contents/contents-page.component.ts
  13. 13
      src/Squidex/app/features/content/pages/messages.ts
  14. 5
      src/Squidex/app/features/content/shared/assets-editor.component.ts
  15. 9
      src/Squidex/app/features/schemas/pages/messages.ts
  16. 10
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.html
  17. 126
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts
  18. 22
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts
  19. 23
      src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts
  20. 14
      src/Squidex/app/features/settings/pages/clients/clients-page.component.ts
  21. 10
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts
  22. 4
      src/Squidex/app/features/settings/pages/plans/plans-page.component.ts
  23. 10
      src/Squidex/app/features/webhooks/pages/webhooks-page.component.ts
  24. 7
      src/Squidex/app/framework/angular/http-extensions-impl.ts
  25. 10
      src/Squidex/app/framework/angular/validators.spec.ts
  26. 6
      src/Squidex/app/framework/angular/validators.ts
  27. 7
      src/Squidex/app/framework/utils/immutable-array.spec.ts
  28. 14
      src/Squidex/app/framework/utils/immutable-array.ts
  29. 75
      src/Squidex/app/shared/components/asset.component.ts
  30. 8
      src/Squidex/app/shared/services/app-clients.service.ts
  31. 4
      src/Squidex/app/shared/services/app-contributors.service.ts
  32. 4
      src/Squidex/app/shared/services/app-languages.service.ts
  33. 11
      src/Squidex/app/shared/services/apps.service.spec.ts
  34. 9
      src/Squidex/app/shared/services/apps.service.ts
  35. 15
      src/Squidex/app/shared/services/assets.service.spec.ts
  36. 56
      src/Squidex/app/shared/services/assets.service.ts
  37. 30
      src/Squidex/app/shared/services/contents.service.ts
  38. 12
      src/Squidex/app/shared/services/event-consumers.service.ts
  39. 23
      src/Squidex/app/shared/services/schemas.service.spec.ts
  40. 182
      src/Squidex/app/shared/services/schemas.service.ts
  41. 3
      src/Squidex/app/shared/services/users.service.spec.ts
  42. 32
      src/Squidex/app/shared/services/users.service.ts
  43. 7
      src/Squidex/app/shared/services/webhooks.service.spec.ts
  44. 17
      src/Squidex/app/shared/services/webhooks.service.ts

5
src/Squidex/Controllers/Api/Webhooks/Models/WebhookCreatedDto.cs

@ -23,5 +23,10 @@ namespace Squidex.Controllers.Api.Webhooks.Models
/// </summary>
[Required]
public string SharedSecret { get; set; }
/// <summary>
/// The id of the schema.
/// </summary>
public string SchemaId { get; set; }
}
}

2
src/Squidex/Controllers/Api/Webhooks/WebhooksController.cs

@ -94,7 +94,7 @@ namespace Squidex.Controllers.Api.Webhooks
await CommandBus.PublishAsync(command);
return CreatedAtAction(nameof(GetWebhooks), new { app }, SimpleMapper.Map(command, new WebhookCreatedDto()));
return CreatedAtAction(nameof(GetWebhooks), new { app }, SimpleMapper.Map(command, new WebhookCreatedDto { SchemaId = command.SchemaId.Id.ToString() }));
}
/// <summary>

6
src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.html

@ -48,13 +48,13 @@
<span>{{eventConsumer.position}}</span>
</td>
<td class="text-right">
<button class="btn btn-link" (click)="reset(eventConsumer.name)" *ngIf="!eventConsumer.isResetting" title="Reset Event Consumer">
<button class="btn btn-link" (click)="reset(eventConsumer)" *ngIf="!eventConsumer.isResetting" title="Reset Event Consumer">
<i class="icon icon-reset"></i>
</button>
<button class="btn btn-link" (click)="start(eventConsumer.name)" *ngIf="eventConsumer.isStopped" title="Start Event Consumer">
<button class="btn btn-link" (click)="start(eventConsumer)" *ngIf="eventConsumer.isStopped" title="Start Event Consumer">
<i class="icon icon-play"></i>
</button>
<button class="btn btn-link" (click)="stop(eventConsumer.name)" *ngIf="!eventConsumer.isStopped" title="Stop Event Consumer">
<button class="btn btn-link" (click)="stop(eventConsumer)" *ngIf="!eventConsumer.isStopped" title="Stop Event Consumer">
<i class="icon icon-pause"></i>
</button>
</td>

23
src/Squidex/app/features/administration/pages/event-consumers/event-consumers-page.component.ts

@ -42,7 +42,7 @@ export class EventConsumersPageComponent extends ComponentBase implements OnInit
public ngOnInit() {
this.subscription =
Observable.timer(0, 4000)
.switchMap(_ => this.eventConsumersService.getEventConsumers())
.switchMap(() => this.eventConsumersService.getEventConsumers())
.subscribe(dtos => {
this.eventConsumers = ImmutableArray.of(dtos);
});
@ -52,37 +52,28 @@ export class EventConsumersPageComponent extends ComponentBase implements OnInit
this.subscription.unsubscribe();
}
public start(name: string) {
public start(consumer: EventConsumerDto) {
this.eventConsumersService.startEventConsumer(name)
.subscribe(() => {
this.eventConsumers =
this.eventConsumers.replaceAll(
e => e.name === name,
e => new EventConsumerDto(name, false, e.isResetting, e.error, e.position));
this.eventConsumers = this.eventConsumers.replaceBy('name', consumer.start());
}, error => {
this.notifyError(error);
});
}
public stop(name: string) {
public stop(consumer: EventConsumerDto) {
this.eventConsumersService.stopEventConsumer(name)
.subscribe(() => {
this.eventConsumers =
this.eventConsumers.replaceAll(
e => e.name === name,
e => new EventConsumerDto(name, true, e.isResetting, e.error, e.position));
this.eventConsumers = this.eventConsumers.replaceBy('name', consumer.stop());
}, error => {
this.notifyError(error);
});
}
public reset(name: string) {
public reset(consumer: EventConsumerDto) {
this.eventConsumersService.resetEventConsumer(name)
.subscribe(() => {
this.eventConsumers =
this.eventConsumers.replaceAll(
e => e.name === name,
e => new EventConsumerDto(name, e.isStopped, true, e.error, e.position));
this.eventConsumers = this.eventConsumers.replaceBy('name', consumer.reset());
}, error => {
this.notifyError(error);
});

22
src/Squidex/app/features/administration/pages/messages.ts

@ -0,0 +1,22 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { UserDto } from 'shared';
export class UserCreated {
constructor(
public readonly user: UserDto
) {
}
}
export class UserUpdated {
constructor(
public readonly user: UserDto
) {
}
}

25
src/Squidex/app/features/administration/pages/users/messages.ts

@ -1,25 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
export class UserCreated {
constructor(
public readonly id: string,
public readonly email: string,
public readonly displayName: string,
public readonly pictureUrl: string
) {
}
}
export class UserUpdated {
constructor(
public readonly id: string,
public readonly email: string,
public readonly displayName: string
) {
}
}

49
src/Squidex/app/features/administration/pages/users/user-page.component.ts

@ -6,7 +6,7 @@
*/
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import {
@ -19,7 +19,7 @@ import {
ValidatorsEx
} from 'shared';
import { UserCreated, UserUpdated } from './messages';
import { UserCreated, UserUpdated } from './../messages';
@Component({
selector: 'sqx-user-page',
@ -27,6 +27,8 @@ import { UserCreated, UserUpdated } from './messages';
templateUrl: './user-page.component.html'
})
export class UserPageComponent extends ComponentBase implements OnInit {
private user: UserDto;
public currentUserId: string;
public userFormSubmitted = false;
public userForm: FormGroup;
@ -52,7 +54,9 @@ export class UserPageComponent extends ComponentBase implements OnInit {
this.route.data.map(p => p['user'])
.subscribe((user: UserDto) => {
this.populateForm(user);
this.user = user;
this.populateForm();
});
}
@ -79,12 +83,14 @@ export class UserPageComponent extends ComponentBase implements OnInit {
if (this.isNewMode) {
this.userManagementService.postUser(requestDto)
.subscribe(created => {
this.messageBus.publish(
new UserCreated(
this.user =
new UserDto(
created.id,
requestDto.email,
requestDto.displayName,
created.pictureUrl!));
created.pictureUrl!,
false);
this.messageBus.publish(new UserCreated(this.user));
this.notifyInfo('User created successfully.');
back();
@ -94,11 +100,12 @@ export class UserPageComponent extends ComponentBase implements OnInit {
} else {
this.userManagementService.putUser(this.userId, requestDto)
.subscribe(() => {
this.messageBus.publish(
new UserUpdated(
this.userId,
this.user =
this.user.update(
requestDto.email,
requestDto.displayName));
requestDto.displayMessage);
this.messageBus.publish(new UserUpdated(this.user));
this.notifyInfo('User saved successfully.');
enable();
@ -109,10 +116,10 @@ export class UserPageComponent extends ComponentBase implements OnInit {
}
}
private populateForm(user: UserDto) {
const input = user || {};
private populateForm() {
const input = this.user || {};
this.isNewMode = !user;
this.isNewMode = !this.user;
this.userId = input['id'];
this.userFormError = '';
this.userFormSubmitted = false;
@ -128,17 +135,17 @@ export class UserPageComponent extends ComponentBase implements OnInit {
[
Validators.required,
Validators.maxLength(100)
]],
password: ['',
[
this.isNewMode ? Validators.required : Validators.nullValidator
]],
passwordConfirm: ['',
[
ValidatorsEx.match('password', 'Passwords must be the same.')
]]
});
if (user) {
this.userForm.addControl('password', new FormControl(''));
} else {
this.userForm.addControl('password', new FormControl('', Validators.required));
}
this.userForm.addControl('passwordConfirm', new FormControl('', ValidatorsEx.match('password', 'Passwords must be the same.')));
this.isCurrentUser = this.userId === this.currentUserId;
}
}

4
src/Squidex/app/features/administration/pages/users/users-page.component.html

@ -70,10 +70,10 @@
</td>
<td class="text-right">
<span *ngIf="user.id !== currentUserId">
<button class="btn btn-link" (click)="lock(user.id); $event.stopPropagation();" *ngIf="!user.isLocked" title="Lock User">
<button class="btn btn-link" (click)="lock(user); $event.stopPropagation();" *ngIf="!user.isLocked" title="Lock User">
<i class="icon icon-unlocked"></i>
</button>
<button class="btn btn-link" (click)="unlock(user.id); $event.stopPropagation();" *ngIf="user.isLocked" title="Unlock User">
<button class="btn btn-link" (click)="unlock(user); $event.stopPropagation();" *ngIf="user.isLocked" title="Unlock User">
<i class="icon icon-lock"></i>
</button>
</span>

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

@ -20,7 +20,7 @@ import {
UserManagementService
} from 'shared';
import { UserCreated, UserUpdated } from './messages';
import { UserCreated, UserUpdated } from './../messages';
@Component({
selector: 'sqx-users-page',
@ -55,19 +55,14 @@ export class UsersPageComponent extends ComponentBase implements OnDestroy, OnIn
this.userCreatedSubscription =
this.messageBus.of(UserCreated)
.subscribe(message => {
const user = new UserDto(message.id, message.email, message.displayName, message.pictureUrl, false);
this.usersItems = this.usersItems.pushFront(user);
this.usersItems = this.usersItems.pushFront(message.user);
this.usersPager = this.usersPager.incrementCount();
});
this.userUpdatedSubscription =
this.messageBus.of(UserUpdated)
.subscribe(message => {
this.usersItems =
this.usersItems.replaceAll(
u => u.id === message.id,
u => new UserDto(u.id, message.email, message.displayName, u.pictureUrl, u.isLocked));
this.usersItems = this.usersItems.replaceBy('id', message.user);
});
this.currentUserId = this.authService.user!.id;
@ -96,25 +91,19 @@ export class UsersPageComponent extends ComponentBase implements OnDestroy, OnIn
});
}
public lock(id: string) {
this.userManagementService.lockUser(id)
public lock(user: UserDto) {
this.userManagementService.lockUser(user.id)
.subscribe(() => {
this.usersItems =
this.usersItems.replaceAll(
u => u.id === id,
u => new UserDto(u.id, u.email, u.displayName, u.pictureUrl, true));
this.usersItems = this.usersItems.replaceBy('id', user.lock());
}, error => {
this.notifyError(error);
});
}
public unlock(id: string) {
this.userManagementService.unlockUser(id)
public unlock(user: UserDto) {
this.userManagementService.unlockUser(user.id)
.subscribe(() => {
this.usersItems =
this.usersItems.replaceAll(
u => u.id === id,
u => new UserDto(u.id, u.email, u.displayName, u.pictureUrl, false));
this.usersItems = this.usersItems.replaceBy('id', user.unlock());
}, error => {
this.notifyError(error);
});

5
src/Squidex/app/features/assets/pages/assets-page.component.ts

@ -50,10 +50,7 @@ export class AssetsPageComponent extends AppComponentBase implements OnDestroy,
this.messageBus.of(AssetUpdated)
.subscribe(event => {
if (event.sender !== this) {
this.assetsItems =
this.assetsItems.replaceAll(
a => a.id === event.assetDto.id,
a => event.assetDto);
this.assetsItems = this.assetsItems.replaceBy('id', event.assetDto);
}
});

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

@ -21,6 +21,7 @@ import {
AppLanguageDto,
AppsStoreService,
allData,
AuthService,
CanComponentDeactivate,
ContentDto,
ContentsService,
@ -44,6 +45,7 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
private contentDeletedSubscription: Subscription;
private version: Version = new Version('');
private cancelPromise: Subject<boolean> | null = null;
private content: ContentDto;
public schema: SchemaDetailsDto;
@ -58,6 +60,7 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
public languages: AppLanguageDto[] = [];
constructor(apps: AppsStoreService, notifications: NotificationService,
private readonly authService: AuthService,
private readonly contentsService: ContentsService,
private readonly route: ActivatedRoute,
private readonly router: Router,
@ -78,7 +81,7 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
this.contentDeletedSubscription =
this.messageBus.of(ContentDeleted)
.subscribe(message => {
if (message.id === this.contentId) {
if (message.contentId === this.contentId) {
this.router.navigate(['../'], { relativeTo: this.route });
}
});
@ -87,7 +90,9 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
this.route.data.map(p => p['content'])
.subscribe((content: ContentDto) => {
this.populateForm(content);
this.content = content;
this.populateForm();
});
}
@ -142,8 +147,10 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
if (this.isNewMode) {
this.appNameOnce()
.switchMap(app => this.contentsService.postContent(app, this.schema.name, requestDto, publish, this.version))
.subscribe(created => {
this.messageBus.publish(new ContentCreated(created.id, created.data, this.version.value, publish));
.subscribe(dto => {
this.content = dto;
this.messageBus.publish(new ContentCreated(dto));
this.notifyInfo('Content created successfully.');
back();
@ -155,7 +162,9 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
this.appNameOnce()
.switchMap(app => this.contentsService.putContent(app, this.schema.name, this.contentId!, requestDto, this.version))
.subscribe(() => {
this.messageBus.publish(new ContentUpdated(this.contentId!, requestDto, this.version.value));
this.content = this.content.update(requestDto, this.authService.user.token);
this.messageBus.publish(new ContentUpdated(this.content));
this.notifyInfo('Content saved successfully.');
this.enable();
@ -205,23 +214,23 @@ export class ContentPageComponent extends AppComponentBase implements CanCompone
this.contentForm = new FormGroup(controls);
}
private populateForm(content: ContentDto) {
private populateForm() {
this.contentForm.markAsPristine();
if (!content) {
if (!this.content) {
this.contentData = null;
this.contentId = null;
this.isNewMode = true;
return;
}
this.contentData = content.data;
this.contentId = content.id;
this.version = content.version;
this.contentData = this.content.data;
this.contentId = this.content.id;
this.version = this.content.version;
this.isNewMode = false;
for (const field of this.schema.fields) {
const fieldValue = content.data[field.name] || {};
const fieldValue = this.content.data[field.name] || {};
const fieldForm = <FormGroup>this.contentForm.get(field.name);
if (field.partitioning === 'language') {

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

@ -24,14 +24,12 @@ import {
AuthService,
ContentDto,
ContentsService,
DateTime,
FieldDto,
ImmutableArray,
MessageBus,
NotificationService,
Pager,
SchemaDetailsDto,
Version
SchemaDetailsDto
} from 'shared';
@Component({
@ -81,14 +79,14 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
this.contentCreatedSubscription =
this.messageBus.of(ContentCreated)
.subscribe(message => {
this.contentItems = this.contentItems.pushFront(this.createContent(message.id, message.data, message.version, message.isPublished));
this.contentItems = this.contentItems.pushFront(message.content);
this.contentsPager = this.contentsPager.incrementCount();
});
this.contentUpdatedSubscription =
this.messageBus.of(ContentUpdated)
.subscribe(message => {
this.updateContents(message.id, undefined, message.data, message.version);
this.contentItems = this.contentItems.replaceBy('id', message.content, (o, n) => o.update(n.data, n.lastModifiedBy));
});
this.route.params.map(p => <string> p['language'])
@ -127,7 +125,7 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
this.appNameOnce()
.switchMap(app => this.contentsService.publishContent(app, this.schema.name, content.id, content.version))
.subscribe(() => {
this.updateContents(content.id, true, content.data, content.version.value);
this.contentItems = this.contentItems.replaceBy('id', content.publish(this.authService.user.token));
}, error => {
this.notifyError(error);
});
@ -137,7 +135,7 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
this.appNameOnce()
.switchMap(app => this.contentsService.unpublishContent(app, this.schema.name, content.id, content.version))
.subscribe(() => {
this.updateContents(content.id, false, content.data, content.version.value);
this.contentItems = this.contentItems.replaceBy('id', content.unpublish(this.authService.user.token));
}, error => {
this.notifyError(error);
});
@ -204,40 +202,5 @@ export class ContentsPageComponent extends AppComponentBase implements OnDestroy
this.load();
}
private updateContents(id: string, p: boolean | undefined, data: any, version: string) {
this.contentItems = this.contentItems.replaceAll(x => x.id === id, c => this.updateContent(c, p === undefined ? c.isPublished : p, data, version));
}
private createContent(id: string, data: any, version: string, isPublished: boolean): ContentDto {
const me = `subject:${this.authService.user!.id}`;
const newContent =
new ContentDto(
id,
isPublished,
me, me,
DateTime.now(),
DateTime.now(),
data,
new Version(version));
return newContent;
}
private updateContent(content: ContentDto, isPublished: boolean, data: any, version: string): ContentDto {
const me = `subject:${this.authService.user!.id}`;
const newContent =
new ContentDto(
content.id,
isPublished,
content.createdBy, me,
content.created, DateTime.now(),
data,
new Version(version));
return newContent;
}
}

13
src/Squidex/app/features/content/pages/messages.ts

@ -5,28 +5,25 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { ContentDto } from 'shared';
export class ContentCreated {
constructor(
public readonly id: string,
public readonly data: any,
public readonly version: string,
public readonly isPublished: boolean
public readonly content: ContentDto
) {
}
}
export class ContentUpdated {
constructor(
public readonly id: string,
public readonly data: any,
public readonly version: string
public readonly content: ContentDto
) {
}
}
export class ContentDeleted {
constructor(
public readonly id: string
public readonly contentId: string
) {
}
}

5
src/Squidex/app/features/content/shared/assets-editor.component.ts

@ -56,10 +56,7 @@ export class AssetsEditorComponent extends AppComponentBase implements ControlVa
this.messageBus.of(AssetUpdated)
.subscribe(event => {
if (event.sender !== this) {
this.oldAssets =
this.oldAssets.replaceAll(
a => a.id === event.assetDto.id,
a => event.assetDto);
this.oldAssets = this.oldAssets.replaceBy('id', event.assetDto);
}
});
}

9
src/Squidex/app/features/schemas/pages/messages.ts

@ -5,21 +5,18 @@
* Copyright (c) Sebastian Stehle. All rights reserved
*/
import { SchemaPropertiesDto } from 'shared';
import { SchemaDto } from 'shared';
export class SchemaUpdated {
constructor(
public readonly name: string,
public readonly properties: SchemaPropertiesDto,
public readonly isPublished: boolean,
public readonly version: string
public readonly schema: SchemaDto
) {
}
}
export class SchemaDeleted {
constructor(
public readonly name: string
public readonly schemaId: string
) {
}
}

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

@ -1,4 +1,4 @@
<sqx-title message="{app} | {schema}" parameter1="app" value1="{{appName() | async}}" parameter2="schema" value2="{{schemaName}}"></sqx-title>
<sqx-title message="{app} | {schema}" parameter1="app" value1="{{appName() | async}}" parameter2="schema" value2="{{schema.name}}"></sqx-title>
<sqx-panel panelWidth="48rem">
<div class="panel-header">
@ -9,10 +9,10 @@
</button>
<div class="btn-group btn-group-sm" data-toggle="buttons">
<button type="button" class="btn btn-publishing btn-secondary" [class.btn-success]="isPublished" [disabled]="isPublished" (click)="publish()">
<button type="button" class="btn btn-publishing btn-secondary" [class.btn-success]="schema.isPublished" [disabled]="schema.isPublished" (click)="publish()">
Published
</button>
<button type="button" class="btn btn-publishing btn-secondary" [class.btn-danger]="!isPublished" [disabled]="!isPublished" (click)="unpublish()">
<button type="button" class="btn btn-publishing btn-secondary" [class.btn-danger]="!schema.isPublished" [disabled]="!schema.isPublished" (click)="unpublish()">
Unpublished
</button>
</div>
@ -48,7 +48,7 @@
(enabling)="enableField(field)"
(showing)="showField(field)"
(hiding)="hideField(field)"
(saving)="saveField(field, $event)"></sqx-field>
(saving)="saveField($event)"></sqx-field>
</div>
<div class="table-items-footer">
@ -134,7 +134,7 @@
</div>
<div class="modal-body">
<sqx-schema-edit-form [appName]="appName() | async" [name]="schemaName" [properties]="schemaProperties" [version]="schemaVersion" (saved)="onSchemaSaved($event)" (cancelled)="editSchemaDialog.hide()"></sqx-schema-edit-form>
<sqx-schema-edit-form [appName]="appName() | async" [name]="schema.name" [properties]="schema.properties" [version]="schema.version" (saved)="onSchemaSaved($event)" (cancelled)="editSchemaDialog.hide()"></sqx-schema-edit-form>
</div>
</div>
</div>

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

@ -13,12 +13,12 @@ import {
AddFieldDto,
AppComponentBase,
AppsStoreService,
AuthService,
createProperties,
fadeAnimation,
FieldDto,
fieldTypes,
HistoryChannelUpdated,
ImmutableArray,
MessageBus,
ModalView,
NotificationService,
@ -27,8 +27,7 @@ import {
SchemaPropertiesDto,
SchemasService,
UpdateFieldDto,
ValidatorsEx,
Version
ValidatorsEx
} from 'shared';
import { SchemaDeleted, SchemaUpdated } from './../messages';
@ -45,11 +44,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public fieldTypes = fieldTypes;
public schemaExport: any;
public schemaName: string;
public schemaFields = ImmutableArray.empty<FieldDto>();
public schemaVersion = new Version('');
public schemaProperties: SchemaPropertiesDto;
public schemaInformation: any;
public schema: SchemaDetailsDto;
public schemas: SchemaDto[];
public confirmDeleteDialog = new ModalView();
@ -59,8 +54,6 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public editOptionsDropdown = new ModalView();
public editSchemaDialog = new ModalView();
public isPublished: boolean;
public addFieldFormSubmitted = false;
public addFieldForm: FormGroup =
this.formBuilder.group({
@ -81,11 +74,12 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
}
constructor(apps: AppsStoreService, notifications: NotificationService,
private readonly schemasService: SchemasService,
private readonly messageBus: MessageBus,
private readonly authService: AuthService,
private readonly formBuilder: FormBuilder,
private readonly messageBus: MessageBus,
private readonly route: ActivatedRoute,
private readonly router: Router
private readonly router: Router,
private readonly schemasService: SchemasService
) {
super(notifications, apps);
}
@ -93,13 +87,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public ngOnInit() {
this.route.data.map(p => p['schema'])
.subscribe((schema: SchemaDetailsDto) => {
this.schemaName = schema.name;
this.schemaFields = ImmutableArray.of(schema.fields);
this.schemaVersion = schema.version;
this.schemaProperties = schema.properties;
this.schemaInformation = { properties: schema.properties, name: schema.name };
this.isPublished = schema.isPublished;
this.schema = schema;
this.export();
});
@ -119,10 +107,9 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public publish() {
this.appNameOnce()
.switchMap(app => this.schemasService.publishSchema(app, this.schemaName, this.schemaVersion)).retry(2)
.switchMap(app => this.schemasService.publishSchema(app, this.schema.name, this.schema.version)).retry(2)
.subscribe(() => {
this.isPublished = true;
this.notify();
this.updateSchema(this.schema.publish(this.authService.user.token));
}, error => {
this.notifyError(error);
});
@ -130,10 +117,9 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public unpublish() {
this.appNameOnce()
.switchMap(app => this.schemasService.unpublishSchema(app, this.schemaName, this.schemaVersion)).retry(2)
.switchMap(app => this.schemasService.unpublishSchema(app, this.schema.name, this.schema.version)).retry(2)
.subscribe(() => {
this.isPublished = false;
this.notify();
this.updateSchema(this.schema.unpublish(this.authService.user.token));
}, error => {
this.notifyError(error);
});
@ -141,9 +127,9 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public enableField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.enableField(app, this.schemaName, field.fieldId, this.schemaVersion)).retry(2)
.switchMap(app => this.schemasService.enableField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateField(field, new FieldDto(field.fieldId, field.name, field.isHidden, false, field.partitioning, field.properties));
this.updateSchema(this.schema.updateField(this.authService.user.token, field.enable()));
}, error => {
this.notifyError(error);
});
@ -151,9 +137,9 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public disableField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.disableField(app, this.schemaName, field.fieldId, this.schemaVersion)).retry(2)
.switchMap(app => this.schemasService.disableField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateField(field, new FieldDto(field.fieldId, field.name, field.isHidden, true, field.partitioning, field.properties));
this.updateSchema(this.schema.updateField(this.authService.user.token, field.disable()));
}, error => {
this.notifyError(error);
});
@ -161,9 +147,9 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public showField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.showField(app, this.schemaName, field.fieldId, this.schemaVersion)).retry(2)
.switchMap(app => this.schemasService.showField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateField(field, new FieldDto(field.fieldId, field.name, false, field.isDisabled, field.partitioning, field.properties));
this.updateSchema(this.schema.updateField(this.authService.user.token, field.show()));
}, error => {
this.notifyError(error);
});
@ -171,9 +157,9 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public hideField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.hideField(app, this.schemaName, field.fieldId, this.schemaVersion)).retry(2)
.switchMap(app => this.schemasService.hideField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateField(field, new FieldDto(field.fieldId, field.name, true, field.isDisabled, field.partitioning, field.properties));
this.updateSchema(this.schema.updateField(this.authService.user.token, field.hide()));
}, error => {
this.notifyError(error);
});
@ -181,33 +167,31 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public deleteField(field: FieldDto) {
this.appNameOnce()
.switchMap(app => this.schemasService.deleteField(app, this.schemaName, field.fieldId, this.schemaVersion)).retry(2)
.switchMap(app => this.schemasService.deleteField(app, this.schema.name, field.fieldId, this.schema.version)).retry(2)
.subscribe(() => {
this.updateFields(this.schemaFields.remove(field));
this.updateSchema(this.schema.removeField(this.authService.user.token, field));
}, error => {
this.notifyError(error);
});
}
public sortFields(fields: FieldDto[]) {
this.updateFields(ImmutableArray.of(fields));
this.appNameOnce()
.switchMap(app => this.schemasService.putFieldOrdering(app, this.schemaName, fields.map(t => t.fieldId), this.schemaVersion)).retry(2)
.switchMap(app => this.schemasService.putFieldOrdering(app, this.schema.name, fields.map(t => t.fieldId), this.schema.version)).retry(2)
.subscribe(() => {
this.updateFields(ImmutableArray.of(fields));
this.updateSchema(this.schema.replaceFields(this.authService.user.token, fields));
}, error => {
this.notifyError(error);
});
}
public saveField(field: FieldDto, newField: FieldDto) {
const requestDto = new UpdateFieldDto(newField.properties);
public saveField(field: FieldDto) {
const requestDto = new UpdateFieldDto(field.properties);
this.appNameOnce()
.switchMap(app => this.schemasService.putField(app, this.schemaName, field.fieldId, requestDto, this.schemaVersion)).retry(2)
.switchMap(app => this.schemasService.putField(app, this.schema.name, field.fieldId, requestDto, this.schema.version)).retry(2)
.subscribe(() => {
this.updateField(field, new FieldDto(field.fieldId, field.name, newField.isHidden, field.isDisabled, field.partitioning, newField.properties));
this.updateSchema(this.schema.updateField(this.authService.user.token, field));
}, error => {
this.notifyError(error);
});
@ -215,16 +199,15 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
public deleteSchema() {
this.appNameOnce()
.switchMap(app => this.schemasService.deleteSchema(app, this.schemaName, this.schemaVersion)).retry(2)
.finally(() => {
this.confirmDeleteDialog.hide();
})
.switchMap(app => this.schemasService.deleteSchema(app, this.schema.name, this.schema.version)).retry(2)
.subscribe(() => {
this.messageBus.publish(new SchemaDeleted(this.schemaName));
this.messageBus.publish(new SchemaDeleted(this.schema.id));
this.router.navigate(['../'], { relativeTo: this.route });
}, error => {
this.notifyError(error);
}, () => {
this.confirmDeleteDialog.hide();
});
}
@ -246,17 +229,9 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
};
this.appNameOnce()
.switchMap(app => this.schemasService.postField(app, this.schemaName, requestDto, this.schemaVersion))
.switchMap(app => this.schemasService.postField(app, this.schema.name, requestDto, this.schema.version))
.subscribe(dto => {
const newField =
new FieldDto(parseInt(dto.id, 10),
requestDto.name,
false,
false,
requestDto.partitioning,
requestDto.properties);
this.updateFields(this.schemaFields.push(newField));
this.updateSchema(this.schema.addField(this.authService.user.token, dto));
reset();
}, error => {
this.notifyError(error);
@ -271,28 +246,13 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
}
public onSchemaSaved(properties: SchemaPropertiesDto) {
this.updateProperties(properties);
this.updateSchema(this.schema.update(this.authService.user.token, properties));
this.editSchemaDialog.hide();
}
private updateProperties(properties: SchemaPropertiesDto) {
this.schemaProperties = properties;
this.schemaInformation = { properties: properties, name: this.schemaName };
this.notify();
this.export();
}
private updateField(field: FieldDto, newField: FieldDto) {
this.schemaFields = this.schemaFields.replace(field, newField);
this.notify();
this.export();
}
private updateFields(fields: ImmutableArray<FieldDto>) {
this.schemaFields = fields;
private updateSchema(schema: SchemaDetailsDto) {
this.schema = schema;
this.notify();
this.export();
@ -300,7 +260,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
private export() {
const result: any = {
fields: this.schemaFields.values.map(field => {
fields: this.schema.fields.map(field => {
const copy: any = Object.assign({}, field);
delete copy.fieldId;
@ -318,12 +278,12 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
properties: {}
};
if (this.schemaProperties.label) {
result.properties.label = this.schemaProperties.label;
if (this.schema.properties.label) {
result.properties.label = this.schema.properties.label;
}
if (this.schemaProperties.hints) {
result.properties.hints = this.schemaProperties.hints;
if (this.schema.properties.hints) {
result.properties.hints = this.schema.properties.hints;
}
this.schemaExport = result;
@ -331,7 +291,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
private notify() {
this.messageBus.publish(new HistoryChannelUpdated());
this.messageBus.publish(new SchemaUpdated(this.schemaName, this.schemaProperties, this.isPublished, this.schemaVersion.value));
this.messageBus.publish(new SchemaUpdated(this.schema));
}
}

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

@ -11,10 +11,8 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import {
ApiUrlConfig,
AuthService,
DateTime,
fadeAnimation,
SchemaDto,
SchemaPropertiesDto,
SchemasService,
ValidatorsEx,
Version
@ -74,7 +72,7 @@ export class SchemaFormComponent {
}
public cancel() {
this.reset();
this.resetForm();
this.cancelled.emit();
}
@ -87,14 +85,14 @@ export class SchemaFormComponent {
const schemaVersion = new Version();
const schemaName = this.createForm.controls['name'].value;
const requestDto = Object.assign(this.createForm.controls['import'].value || {}, {});
const requestDto = Object.assign(this.createForm.controls['import'].value || {}, { name: schemaName });
requestDto.name = schemaName;
const me = this.authService.user!.token;
this.schemas.postSchema(this.appName, requestDto, schemaVersion)
this.schemas.postSchema(this.appName, requestDto, me, undefined, schemaVersion)
.subscribe(dto => {
this.reset();
this.created.emit(this.createSchemaDto(dto.id, requestDto.properties || {}, schemaName, schemaVersion));
this.resetForm();
this.created.emit(dto);
}, error => {
this.createForm.enable();
this.createFormError = error.displayMessage;
@ -102,16 +100,10 @@ export class SchemaFormComponent {
}
}
private reset() {
private resetForm() {
this.createFormError = '';
this.createForm.reset();
this.createFormSubmitted = false;
}
private createSchemaDto(id: string, properties: SchemaPropertiesDto, name: string, version: Version) {
const user = this.authService.user!.token;
const now = DateTime.now();
return new SchemaDto(id, name, properties, false, user, user, now, now, version);
}
}

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

@ -13,16 +13,13 @@ import { Subscription } from 'rxjs';
import {
AppComponentBase,
AppsStoreService,
AuthService,
DateTime,
fadeAnimation,
ImmutableArray,
MessageBus,
ModalView,
NotificationService,
SchemaDto,
SchemasService,
Version
SchemasService
} from 'shared';
import { SchemaDeleted, SchemaUpdated } from './../messages';
@ -48,7 +45,6 @@ export class SchemasPageComponent extends AppComponentBase implements OnDestroy,
constructor(apps: AppsStoreService, notifications: NotificationService,
private readonly schemasService: SchemasService,
private readonly authService: AuthService,
private readonly messageBus: MessageBus,
private readonly route: ActivatedRoute
) {
@ -78,13 +74,13 @@ export class SchemasPageComponent extends AppComponentBase implements OnDestroy,
this.schemaUpdatedSubscription =
this.messageBus.of(SchemaUpdated)
.subscribe(m => {
this.updateSchemas(this.schemas.replaceAll(s => s.name === m.name, s => updateSchema(s, this.authService, m)));
this.updateSchemas(this.schemas.replaceBy('id', m.schema));
});
this.schemaDeletedSubscription =
this.messageBus.of(SchemaDeleted)
.subscribe(m => {
this.updateSchemas(this.schemas.filter(s => s.name !== m.name));
this.updateSchemas(this.schemas.filter(s => s.id !== m.schemaId));
});
this.load();
@ -130,16 +126,3 @@ export class SchemasPageComponent extends AppComponentBase implements OnDestroy,
}
}
function updateSchema(schema: SchemaDto, authService: AuthService, message: SchemaUpdated): SchemaDto {
const me = `subject:${authService.user!.id}`;
return new SchemaDto(
schema.id,
schema.name,
message.properties,
message.isPublished,
schema.createdBy, me,
schema.created, DateTime.now(),
new Version(message.version));
}

14
src/Squidex/app/features/settings/pages/clients/clients-page.component.ts

@ -85,7 +85,7 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
this.appNameOnce()
.switchMap(app => this.appClientsService.updateClient(app, client.id, requestDto, this.version))
.subscribe(() => {
this.updateClients(this.appClients.replace(client, rename(client, name)));
this.updateClients(this.appClients.replaceBy('id', client.rename(name)));
}, error => {
this.notifyError(error);
});
@ -97,7 +97,7 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
this.appNameOnce()
.switchMap(app => this.appClientsService.updateClient(app, client.id, requestDto, this.version))
.subscribe(() => {
this.updateClients(this.appClients.replace(client, change(client, isReader)));
this.updateClients(this.appClients.replaceBy('id', client.change(isReader)));
}, error => {
this.notifyError(error);
});
@ -134,12 +134,4 @@ export class ClientsPageComponent extends AppComponentBase implements OnInit {
this.messageBus.publish(new HistoryChannelUpdated());
}
}
function change(client: AppClientDto, isReader: boolean): AppClientDto {
return new AppClientDto(client.id, client.name, client.secret, isReader);
};
function rename(client: AppClientDto, name: string): AppClientDto {
return new AppClientDto(client.id, name, client.secret, client.isReader);
};
}

10
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts

@ -65,7 +65,8 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
public usersPermissions = [
'Owner',
'Developer',
'Editor'
'Editor',
'Reader'
];
public get canAddContributor() {
@ -118,7 +119,7 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
}
public changePermission(contributor: AppContributorDto, permission: string) {
const requestDto = changePermission(contributor, permission);
const requestDto = contributor.changePermission(permission);
this.appNameOnce()
.switchMap(app => this.appContributorsService.postContributor(app, requestDto, this.version))
@ -155,8 +156,3 @@ export class ContributorsPageComponent extends AppComponentBase implements OnIni
this.messageBus.publish(new HistoryChannelUpdated());
}
}
function changePermission(contributor: AppContributorDto, permission: string): AppContributorDto {
return new AppContributorDto(contributor.contributorId, permission);
}

4
src/Squidex/app/features/settings/pages/plans/plans-page.component.ts

@ -52,7 +52,6 @@ export class PlansPageComponent extends AppComponentBase implements OnInit {
.switchMap(app => this.plansService.getPlans(app, this.version).retry(2))
.subscribe(dto => {
this.plans = dto;
this.planOwned = !dto.planOwner || (dto.planOwner === this.authService.user!.id);
if (showInfo) {
@ -75,10 +74,9 @@ export class PlansPageComponent extends AppComponentBase implements OnInit {
this.plans.hasPortal,
this.plans.hasConfigured,
this.plans.plans);
this.isDisabled = false;
}, error => {
this.notifyError(error);
}, () => {
this.isDisabled = false;
});
}

10
src/Squidex/app/features/webhooks/pages/webhooks-page.component.ts

@ -94,23 +94,23 @@ export class WebhooksPageComponent extends AppComponentBase implements OnInit {
}
public addWebhook() {
this.addWebhookFormSubmitted = true;
if (this.addWebhookForm.valid) {
this.addWebhookFormSubmitted = true;
this.addWebhookForm.disable();
const requestDto = new CreateWebhookDto(this.addWebhookForm.controls['url'].value);
const schemaId = this.addWebhookForm.controls['schemaId'].value;
const schema = this.schemas.find(s => s.id === schemaId);
this.appNameOnce()
.switchMap(app => this.webhooksService.postWebhook(app, schema.name, requestDto, this.version))
.subscribe(dto => {
const webhook = new WebhookDto(dto.id, schemaId, dto.sharedSecret, requestDto.url, 0, 0, 0, 0, []);
this.webhooks = this.webhooks.push({ schema, webhook, showDetails: false });
this.resetWebhookForm();
this.webhooks = this.webhooks.push({ webhook: dto, schema: schema, showDetails: false });
}, error => {
this.notifyError(error);
}, () => {
this.resetWebhookForm();
});
}

7
src/Squidex/app/framework/angular/http-extensions-impl.ts

@ -10,13 +10,6 @@ import { Observable } from 'rxjs';
import { Version } from './../utils/version';
export class EntityCreatedDto {
constructor(
public readonly id: any
) {
}
}
export class ErrorDto {
public get displayMessage(): string {
let result = this.message;

10
src/Squidex/app/framework/angular/validators.spec.ts

@ -243,3 +243,13 @@ describe('ValidatorsEx.pattern', () => {
expect(error).toEqual(expected);
});
});
describe('ValidatorsEx.noop', () => {
it('should return null validator', () => {
const input = new FormControl(null);
const error = <any>ValidatorsEx.noop()(input);
expect(error).toBeNull();
});
});

6
src/Squidex/app/framework/angular/validators.ts

@ -128,4 +128,10 @@ export module ValidatorsEx {
return null;
};
}
export function noop() {
return (control: AbstractControl): { [key: string]: any } => {
return null;
};
}
}

7
src/Squidex/app/framework/utils/immutable-array.spec.ts

@ -112,6 +112,13 @@ describe('ImmutableArray', () => {
expect(array_2.values).toEqual([1, 4, 3, 8]);
});
it('should replace by field', () => {
const array_1 = ImmutableArray.of([{ id: 1, v: 1 }, { id: 2, v: 2 }]);
const array_2 = array_1.replaceBy('id', { id: 1, v: 11 });
expect(array_2.values).toEqual([{ id: 1, v: 11 }, { id: 2, v: 2 }]);
});
it('should return original if nothing has been replace', () => {
const array_1 = ImmutableArray.of([1, 2, 3, 4]);
const array_2 = array_1.replaceAll((i: number) => i % 200 === 0, i => i);

14
src/Squidex/app/framework/utils/immutable-array.ts

@ -1,3 +1,13 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Sebastian Stehle. All rights reserved
*/
export interface IdField {
id: string;
}
function freeze<T>(items: T[]): T[] {
for (let item of items) {
@ -152,4 +162,8 @@ export class ImmutableArray<T> implements Iterable<T> {
return hasChange ? new ImmutableArray<T>(copy) : this;
}
public replaceBy(field: string, newValue: T, replacer?: (o: T, n: T) => T) {
return this.replaceAll(x => x[field] === newValue[field], o => replacer ? replacer(o, newValue) : newValue);
}
}

75
src/Squidex/app/shared/components/asset.component.ts

@ -13,12 +13,10 @@ import { AppComponentBase } from './app.component-base';
import {
ApiUrlConfig,
AppsStoreService,
AssetCreatedDto,
AssetDto,
AssetReplacedDto,
AssetsService,
AuthService,
DateTime,
fadeAnimation,
FileHelper,
ModalView,
@ -93,28 +91,15 @@ export class AssetComponent extends AppComponentBase implements OnInit {
const initFile = this.initFile;
if (initFile) {
const me = `subject:${this.authService.user!.id}`;
this.appNameOnce()
.switchMap(app => this.assetsService.uploadFile(app, initFile))
.subscribe(result => {
if (result instanceof AssetCreatedDto) {
const me = `subject:${this.authService.user!.id}`;
const asset = new AssetDto(
result.id,
me, me,
DateTime.now(),
DateTime.now(),
result.fileName,
result.fileSize,
result.fileVersion,
result.mimeType,
result.isImage,
result.pixelWidth,
result.pixelHeight,
result.version);
this.loaded.emit(asset);
.switchMap(app => this.assetsService.uploadFile(app, initFile, me))
.subscribe(dto => {
if (dto instanceof AssetDto) {
this.loaded.emit(dto);
} else {
this.progress = result;
this.progress = dto;
}
}, error => {
this.failed.emit();
@ -128,32 +113,20 @@ export class AssetComponent extends AppComponentBase implements OnInit {
public updateFile(files: FileList) {
if (files.length === 1) {
const me = `subject:${this.authService.user!.id}`;
this.appNameOnce()
.switchMap(app => this.assetsService.replaceFile(app, this.asset.id, files[0], this.version))
.subscribe(result => {
if (result instanceof AssetReplacedDto) {
const me = `subject:${this.authService.user!.id}`;
const asset = new AssetDto(
this.asset.id,
this.asset.createdBy, me,
this.asset.created, DateTime.now(),
this.asset.fileName,
result.fileSize,
result.fileVersion,
result.mimeType,
result.isImage,
result.pixelWidth,
result.pixelHeight,
result.version);
this.updateAsset(asset, true);
.subscribe(dto => {
if (dto instanceof AssetReplacedDto) {
this.updateAsset(this.asset.update(dto, me), true);
} else {
this.progress = result;
this.progress = dto;
}
}, error => {
this.progress = 0;
this.notifyError(error);
}, () => {
this.progress = 0;
});
}
}
@ -166,24 +139,12 @@ export class AssetComponent extends AppComponentBase implements OnInit {
const requestDto = new UpdateAssetDto(this.renameForm.controls['name'].value);
const me = `subject:${this.authService.user!.id}`;
this.appNameOnce()
.switchMap(app => this.assetsService.putAsset(app, this.asset.id, requestDto, this.version))
.subscribe(() => {
const me = `subject:${this.authService.user!.id}`;
const asset = new AssetDto(
this.asset.id,
this.asset.createdBy, me,
this.asset.created, DateTime.now(), requestDto.fileName,
this.asset.fileSize,
this.asset.fileVersion,
this.asset.mimeType,
this.asset.isImage,
this.asset.pixelWidth,
this.asset.pixelHeight,
this.asset.version);
this.updateAsset(asset, true);
this.updateAsset(this.asset.rename(requestDto.fileName, me), true);
}, error => {
this.notifyError(error);
}, () => {

8
src/Squidex/app/shared/services/app-clients.service.ts

@ -25,6 +25,14 @@ export class AppClientDto {
public readonly isReader: boolean
) {
}
public rename(name: string): AppClientDto {
return new AppClientDto(this.id, name, this.secret, this.isReader);
}
public change(isReader: boolean): AppClientDto {
return new AppClientDto(this.id, this.name, this.secret, isReader);
}
}
export class CreateAppClientDto {

4
src/Squidex/app/shared/services/app-contributors.service.ts

@ -23,6 +23,10 @@ export class AppContributorDto {
public readonly permission: string
) {
}
public changePermission(permission: string): AppContributorDto {
return new AppContributorDto(this.contributorId, permission);
}
}
export class AppContributorsDto {

4
src/Squidex/app/shared/services/app-languages.service.ts

@ -26,6 +26,10 @@ export class AppLanguageDto {
public readonly fallback: string[]
) {
}
public update(isMaster: boolean, isOptional: boolean, fallback: string[]): AppLanguageDto {
return new AppLanguageDto(this.iso2Code, this.englishName, isMaster, isOptional, fallback);
}
}
export class AddAppLanguageDto {

11
src/Squidex/app/shared/services/apps.service.spec.ts

@ -13,11 +13,12 @@ import {
AppDto,
AppsService,
CreateAppDto,
DateTime,
EntityCreatedDto
DateTime
} from './../';
describe('AppsService', () => {
const now = DateTime.now();
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
@ -76,10 +77,10 @@ describe('AppsService', () => {
const dto = new CreateAppDto('new-app');
let created: EntityCreatedDto | null = null;
let app: AppDto | null = null;
appsService.postApp(dto).subscribe(result => {
created = result;
app = result;
});
const req = httpMock.expectOne('http://service/p/api/apps');
@ -89,6 +90,6 @@ describe('AppsService', () => {
req.flush({ id: '123' });
expect(created).toEqual(new EntityCreatedDto('123'));
expect(app).toEqual(new AppDto('123', dto.name, 'Owner', now, now));
}));
});

9
src/Squidex/app/shared/services/apps.service.ts

@ -14,8 +14,7 @@ import 'framework/angular/http-extensions';
import {
ApiUrlConfig,
DateTime,
HTTP,
EntityCreatedDto
HTTP
} from 'framework';
export class AppDto {
@ -63,12 +62,14 @@ export class AppsService {
.pretifyError('Failed to load apps. Please reload.');
}
public postApp(dto: CreateAppDto): Observable<EntityCreatedDto> {
public postApp(dto: CreateAppDto, now?: DateTime): Observable<AppDto> {
const url = this.apiUrl.buildUrl('api/apps');
return HTTP.postVersioned(this.http, url, dto)
.map(response => {
return new EntityCreatedDto(response.id);
now = now || DateTime.now();
return new AppDto(response.id, dto.name, 'Owner', now, now);
})
.pretifyError('Failed to create app. Please reload.');
}

15
src/Squidex/app/shared/services/assets.service.spec.ts

@ -12,7 +12,6 @@ import {
ApiUrlConfig,
AssetsDto,
AssetDto,
AssetCreatedDto,
AssetReplacedDto,
AssetsService,
DateTime,
@ -21,6 +20,8 @@ import {
} from './../';
describe('AssetsService', () => {
let now = DateTime.now();
let user = 'me';
let version = new Version('1');
beforeEach(() => {
@ -204,10 +205,10 @@ describe('AssetsService', () => {
it('should make post request to create asset',
inject([AssetsService, HttpTestingController], (assetsService: AssetsService, httpMock: HttpTestingController) => {
let asset: AssetCreatedDto | null = null;
let asset: AssetDto | null = null;
assetsService.uploadFile('my-app', null!).subscribe(result => {
asset = <AssetCreatedDto>result;
assetsService.uploadFile('my-app', null!, user).subscribe(result => {
asset = <AssetDto>result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/assets');
@ -228,8 +229,12 @@ describe('AssetsService', () => {
});
expect(asset).toEqual(
new AssetCreatedDto(
new AssetDto(
'id1',
user,
user,
now,
now,
'my-asset1.png',
1024, 2,
'text/plain',

56
src/Squidex/app/shared/services/assets.service.ts

@ -41,26 +41,40 @@ export class AssetDto {
public readonly version: Version
) {
}
}
export class UpdateAssetDto {
constructor(
public readonly fileName: string
) {
public update(result: AssetReplacedDto, user: string): AssetDto {
return new AssetDto(
this.id,
this.createdBy, user,
this.created, DateTime.now(),
this.fileName,
result.fileSize,
result.fileVersion,
result.mimeType,
result.isImage,
result.pixelWidth,
result.pixelHeight,
result.version)
}
public rename(name: string, user: string): AssetDto {
return new AssetDto(
this.id,
this.createdBy, user,
this.created, DateTime.now(), name,
this.fileSize,
this.fileVersion,
this.mimeType,
this.isImage,
this.pixelWidth,
this.pixelHeight,
this.version);
}
}
export class AssetCreatedDto {
export class UpdateAssetDto {
constructor(
public readonly id: string,
public readonly fileName: string,
public readonly fileSize: number,
public readonly fileVersion: number,
public readonly mimeType: string,
public readonly isImage: boolean,
public readonly pixelWidth: number | null,
public readonly pixelHeight: number | null,
public readonly version: Version
public readonly fileName: string
) {
}
}
@ -132,8 +146,8 @@ export class AssetsService {
.pretifyError('Failed to load assets. Please reload.');
}
public uploadFile(appName: string, file: File): Observable<number | AssetCreatedDto> {
return new Observable<number | AssetCreatedDto>(subscriber => {
public uploadFile(appName: string, file: File, user: string, now?: DateTime): Observable<number | AssetDto> {
return new Observable<number | AssetDto>(subscriber => {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/assets`);
const req = new HttpRequest('POST', url, getFormData(file), {
@ -150,8 +164,14 @@ export class AssetsService {
} else if (event instanceof HttpResponse) {
const response = event.body;
const dto = new AssetCreatedDto(
now = now || DateTime.now();
const dto = new AssetDto(
response.id,
user,
user,
now,
now,
response.fileName,
response.fileSize,
response.fileVersion,

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

@ -38,6 +38,36 @@ export class ContentDto {
public readonly version: Version
) {
}
public publish(user: string): ContentDto {
return new ContentDto(
this.id,
true,
this.createdBy, user,
this.created, DateTime.now(),
this.data,
this.version);
}
public unpublish(user: string): ContentDto {
return new ContentDto(
this.id,
false,
this.createdBy, user,
this.created, DateTime.now(),
this.data,
this.version);
}
public update(data: any, user: string): ContentDto {
return new ContentDto(
this.id,
this.isPublished,
this.createdBy, user,
this.created, DateTime.now(),
data,
this.version);
}
}
@Injectable()

12
src/Squidex/app/shared/services/event-consumers.service.ts

@ -22,6 +22,18 @@ export class EventConsumerDto {
public readonly position: string
) {
}
public start(): EventConsumerDto {
return new EventConsumerDto(this.name, false, false, this.error, this.position);
}
public stop(): EventConsumerDto {
return new EventConsumerDto(this.name, true, false, this.error, this.position);
}
public reset(): EventConsumerDto {
return new EventConsumerDto(this.name, this.isStopped, true, this.error, this.position);
}
}
@Injectable()

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

@ -14,7 +14,6 @@ import {
CreateSchemaDto,
createProperties,
DateTime,
EntityCreatedDto,
FieldDto,
SchemaDetailsDto,
SchemaDto,
@ -26,6 +25,8 @@ import {
} from './../';
describe('SchemasService', () => {
let now = DateTime.now();
let user = 'me';
let version = new Version('1');
beforeEach(() => {
@ -240,10 +241,10 @@ describe('SchemasService', () => {
const dto = new CreateSchemaDto('name');
let created: EntityCreatedDto | null = null;
let schema: SchemaDto | null = null;
schemasService.postSchema('my-app', dto, version).subscribe(result => {
created = result;
schemasService.postSchema('my-app', dto, user, now, version).subscribe(result => {
schema = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas');
@ -251,10 +252,10 @@ describe('SchemasService', () => {
expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({ id: 'my-schema' });
req.flush({ id: '1' });
expect(created).toEqual(
new EntityCreatedDto('my-schema'));
expect(schema).toEqual(
new SchemaDto('my-schema', dto.name, new SchemaPropertiesDto(null, null), false, user, user, now, now, version));
}));
it('should make post request to add field',
@ -262,10 +263,10 @@ describe('SchemasService', () => {
const dto = new AddFieldDto('name', 'invariant', createProperties('Number'));
let created: EntityCreatedDto | null = null;
let field: FieldDto | null = null;
schemasService.postField('my-app', 'my-schema', dto, version).subscribe(result => {
created = result;
field = result;
});
const req = httpMock.expectOne('http://service/p/api/apps/my-app/schemas/my-schema/fields');
@ -275,8 +276,8 @@ describe('SchemasService', () => {
req.flush({ id: 123 });
expect(created).toEqual(
new EntityCreatedDto(123));
expect(field).toEqual(
new FieldDto(123, dto.name, false, false, dto.partitioning, dto.properties));
}));
it('should make put request to update schema',

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

@ -15,7 +15,6 @@ import 'framework/angular/http-extensions';
import {
ApiUrlConfig,
DateTime,
EntityCreatedDto,
HTTP,
ValidatorsEx,
Version
@ -84,21 +83,130 @@ export class SchemaDto {
public readonly version: Version
) {
}
public publish(user: string): SchemaDto {
return new SchemaDto(
this.id,
this.name,
this.properties,
true,
this.createdBy, user,
this.created, DateTime.now(),
this.version);
}
public unpublish(user: string): SchemaDto {
return new SchemaDto(
this.id,
this.name,
this.properties,
false,
this.createdBy, user,
this.created, DateTime.now(),
this.version);
}
public update(user: string, properties: SchemaPropertiesDto): SchemaDto {
return new SchemaDto(
this.id,
this.name,
properties,
this.isPublished,
this.createdBy, user,
this.created, DateTime.now(),
this.version);
}
}
export class SchemaDetailsDto {
constructor(
public readonly id: string,
public readonly name: string,
public readonly properties: SchemaPropertiesDto,
public readonly isPublished: boolean,
public readonly createdBy: string,
public readonly lastModifiedBy: string,
public readonly created: DateTime,
public readonly lastModified: DateTime,
public readonly version: Version,
export class SchemaDetailsDto extends SchemaDto {
constructor(id: string, name: string, properties: SchemaPropertiesDto, isPublished: boolean, createdBy: string, lastModifiedBy: string, created: DateTime, lastModified: DateTime, version: Version,
public readonly fields: FieldDto[]
) {
super(id, name, properties, isPublished, createdBy, lastModifiedBy, created, lastModified, version);
}
public publish(user: string): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
this.properties,
true,
this.createdBy, user,
this.created, DateTime.now(),
this.version,
this.fields);
}
public unpublish(user: string): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
this.properties,
false,
this.createdBy, user,
this.created, DateTime.now(),
this.version,
this.fields);
}
public update(user: string, properties: SchemaPropertiesDto): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
properties,
this.isPublished,
this.createdBy, user,
this.created, DateTime.now(),
this.version,
this.fields);
}
public addField(user: string, field: FieldDto): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
this.properties,
this.isPublished,
this.createdBy, user,
this.created, DateTime.now(),
this.version,
[...this.fields, field]);
}
public updateField(user: string, field: FieldDto): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
this.properties,
this.isPublished,
this.createdBy, user,
this.created, DateTime.now(),
this.version,
this.fields.map(f => f.fieldId === field.fieldId ? field : f));
}
public replaceFields(user: string, fields: FieldDto[]): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
this.properties,
this.isPublished,
this.createdBy, user,
this.created, DateTime.now(),
this.version,
fields);
}
public removeField(user: string, field: FieldDto): SchemaDetailsDto {
return new SchemaDetailsDto(
this.id,
this.name,
this.properties,
this.isPublished,
this.createdBy, user,
this.created, DateTime.now(),
this.version,
this.fields.filter(f => f.fieldId !== field.fieldId));
}
}
@ -113,6 +221,26 @@ export class FieldDto {
) {
}
public show(): FieldDto {
return new FieldDto(this.fieldId, this.name, false, this.isDisabled, this.partitioning, this.properties);
}
public hide(): FieldDto {
return new FieldDto(this.fieldId, this.name, false, this.isDisabled, this.partitioning, this.properties);
}
public enable(): FieldDto {
return new FieldDto(this.fieldId, this.name, this.isHidden, false, this.partitioning, this.properties);
}
public disable(): FieldDto {
return new FieldDto(this.fieldId, this.name, this.isHidden, true, this.partitioning, this.properties);
}
public update(properties: FieldPropertiesDto): FieldDto {
return new FieldDto(this.fieldId, this.name, this.isHidden, this.isDisabled, this.partitioning, properties);
}
public formatValue(value: any): string {
return this.properties.formatValue(value);
}
@ -475,7 +603,9 @@ export class UpdateFieldDto {
export class CreateSchemaDto {
constructor(
public readonly name: string
public readonly name: string,
public readonly fields?: FieldDto[],
public readonly properties?: SchemaPropertiesDto
) {
}
}
@ -548,22 +678,40 @@ export class SchemasService {
.pretifyError('Failed to load schema. Please reload.');
}
public postSchema(appName: string, dto: CreateSchemaDto, version?: Version): Observable<EntityCreatedDto> {
public postSchema(appName: string, dto: CreateSchemaDto, user: string, now?: DateTime, version?: Version): Observable<SchemaDetailsDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas`);
return HTTP.postVersioned(this.http, url, dto, version)
.map(response => {
return new EntityCreatedDto(response.id);
now = now || DateTime.now();
return new SchemaDetailsDto(
response.id,
dto.name,
dto.properties || new SchemaPropertiesDto(null, null),
false,
user,
user,
now,
now,
version,
dto.fields || []);
})
.pretifyError('Failed to create schema. Please reload.');
}
public postField(appName: string, schemaName: string, dto: AddFieldDto, version?: Version): Observable<EntityCreatedDto> {
public postField(appName: string, schemaName: string, dto: AddFieldDto, version?: Version): Observable<FieldDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields`);
return HTTP.postVersioned(this.http, url, dto, version)
.map(response => {
return new EntityCreatedDto(response.id);
return new FieldDto(
response.id,
dto.name,
false,
false,
dto.partitioning,
dto.properties);
})
.pretifyError('Failed to add field. Please reload.');
}

3
src/Squidex/app/shared/services/users.service.spec.ts

@ -12,7 +12,6 @@ import {
ApiUrlConfig,
CreateUserDto,
UpdateUserDto,
UserCreatedDto,
UserDto,
UserManagementService,
UsersDto,
@ -280,7 +279,7 @@ describe('UserManagementService', () => {
req.flush({ id: '123', pictureUrl: 'path/to/image1' });
expect(user).toEqual(new UserCreatedDto('123', 'path/to/image1'));
expect(user).toEqual(new UserDto('123', dto.email, dto.displayName, 'path/to/image1', false));
}));
it('should make put request to update user',

32
src/Squidex/app/shared/services/users.service.ts

@ -21,12 +21,27 @@ export class UsersDto {
}
}
export class UserCreatedDto {
export class UserDto {
constructor(
public readonly id: string,
public readonly pictureUrl: string
public readonly email: string,
public readonly displayName: string,
public readonly pictureUrl: string | null,
public readonly isLocked: boolean
) {
}
public update(email: string, displayName: string): UserDto {
return new UserDto(this.id, email, displayName, this.pictureUrl, this.isLocked);
}
public lock(): UserDto {
return new UserDto(this.id, this.email, this.displayName, this.pictureUrl, true);
}
public unlock(): UserDto {
return new UserDto(this.id, this.email, this.displayName, this.pictureUrl, false);
}
}
export class CreateUserDto {
@ -47,17 +62,6 @@ export class UpdateUserDto {
}
}
export class UserDto {
constructor(
public readonly id: string,
public readonly email: string,
public readonly displayName: string,
public readonly pictureUrl: string | null,
public readonly isLocked: boolean
) {
}
}
@Injectable()
export class UsersService {
constructor(
@ -150,7 +154,7 @@ export class UserManagementService {
return HTTP.postVersioned(this.http, url, dto)
.map(response => {
return new UserCreatedDto(response.id, response.pictureUrl);
return new UserDto(response.id, dto.email, dto.displayName, response.pictureUrl, false);
})
.pretifyError('Failed to create user. Please reload.');
}

7
src/Squidex/app/shared/services/webhooks.service.spec.ts

@ -12,7 +12,6 @@ import {
ApiUrlConfig,
CreateWebhookDto,
Version,
WebhookCreatedDto,
WebhookDto,
WebhooksService
} from './../';
@ -86,7 +85,7 @@ describe('WebhooksService', () => {
const dto = new CreateWebhookDto('http://squidex.io/hook');
let webhook: WebhookCreatedDto | null = null;
let webhook: WebhookDto | null = null;
webhooksService.postWebhook('my-app', 'my-schema', dto, version).subscribe(result => {
webhook = result;
@ -97,9 +96,9 @@ describe('WebhooksService', () => {
expect(req.request.method).toEqual('POST');
expect(req.request.headers.get('If-Match')).toBe(version.value);
req.flush({ id: 'id1', sharedSecret: 'token1' });
req.flush({ id: 'id1', sharedSecret: 'token1', schemaId: 'schema1' });
expect(webhook).toEqual(new WebhookCreatedDto('id1', 'token1'));
expect(webhook).toEqual(new WebhookDto('id1', 'schema1', 'token1', dto.url, 0, 0, 0, 0, []));
}));
it('should make delete request to delete webhook',

17
src/Squidex/app/shared/services/webhooks.service.ts

@ -32,14 +32,6 @@ export class WebhookDto {
}
}
export class WebhookCreatedDto {
constructor(
public readonly id: string,
public readonly sharedSecret: string
) {
}
}
export class CreateWebhookDto {
constructor(
public readonly url: string
@ -78,14 +70,17 @@ export class WebhooksService {
.pretifyError('Failed to load webhooks. Please reload.');
}
public postWebhook(appName: string, schemaName: string, dto: CreateWebhookDto, version?: Version): Observable<WebhookCreatedDto> {
public postWebhook(appName: string, schemaName: string, dto: CreateWebhookDto, version?: Version): Observable<WebhookDto> {
const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/webhooks`);
return HTTP.postVersioned(this.http, url, dto, version)
.map(response => {
return new WebhookCreatedDto(
return new WebhookDto(
response.id,
response.sharedSecret);
response.schemaId,
response.sharedSecret,
dto.url,
0, 0, 0, 0, []);
})
.pretifyError('Failed to create webhook. Please reload.');
}

Loading…
Cancel
Save