mirror of https://github.com/Squidex/squidex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
7.6 KiB
274 lines
7.6 KiB
/*
|
|
* Squidex Headless CMS
|
|
*
|
|
* @license
|
|
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
|
|
*/
|
|
|
|
import { Injectable } from '@angular/core';
|
|
import { hasAnyLink, State, Types } from '@app/framework';
|
|
import { distinctUntilChanged, filter, map } from 'rxjs/operators';
|
|
import { UIService, UISettingsDto } from './../services/ui.service';
|
|
import { UsersService } from './../services/users.service';
|
|
import { AppsState } from './apps.state';
|
|
|
|
interface Snapshot {
|
|
// All common settings.
|
|
settingsCommon: object & any;
|
|
|
|
// All shared app settings.
|
|
settingsShared?: object & any;
|
|
|
|
// All user app settings.
|
|
settingsUser?: object & any;
|
|
|
|
// The merged settings of app and common settings.
|
|
settings: object & any;
|
|
|
|
// Indicates if the user can read events.
|
|
canReadEvents?: boolean;
|
|
|
|
// Indicates if the user can read users.
|
|
canReadUsers?: boolean;
|
|
|
|
// Indicates if the user can restore backups.
|
|
canRestore?: boolean;
|
|
|
|
// Indicates if the user can see the orleans dashboard.
|
|
canUseOrleans?: boolean;
|
|
|
|
// Indicates if the user can use at least one admin resource.
|
|
canUserAdminResource?: boolean;
|
|
}
|
|
|
|
@Injectable()
|
|
export class UIState extends State<Snapshot> {
|
|
public settings =
|
|
this.project(x => x.settings);
|
|
|
|
public settingsShared =
|
|
this.project(x => x.settingsShared).pipe(filter(x => !!x));
|
|
|
|
public settingsUser =
|
|
this.project(x => x.settingsUser).pipe(filter(x => !!x));
|
|
|
|
public canReadEvents =
|
|
this.project(x => x.canReadEvents === true);
|
|
|
|
public canReadUsers =
|
|
this.project(x => x.canReadUsers === true);
|
|
|
|
public canRestore =
|
|
this.project(x => x.canRestore === true);
|
|
|
|
public canUseOrleans =
|
|
this.project(x => x.canUseOrleans === true);
|
|
|
|
public canUserAdminResource =
|
|
this.project(x => x.canRestore === true || x.canReadUsers === true || x.canReadEvents === true || x.canUseOrleans);
|
|
|
|
public get<T>(path: string, defaultValue: T) {
|
|
return this.settings.pipe(map(x => this.getValue(x, path, defaultValue)),
|
|
distinctUntilChanged());
|
|
}
|
|
|
|
public getShared<T>(path: string, defaultValue: T) {
|
|
return this.settingsShared.pipe(map(x => this.getValue(x, path, defaultValue)),
|
|
distinctUntilChanged());
|
|
}
|
|
|
|
public getUser<T>(path: string, defaultValue: T) {
|
|
return this.settingsUser.pipe(map(x => this.getValue(x, path, defaultValue)),
|
|
distinctUntilChanged());
|
|
}
|
|
|
|
constructor(
|
|
private readonly appsState: AppsState,
|
|
private readonly uiService: UIService,
|
|
private readonly usersService: UsersService
|
|
) {
|
|
super({
|
|
settings: {},
|
|
settingsCommon: {}
|
|
});
|
|
|
|
this.loadResources();
|
|
this.loadCommon();
|
|
|
|
appsState.selectedApp.subscribe(app => {
|
|
this.load(app.name);
|
|
});
|
|
}
|
|
|
|
private load(app: string) {
|
|
this.next(s => ({
|
|
...s,
|
|
settings: s.settingsCommon,
|
|
settingsShared: undefined,
|
|
settingsUser: undefined
|
|
}));
|
|
|
|
this.uiService.getSharedSettings(app)
|
|
.subscribe(payload => {
|
|
this.next(s => updateSettings(s, { settingsShared: payload }));
|
|
});
|
|
|
|
this.uiService.getUserSettings(app)
|
|
.subscribe(payload => {
|
|
this.next(s => updateSettings(s, { settingsUser: payload }));
|
|
});
|
|
}
|
|
|
|
private loadCommon() {
|
|
this.uiService.getCommonSettings()
|
|
.subscribe(payload => {
|
|
this.next(s => updateSettings(s, { settingsCommon: payload }));
|
|
});
|
|
}
|
|
|
|
private loadResources() {
|
|
this.usersService.getResources()
|
|
.subscribe(payload => {
|
|
this.next(s => ({ ...s,
|
|
canReadEvents: hasAnyLink(payload, 'admin/events'),
|
|
canReadUsers: hasAnyLink(payload, 'admin/users'),
|
|
canRestore: hasAnyLink(payload, 'admin/restore'),
|
|
canUseOrleans: hasAnyLink(payload, 'admin/orleans')
|
|
}));
|
|
});
|
|
}
|
|
|
|
public set(path: string, value: any, user = false) {
|
|
if (user) {
|
|
this.setUser(path, value);
|
|
} else {
|
|
this.setShared(path, value);
|
|
}
|
|
}
|
|
|
|
private setUser(path: string, value: any) {
|
|
const { key, current, root } = getContainer(this.snapshot.settingsUser, path);
|
|
|
|
if (current && key) {
|
|
this.uiService.putUserSetting(this.appName, path, value).subscribe();
|
|
|
|
current[key] = value;
|
|
|
|
this.next(s => updateSettings(s, { settingsUser: root }));
|
|
}
|
|
}
|
|
|
|
private setShared(path: string, value: any) {
|
|
const { key, current, root } = getContainer(this.snapshot.settingsShared, path);
|
|
|
|
if (current && key) {
|
|
this.uiService.putSharedSetting(this.appName, path, value).subscribe();
|
|
|
|
current[key] = value;
|
|
|
|
this.next(s => updateSettings(s, { settingsShared: root }));
|
|
}
|
|
}
|
|
|
|
public remove(path: string) {
|
|
return this.removeUser(path) || this.removeShared(path);
|
|
}
|
|
|
|
public removeUser(path: string) {
|
|
const { key, current, root } = getContainer(this.snapshot.settingsUser, path);
|
|
|
|
if (current && key && current[key]) {
|
|
this.uiService.deleteUserSetting(this.appName, path).subscribe();
|
|
|
|
delete current[key];
|
|
|
|
this.next(s => updateSettings(s, { settingsUser: root }));
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public removeShared(path: string) {
|
|
const { key, current, root } = getContainer(this.snapshot.settingsShared, path);
|
|
|
|
if (current && key && current[key]) {
|
|
this.uiService.deleteSharedSetting(this.appName, path).subscribe();
|
|
|
|
delete current[key];
|
|
|
|
this.next(s => updateSettings(s, { settingsShared: root }));
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private getValue<T>(setting: object & UISettingsDto, path: string, defaultValue: T) {
|
|
const segments = path.split('.');
|
|
|
|
let current = setting;
|
|
|
|
for (const segment of segments) {
|
|
const temp = current[segment];
|
|
|
|
if (temp) {
|
|
current[segment] = temp;
|
|
} else {
|
|
return defaultValue;
|
|
}
|
|
|
|
current = temp;
|
|
}
|
|
|
|
return <T><any>current;
|
|
}
|
|
|
|
private get appName() {
|
|
return this.appsState.appName;
|
|
}
|
|
}
|
|
|
|
function getContainer(settings: object, path: string) {
|
|
const segments = path.split('.');
|
|
|
|
let current = { ...settings };
|
|
|
|
const root = current;
|
|
|
|
if (segments.length > 0) {
|
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
const segment = segments[i];
|
|
|
|
let temp = current[segment];
|
|
|
|
if (!temp) {
|
|
temp = {};
|
|
} else {
|
|
temp = { ...temp };
|
|
}
|
|
|
|
current[segment] = temp;
|
|
|
|
if (!Types.isObject(temp)) {
|
|
return { key: null, current: null, root: null };
|
|
}
|
|
|
|
current = temp;
|
|
}
|
|
}
|
|
|
|
return { key: segments[segments.length - 1], current, root };
|
|
}
|
|
|
|
function updateSettings(state: Snapshot, update: Partial<Snapshot>) {
|
|
const settings = {};
|
|
|
|
Types.mergeInto(settings, update.settingsCommon || state.settingsCommon);
|
|
Types.mergeInto(settings, update.settingsShared || state.settingsShared);
|
|
Types.mergeInto(settings, update.settingsUser || state.settingsUser);
|
|
|
|
return { ...state, settings, ...update };
|
|
}
|