Browse Source

Take the guards back.

pull/271/head
Sebastian Stehle 9 years ago
parent
commit
f2e8c010bc
  1. 3
      src/Squidex/app/features/administration/declarations.ts
  2. 24
      src/Squidex/app/features/administration/guards/unset-user.guard.ts
  3. 38
      src/Squidex/app/features/administration/guards/user-must-exist.guard.ts
  4. 16
      src/Squidex/app/features/administration/module.ts
  5. 65
      src/Squidex/app/features/administration/pages/users/user-page.component.html
  6. 24
      src/Squidex/app/features/administration/pages/users/user-page.component.ts
  7. 2
      src/Squidex/app/features/administration/pages/users/users-page.component.html
  8. 5
      src/Squidex/app/features/administration/pages/users/users-page.component.ts
  9. 15
      src/Squidex/app/features/administration/state/users.state.ts
  10. 4
      src/Squidex/app/features/api/api-area.component.html
  11. 9
      src/Squidex/app/features/api/api-area.component.ts
  12. 2
      src/Squidex/app/features/api/pages/graphql/graphql-page.component.html
  13. 10
      src/Squidex/app/features/api/pages/graphql/graphql-page.component.ts
  14. 10
      src/Squidex/app/features/apps/pages/apps-page.component.html
  15. 13
      src/Squidex/app/features/apps/pages/apps-page.component.ts
  16. 8
      src/Squidex/app/features/schemas/guards/schema-must-exist.guard.ts
  17. 23
      src/Squidex/app/features/schemas/module.ts
  18. 2
      src/Squidex/app/features/schemas/pages/messages.ts
  19. 2
      src/Squidex/app/features/schemas/pages/schema/field-wizard.component.html
  20. 6
      src/Squidex/app/features/schemas/pages/schema/field-wizard.component.ts
  21. 8
      src/Squidex/app/features/schemas/pages/schema/field.component.ts
  22. 22
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.html
  23. 2
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.scss
  24. 19
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts
  25. 2
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html
  26. 7
      src/Squidex/app/features/schemas/pages/schemas/schema-form.component.ts
  27. 28
      src/Squidex/app/features/schemas/pages/schemas/schemas-page.component.ts
  28. 69
      src/Squidex/app/features/schemas/state/schemas.state.ts
  29. 2
      src/Squidex/app/framework/angular/root-view.component.ts
  30. 2
      src/Squidex/app/shared/services/schemas.service.ts
  31. 4
      src/Squidex/app/shared/state/apps.state.ts

3
src/Squidex/app/features/administration/declarations.ts

@ -7,6 +7,9 @@
export * from './administration-area.component';
export * from './guards/user-must-exist.guard';
export * from './guards/unset-user.guard';
export * from './pages/event-consumers/event-consumers-page.component';
export * from './pages/users/user-page.component';
export * from './pages/users/users-page.component';

24
src/Squidex/app/features/administration/guards/unset-user.guard.ts

@ -0,0 +1,24 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { UsersState } from './../state/users.state';
@Injectable()
export class UnsetUserGuard implements CanActivate {
constructor(
private readonly usersState: UsersState
) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.usersState.selectUser(null).map(u => u === null);
}
}

38
src/Squidex/app/features/administration/guards/user-must-exist.guard.ts

@ -0,0 +1,38 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { allParams } from 'framework';
import { UsersState } from './../state/users.state';
@Injectable()
export class UserMustExistGuard implements CanActivate {
constructor(
private readonly usersState: UsersState,
private readonly router: Router
) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
const userId = allParams(route)['userId'];
const result =
this.usersState.selectUser(userId)
.do(dto => {
if (!dto) {
this.router.navigate(['/404']);
}
})
.map(u => u !== null);
return result;
}
}

16
src/Squidex/app/features/administration/module.ts

@ -17,9 +17,12 @@ import {
AdministrationAreaComponent,
EventConsumersPageComponent,
EventConsumersService,
UnsetUserGuard,
UserMustExistGuard,
UserPageComponent,
UsersPageComponent,
UsersService
UsersService,
UsersState
} from './declarations';
const routes: Routes = [
@ -40,11 +43,13 @@ const routes: Routes = [
children: [
{
path: 'new',
component: UserPageComponent
component: UserPageComponent,
canActivate: [UnsetUserGuard]
},
{
path: ':userId',
component: UserPageComponent
component: UserPageComponent,
canActivate: [UserMustExistGuard]
}
]
}
@ -68,7 +73,10 @@ const routes: Routes = [
],
providers: [
EventConsumersService,
UsersService
UnsetUserGuard,
UserMustExistGuard,
UsersService,
UsersState
]
})
export class SqxFeatureAdministrationModule { }

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

@ -1,24 +1,27 @@
<sqx-title message="User Management"></sqx-title>
<form [formGroup]="userForm" (ngSubmit)="save()">
<input style="display:none" type="password" name="foilautofill" />
<input style="display:none" type="password" name="foilautofill"/>
<sqx-panel desiredWidth="26rem">
<div class="panel-header">
<div class="panel-title-row">
<div class="float-right" *ngIf="!isNotFound">
<div class="float-right">
<button type="submit" class="btn btn-primary" title="CTRL + S">
Save
</button>
</div>
<sqx-shortcut keys="ctrl+s" (trigger)="save()"></sqx-shortcut>
<h3 class="panel-title">
{{isNotFound || user ? 'Edit User' : 'New User'}}
<h3 class="panel-title" *ngIf="!user">
New User
</h3>
<h3 class="panel-title" *ngIf="user">
Edit User
</h3>
</div>
<sqx-shortcut keys="ctrl+s" (trigger)="save()"></sqx-shortcut>
<a class="panel-close" sqxParentLink>
@ -28,46 +31,40 @@
<div class="panel-main">
<div class="panel-content panel-content-blank">
<div *ngIf="isNotFound">
The user does not exist.
<div *ngIf="userFormError">
<div class="form-alert form-alert-error" [innerHTML]="userFormError"></div>
</div>
<div *ngIf="!isNotFound">
<div *ngIf="userFormError">
<div class="form-alert form-alert-error" [innerHTML]="userFormError"></div>
</div>
<div class="form-group">
<label for="email">Email</label>
<div class="form-group">
<label for="email">Email</label>
<sqx-control-errors for="email" [submitted]="userFormSubmitted"></sqx-control-errors>
<sqx-control-errors for="email" [submitted]="userFormSubmitted"></sqx-control-errors>
<input type="email" class="form-control" id="email" maxlength="100" formControlName="email" autocomplete="false" />
</div>
<div class="form-group">
<label for="displayName">Display Name</label>
<input type="email" class="form-control" id="email" maxlength="100" formControlName="email" autocomplete="false" />
</div>
<div class="form-group">
<label for="displayName">Display Name</label>
<sqx-control-errors for="displayName" [submitted]="userFormSubmitted"></sqx-control-errors>
<sqx-control-errors for="displayName" [submitted]="userFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="displayName" maxlength="100" formControlName="displayName" autocomplete="false" />
</div>
<input type="text" class="form-control" id="displayName" maxlength="100" formControlName="displayName" autocomplete="false" />
</div>
<div class="form-group form-group-password" [class.hidden]="isCurrentUser">
<div class="form-group">
<label for="password">Password</label>
<div class="form-group form-group-password" [class.hidden]="isCurrentUser">
<div class="form-group">
<label for="password">Password</label>
<sqx-control-errors for="password" [submitted]="userFormSubmitted"></sqx-control-errors>
<sqx-control-errors for="password" [submitted]="userFormSubmitted"></sqx-control-errors>
<input type="password" class="form-control" id="password" maxlength="100" formControlName="password" autocomplete="false" />
</div>
<input type="password" class="form-control" id="password" maxlength="100" formControlName="password" autocomplete="false" />
</div>
<div class="form-group">
<label for="password">Confirm Password</label>
<div class="form-group">
<label for="password">Confirm Password</label>
<sqx-control-errors for="passwordConfirm" [submitted]="userFormSubmitted"></sqx-control-errors>
<sqx-control-errors for="passwordConfirm" [submitted]="userFormSubmitted"></sqx-control-errors>
<input type="password" class="form-control" id="passwordConfirm" maxlength="100" formControlName="passwordConfirm" autocomplete="false" />
</div>
<input type="password" class="form-control" id="passwordConfirm" maxlength="100" formControlName="passwordConfirm" autocomplete="false" />
</div>
</div>
</div>

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

@ -5,9 +5,10 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { AuthService, ValidatorsEx } from 'shared';
@ -19,7 +20,9 @@ import { UsersState } from './../../state/users.state';
styleUrls: ['./user-page.component.scss'],
templateUrl: './user-page.component.html'
})
export class UserPageComponent implements OnInit {
export class UserPageComponent implements OnDestroy, OnInit {
private selectedUserSubscription: Subscription;
public userFormSubmitted = false;
public userFormError = '';
public userForm =
@ -48,7 +51,6 @@ export class UserPageComponent implements OnInit {
public user: UserDto | null;
public isCurrentUser = false;
public isNotFound = false;
constructor(
private readonly authService: AuthService,
@ -59,14 +61,13 @@ export class UserPageComponent implements OnInit {
) {
}
public ngOnInit() {
this.route.params.map(p => p['userId'])
.switchMap(id => this.usersState.selectUser(id).map(u => { return { user: u, expected: !!id }; }))
.subscribe(result => {
this.isNotFound = !result.user && result.expected;
public ngOnDestroy() {
this.selectedUserSubscription.unsubscribe();
}
this.setupAndPopulateForm(result.user);
});
public ngOnInit() {
this.selectedUserSubscription =
this.usersState.selectedUser.subscribe(user => this.setupAndPopulateForm(user!));
}
public save() {
@ -118,9 +119,8 @@ export class UserPageComponent implements OnInit {
private resetFormState(message: string = '') {
this.userFormSubmitted = false;
this.userFormError = message;
this.userForm.enable();
this.userForm.controls['password'].reset();
this.userForm.controls['passwordConfirm'].reset();
this.userForm.enable();
}
}

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

@ -76,7 +76,7 @@
<i class="icon icon-lock"></i>
</button>
</span>
<button *ngIf="user.id === authState.user.id" class="btn btn-link invisible">
<button *ngIf="user.id === authState.user?.id" class="btn btn-link invisible">
&nbsp;
</button>
</td>

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

@ -16,10 +16,7 @@ import { UsersState } from './../../state/users.state';
@Component({
selector: 'sqx-users-page',
styleUrls: ['./users-page.component.scss'],
templateUrl: './users-page.component.html',
providers: [
UsersState
]
templateUrl: './users-page.component.html'
})
export class UsersPageComponent implements OnInit {
public usersFilter = new FormControl();

15
src/Squidex/app/features/administration/state/users.state.ts

@ -37,8 +37,14 @@ export class UsersState {
}
public selectUser(id: string | null): Observable<UserDto | null> {
const observable =
!id ?
return this.loadUser(id)
.do(user => {
this.selectedUser.next(user);
});
}
private loadUser(id: string | null) {
return !id ?
Observable.of(null) :
Observable.of(this.users.value.find(x => x.id === id))
.switchMap(user => {
@ -48,11 +54,6 @@ export class UsersState {
return Observable.of(user);
}
});
return observable
.do(user => {
this.selectedUser.next(user);
});
}
public loadUsers(): Observable<any> {

4
src/Squidex/app/features/api/api-area.component.html

@ -1,4 +1,4 @@
<sqx-title message="{app} | Settings" parameter1="app" [value1]="ctx.appName"></sqx-title>
<sqx-title message="{app} | Settings" parameter1="app" [value1]="appsState.appName"></sqx-title>
<sqx-panel theme="dark" desiredWidth="12rem">
<div class="panel-header">
@ -21,7 +21,7 @@
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/api/content/{{ctx.appName}}/docs" target="_blank">
<a class="nav-link" href="/api/content/{{appsState.appName}}/docs" target="_blank">
Swagger
</a>
</li>

9
src/Squidex/app/features/api/api-area.component.ts

@ -7,19 +7,16 @@
import { Component } from '@angular/core';
import { AppContext } from 'shared';
import { AppsState } from 'shared';
@Component({
selector: 'sqx-api-area',
styleUrls: ['./api-area.component.scss'],
templateUrl: './api-area.component.html',
providers: [
AppContext
]
templateUrl: './api-area.component.html'
})
export class ApiAreaComponent {
constructor(
public readonly ctx: AppContext
public readonly appsState: AppsState
) {
}
}

2
src/Squidex/app/features/api/pages/graphql/graphql-page.component.html

@ -1,4 +1,4 @@
<sqx-title message="{app} | API | GraphQL" parameter1="app" [value1]="ctx.appName"></sqx-title>
<sqx-title message="{app} | API | GraphQL" parameter1="app" [value1]="appsState.appName"></sqx-title>
<sqx-panel desiredWidth="*">
<div #graphiQLContainer></div>

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

@ -16,7 +16,7 @@ const GraphiQL = require('graphiql');
/* tslint:disable:use-view-encapsulation */
import {
AppContext,
AppsState,
GraphQlService,
LocalStoreService
} from 'shared';
@ -25,16 +25,14 @@ import {
selector: 'sqx-graphql-page',
styleUrls: ['./graphql-page.component.scss'],
templateUrl: './graphql-page.component.html',
providers: [
AppContext
],
encapsulation: ViewEncapsulation.None
})
export class GraphQLPageComponent implements OnInit {
@ViewChild('graphiQLContainer')
public graphiQLContainer: ElementRef;
constructor(public readonly ctx: AppContext,
constructor(
public readonly appsState: AppsState,
private readonly graphQlService: GraphQlService,
private readonly localStoreService: LocalStoreService
) {
@ -56,7 +54,7 @@ export class GraphQLPageComponent implements OnInit {
}
private request(params: any) {
return this.graphQlService.query(this.ctx.appName, params).catch(response => Observable.of(response.error)).toPromise();
return this.graphQlService.query(this.appsState.appName, params).catch(response => Observable.of(response.error)).toPromise();
}
}

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

@ -1,7 +1,7 @@
<sqx-title message="Apps"></sqx-title>
<div class="apps-section">
<h1 class="apps-title">Hi {{ctx.user.displayName}}</h1>
<h1 class="apps-title">Hi {{authState.user?.displayName}}</h1>
<div class="subtext">
Welcome to Squidex.
@ -49,7 +49,9 @@
<div class="card-text">
<div>Start with our ready to use blog.</div>
<div>Sample Code: <a href="https://github.com/Squidex/squidex-samples/tree/master/csharp/Sample.Blog" (click)="$event.stopPropagation()" target="_blank">ASP.NET Core</a></div>
<div>
Sample Code: <a href="https://github.com/Squidex/squidex-samples/tree/master/csharp/Sample.Blog" (click)="$event.stopPropagation()" target="_blank">C#</a>
</div>
</div>
</div>
</div>
@ -64,7 +66,9 @@
<div class="card-text">
<div>Create your profile page.</div>
<div>Sample Code: <a href="https://github.com/Squidex/squidex-samples/tree/master/csharp/Sample.Profile" (click)="$event.stopPropagation()" target="_blank">ASP.NET Core</a></div>
<div>
Sample Code: <a href="https://github.com/Squidex/squidex-samples/tree/master/csharp/Sample.Profile" (click)="$event.stopPropagation()" target="_blank">C#</a>
</div>
</div>
</div>
</div>

13
src/Squidex/app/features/apps/pages/apps-page.component.ts

@ -9,9 +9,9 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import {
AppContext,
AppDto,
AppsState,
AuthService,
fadeAnimation,
ImmutableArray,
ModalView,
@ -22,9 +22,6 @@ import {
selector: 'sqx-apps-page',
styleUrls: ['./apps-page.component.scss'],
templateUrl: './apps-page.component.html',
providers: [
AppContext
],
animations: [
fadeAnimation
]
@ -35,13 +32,13 @@ export class AppsPageComponent implements OnDestroy, OnInit {
public addAppDialog = new ModalView();
public apps: ImmutableArray<AppDto>;
public apptemplate = '';
public appTemplate = '';
public onboardingModal = new ModalView();
constructor(
public readonly ctx: AppContext,
private readonly appsState: AppsState,
public readonly appsState: AppsState,
public readonly authState: AuthService,
private readonly onboardingService: OnboardingService
) {
}
@ -64,7 +61,7 @@ export class AppsPageComponent implements OnDestroy, OnInit {
}
public createNewApp(template: string) {
this.apptemplate = template;
this.appTemplate = template;
this.addAppDialog.show();
}

8
src/Squidex/app/features/schemas/guards/schema-must-exist.guard.ts

@ -22,13 +22,7 @@ export class SchemaMustExistGuard implements CanActivate {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
const params = allParams(route);
const schemaName = params['schemaName'];
if (!schemaName) {
throw 'Route must contain schema name.';
}
const schemaName = allParams(route)['schemaName'];
const result =
this.schemasState.selectSchema(schemaName)

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

@ -10,8 +10,6 @@ import { RouterModule, Routes } from '@angular/router';
import { DndModule } from 'ng2-dnd';
import {
HelpComponent,
HistoryComponent,
SqxFrameworkModule,
SqxSharedModule
} from 'shared';
@ -54,29 +52,10 @@ const routes: Routes = [
path: '',
component: SchemasPageComponent,
children: [
{
path: ''
},
{
path: ':schemaName',
component: SchemaPageComponent,
canActivate: [SchemaMustExistGuard],
children: [
{
path: 'history',
component: HistoryComponent,
data: {
channel: 'schemas.{schemaName}'
}
},
{
path: 'help',
component: HelpComponent,
data: {
helpPage: '05-integrated/schemas'
}
}
]
canActivate: [SchemaMustExistGuard]
}]
}
];

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

@ -7,7 +7,7 @@
export class SchemaCloning {
constructor(
public readonly importing: any
public readonly schema: any
) {
}
}

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

@ -42,7 +42,7 @@
</div>
<div class="form-group">
<sqx-control-errors for="name" [submitted]="addFieldFormSubmitted"></sqx-control-errors>
<sqx-control-errors for="name" submitOnly="true" [submitted]="addFieldFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" formControlName="name" maxlength="40" #nameInput placeholder="Enter field name" sqxFocusOnInit />
</div>

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

@ -10,7 +10,6 @@ import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import {
AddFieldDto,
AppContext,
createProperties,
fadeAnimation,
FieldDto,
@ -26,9 +25,6 @@ import { SchemasState } from './../../state/schemas.state';
selector: 'sqx-field-wizard',
styleUrls: ['./field-wizard.component.scss'],
templateUrl: './field-wizard.component.html',
providers: [
AppContext
],
animations: [
fadeAnimation
]
@ -146,9 +142,9 @@ export class FieldWizardComponent {
private resetFieldForm(error = '') {
this.addFieldFormSubmitted = false;
this.addFieldError = error;
this.addFieldForm.enable();
this.addFieldForm.reset({ type: 'String' }, { emitEvent: false });
this.addFieldError = error;
}
}

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

@ -93,19 +93,21 @@ export class FieldComponent implements OnInit {
if (this.editForm.valid) {
const properties = createProperties(this.field.properties['fieldType'], this.editForm.value);
this.schemasState.updateField(this.schema, this.field, new UpdateFieldDto(properties)).subscribe();
this.schemasState.updateField(this.schema, this.field, new UpdateFieldDto(properties))
.subscribe(() => {
this.resetEditForm();
});
}
}
private resetEditForm() {
this.isEditing = false;
this.editFormSubmitted = false;
this.editForm.reset(this.field.properties);
if (this.field.isLocked) {
this.editForm.disable();
}
this.isEditing = false;
}
}

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

@ -1,4 +1,4 @@
<sqx-title message="{app} | {schema}" parameter1="app" [value1]="ctx.appName" parameter2="schema" [value2]="schema?.displayName"></sqx-title>
<sqx-title message="{app} | {schema}" parameter1="app" [value1]="appsState.appName" parameter2="schema" [value2]="schema?.displayName"></sqx-title>
<sqx-panel desiredWidth="56rem">
<div class="panel-header">
@ -68,22 +68,6 @@
<i class="icon icon-plus field-button-icon"></i> <div class="field-button-text">Add Field</div>
</button>
</div>
<div class="panel-sidebar">
<a class="panel-link" routerLink="history" routerLinkActive="active" #linkHistory>
<i class="icon-time"></i>
</a>
<a class="panel-link" routerLink="help" routerLinkActive="active" #linkHelp>
<i class="icon-help"></i>
</a>
<sqx-onboarding-tooltip id="history" [for]="linkHistory" position="leftTop" after="120000">
The sidebar navigation contains useful context specific links. Here you can view the history how this schema has changed over time.
</sqx-onboarding-tooltip>
<sqx-onboarding-tooltip id="help" [for]="linkHelp" position="leftTop" after="180000">
Click the help icon to show a context specific help page. Go to <a href="https://docs.squidex.io" target="_blank">https://docs.squidex.io</a> for the full documentation.
</sqx-onboarding-tooltip>
</div>
</div>
</sqx-panel>
@ -128,6 +112,4 @@
</div>
</div>
</div>
</div>
<router-outlet></router-outlet>
</div>

2
src/Squidex/app/features/schemas/pages/schema/schema-page.component.scss

@ -40,7 +40,7 @@
& {
@include circle(5.25rem);
@include box-shadow(0, 8px, 16px, .3);
@include absolute(auto, 6.5rem, 1rem, auto);
@include absolute(auto, 2rem, 1rem, auto);
}
&-icon {

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

@ -6,14 +6,15 @@
*/
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import {
AppContext,
AppsState,
fadeAnimation,
FieldDto,
fieldTypes,
MessageBus,
ModalView,
SchemaDetailsDto
} from 'shared';
@ -28,9 +29,6 @@ import { SchemasState } from './../../state/schemas.state';
selector: 'sqx-schema-page',
styleUrls: ['./schema-page.component.scss'],
templateUrl: './schema-page.component.html',
providers: [
AppContext
],
animations: [
fadeAnimation
]
@ -52,9 +50,12 @@ export class SchemaPageComponent implements OnDestroy, OnInit {
public addFieldDialog = new ModalView();
constructor(public readonly ctx: AppContext,
constructor(
public readonly appsState: AppsState,
public readonly schemasState: SchemasState,
private readonly route: ActivatedRoute,
private readonly router: Router,
private readonly schemasState: SchemasState
private readonly messageBus: MessageBus
) {
}
@ -92,7 +93,7 @@ export class SchemaPageComponent implements OnDestroy, OnInit {
}
public cloneSchema() {
this.ctx.bus.emit(new SchemaCloning(this.schemaExport));
this.messageBus.emit(new SchemaCloning(this.schemaExport));
}
private export() {
@ -127,7 +128,7 @@ export class SchemaPageComponent implements OnDestroy, OnInit {
}
private back() {
this.router.navigate(['../'], { relativeTo: this.ctx.route });
this.router.navigate(['../'], { relativeTo: this.route });
}
}

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

@ -23,7 +23,7 @@
<input type="text" class="form-control" id="schemaName" formControlName="name" autocomplete="off" sqxLowerCaseInput sqxFocusOnInit />
<small class="form-text text-muted">
The schema name becomes part of the api url,<br /> e.g {{apiUrl.buildUrl("api/content/")}}{{ctx.appName}}/<b>{{schemaName | async}}</b>/.
The schema name becomes part of the api url,<br /> e.g {{apiUrl.buildUrl("api/content/")}}{{appsState.appName}}/<b>{{schemaName | async}}</b>/.
</small>
<small class="form-text text-muted">
It must contain lower case letters (a-z), numbers and dashes only, and cannot be longer than 40 characters. The name cannot be changed later.

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

@ -8,7 +8,11 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { ApiUrlConfig, ValidatorsEx } from 'shared';
import {
ApiUrlConfig,
AppsState,
ValidatorsEx
} from 'shared';
import { SchemasState } from './../../state/schemas.state';
@ -47,6 +51,7 @@ export class SchemaFormComponent implements OnInit {
constructor(
public readonly apiUrl: ApiUrlConfig,
public readonly appsState: AppsState,
private readonly schemasState: SchemasState,
private readonly formBuilder: FormBuilder
) {

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

@ -5,16 +5,20 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import {
AppsState,
fadeAnimation,
MessageBus,
ModalView
} from 'shared';
import { SchemaCloning } from './../messages';
import { SchemasState } from './../../state/schemas.state';
@Component({
@ -25,7 +29,9 @@ import { SchemasState } from './../../state/schemas.state';
fadeAnimation
]
})
export class SchemasPageComponent implements OnInit {
export class SchemasPageComponent implements OnDestroy, OnInit {
private schemaCloningSubscription: Subscription;
public addSchemaDialog = new ModalView();
public schemasFilter = new FormControl();
@ -42,13 +48,27 @@ export class SchemasPageComponent implements OnInit {
public import: any;
constructor(public readonly appsState: AppsState,
constructor(
public readonly appsState: AppsState,
private readonly schemasState: SchemasState,
private readonly route: ActivatedRoute,
private readonly schemasState: SchemasState
private readonly messageBus: MessageBus
) {
}
public ngOnDestroy() {
this.schemaCloningSubscription.unsubscribe();
}
public ngOnInit() {
this.schemaCloningSubscription =
this.messageBus.of(SchemaCloning)
.subscribe(m => {
this.import = m.schema;
this.addSchemaDialog.show();
});
this.route.params.map(q => q['showDialog'])
.subscribe(showDialog => {
if (showDialog) {

69
src/Squidex/app/features/schemas/state/schemas.state.ts

@ -32,9 +32,10 @@ export class SchemasState {
public schemasItems = new BehaviorSubject<ImmutableArray<SchemaDto>>(ImmutableArray.empty());
public selectedSchema = new BehaviorSubject<SchemaDetailsDto | null>(null);
public selectedReferencedSchema = new BehaviorSubject<SchemaDetailsDto | null>(null);
private get app() {
return this.appsState.selectedApp.value!.name;
private get appName() {
return this.appsState.appName;
}
private get user() {
@ -49,28 +50,35 @@ export class SchemasState {
) {
}
public selectSchema(id: string | null): Observable<boolean> {
const observable =
!id ?
public selectSchema(id: string | null): Observable<SchemaDetailsDto | null> {
return this.loadSchema(id)
.do(schema => {
this.selectedSchema.next(schema);
});
}
public selectReferencedSchema(id: string | null): Observable<SchemaDetailsDto | null> {
return this.loadSchema(id)
.do(schema => {
this.selectedReferencedSchema.next(schema);
});
}
private loadSchema(id: string | null) {
return !id ?
Observable.of(null) :
Observable.of(<SchemaDetailsDto>this.schemasItems.value.find(x => x.id === id && x instanceof SchemaDetailsDto))
.switchMap(schema => {
if (!schema) {
return this.schemasService.getSchema(this.app, id).catch(() => Observable.of(null));
return this.schemasService.getSchema(this.appName, id).catch(() => Observable.of(null));
} else {
return Observable.of(schema);
}
});
return observable
.do(schema => {
this.selectedSchema.next(schema);
})
.map(s => s !== null);
}
public load(): Observable<any> {
return this.schemasService.getSchemas(this.app)
return this.schemasService.getSchemas(this.appName)
.catch(error => this.dialogs.notifyError(error))
.do(dtos => {
this.schemasItems.nextBy(v => ImmutableArray.of(dtos));
@ -78,21 +86,21 @@ export class SchemasState {
}
public create(request: CreateSchemaDto) {
return this.schemasService.postSchema(this.app, request, this.user, DateTime.now())
return this.schemasService.postSchema(this.appName, request, this.user, DateTime.now())
.do(dto => {
this.schemasItems.nextBy(v => v.push(dto));
});
}
public addField(schema: SchemaDetailsDto, request: AddFieldDto): Observable<FieldDto> {
return this.schemasService.postField(this.app, schema.name, request, schema.version)
return this.schemasService.postField(this.appName, schema.name, request, schema.version)
.do(dto => {
this.replaceSchema(schema.addField(dto.payload, this.user, dto.version));
}).map(d => d.payload);
}
public publish(schema: SchemaDto): Observable<any> {
return this.schemasService.publishSchema(this.app, schema.name, schema.version)
return this.schemasService.publishSchema(this.appName, schema.name, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.publish(this.user, dto.version));
@ -100,7 +108,7 @@ export class SchemasState {
}
public unpublish(schema: SchemaDto): Observable<any> {
return this.schemasService.unpublishSchema(this.app, schema.name, schema.version)
return this.schemasService.unpublishSchema(this.appName, schema.name, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.unpublish(this.user, dto.version));
@ -108,7 +116,7 @@ export class SchemasState {
}
public enableField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> {
return this.schemasService.enableField(this.app, schema.name, field.fieldId, schema.version)
return this.schemasService.enableField(this.appName, schema.name, field.fieldId, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.updateField(field.enable(), this.user, dto.version));
@ -116,7 +124,7 @@ export class SchemasState {
}
public disableField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> {
return this.schemasService.disableField(this.app, schema.name, field.fieldId, schema.version)
return this.schemasService.disableField(this.appName, schema.name, field.fieldId, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.updateField(field.disable(), this.user, dto.version));
@ -124,7 +132,7 @@ export class SchemasState {
}
public lockField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> {
return this.schemasService.lockField(this.app, schema.name, field.fieldId, schema.version)
return this.schemasService.lockField(this.appName, schema.name, field.fieldId, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.updateField(field.lock(), this.user, dto.version));
@ -132,7 +140,7 @@ export class SchemasState {
}
public showField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> {
return this.schemasService.showField(this.app, schema.name, field.fieldId, schema.version)
return this.schemasService.showField(this.appName, schema.name, field.fieldId, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.updateField(field.show(), this.user, dto.version));
@ -140,7 +148,7 @@ export class SchemasState {
}
public hideField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> {
return this.schemasService.hideField(this.app, schema.name, field.fieldId, schema.version)
return this.schemasService.hideField(this.appName, schema.name, field.fieldId, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.updateField(field.hide(), this.user, dto.version));
@ -148,7 +156,7 @@ export class SchemasState {
}
public deleteField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> {
return this.schemasService.deleteField(this.app, schema.name, field.fieldId, schema.version)
return this.schemasService.deleteField(this.appName, schema.name, field.fieldId, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.removeField(field, this.user, dto.version));
@ -156,7 +164,7 @@ export class SchemasState {
}
public sortFields(schema: SchemaDetailsDto, fields: FieldDto[]): Observable<any> {
return this.schemasService.putFieldOrdering(this.app, schema.name, fields.map(t => t.fieldId), schema.version)
return this.schemasService.putFieldOrdering(this.appName, schema.name, fields.map(t => t.fieldId), schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.replaceFields(fields, this.user, dto.version));
@ -164,7 +172,7 @@ export class SchemasState {
}
public updateField(schema: SchemaDetailsDto, field: FieldDto, request: UpdateFieldDto): Observable<any> {
return this.schemasService.putField(this.app, schema.name, field.fieldId, request, schema.version)
return this.schemasService.putField(this.appName, schema.name, field.fieldId, request, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.updateField(field.update(request.properties), this.user, dto.version));
@ -172,7 +180,7 @@ export class SchemasState {
}
public configureScripts(schema: SchemaDetailsDto, request: UpdateSchemaScriptsDto): Observable<any> {
return this.schemasService.putSchemaScripts(this.app, schema.name, request, schema.version)
return this.schemasService.putSchemaScripts(this.appName, schema.name, request, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.configureScripts(request, this.user, dto.version));
@ -180,7 +188,7 @@ export class SchemasState {
}
public update(schema: SchemaDetailsDto, request: UpdateSchemaDto): Observable<any> {
return this.schemasService.putSchema(this.app, schema.name, request, schema.version)
return this.schemasService.putSchema(this.appName, schema.name, request, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.replaceSchema(schema.update(request, this.user, dto.version));
@ -188,7 +196,7 @@ export class SchemasState {
}
public delete(schema: SchemaDto): Observable<any> {
return this.schemasService.deleteSchema(this.app, schema.name, schema.version)
return this.schemasService.deleteSchema(this.appName, schema.name, schema.version)
.catch(error => this.dialogs.notifyError(error))
.do(dto => {
this.schemasItems.nextBy(v => v.filter(s => s.id !== schema.id));
@ -198,7 +206,10 @@ export class SchemasState {
private replaceSchema(schema: SchemaDto) {
this.schemasItems.nextBy(v => v.replaceBy('id', schema));
this.selectedSchema.nextBy(v => v !== null && v.id === schema.id ? <SchemaDetailsDto>schema : v);
if (schema instanceof SchemaDetailsDto) {
this.selectedSchema.nextBy(v => v !== null && v.id === schema.id ? schema : v);
this.selectedReferencedSchema.nextBy(v => v !== null && v.id === schema.id ? schema : v);
}
}
public trackBySchema(index: number, schema: SchemaDto): any {

2
src/Squidex/app/framework/angular/root-view.component.ts

@ -16,6 +16,6 @@ import { Component, ViewChild, ViewContainerRef } from '@angular/core';
`
})
export class RootViewComponent {
@ViewChild('element')
@ViewChild('element', { read: ViewContainerRef })
public viewContainer: ViewContainerRef;
}

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

@ -32,7 +32,7 @@ export const fieldTypes = [
description: 'Images, videos, documents.'
}, {
type: 'Boolean',
description: 'Yes or no, true or false, 1 or 0.'
description: 'Yes or no, true or false.'
}, {
type: 'DateTime',
description: 'Events date, opening hours.'

4
src/Squidex/app/shared/state/apps.state.ts

@ -25,6 +25,10 @@ export class AppsState {
public selectedApp = new BehaviorSubject<AppDto | null>(null);
public get appName() {
return this.selectedApp.value!.name;
}
constructor(
private readonly appsService: AppsService
) {

Loading…
Cancel
Save