mirror of https://github.com/Squidex/squidex.git
24 changed files with 160 additions and 429 deletions
@ -1,9 +1,13 @@ |
|||||
<main> |
<main> |
||||
<router-outlet (activate)="isLoaded = true"> |
<sqx-root-view> |
||||
<div class="loading" *ngIf="!isLoaded"> |
<sqx-dialog-renderer> |
||||
<img src="/images/loader.gif" /> |
<router-outlet (activate)="isLoaded = true"> |
||||
|
<div class="loading" *ngIf="!isLoaded"> |
||||
|
<img src="/images/loader.gif" /> |
||||
|
|
||||
<div>Loading Squidex</div> |
<div>Loading Squidex</div> |
||||
</div> |
</div> |
||||
</router-outlet> |
</router-outlet> |
||||
|
</sqx-dialog-renderer> |
||||
|
</sqx-root-view> |
||||
</main> |
</main> |
||||
|
|||||
@ -1,87 +0,0 @@ |
|||||
/* |
|
||||
* Squidex Headless CMS |
|
||||
* |
|
||||
* @license |
|
||||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|
||||
*/ |
|
||||
/* |
|
||||
import { IMock, Mock } from 'typemoq'; |
|
||||
import { Observable } from 'rxjs'; |
|
||||
|
|
||||
import { UserManagementService } from 'shared'; |
|
||||
|
|
||||
import { ResolveUserGuard } from './resolve-user.guard'; |
|
||||
import { RouterMockup } from './router-mockup'; |
|
||||
|
|
||||
describe('ResolveUserGuard', () => { |
|
||||
const route = { |
|
||||
params: {}, |
|
||||
parent: { |
|
||||
params: { |
|
||||
userId: 'my-user' |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
let usersService: IMock<UserManagementService>; |
|
||||
|
|
||||
beforeEach(() => { |
|
||||
usersService = Mock.ofType(UserManagementService); |
|
||||
}); |
|
||||
|
|
||||
it('should throw if route does not contain parameter', () => { |
|
||||
const guard = new ResolveUserGuard(usersService.object, <any>new RouterMockup()); |
|
||||
|
|
||||
expect(() => guard.resolve(<any>{ params: {} }, <any>{})).toThrow('Route must contain user id.'); |
|
||||
}); |
|
||||
|
|
||||
it('should navigate to 404 page if user is not found', (done) => { |
|
||||
usersService.setup(x => x.getUser('my-user')) |
|
||||
.returns(() => Observable.of(null!)); |
|
||||
const router = new RouterMockup(); |
|
||||
|
|
||||
const guard = new ResolveUserGuard(usersService.object, <any>router); |
|
||||
|
|
||||
guard.resolve(<any>route, <any>{}) |
|
||||
.subscribe(result => { |
|
||||
expect(result).toBeFalsy(); |
|
||||
expect(router.lastNavigation).toEqual(['/404']); |
|
||||
|
|
||||
done(); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
it('should navigate to 404 page if user loading fails', (done) => { |
|
||||
usersService.setup(x => x.getUser('my-user')) |
|
||||
.returns(() => Observable.throw(null!)); |
|
||||
const router = new RouterMockup(); |
|
||||
|
|
||||
const guard = new ResolveUserGuard(usersService.object, <any>router); |
|
||||
|
|
||||
guard.resolve(<any>route, <any>{}) |
|
||||
.subscribe(result => { |
|
||||
expect(result).toBeFalsy(); |
|
||||
expect(router.lastNavigation).toEqual(['/404']); |
|
||||
|
|
||||
done(); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
it('should return user if loading succeeded', (done) => { |
|
||||
const user: any = {}; |
|
||||
|
|
||||
usersService.setup(x => x.getUser('my-user')) |
|
||||
.returns(() => Observable.of(user)); |
|
||||
const router = new RouterMockup(); |
|
||||
|
|
||||
const guard = new ResolveUserGuard(usersService.object, <any>router); |
|
||||
|
|
||||
guard.resolve(<any>route, <any>{}) |
|
||||
.subscribe(result => { |
|
||||
expect(result).toBe(user); |
|
||||
|
|
||||
done(); |
|
||||
}); |
|
||||
}); |
|
||||
}); |
|
||||
*/ |
|
||||
@ -1,24 +0,0 @@ |
|||||
/* |
|
||||
* 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); |
|
||||
} |
|
||||
} |
|
||||
@ -1,44 +0,0 @@ |
|||||
/* |
|
||||
* 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 params = allParams(route); |
|
||||
|
|
||||
const userId = params['userId']; |
|
||||
|
|
||||
if (!userId) { |
|
||||
throw 'Route must contain user id.'; |
|
||||
} |
|
||||
|
|
||||
const result = |
|
||||
this.usersState.selectUser(userId) |
|
||||
.do(dto => { |
|
||||
if (!dto) { |
|
||||
this.router.navigate(['/404']); |
|
||||
} |
|
||||
}) |
|
||||
.map(u => u !== null); |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
} |
|
||||
@ -1,36 +1,51 @@ |
|||||
<form [formGroup]="createForm" (ngSubmit)="createSchema()"> |
<div class="modal-dialog"> |
||||
<div *ngIf="createFormError"> |
<div class="modal-content"> |
||||
<div class="form-alert form-alert-error" [innerHTML]="createFormError"></div> |
<form [formGroup]="createForm" (ngSubmit)="createSchema()"> |
||||
</div> |
<div class="modal-header"> |
||||
|
<h4 class="modal-title" *ngIf="import">Clone Schema</h4> |
||||
<div class="form-group"> |
<h4 class="modal-title" *ngIf="!import">Create Schema</h4> |
||||
<label for="schemaName">Name</label> |
|
||||
|
|
||||
<sqx-control-errors for="name" submitOnly="true" [submitted]="createFormSubmitted"></sqx-control-errors> |
|
||||
|
|
||||
<input type="text" class="form-control" id="schemaName" formControlName="name" autocomplete="off" sqxLowerCaseInput sqxFocusOnInit /> |
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="complete()"> |
||||
|
<span aria-hidden="true">×</span> |
||||
<small class="form-text text-muted"> |
</button> |
||||
The schema name becomes part of the api url,<br /> e.g {{apiUrl.buildUrl("api/content/")}}{{ctx.appName}}/<b>{{schemaName | async}}</b>/. |
</div> |
||||
</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. |
|
||||
</small> |
|
||||
</div> |
|
||||
|
|
||||
<div class="form-group clearfix"> |
|
||||
<button type="reset" class="float-left btn btn-secondary" (click)="cancel()">Cancel</button> |
|
||||
<button type="submit" class="float-right btn btn-success">Create</button> |
|
||||
</div> |
|
||||
|
|
||||
<div> |
<div class="modal-body"> |
||||
<button class="btn btn-sm btn-link" (click)="toggleImport()" [class.hidden]="showImport"> |
<div *ngIf="createFormError"> |
||||
Import schema |
<div class="form-alert form-alert-error" [innerHTML]="createFormError"></div> |
||||
</button> |
</div> |
||||
<button class="btn btn-sm btn-link" (click)="toggleImport()" [class.hidden]="!showImport"> |
|
||||
Hide |
<div class="form-group"> |
||||
</button> |
<label for="schemaName">Name</label> |
||||
|
|
||||
|
<sqx-control-errors for="name" submitOnly="true" [submitted]="createFormSubmitted"></sqx-control-errors> |
||||
|
|
||||
|
<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>/. |
||||
|
</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. |
||||
|
</small> |
||||
|
</div> |
||||
|
|
||||
<sqx-json-editor *ngIf="showImport" formControlName="import"></sqx-json-editor> |
<div class="form-group clearfix"> |
||||
|
<button type="reset" class="float-left btn btn-secondary" (click)="complete()">Cancel</button> |
||||
|
<button type="submit" class="float-right btn btn-success">Create</button> |
||||
|
</div> |
||||
|
|
||||
|
<div> |
||||
|
<button class="btn btn-sm btn-link" (click)="toggleImport()" [class.hidden]="showImport"> |
||||
|
Import schema |
||||
|
</button> |
||||
|
<button class="btn btn-sm btn-link" (click)="toggleImport()" [class.hidden]="!showImport"> |
||||
|
Hide |
||||
|
</button> |
||||
|
|
||||
|
<sqx-json-editor *ngIf="showImport" formControlName="import"></sqx-json-editor> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
</div> |
</div> |
||||
</form> |
</div> |
||||
@ -0,0 +1,21 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
||||
|
*/ |
||||
|
|
||||
|
import { Component, ViewChild, ViewContainerRef } from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-root-view', |
||||
|
template: ` |
||||
|
<div #element></div> |
||||
|
|
||||
|
<ng-content></ng-content> |
||||
|
` |
||||
|
}) |
||||
|
export class RootViewComponent { |
||||
|
@ViewChild('element') |
||||
|
public viewContainer: ViewContainerRef; |
||||
|
} |
||||
@ -1,27 +0,0 @@ |
|||||
/* |
|
||||
* Squidex Headless CMS |
|
||||
* |
|
||||
* @license |
|
||||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|
||||
*/ |
|
||||
|
|
||||
import { RootViewDirective } from './../'; |
|
||||
|
|
||||
/* tslint:disable:no-unused-expression */ |
|
||||
|
|
||||
describe('RootViewDirective', () => { |
|
||||
it('should call init of service in ctor', () => { |
|
||||
let viewRef = {}; |
|
||||
let viewRefPassed: any = null; |
|
||||
|
|
||||
const service = { |
|
||||
init: (ref: any) => { |
|
||||
viewRefPassed = ref; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
new RootViewDirective(<any>viewRef, <any>service); |
|
||||
|
|
||||
expect(viewRef).toBe(viewRefPassed); |
|
||||
}); |
|
||||
}); |
|
||||
@ -1,19 +0,0 @@ |
|||||
/* |
|
||||
* Squidex Headless CMS |
|
||||
* |
|
||||
* @license |
|
||||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|
||||
*/ |
|
||||
|
|
||||
import { Directive, ViewContainerRef } from '@angular/core'; |
|
||||
|
|
||||
import { RootViewService } from './../services/root-view.service'; |
|
||||
|
|
||||
@Directive({ |
|
||||
selector: '[sqxRootView]' |
|
||||
}) |
|
||||
export class RootViewDirective { |
|
||||
constructor(public viewContainer: ViewContainerRef, rootViewService: RootViewService) { |
|
||||
rootViewService.init(viewContainer); |
|
||||
} |
|
||||
} |
|
||||
@ -1,43 +0,0 @@ |
|||||
/* |
|
||||
* Squidex Headless CMS |
|
||||
* |
|
||||
* @license |
|
||||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|
||||
*/ |
|
||||
|
|
||||
import { RootViewService } from './../'; |
|
||||
|
|
||||
describe('RootViewService', () => { |
|
||||
const rootViewService = new RootViewService(); |
|
||||
|
|
||||
it('should call view when clearing element', () => { |
|
||||
let isClearCalled = false; |
|
||||
|
|
||||
const viewRef = { |
|
||||
clear: () => { |
|
||||
isClearCalled = true; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
rootViewService.init(<any>viewRef); |
|
||||
rootViewService.clear(); |
|
||||
|
|
||||
expect(isClearCalled).toBeTruthy(); |
|
||||
}); |
|
||||
|
|
||||
it('should call view when creating element', () => { |
|
||||
const view = {}; |
|
||||
|
|
||||
const viewRef = { |
|
||||
createEmbeddedView: () => { |
|
||||
return view; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
rootViewService.init(<any>viewRef); |
|
||||
|
|
||||
const result = rootViewService.createEmbeddedView(<any>{}); |
|
||||
|
|
||||
expect(result).toBe(view); |
|
||||
}); |
|
||||
}); |
|
||||
@ -1,25 +0,0 @@ |
|||||
/* |
|
||||
* Squidex Headless CMS |
|
||||
* |
|
||||
* @license |
|
||||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|
||||
*/ |
|
||||
|
|
||||
import { EmbeddedViewRef, Injectable, TemplateRef, ViewContainerRef } from '@angular/core'; |
|
||||
|
|
||||
@Injectable() |
|
||||
export class RootViewService { |
|
||||
private rootView: ViewContainerRef; |
|
||||
|
|
||||
public init(view: ViewContainerRef) { |
|
||||
this.rootView = view; |
|
||||
} |
|
||||
|
|
||||
public createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C> { |
|
||||
return this.rootView.createEmbeddedView(templateRef, context, index); |
|
||||
} |
|
||||
|
|
||||
public clear() { |
|
||||
this.rootView.clear(); |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue