Add permissions to ListViews
Adds the AuthGuard to certain routes Adds an error-component Also hides certain other elements where permissions should apply
This commit is contained in:
parent
028c358a7f
commit
bd33c59ddf
@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild } from '@angular/router';
|
||||
import { CanActivate, ActivatedRouteSnapshot, CanActivateChild, Router } from '@angular/router';
|
||||
|
||||
import { OperatorService } from './operator.service';
|
||||
|
||||
@ -11,9 +11,12 @@ import { OperatorService } from './operator.service';
|
||||
})
|
||||
export class AuthGuard implements CanActivate, CanActivateChild {
|
||||
/**
|
||||
* @param operator
|
||||
* Constructor
|
||||
*
|
||||
* @param router To navigate to a target URL
|
||||
* @param operator Asking for the required permission
|
||||
*/
|
||||
public constructor(private operator: OperatorService) {}
|
||||
public constructor(private router: Router, private operator: OperatorService) {}
|
||||
|
||||
/**
|
||||
* Checks of the operator has the required permission to see the state.
|
||||
@ -22,10 +25,9 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
||||
* `data: {basePerm: ['<perm1>', '<perm2>']}` to lock the access to users
|
||||
* only with the given permission(s).
|
||||
*
|
||||
* @param route required by `canActivate()`
|
||||
* @param state the state (URL) that the user want to access
|
||||
* @param route the route the user wants to navigate to
|
||||
*/
|
||||
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||
public canActivate(route: ActivatedRouteSnapshot): boolean {
|
||||
const basePerm: string | string[] = route.data.basePerm;
|
||||
|
||||
if (!basePerm) {
|
||||
@ -39,10 +41,19 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
||||
|
||||
/**
|
||||
* Calls {@method canActivate}. Should have the same logic.
|
||||
* @param route
|
||||
* @param state
|
||||
*
|
||||
* @param route the route the user wants to navigate to
|
||||
*/
|
||||
public canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||
return this.canActivate(route, state);
|
||||
public canActivateChild(route: ActivatedRouteSnapshot): boolean {
|
||||
if (this.canActivate(route)) {
|
||||
return true;
|
||||
} else {
|
||||
this.router.navigate(['/error'], {
|
||||
queryParams: {
|
||||
error: 'Authentication Error',
|
||||
msg: route.data.basePerm
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,17 @@ import { WatchSortingTreeGuard } from 'app/shared/utils/watch-sorting-tree.guard
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: AgendaListComponent, pathMatch: 'full' },
|
||||
{ path: 'import', component: AgendaImportListComponent },
|
||||
{ path: 'topics/new', component: TopicDetailComponent },
|
||||
{ path: 'sort-agenda', component: AgendaSortComponent, canDeactivate: [WatchSortingTreeGuard] },
|
||||
{ path: 'speakers', component: ListOfSpeakersComponent },
|
||||
{ path: 'topics/:id', component: TopicDetailComponent },
|
||||
{ path: ':id/speakers', component: ListOfSpeakersComponent }
|
||||
{ path: 'import', component: AgendaImportListComponent, data: { basePerm: 'agenda.can_manage' } },
|
||||
{ path: 'topics/new', component: TopicDetailComponent, data: { basePerm: 'agenda.can_manage' } },
|
||||
{
|
||||
path: 'sort-agenda',
|
||||
component: AgendaSortComponent,
|
||||
canDeactivate: [WatchSortingTreeGuard],
|
||||
data: { basePerm: 'agenda.can_manage' }
|
||||
},
|
||||
{ path: 'speakers', component: ListOfSpeakersComponent, data: { basePerm: 'agenda.can_see' } },
|
||||
{ path: 'topics/:id', component: TopicDetailComponent, data: { basePerm: 'agenda.can_see' } },
|
||||
{ path: ':id/speakers', component: ListOfSpeakersComponent, data: { basePerm: 'agenda.can_see' } }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -12,6 +12,7 @@
|
||||
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<mat-drawer-container class="on-transition-fade">
|
||||
<os-sort-filter-bar
|
||||
[filterCount]="filteredCount"
|
||||
|
@ -5,12 +5,14 @@ import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-poli
|
||||
import { StartComponent } from './components/start/start.component';
|
||||
import { LegalNoticeComponent } from './components/legal-notice/legal-notice.component';
|
||||
import { SearchComponent } from './components/search/search.component';
|
||||
import { ErrorComponent } from './components/error/error.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: StartComponent,
|
||||
pathMatch: 'full'
|
||||
pathMatch: 'full',
|
||||
data: { basePerm: 'core.can_see_frontpage' }
|
||||
},
|
||||
{
|
||||
path: 'legalnotice',
|
||||
@ -23,6 +25,10 @@ const routes: Routes = [
|
||||
{
|
||||
path: 'search',
|
||||
component: SearchComponent
|
||||
},
|
||||
{
|
||||
path: 'error',
|
||||
component: ErrorComponent
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
<os-head-bar>
|
||||
<div class="title-slot">
|
||||
<h2 translate>Error</h2>
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<mat-card class="os-card on-transition-fade">
|
||||
<h1 translate>You do not have the required permission to see that page!</h1>
|
||||
</mat-card>
|
@ -0,0 +1,26 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ErrorComponent } from './error.component';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
describe('ErrorComponent', () => {
|
||||
let component: ErrorComponent;
|
||||
let fixture: ComponentFixture<ErrorComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ErrorComponent],
|
||||
imports: [E2EImportsModule]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ErrorComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,32 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
/**
|
||||
* A component to show error states
|
||||
*/
|
||||
@Component({
|
||||
selector: 'os-error',
|
||||
templateUrl: './error.component.html',
|
||||
styleUrls: ['./error.component.scss']
|
||||
})
|
||||
export class ErrorComponent implements OnInit {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param route get paramters
|
||||
*/
|
||||
public constructor(private route: ActivatedRoute) {}
|
||||
|
||||
/**
|
||||
* Show the required debug output in the log
|
||||
*/
|
||||
public ngOnInit(): void {
|
||||
this.route.queryParams.subscribe(params => {
|
||||
if (params && params.error) {
|
||||
// print the error and the error message in terminal for debug purposes.
|
||||
// Will make it easier tell where user errors are
|
||||
console.error(`${params.error}! Required: "${params.msg}"`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -7,6 +7,6 @@
|
||||
<mat-card class="os-card">
|
||||
<div class="app-content" translate>
|
||||
<h1>{{ welcomeTitle | translate }}</h1>
|
||||
<div [innerHTML]="(welcomeText) | translate"></div>
|
||||
<div [innerHTML]="welcomeText | translate"></div>
|
||||
</div>
|
||||
</mat-card>
|
||||
|
@ -8,9 +8,17 @@ import { StartComponent } from './components/start/start.component';
|
||||
import { LegalNoticeComponent } from './components/legal-notice/legal-notice.component';
|
||||
import { SearchComponent } from './components/search/search.component';
|
||||
import { CountUsersComponent } from './components/count-users/count-users.component';
|
||||
import { ErrorComponent } from './components/error/error.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, CommonRoutingModule, SharedModule],
|
||||
declarations: [PrivacyPolicyComponent, StartComponent, LegalNoticeComponent, SearchComponent, CountUsersComponent]
|
||||
declarations: [
|
||||
PrivacyPolicyComponent,
|
||||
StartComponent,
|
||||
LegalNoticeComponent,
|
||||
SearchComponent,
|
||||
CountUsersComponent,
|
||||
ErrorComponent
|
||||
]
|
||||
})
|
||||
export class OsCommonModule {}
|
||||
|
@ -17,6 +17,7 @@
|
||||
<mat-icon matSuffix>search</mat-icon>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<mat-table class="os-headed-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
||||
<!-- Timestamp -->
|
||||
<ng-container matColumnDef="time">
|
||||
|
@ -1,5 +1,5 @@
|
||||
<os-head-bar
|
||||
[mainButton]="canEdit"
|
||||
[mainButton]="canUploadFiles"
|
||||
[editMode]="editFile"
|
||||
[multiSelectMode]="isMultiSelect"
|
||||
(mainEvent)="onMainEvent()"
|
||||
@ -38,7 +38,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Menu -->
|
||||
<div class="menu-slot" *ngIf="canEdit">
|
||||
<div class="menu-slot" *osPerms="'mediafiles.can_manage'">
|
||||
<button type="button" mat-icon-button [matMenuTriggerFor]="mediafilesMenu">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
@ -142,71 +142,68 @@
|
||||
</mat-table>
|
||||
|
||||
<mat-paginator class="on-transition-fade" [pageSizeOptions]="pageSize"></mat-paginator>
|
||||
</mat-drawer-container>
|
||||
|
||||
<mat-menu #singleFileMenu="matMenu">
|
||||
<ng-template matMenuContent let-file="file">
|
||||
<!-- Exclusive for images -->
|
||||
<div *ngIf="file.isImage()">
|
||||
<div *ngFor="let action of logoActions">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="manageButton; context: { file: file, action: action }"
|
||||
></ng-container>
|
||||
</div>
|
||||
<!-- Template for the managing buttons -->
|
||||
<ng-template #manageButton let-file="file" let-action="action">
|
||||
<button mat-menu-item (click)="onManageButton($event, file, action)">
|
||||
<mat-icon color="accent"> {{ isUsedAs(file, action) ? 'check_box' : 'check_box_outline_blank' }} </mat-icon>
|
||||
<span>{{ getNameOfAction(action) }}</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<!-- Menu for single files in the list -->
|
||||
<mat-menu #singleFileMenu="matMenu">
|
||||
<ng-template matMenuContent let-file="file">
|
||||
<!-- Exclusive for images -->
|
||||
<div *ngIf="file.isImage()">
|
||||
<div *ngFor="let action of logoActions">
|
||||
<ng-container *ngTemplateOutlet="manageButton; context: { file: file, action: action }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Exclusive for fonts -->
|
||||
<div *ngIf="file.isFont()">
|
||||
<div *ngFor="let action of fontActions">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="manageButton; context: { file: file, action: action }"
|
||||
></ng-container>
|
||||
</div>
|
||||
<!-- Exclusive for fonts -->
|
||||
<div *ngIf="file.isFont()">
|
||||
<div *ngFor="let action of fontActions">
|
||||
<ng-container *ngTemplateOutlet="manageButton; context: { file: file, action: action }"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit and delete for all images -->
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item (click)="onEditFile(file)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
<span translate>Edit</span>
|
||||
</button>
|
||||
<button mat-menu-item class="red-warning-text" (click)="onDelete(file)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span translate>Delete</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
|
||||
<!-- Template for the managing buttons -->
|
||||
<ng-template #manageButton let-file="file" let-action="action">
|
||||
<button mat-menu-item (click)="onManageButton($event, file, action)">
|
||||
<mat-icon color="accent"> {{ isUsedAs(file, action) ? 'check_box' : 'check_box_outline_blank' }} </mat-icon>
|
||||
<span>{{ getNameOfAction(action) }}</span>
|
||||
<!-- Edit and delete for all images -->
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item (click)="onEditFile(file)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
<span translate>Edit</span>
|
||||
</button>
|
||||
<button mat-menu-item class="red-warning-text" (click)="onDelete(file)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span translate>Delete</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
|
||||
<!-- Menu for Mediafiles -->
|
||||
<mat-menu #mediafilesMenu="matMenu">
|
||||
<div *ngIf="!isMultiSelect">
|
||||
<button mat-menu-item *osPerms="'mediafiles.can_manage'" (click)="toggleMultiSelect()">
|
||||
<mat-icon>library_add</mat-icon>
|
||||
<span translate>Multiselect</span>
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="isMultiSelect">
|
||||
<mat-divider></mat-divider>
|
||||
<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>
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item *osPerms="'mediafiles.can_manage'" (click)="deleteSelected()">
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span translate>Delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</mat-drawer-container>
|
||||
<!-- Menu for Mediafiles -->
|
||||
<mat-menu #mediafilesMenu="matMenu">
|
||||
<div *ngIf="!isMultiSelect">
|
||||
<button mat-menu-item *osPerms="'mediafiles.can_manage'" (click)="toggleMultiSelect()">
|
||||
<mat-icon>library_add</mat-icon>
|
||||
<span translate>Multiselect</span>
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="isMultiSelect">
|
||||
<mat-divider></mat-divider>
|
||||
<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>
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item *osPerms="'mediafiles.can_manage'" (click)="deleteSelected()">
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span translate>Delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
|
@ -60,6 +60,13 @@ export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile,
|
||||
/**
|
||||
* @returns true if the user can manage media files
|
||||
*/
|
||||
public get canUploadFiles(): boolean {
|
||||
return this.operator.hasPerms('mediafiles.can_see') && this.operator.hasPerms('mediafiles.can_upload');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the user can manage media files
|
||||
*/
|
||||
public get canEdit(): boolean {
|
||||
return this.operator.hasPerms('mediafiles.can_manage');
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { MediaUploadComponent } from './components/media-upload/media-upload.com
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: MediafileListComponent, pathMatch: 'full' },
|
||||
{ path: 'upload', component: MediaUploadComponent }
|
||||
{ path: 'upload', component: MediaUploadComponent, data: { basePerm: 'mediafiles.can_upload' } }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -704,7 +704,12 @@
|
||||
listname="{{ 'Attachments' | translate }}"
|
||||
[InputListValues]="mediafilesObserver"
|
||||
></os-search-value-selector>
|
||||
<button type="button" mat-icon-button (click)="onUploadAttachmentsButton(uploadDialog)">
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
(click)="onUploadAttachmentsButton(uploadDialog)"
|
||||
*osPerms="'mediafiles.can_upload'"
|
||||
>
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -9,40 +9,49 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'import',
|
||||
loadChildren: './modules/motion-import/motion-import.module#MotionImportModule'
|
||||
loadChildren: './modules/motion-import/motion-import.module#MotionImportModule',
|
||||
data: { basePerm: 'motions.can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'statute-paragraphs',
|
||||
loadChildren: './modules/statute-paragraph/statute-paragraph.module#StatuteParagraphModule'
|
||||
loadChildren: './modules/statute-paragraph/statute-paragraph.module#StatuteParagraphModule',
|
||||
data: { basePerm: 'motions.can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'comment-section',
|
||||
loadChildren: './modules/motion-comment-section/motion-comment-section.module#MotionCommentSectionModule'
|
||||
loadChildren: './modules/motion-comment-section/motion-comment-section.module#MotionCommentSectionModule',
|
||||
data: { basePerm: 'motions.can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'call-list',
|
||||
loadChildren: './modules/call-list/call-list.module#CallListModule'
|
||||
loadChildren: './modules/call-list/call-list.module#CallListModule',
|
||||
data: { basePerm: 'motions.can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'category',
|
||||
loadChildren: './modules/category/category.module#CategoryModule'
|
||||
loadChildren: './modules/category/category.module#CategoryModule',
|
||||
data: { basePerm: 'motions.can_see' }
|
||||
},
|
||||
{
|
||||
path: 'blocks',
|
||||
loadChildren: './modules/motion-block/motion-block.module#MotionBlockModule'
|
||||
loadChildren: './modules/motion-block/motion-block.module#MotionBlockModule',
|
||||
data: { basePerm: 'motions.can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'workflow',
|
||||
loadChildren: './modules/motion-workflow/motion-workflow.module#MotionWorkflowModule'
|
||||
loadChildren: './modules/motion-workflow/motion-workflow.module#MotionWorkflowModule',
|
||||
data: { basePerm: 'motions.can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
loadChildren: './modules/motion-detail/motion-detail.module#MotionDetailModule'
|
||||
loadChildren: './modules/motion-detail/motion-detail.module#MotionDetailModule',
|
||||
data: { basePerm: 'motions.can_create' }
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
loadChildren: './modules/motion-detail/motion-detail.module#MotionDetailModule',
|
||||
runGuardsAndResolvers: 'paramsChange'
|
||||
runGuardsAndResolvers: 'paramsChange',
|
||||
data: { basePerm: 'motions.can_see' }
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -6,9 +6,7 @@
|
||||
</os-head-bar>
|
||||
|
||||
<mat-card *ngIf="!projectorToCreate && projectors && projectors.length > 1">
|
||||
<span translate>
|
||||
Reference projector for current list of speakers: </span
|
||||
>
|
||||
<span translate> Reference projector for current list of speakers: </span>
|
||||
<mat-form-field>
|
||||
<mat-select
|
||||
[disabled]="!!editId"
|
||||
|
@ -11,7 +11,8 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'detail/:id',
|
||||
component: ProjectorDetailComponent
|
||||
component: ProjectorDetailComponent,
|
||||
data: { basePerm: 'core.can_see_projector' }
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -20,39 +20,48 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'agenda',
|
||||
loadChildren: './agenda/agenda.module#AgendaModule'
|
||||
loadChildren: './agenda/agenda.module#AgendaModule',
|
||||
data: { basePerm: 'agenda.can_see' }
|
||||
},
|
||||
{
|
||||
path: 'assignments',
|
||||
loadChildren: './assignments/assignments.module#AssignmentsModule'
|
||||
loadChildren: './assignments/assignments.module#AssignmentsModule',
|
||||
data: { basePerm: 'assignment.can_see' }
|
||||
},
|
||||
{
|
||||
path: 'mediafiles',
|
||||
loadChildren: './mediafiles/mediafiles.module#MediafilesModule'
|
||||
loadChildren: './mediafiles/mediafiles.module#MediafilesModule',
|
||||
data: { basePerm: 'mediafiles.can_see' }
|
||||
},
|
||||
{
|
||||
path: 'motions',
|
||||
loadChildren: './motions/motions.module#MotionsModule'
|
||||
loadChildren: './motions/motions.module#MotionsModule',
|
||||
data: { basePerm: 'motions.can_see' }
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
loadChildren: './config/config.module#ConfigModule'
|
||||
loadChildren: './config/config.module#ConfigModule',
|
||||
data: { basePerm: 'core.can_manage_config' }
|
||||
},
|
||||
{
|
||||
path: 'users',
|
||||
loadChildren: './users/users.module#UsersModule'
|
||||
loadChildren: './users/users.module#UsersModule',
|
||||
data: { basePerm: 'users.can_see_name' }
|
||||
},
|
||||
{
|
||||
path: 'tags',
|
||||
loadChildren: './tags/tag.module#TagModule'
|
||||
loadChildren: './tags/tag.module#TagModule',
|
||||
data: { basePerm: 'core.can_manage_tags' }
|
||||
},
|
||||
{
|
||||
path: 'history',
|
||||
loadChildren: './history/history.module#HistoryModule'
|
||||
loadChildren: './history/history.module#HistoryModule',
|
||||
data: { basePerm: 'core.can_see_history' }
|
||||
},
|
||||
{
|
||||
path: 'projectors',
|
||||
loadChildren: './projector/projector.module#ProjectorModule'
|
||||
loadChildren: './projector/projector.module#ProjectorModule',
|
||||
data: { basePerm: 'core.can_see_projector' }
|
||||
}
|
||||
],
|
||||
canActivateChild: [AuthGuard]
|
||||
|
@ -105,17 +105,63 @@
|
||||
</mat-table>
|
||||
|
||||
<mat-paginator class="on-transition-fade" [pageSizeOptions]="pageSize"></mat-paginator>
|
||||
</mat-drawer-container>
|
||||
|
||||
<mat-menu #userMenu="matMenu">
|
||||
<div *ngIf="!isMultiSelect">
|
||||
<button mat-menu-item *osPerms="'users.can_manage'" (click)="toggleMultiSelect()">
|
||||
<mat-icon>library_add</mat-icon>
|
||||
<span translate>Multiselect</span>
|
||||
<mat-menu #userMenu="matMenu">
|
||||
<div *ngIf="!isMultiSelect">
|
||||
<button mat-menu-item *osPerms="'users.can_manage'" (click)="toggleMultiSelect()">
|
||||
<mat-icon>library_add</mat-icon>
|
||||
<span translate>Multiselect</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="groups">
|
||||
<mat-icon>people</mat-icon>
|
||||
<span translate>Groups</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="groups">
|
||||
<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 *osPerms="'users.can_manage'" (click)="onDownloadAccessPdf()">
|
||||
<mat-icon>picture_as_pdf</mat-icon>
|
||||
<span translate>Access data (PDF)</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item *osPerms="'users.can_manage'" (click)="csvExportUserList()">
|
||||
<mat-icon>archive</mat-icon>
|
||||
<span translate>Export as CSV</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="import">
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
<span translate>Import</span><span> ...</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>Groups</span>
|
||||
<span translate>Add/remove groups ...</span>
|
||||
</button>
|
||||
|
||||
<div *ngIf="presenceViewConfigured">
|
||||
@ -125,95 +171,49 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<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 *osPerms="'users.can_manage'" (click)="onDownloadAccessPdf()">
|
||||
<mat-icon>picture_as_pdf</mat-icon>
|
||||
<span translate>Access data (PDF)</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item *osPerms="'users.can_manage'" (click)="csvExportUserList()">
|
||||
<mat-icon>archive</mat-icon>
|
||||
<span translate>Export as CSV</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item *osPerms="'users.can_manage'" routerLink="import">
|
||||
<mat-icon>cloud_upload</mat-icon>
|
||||
<mat-icon>save_alt</mat-icon>
|
||||
<span translate>Import</span><span> ...</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>
|
||||
|
||||
<button mat-menu-item (click)="setActiveSelected()">
|
||||
<mat-icon>block</mat-icon>
|
||||
<span translate>Enable/disable account ...</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>save_alt</mat-icon>
|
||||
<span translate>Import</span><span> ...</span>
|
||||
<button mat-menu-item (click)="setPresentSelected()">
|
||||
<mat-icon>check_box</mat-icon>
|
||||
<span translate>Set presence ...</span>
|
||||
</button>
|
||||
|
||||
<div *osPerms="'users.can_manage'">
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item (click)="setCommitteeSelected()">
|
||||
<mat-icon>account_balance</mat-icon>
|
||||
<span translate>Set committee ...</span>
|
||||
</button>
|
||||
|
||||
<button mat-menu-item (click)="setActiveSelected()">
|
||||
<mat-icon>block</mat-icon>
|
||||
<span translate>Enable/disable account ...</span>
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<button mat-menu-item (click)="setPresentSelected()">
|
||||
<mat-icon>check_box</mat-icon>
|
||||
<span translate>Set presence ...</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)="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>
|
||||
<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>
|
||||
</mat-menu>
|
||||
</mat-drawer-container>
|
||||
</div>
|
||||
</mat-menu>
|
||||
|
||||
<ng-template #userInfoDialog>
|
||||
<h1 mat-dialog-title>
|
||||
|
@ -16,39 +16,39 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'password',
|
||||
component: PasswordComponent
|
||||
component: PasswordComponent,
|
||||
data: { basePerm: 'users.can_change_password' }
|
||||
},
|
||||
{
|
||||
path: 'password/:id',
|
||||
component: PasswordComponent
|
||||
component: PasswordComponent,
|
||||
data: { basePerm: 'can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
component: UserDetailComponent
|
||||
component: UserDetailComponent,
|
||||
data: { basePerm: 'users.can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'import',
|
||||
component: UserImportListComponent
|
||||
component: UserImportListComponent,
|
||||
data: { basePerm: 'users.can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'presence',
|
||||
component: PresenceDetailComponent
|
||||
// FIXME: CRITICAL: restricted to basePerm: 'users.can_manage' and config 'users_enable_presence_view'
|
||||
component: PresenceDetailComponent,
|
||||
// TODO: 'users_enable_presence_view' missing in permissions
|
||||
data: { basePerm: 'users.can_manage' }
|
||||
},
|
||||
{
|
||||
path: 'groups',
|
||||
component: GroupListComponent
|
||||
/**
|
||||
* FIXME: CRITICAL:
|
||||
* Refreshing the page, even while having the required permission, will navigate you back to "/"
|
||||
* Makes developing protected areas impossible.
|
||||
* Has the be (temporarily) removed if this page should be edited.
|
||||
*/
|
||||
// data: { basePerm: 'users.can_manage' }
|
||||
component: GroupListComponent,
|
||||
data: { basePerm: 'users.can_manage' }
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
component: UserDetailComponent
|
||||
component: UserDetailComponent,
|
||||
data: { basePerm: 'users.can_see_name' }
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -83,7 +83,7 @@ class MotionChangeRecommendationAccessPermissions(BaseAccessPermissions):
|
||||
"""
|
||||
# Parse data.
|
||||
if await async_has_perm(user_id, "motions.can_see"):
|
||||
has_manage_perms = await async_has_perm(user_id, "motion.can_manage")
|
||||
has_manage_perms = await async_has_perm(user_id, "motions.can_manage")
|
||||
data = []
|
||||
for full in full_data:
|
||||
if not full["internal"] or has_manage_perms:
|
||||
|
Loading…
Reference in New Issue
Block a user