Merge pull request #5086 from GabrielInTheWorld/editStart
Adds the 'edit-mode' to start, legal-notice and privacy-policy
This commit is contained in:
commit
9ae4472b0d
@ -1,9 +1,18 @@
|
||||
<mat-card class="os-card">
|
||||
<div>
|
||||
<div *ngIf="legalNotice" class="legal-notice-text" [innerHtml]="legalNotice | trust: 'html'"></div>
|
||||
<div *ngIf="!legalNotice" translate>
|
||||
The event manager hasn't set up a legal notice yet.
|
||||
</div>
|
||||
<ng-container *ngIf="!isEditing">
|
||||
<div *ngIf="legalNotice" class="legal-notice-text" [innerHtml]="legalNotice | trust: 'html'"></div>
|
||||
<div *ngIf="!legalNotice" translate>
|
||||
The event manager hasn't set up a legal notice yet.
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="isEditing">
|
||||
<div>
|
||||
<form [formGroup]="formGroup">
|
||||
<editor formControlName="legalNotice" [init]="tinyMceSettings"></editor>
|
||||
</form>
|
||||
</div>
|
||||
</ng-container>
|
||||
<mat-divider></mat-divider>
|
||||
<div *ngIf="versionInfo" class="version-text">
|
||||
<a [attr.href]="versionInfo.openslides_url" target="_blank">
|
||||
|
@ -1,9 +1,14 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { environment } from 'environments/environment';
|
||||
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { LoginDataService } from 'app/core/ui-services/login-data.service';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
|
||||
/**
|
||||
* Characterize a plugin. This data is retrieved from the server
|
||||
@ -64,7 +69,50 @@ interface VersionResponse {
|
||||
templateUrl: './legal-notice-content.component.html',
|
||||
styleUrls: ['./legal-notice-content.component.scss']
|
||||
})
|
||||
export class LegalNoticeContentComponent implements OnInit {
|
||||
export class LegalNoticeContentComponent extends BaseViewComponent implements OnInit {
|
||||
/**
|
||||
* Decides, whether the component can be edited at all.
|
||||
* Defaults to `false`.
|
||||
*/
|
||||
@Input()
|
||||
public canBeEdited = false;
|
||||
|
||||
/**
|
||||
* Sets the editing-state and updates the FormGroup with the current value.
|
||||
*
|
||||
* @param isEditing whether the component is currently in editing-mode.
|
||||
*/
|
||||
@Input()
|
||||
public set isEditing(isEditing: boolean) {
|
||||
this.formGroup.patchValue({ legalNotice: this.legalNotice });
|
||||
this._isEditing = isEditing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the editing-state.
|
||||
*
|
||||
* @returns `isEditing`.
|
||||
*/
|
||||
public get isEditing(): boolean {
|
||||
return this._isEditing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitter to send updated value to the parent-component.
|
||||
*/
|
||||
@Output()
|
||||
public update = new EventEmitter<string>();
|
||||
|
||||
/**
|
||||
* FormGroup for editing value.
|
||||
*/
|
||||
public formGroup: FormGroup;
|
||||
|
||||
/**
|
||||
* State, whether this is in editing-mode.
|
||||
*/
|
||||
private _isEditing = false;
|
||||
|
||||
/**
|
||||
* The legal notive text for the ui.
|
||||
*/
|
||||
@ -81,7 +129,19 @@ export class LegalNoticeContentComponent implements OnInit {
|
||||
* @param translate
|
||||
* @param http
|
||||
*/
|
||||
public constructor(private loginDataService: LoginDataService, private http: HttpService) {}
|
||||
public constructor(
|
||||
title: Title,
|
||||
translate: TranslateService,
|
||||
matSnackbar: MatSnackBar,
|
||||
private loginDataService: LoginDataService,
|
||||
private http: HttpService,
|
||||
fb: FormBuilder
|
||||
) {
|
||||
super(title, translate, matSnackbar);
|
||||
this.formGroup = fb.group({
|
||||
legalNotice: ''
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes for the legal notice text.
|
||||
@ -100,5 +160,11 @@ export class LegalNoticeContentComponent implements OnInit {
|
||||
// TODO: error handling if the version info could not be loaded
|
||||
}
|
||||
);
|
||||
|
||||
if (this.canBeEdited) {
|
||||
this.subscriptions.push(
|
||||
this.formGroup.get('legalNotice').valueChanges.subscribe(value => this.update.emit(value))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,13 @@
|
||||
<mat-card class="os-card">
|
||||
<div *ngIf="privacyPolicy" [innerHtml]="privacyPolicy | trust: 'html'"></div>
|
||||
<div *ngIf="!privacyPolicy" translate>
|
||||
The event manager hasn't set up a privacy policy yet.
|
||||
</div>
|
||||
<mat-card [ngClass]="isEditing ? 'os-form-card' : 'os-card'">
|
||||
<ng-container *ngIf="!isEditing">
|
||||
<div *ngIf="privacyPolicy" [innerHtml]="privacyPolicy | trust: 'html'"></div>
|
||||
<div *ngIf="!privacyPolicy" translate>
|
||||
The event manager hasn't set up a privacy policy yet.
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="isEditing">
|
||||
<form [formGroup]="formGroup">
|
||||
<editor formControlName="privacyPolicy" [init]="tinyMceSettings"></editor>
|
||||
</form>
|
||||
</ng-container>
|
||||
</mat-card>
|
||||
|
@ -1,6 +1,12 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { LoginDataService } from 'app/core/ui-services/login-data.service';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
|
||||
/**
|
||||
* Shared component to hold the content of the Privacy Policy.
|
||||
@ -11,18 +17,76 @@ import { LoginDataService } from 'app/core/ui-services/login-data.service';
|
||||
templateUrl: './privacy-policy-content.component.html',
|
||||
styleUrls: ['./privacy-policy-content.component.scss']
|
||||
})
|
||||
export class PrivacyPolicyContentComponent implements OnInit {
|
||||
export class PrivacyPolicyContentComponent extends BaseViewComponent implements OnInit {
|
||||
/**
|
||||
* Decides, whether the component can be edited at all.
|
||||
* Defaults to `false`.
|
||||
*/
|
||||
@Input()
|
||||
public canBeEdited = false;
|
||||
|
||||
/**
|
||||
* Sets the editing-state and updates the FormGroup with the current value.
|
||||
*
|
||||
* @param isEditing whether the component is currently in editing-mode.
|
||||
*/
|
||||
@Input()
|
||||
public set isEditing(isEditing: boolean) {
|
||||
this.formGroup.patchValue({ privacyPolicy: this.privacyPolicy });
|
||||
this._isEditing = isEditing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the editing-state.
|
||||
*
|
||||
* @returns `isEditing`.
|
||||
*/
|
||||
public get isEditing(): boolean {
|
||||
return this._isEditing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitter to send updated value to the parent-component.
|
||||
*/
|
||||
@Output()
|
||||
public update = new EventEmitter<{ [key: string]: string }>();
|
||||
|
||||
/**
|
||||
* FormGroup for editing value.
|
||||
*/
|
||||
public formGroup: FormGroup;
|
||||
|
||||
/**
|
||||
* State, whether this is in editing-mode.
|
||||
*/
|
||||
private _isEditing = false;
|
||||
|
||||
/**
|
||||
* The actual privacy policy as string
|
||||
*/
|
||||
public privacyPolicy: string;
|
||||
|
||||
/**
|
||||
* Imports the loginDataService and the translation service
|
||||
* @param loginDataService Login Data
|
||||
* @param translate for the translation
|
||||
* Constructor.
|
||||
*
|
||||
* @param title
|
||||
* @param translate
|
||||
* @param matSnackbar
|
||||
* @param loginDataService
|
||||
* @param fb
|
||||
*/
|
||||
public constructor(private loginDataService: LoginDataService) {}
|
||||
public constructor(
|
||||
title: Title,
|
||||
protected translate: TranslateService,
|
||||
matSnackbar: MatSnackBar,
|
||||
private loginDataService: LoginDataService,
|
||||
fb: FormBuilder
|
||||
) {
|
||||
super(title, translate, matSnackbar);
|
||||
this.formGroup = fb.group({
|
||||
privacyPolicy: ''
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes for the privacy policy text
|
||||
@ -31,5 +95,10 @@ export class PrivacyPolicyContentComponent implements OnInit {
|
||||
this.loginDataService.privacyPolicy.subscribe(privacyPolicy => {
|
||||
this.privacyPolicy = privacyPolicy;
|
||||
});
|
||||
if (this.canBeEdited) {
|
||||
this.subscriptions.push(
|
||||
this.formGroup.get('privacyPolicy').valueChanges.subscribe(value => this.update.emit(value))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,24 @@
|
||||
<os-head-bar [nav]="false" [goBack]="true">
|
||||
<os-head-bar
|
||||
[nav]="false"
|
||||
[goBack]="true"
|
||||
[editMode]="isEditing"
|
||||
[hasMainButton]="canManage()"
|
||||
[mainButtonIcon]="'edit'"
|
||||
[mainActionTooltip]="'Edit' | translate"
|
||||
(cancelEditEvent)="isEditing = !isEditing"
|
||||
(mainEvent)="isEditing = !isEditing"
|
||||
(saveEvent)="saveChanges()"
|
||||
>
|
||||
<div class="title-slot">
|
||||
<h2 translate>Legal notice</h2>
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<os-legal-notice-content></os-legal-notice-content>
|
||||
<os-legal-notice-content
|
||||
[canBeEdited]="true"
|
||||
[isEditing]="isEditing"
|
||||
(update)="legalNotice = $event"
|
||||
></os-legal-notice-content>
|
||||
|
||||
<mat-card class="os-card">
|
||||
<div>
|
||||
|
@ -1,25 +1,47 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { OpenSlidesService } from 'app/core/core-services/openslides.service';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { ConfigRepositoryService } from 'app/core/repositories/config/config-repository.service';
|
||||
import { UpdateService } from 'app/core/ui-services/update.service';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
|
||||
@Component({
|
||||
selector: 'os-legal-notice',
|
||||
templateUrl: './legal-notice.component.html'
|
||||
})
|
||||
export class LegalNoticeComponent implements OnInit {
|
||||
export class LegalNoticeComponent extends BaseViewComponent implements OnInit {
|
||||
/**
|
||||
* Whether this component is in editing-mode.
|
||||
*/
|
||||
public isEditing = false;
|
||||
|
||||
/**
|
||||
* Holds the current legal-notice.
|
||||
*/
|
||||
public legalNotice = '';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public constructor(
|
||||
title: Title,
|
||||
protected translate: TranslateService,
|
||||
matSnackbar: MatSnackBar,
|
||||
private openSlidesService: OpenSlidesService,
|
||||
private update: UpdateService,
|
||||
private titleService: Title,
|
||||
private translate: TranslateService
|
||||
) {}
|
||||
private configRepo: ConfigRepositoryService,
|
||||
private operator: OperatorService
|
||||
) {
|
||||
super(title, translate, matSnackbar);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.titleService.setTitle(this.translate.instant('Legal notice'));
|
||||
super.setTitle(this.translate.instant('Legal notice'));
|
||||
}
|
||||
|
||||
public resetCache(): void {
|
||||
@ -33,4 +55,20 @@ export class LegalNoticeComponent implements OnInit {
|
||||
public initiateUpdateCheckForAllClients(): void {
|
||||
this.update.initiateUpdateCheckForAllClients();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves changes.
|
||||
*/
|
||||
public saveChanges(): void {
|
||||
this.configRepo
|
||||
.bulkUpdate([{ key: 'general_event_legal_notice', value: this.legalNotice }])
|
||||
.then(() => (this.isEditing = !this.isEditing), this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns, if the current user has the necessary permissions.
|
||||
*/
|
||||
public canManage(): boolean {
|
||||
return this.operator.hasPerms('core.can_manage_config');
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,21 @@
|
||||
<os-head-bar [nav]=false [goBack]=true>
|
||||
<os-head-bar
|
||||
[nav]="false"
|
||||
[goBack]="true"
|
||||
[editMode]="isEditing"
|
||||
[hasMainButton]="canManage()"
|
||||
[mainButtonIcon]="'edit'"
|
||||
[mainActionTooltip]="'Edit' | translate"
|
||||
(cancelEditEvent)="isEditing = !isEditing"
|
||||
(mainEvent)="isEditing = !isEditing"
|
||||
(saveEvent)="saveChanges()"
|
||||
>
|
||||
<div class="title-slot">
|
||||
<h2 translate>Privacy Policy</h2>
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<os-privacy-policy-content></os-privacy-policy-content>
|
||||
<os-privacy-policy-content
|
||||
[canBeEdited]="true"
|
||||
[isEditing]="isEditing"
|
||||
(update)="privacyProlicy = $event"
|
||||
></os-privacy-policy-content>
|
||||
|
@ -1,17 +1,63 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { ConfigRepositoryService } from 'app/core/repositories/config/config-repository.service';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
|
||||
@Component({
|
||||
selector: 'os-privacy-policy',
|
||||
templateUrl: './privacy-policy.component.html',
|
||||
styleUrls: ['./privacy-policy.component.scss']
|
||||
})
|
||||
export class PrivacyPolicyComponent implements OnInit {
|
||||
public constructor(private titleService: Title, private translate: TranslateService) {}
|
||||
export class PrivacyPolicyComponent extends BaseViewComponent implements OnInit {
|
||||
/**
|
||||
* Whether the component is in editing-mode.
|
||||
*/
|
||||
public isEditing = false;
|
||||
|
||||
/**
|
||||
* Holds the current privacy-policy.
|
||||
*/
|
||||
public privacyProlicy = '';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param titleService
|
||||
* @param translate
|
||||
* @param configRepo
|
||||
*/
|
||||
public constructor(
|
||||
title: Title,
|
||||
protected translate: TranslateService,
|
||||
matSnackbar: MatSnackBar,
|
||||
private configRepo: ConfigRepositoryService,
|
||||
private operator: OperatorService
|
||||
) {
|
||||
super(title, translate, matSnackbar);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.titleService.setTitle(this.translate.instant('Privacy policy'));
|
||||
super.setTitle(this.translate.instant('Privacy policy'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves changes.
|
||||
*/
|
||||
public saveChanges(): void {
|
||||
this.configRepo
|
||||
.bulkUpdate([{ key: 'general_event_privacy_policy', value: this.privacyProlicy }])
|
||||
.then(() => (this.isEditing = !this.isEditing), this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns, if the current user has the necessary permissions.
|
||||
*/
|
||||
public canManage(): boolean {
|
||||
return this.operator.hasPerms('core.can_manage_config');
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,38 @@
|
||||
<os-head-bar>
|
||||
<os-head-bar
|
||||
[hasMainButton]="canManage()"
|
||||
[editMode]="isEditing"
|
||||
[mainButtonIcon]="'edit'"
|
||||
[isSaveButtonEnabled]="startForm.valid"
|
||||
(mainEvent)="editStartPage()"
|
||||
(saveEvent)="saveChanges()"
|
||||
(cancelEditEvent)="isEditing = !isEditing"
|
||||
>
|
||||
<div class="title-slot">
|
||||
<h2 translate>Home</h2>
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<mat-card class="os-card">
|
||||
<div class="app-content">
|
||||
<h1>{{ welcomeTitle | translate }}</h1>
|
||||
<mat-card [ngClass]="isEditing ? 'os-form-card' : 'os-card'">
|
||||
<ng-container *ngIf="!isEditing">
|
||||
<div class="app-content">
|
||||
<h1>{{ startContent.general_event_welcome_title | translate }}</h1>
|
||||
|
||||
<div [innerHTML]="welcomeText | trust: 'html'"></div>
|
||||
</div>
|
||||
<div [innerHTML]="startContent.general_event_welcome_text | trust: 'html'"></div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="isEditing">
|
||||
<form [formGroup]="startForm">
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
formControlName="general_event_welcome_title"
|
||||
required
|
||||
placeholder="{{ 'Front page title' | translate }}"
|
||||
/>
|
||||
<mat-error translate>The title is required</mat-error>
|
||||
</mat-form-field>
|
||||
<editor formControlName="general_event_welcome_text" [init]="tinyMceSettings"></editor>
|
||||
</form>
|
||||
</ng-container>
|
||||
</mat-card>
|
||||
|
@ -1,10 +1,22 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core'; // showcase
|
||||
|
||||
import { BaseComponent } from 'app/base.component';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { ConfigRepositoryService } from 'app/core/repositories/config/config-repository.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
|
||||
/**
|
||||
* Interface describes the keys for the fields at start-component.
|
||||
*/
|
||||
interface IStartContent {
|
||||
general_event_welcome_title: string;
|
||||
general_event_welcome_text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The start component. Greeting page for OpenSlides
|
||||
@ -14,9 +26,24 @@ import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
templateUrl: './start.component.html',
|
||||
styleUrls: ['./start.component.scss']
|
||||
})
|
||||
export class StartComponent extends BaseComponent implements OnInit {
|
||||
public welcomeTitle: string;
|
||||
public welcomeText: string;
|
||||
export class StartComponent extends BaseViewComponent implements OnInit {
|
||||
/**
|
||||
* Whether the user is editing the content.
|
||||
*/
|
||||
public isEditing = false;
|
||||
|
||||
/**
|
||||
* Formular for the content.
|
||||
*/
|
||||
public startForm: FormGroup;
|
||||
|
||||
/**
|
||||
* Holding the values for the content.
|
||||
*/
|
||||
public startContent: IStartContent = {
|
||||
general_event_welcome_title: '',
|
||||
general_event_welcome_text: ''
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor of the StartComponent
|
||||
@ -25,8 +52,20 @@ export class StartComponent extends BaseComponent implements OnInit {
|
||||
* @param translate to translation module
|
||||
* @param configService read out config values
|
||||
*/
|
||||
public constructor(titleService: Title, translate: TranslateService, private configService: ConfigService) {
|
||||
super(titleService, translate);
|
||||
public constructor(
|
||||
titleService: Title,
|
||||
translate: TranslateService,
|
||||
matSnackbar: MatSnackBar,
|
||||
private configService: ConfigService,
|
||||
private configRepo: ConfigRepositoryService,
|
||||
private fb: FormBuilder,
|
||||
private operator: OperatorService
|
||||
) {
|
||||
super(titleService, translate, matSnackbar);
|
||||
this.startForm = this.fb.group({
|
||||
general_event_welcome_title: ['', Validators.required],
|
||||
general_event_welcome_text: ''
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,11 +79,42 @@ export class StartComponent extends BaseComponent implements OnInit {
|
||||
// set the welcome title
|
||||
this.configService
|
||||
.get<string>('general_event_welcome_title')
|
||||
.subscribe(welcomeTitle => (this.welcomeTitle = welcomeTitle));
|
||||
.subscribe(welcomeTitle => (this.startContent.general_event_welcome_title = welcomeTitle));
|
||||
|
||||
// set the welcome text
|
||||
this.configService.get<string>('general_event_welcome_text').subscribe(welcomeText => {
|
||||
this.welcomeText = this.translate.instant(welcomeText);
|
||||
this.startContent.general_event_welcome_text = this.translate.instant(welcomeText);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes to editing mode.
|
||||
*/
|
||||
public editStartPage(): void {
|
||||
Object.keys(this.startForm.controls).forEach(control => {
|
||||
this.startForm.patchValue({ [control]: this.startContent[control] });
|
||||
});
|
||||
this.isEditing = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves changes and updates the content.
|
||||
*/
|
||||
public saveChanges(): void {
|
||||
this.configRepo
|
||||
.bulkUpdate(
|
||||
Object.keys(this.startForm.controls).map(control => ({
|
||||
key: control,
|
||||
value: this.startForm.value[control]
|
||||
}))
|
||||
)
|
||||
.then(() => (this.isEditing = !this.isEditing), this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns, if the current user has the necessary permissions.
|
||||
*/
|
||||
public canManage(): boolean {
|
||||
return this.operator.hasPerms('core.can_manage_config');
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user