Merge pull request #4322 from MaximilianKrambach/motionDelegates

direct pdf export and hidden menus for unprivileged users
This commit is contained in:
Emanuel Schütze 2019-02-13 15:37:07 +01:00 committed by GitHub
commit c5912f8515
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 203 additions and 121 deletions

View File

@ -1,4 +1,4 @@
<os-head-bar [mainButton]="true" (mainEvent)="onPlusButton()" [multiSelectMode]="isMultiSelect">
<os-head-bar [mainButton]="canManage" (mainEvent)="onPlusButton()" [multiSelectMode]="isMultiSelect">
<!-- Title -->
<div class="title-slot"><h2 translate>Agenda</h2></div>
<!-- Menu -->
@ -80,7 +80,9 @@
<ng-container matColumnDef="menu">
<mat-header-cell *matHeaderCellDef mat-sort-header>Menu</mat-header-cell>
<mat-cell *matCellDef="let item">
<button mat-icon-button [matMenuTriggerFor]="singleItemMenu" [matMenuTriggerData]="{ item: item }">
<button mat-icon-button
*osPerms="'agenda.can_manage'"
[matMenuTriggerFor]="singleItemMenu" [matMenuTriggerData]="{ item: item }">
<mat-icon>more_vert</mat-icon>
</button>
</mat-cell>
@ -121,7 +123,7 @@
<span translate>Current list of speakers</span>
</button>
<!-- CSV export -->
<button mat-menu-item (click)="csvExportItemList()">
<button mat-menu-item *osPerms="'agenda.can_manage'" (click)="csvExportItemList()">
<mat-icon>archive</mat-icon>
<span translate>Export as CSV</span>
</button>
@ -201,17 +203,6 @@
<span translate>Done</span>
</button>
<!-- List of speakers for mobile -->
<button mat-menu-item (click)="onSpeakerIcon(item)" *ngIf="vp.isMobile">
<mat-icon
[matBadge]="item.waitingSpeakerAmount > 0 ? item.waitingSpeakerAmount : null"
matBadgeColor="accent"
>
mic
</mat-icon>
<span translate>List of speakers</span>
</button>
<!-- Edit button -->
<button mat-menu-item (click)="openEditInfo(item)">
<mat-icon>edit</mat-icon>

View File

@ -17,6 +17,7 @@ import { DurationService } from 'app/core/ui-services/duration.service';
import { ItemInfoDialogComponent } from '../item-info-dialog/item-info-dialog.component';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { ViewportService } from 'app/core/ui-services/viewport.service';
import { OperatorService } from 'app/core/core-services/operator.service';
/**
* List view for the agenda.
@ -35,10 +36,19 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
/**
* Determine the display columns in mobile view
*/
public displayedColumnsMobile: string[] = ['title', 'menu'];
public displayedColumnsMobile: string[] = ['title', 'speakers', 'menu'];
public isNumberingAllowed: boolean;
/**
* Helper to check main button permissions
*
* @returns true if the operator can manage agenda items
*/
public get canManage(): boolean {
return this.operator.hasPerms('agenda.can_manage');
}
/**
* The usual constructor for components
* @param titleService Setting the browser tab title
@ -56,6 +66,7 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
* @param filterService: service for filtering data
* @param agendaPdfService: service for preparing a pdf of the agenda
* @param pdfService: Service for exporting a pdf
* @param operator the current user
*/
public constructor(
titleService: Title,
@ -72,7 +83,8 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
private csvExport: AgendaCsvExportService,
public filterService: AgendaFilterListService,
private agendaPdfService: AgendaPdfService,
private pdfService: PdfDocumentService
private pdfService: PdfDocumentService,
private operator: OperatorService
) {
super(titleService, translate, matSnackBar);
@ -116,6 +128,9 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
* @param item The view item that was clicked
*/
public openEditInfo(item: ViewItem): void {
if (!this.canManage) {
return;
}
const dialogRef = this.dialog.open(ItemInfoDialogComponent, {
width: '400px',
data: item,

View File

@ -30,8 +30,14 @@
</os-head-bar>
<mat-card>
<button mat-raised-button color="primary" (click)="onFollowRecButton()" [disabled]="isFollowingProhibited()">
<mat-icon>done_all</mat-icon>
<button
*osPerms="['motions.can_manage', 'motions.can_manage_metadata']"
mat-raised-button
color="primary"
(click)="onFollowRecButton()"
[disabled]="isFollowingProhibited()"
>
<mat-icon>done_all</mat-icon>&nbsp;
<span translate>Follow recommendations for all motions</span>
</button>
@ -98,20 +104,20 @@
<span translate>List of speakers</span>
</button>
<button mat-menu-item>
<button mat-menu-item *osPerms="'core.can_manage_projector'">
<mat-icon>videocam</mat-icon>
<span translate>Project</span>
</button>
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
<button mat-menu-item (click)="toggleEditMode()">
<mat-icon>edit</mat-icon>
<span translate>Edit title</span>
</button>
<mat-divider></mat-divider>
<mat-divider></mat-divider>
<button mat-menu-item (click)="toggleEditMode()">
<mat-icon>edit</mat-icon>
<span translate>Edit title</span>
</button>
<button mat-menu-item class="red-warning-text" (click)="onDeleteBlockButton()">
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
<button mat-menu-item class="red-warning-text" (click)="onDeleteBlockButton()">
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
</div>
</mat-menu>

View File

@ -13,6 +13,7 @@ import { MotionRepositoryService } from 'app/core/repositories/motions/motion-re
import { ViewMotionBlock } from '../../models/view-motion-block';
import { PromptService } from 'app/core/ui-services/prompt.service';
import { ViewMotion } from '../../models/view-motion';
import { OperatorService } from '../../../../core/core-services/operator.service';
/**
* Detail component to display one motion block
@ -50,6 +51,7 @@ export class MotionBlockDetailComponent extends ListViewBaseComponent<ViewMotion
* @param titleService Setting the title
* @param translate translations
* @param matSnackBar showing errors
* @param operator the current user
* @param router navigating
* @param route determine the blocks ID by the route
* @param repo the motion blocks repository
@ -60,6 +62,7 @@ export class MotionBlockDetailComponent extends ListViewBaseComponent<ViewMotion
titleService: Title,
translate: TranslateService,
matSnackBar: MatSnackBar,
private operator: OperatorService,
private router: Router,
private route: ActivatedRoute,
private repo: MotionBlockRepositoryService,
@ -117,7 +120,11 @@ export class MotionBlockDetailComponent extends ListViewBaseComponent<ViewMotion
* @returns an array of strings building the column definition
*/
public getColumnDefinition(): string[] {
return ['title', 'state', 'recommendation', 'remove'];
let columns = ['title', 'state', 'recommendation'];
if (this.operator.hasPerms('motions.can_manage_manage')) {
columns = columns.concat('remove');
}
return columns;
}
/**

View File

@ -1,4 +1,4 @@
<os-head-bar [nav]="false" [mainButton]="true" (mainEvent)="onPlusButton()">
<os-head-bar [nav]="false" [mainButton]="canEdit" (mainEvent)="onPlusButton()">
<!-- Title -->
<div class="title-slot"><h2 translate>Motion blocks</h2></div>
</os-head-bar>
@ -7,17 +7,11 @@
<mat-card class="os-card" *ngIf="blockToCreate">
<mat-card-title translate>New motion block</mat-card-title>
<mat-card-content>
<form [formGroup]="createBlockForm"
(keydown)="onKeyDown($event)">
<form [formGroup]="createBlockForm" (keydown)="onKeyDown($event)">
<!-- Title -->
<p>
<mat-form-field>
<input
formControlName="title"
matInput
placeholder="{{ 'Title' | translate }}"
required
/>
<input formControlName="title" matInput placeholder="{{ 'Title' | translate }}" required />
<mat-error *ngIf="createBlockForm.get('title').hasError('required')" translate>
A name is required
</mat-error>
@ -76,7 +70,12 @@
<ng-container matColumnDef="menu">
<mat-header-cell *matHeaderCellDef>Menu</mat-header-cell>
<mat-cell *matCellDef="let block">
<button mat-icon-button [matMenuTriggerFor]="singleItemMenu" [matMenuTriggerData]="{ item: block }">
<button
*ngIf="canEdit"
mat-icon-button
[matMenuTriggerFor]="singleItemMenu"
[matMenuTriggerData]="{ item: block }"
>
<mat-icon>more_vert</mat-icon>
</button>
</mat-cell>

View File

@ -7,14 +7,15 @@ import { MatSnackBar } from '@angular/material';
import { BehaviorSubject } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
import { itemVisibilityChoices } from 'app/shared/models/agenda/item';
import { ListViewBaseComponent } from 'app/site/base/list-view-base';
import { MotionBlock } from 'app/shared/models/motions/motion-block';
import { itemVisibilityChoices } from 'app/shared/models/agenda/item';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
import { ViewMotionBlock } from '../../models/view-motion-block';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
import { OperatorService } from 'app/core/core-services/operator.service';
import { PromptService } from 'app/core/ui-services/prompt.service';
import { ViewItem } from 'app/site/agenda/models/view-item';
import { ViewMotionBlock } from '../../models/view-motion-block';
/**
* Table for the motion blocks
@ -50,6 +51,15 @@ export class MotionBlockListComponent extends ListViewBaseComponent<ViewMotionBl
*/
public itemVisibility = itemVisibilityChoices;
/**
* helper for permission checks
*
* @returns true if the user may alter motions or their metadata
*/
public get canEdit(): boolean {
return this.operator.hasPerms('motions.can_manage', 'motions.can_manage_metadata');
}
/**
* Constructor for the motion block list view
*
@ -63,6 +73,8 @@ export class MotionBlockListComponent extends ListViewBaseComponent<ViewMotionBl
* @param DS the dataStore
* @param formBuilder creates forms
* @param promptService the delete prompt
* @param itemRepo
* @param operator permission checks
*/
public constructor(
titleService: Title,
@ -74,7 +86,8 @@ export class MotionBlockListComponent extends ListViewBaseComponent<ViewMotionBl
private agendaRepo: ItemRepositoryService,
private formBuilder: FormBuilder,
private promptService: PromptService,
private itemRepo: ItemRepositoryService
private itemRepo: ItemRepositoryService,
private operator: OperatorService
) {
super(titleService, translate, matSnackBar);

View File

@ -71,7 +71,7 @@
<span translate>List of speakers</span>
</button>
<!-- Project -->
<button mat-menu-item>
<button mat-menu-item *osPerms="'core.can_manage_projector'">
<mat-icon>videocam</mat-icon>
<span translate>Project</span>
</button>

View File

@ -193,6 +193,12 @@
<span translate>Import</span>
</button>
</div>
<div *ngIf="!perms.isAllowed('manage')">
<button mat-menu-item (click)="directPdfExport()">
<mat-icon>archive</mat-icon>
<span translate>Export as PDF</span>
</button>
</div>
</div>
<div *ngIf="isMultiSelect">
<button mat-menu-item (click)="selectAll()">

View File

@ -17,7 +17,7 @@ import { MotionRepositoryService } from 'app/core/repositories/motions/motion-re
import { MotionSortListService } from '../../services/motion-sort-list.service';
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
import { ViewCategory } from '../../models/view-category';
import { ViewMotion } from '../../models/view-motion';
import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../../models/view-motion';
import { ViewMotionBlock } from '../../models/view-motion-block';
import { ViewTag } from 'app/site/tags/models/view-tag';
import { ViewWorkflow } from '../../models/view-workflow';
@ -274,4 +274,15 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
public getStateLabel(motion: ViewMotion): string {
return this.motionRepo.getExtendedStateLabel(motion);
}
/**
* Directly export all motions as pdf, using the current default config settings
*/
public directPdfExport(): void {
this.pdfExport.exportMotionCatalog(
this.dataSource.data,
this.configService.instant<string>('motions_default_line_numbering') as LineNumberingMode,
this.configService.instant<string>('motions_recommendation_text_mode') as ChangeRecoMode
);
}
}

View File

@ -108,6 +108,7 @@ export class LocalPermissionsService {
(motion.state &&
motion.state.allow_submitter_edit &&
motion.submitters &&
motion.submitters.length &&
motion.submitters.some(submitter => submitter.id === this.operator.user.id))
);
}
@ -123,6 +124,7 @@ export class LocalPermissionsService {
motion.state &&
motion.state.allow_submitter_edit &&
motion.submitters &&
motion.submitters.length &&
motion.submitters.some(submitter => submitter.id === this.operator.user.id)
);
}

View File

@ -13,7 +13,7 @@
</div>
</a>
</div>
<div class="column-right">
<div class="column-right" *osPerms="'core.can_manage_projector'">
<div class="control-group">
<div class="button-size">{{ projector.scroll }}</div>
<button type="button" mat-icon-button (click)="scroll(scrollScaleDirection.Up)">

View File

@ -1,4 +1,4 @@
<os-head-bar [nav]="true" [mainButton]="true" (mainEvent)="onPlusButton()">
<os-head-bar [nav]="true" [mainButton]="canManage" (mainEvent)="onPlusButton()">
<!-- Title -->
<div class="title-slot">
<h2 translate>Projectors</h2>
@ -44,7 +44,7 @@
<ng-container class="meta-text-block-title">
{{ projector.name | translate }}
</ng-container>
<ng-container class="meta-text-block-action-row">
<ng-container class="meta-text-block-action-row" *ngIf="canManage">
<button mat-icon-button *ngIf="editId !== projector.id" (click)=onEditButton(projector)>
<mat-icon>edit</mat-icon>
</button>

View File

@ -11,6 +11,7 @@ import { Projector } from 'app/shared/models/core/projector';
import { BaseViewComponent } from 'app/site/base/base-view';
import { PromptService } from 'app/core/ui-services/prompt.service';
import { ClockSlideService } from '../../services/clock-slide.service';
import { OperatorService } from 'app/core/core-services/operator.service';
/**
* All supported aspect rations for projectors.
@ -61,6 +62,15 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
*/
public projectors: ViewProjector[];
/**
* Helper to check manage permissions
*
* @returns true if the user can manage projectors
*/
public get canManage(): boolean {
return this.operator.hasPerms('core.can_manage_projector');
}
/**
* Constructor. Initializes all forms.
*
@ -70,6 +80,8 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
* @param repo
* @param formBuilder
* @param promptService
* @param clockSlideService
* @param operator OperatorService
*/
public constructor(
titleService: Title,
@ -78,7 +90,8 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
private repo: ProjectorRepositoryService,
private formBuilder: FormBuilder,
private promptService: PromptService,
private clockSlideService: ClockSlideService
private clockSlideService: ClockSlideService,
private operator: OperatorService
) {
super(titleService, translate, matSnackBar);

View File

@ -15,25 +15,33 @@
<!-- Menu -->
<div class="menu-slot">
<button type="button" mat-icon-button [matMenuTriggerFor]="userExtraMenu">
<button
type="button"
mat-icon-button
*ngIf="isAllowed('changePersonal')"
[matMenuTriggerFor]="userExtraMenu"
>
<mat-icon>more_vert</mat-icon>
</button>
</div>
<mat-menu #userExtraMenu="matMenu">
<button mat-menu-item class="red-warning-text" (click)="deleteUserButton()">
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
<button mat-menu-item (click)="changePassword()">
<button mat-menu-item *ngIf="isAllowed('changePersonal')" (click)="changePassword()">
<mat-icon>security</mat-icon>
<span translate>Change password</span>
</button>
<!-- PDF -->
<button mat-menu-item *ngIf="isAllowed('seePersonal')" (click)="onDownloadPdf()">
<button mat-menu-item *ngIf="isAllowed('manage')" (click)="onDownloadPdf()">
<mat-icon>picture_as_pdf</mat-icon>
<span translate>PDF</span>
</button>
<div *ngIf="isAllowed('delete')">
<mat-divider></mat-divider>
<button mat-menu-item class="red-warning-text" (click)="deleteUserButton()">
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
</div>
</mat-menu>
</os-head-bar>
@ -61,7 +69,6 @@
[value]="user.title"
/>
</mat-form-field>
<!-- First name -->
<mat-form-field
class="form37 distance force-min-with"
@ -97,7 +104,7 @@
placeholder="{{ 'Email' | translate }}"
name="email"
formControlName="email"
[value]="user.email"
[value]="user.email ? user.email: null"
/>
<mat-error *ngIf="personalInfoForm.get('email').hasError('email')" translate>
Please enter a valid email address
@ -126,10 +133,7 @@
</mat-form-field>
<!-- Participant Number -->
<mat-form-field
class="form25 force-min-with"
*ngIf="user.number || (editUser && isAllowed('manage'))"
>
<mat-form-field class="form25 force-min-with" *ngIf="user.number || (editUser && isAllowed('manage'))">
<input
type="text"
matInput
@ -144,7 +148,9 @@
<!-- Groups -->
<mat-form-field *ngIf="(user.groups && user.groups.length > 0) || editUser">
<mat-select placeholder="{{ 'Groups' | translate }}" formControlName="groups_id" multiple>
<mat-option *ngFor="let group of groups" [value]="group.id">{{ group.getTitle() | translate }}</mat-option>
<mat-option *ngFor="let group of groups" [value]="group.id">{{
group.getTitle() | translate
}}</mat-option>
</mat-select>
</mat-form-field>
</div>

View File

@ -166,6 +166,7 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
* correct permission to perform the given action.
*
* actions might be:
* - delete (deleting the user) (users.can_manage and not ownPage)
* - seeName (title, 1st, last) (user.can_see_name or ownPage)
* - seeExtra (checkboxes, comment) (user.can_see_extra_data)
* - seePersonal (mail, username, about) (user.can_see_extra_data or ownPage)
@ -176,6 +177,8 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
*/
public isAllowed(action: string): boolean {
switch (action) {
case 'delete':
return this.operator.hasPerms('users.can_manage') && !this.ownPage;
case 'manage':
return this.operator.hasPerms('users.can_manage');
case 'seeName':

View File

@ -1,4 +1,4 @@
<os-head-bar mainButton="true" (mainEvent)="onPlusButton()" [multiSelectMode]="isMultiSelect">
<os-head-bar [mainButton]="canAddUser" (mainEvent)="onPlusButton()" [multiSelectMode]="isMultiSelect">
<!-- Title -->
<div class="title-slot"><h2 translate>Participants</h2></div>
@ -50,7 +50,7 @@
<ng-container matColumnDef="group">
<mat-header-cell *matHeaderCellDef mat-sort-header>Group</mat-header-cell>
<mat-cell *matCellDef="let user">
<div class='groupsCell'>
<div class="groupsCell">
<span *ngIf="user.groups && user.groups.length">
<mat-icon>people</mat-icon>
{{ user.groups }}
@ -108,15 +108,14 @@
<button mat-menu-item (click)="pdfExportUserList()">
<mat-icon>picture_as_pdf</mat-icon>
<span translate>List of participants (PDF)</span>
</button>
<button mat-menu-item (click)="onDownloadAccessPdf()">
<button mat-menu-item *osPerms="'users.can_manage'" (click)="onDownloadAccessPdf()">
<mat-icon>picture_as_pdf</mat-icon>
<span translate>Access data (PDF)</span>
</button>
<button mat-menu-item (click)="csvExportUserList()">
<button mat-menu-item *osPerms="'users.can_manage'" (click)="csvExportUserList()">
<mat-icon>archive</mat-icon>
<span translate>Export as CSV</span>
</button>
@ -125,70 +124,72 @@
<mat-icon>cloud_upload</mat-icon>
<span translate>Import</span><span>&nbsp;...</span>
</button>
</div>
<div *ngIf="isMultiSelect">
<button mat-menu-item (click)="selectAll()">
<mat-icon>done_all</mat-icon>
<span translate>Select all</span>
</button>
<button mat-menu-item (click)="deselectAll()">
<mat-icon>clear</mat-icon>
<span translate>Deselect all</span>
</button>
<div *osPerms="'users.can_manage'">
<mat-divider></mat-divider>
<button mat-menu-item (click)="setGroupSelected()">
<mat-icon>people</mat-icon>
<span translate>Add/remove groups ...</span>
<div *ngIf="isMultiSelect">
<button mat-menu-item (click)="selectAll()">
<mat-icon>done_all</mat-icon>
<span translate>Select all</span>
</button>
<div *ngIf="presenceViewConfigured">
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="presence">
<mat-icon>transfer_within_a_station</mat-icon>
<span translate>Presence</span>
</button>
</div>
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="import">
<mat-icon>cloud_upload</mat-icon>
<span translate>Import</span><span>&nbsp;...</span>
<button mat-menu-item (click)="deselectAll()">
<mat-icon>clear</mat-icon>
<span translate>Deselect all</span>
</button>
<div *osPerms="'users.can_manage'">
<mat-divider></mat-divider>
<button mat-menu-item (click)="setActiveSelected()">
<mat-icon>block</mat-icon>
<span translate>Enable/disable account ...</span>
<button mat-menu-item (click)="setGroupSelected()">
<mat-icon>people</mat-icon>
<span translate>Add/remove groups ...</span>
</button>
<button mat-menu-item (click)="setPresentSelected()">
<mat-icon>check_box</mat-icon>
<span translate>Set presence ...</span>
<div *ngIf="presenceViewConfigured">
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="presence">
<mat-icon>transfer_within_a_station</mat-icon>
<span translate>Presence</span>
</button>
</div>
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="import">
<mat-icon>save_alt</mat-icon>
<span translate>Import</span><span>&nbsp;...</span>
</button>
<button mat-menu-item (click)="setCommitteeSelected()">
<mat-icon>account_balance</mat-icon>
<span translate>Set committee ...</span>
</button>
<div *osPerms="'users.can_manage'">
<mat-divider></mat-divider>
<mat-divider></mat-divider>
<button mat-menu-item (click)="setActiveSelected()">
<mat-icon>block</mat-icon>
<span translate>Enable/disable account ...</span>
</button>
<button mat-menu-item (click)="sendInvitationEmailSelected()">
<mat-icon>mail</mat-icon>
<span translate>Send invitation email</span>
</button>
<button mat-menu-item (click)="resetPasswordsSelected()">
<mat-icon>vpn_key</mat-icon>
<span translate>Generate new passwords</span>
</button>
<mat-divider></mat-divider>
<button mat-menu-item class="red-warning-text" (click)="deleteSelected()">
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
<button mat-menu-item (click)="setPresentSelected()">
<mat-icon>check_box</mat-icon>
<span translate>Set presence ...</span>
</button>
<button mat-menu-item (click)="setCommitteeSelected()">
<mat-icon>account_balance</mat-icon>
<span translate>Set committee ...</span>
</button>
<mat-divider></mat-divider>
<button mat-menu-item (click)="sendInvitationEmailSelected()">
<mat-icon>mail</mat-icon>
<span translate>Send invitation email</span>
</button>
<button mat-menu-item (click)="resetPasswordsSelected()">
<mat-icon>vpn_key</mat-icon>
<span translate>Generate new passwords</span>
</button>
<mat-divider></mat-divider>
<button mat-menu-item class="red-warning-text" (click)="deleteSelected()">
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
</div>
</div>
</div>
</div>

View File

@ -51,6 +51,15 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
return this._presenceViewConfigured;
}
/**
* Helper to check for main button permissions
*
* @returns true if the user should be able to create users
*/
public get canAddUser(): boolean {
return this.operator.hasPerms('users.can_manage');
}
/**
* The usual constructor for components
* @param titleService Serivce for setting the title