Skip to content
Snippets Groups Projects
Commit f89ec02f authored by Alex ORLUC's avatar Alex ORLUC
Browse files

FEAT #10733 TIME 3 add front group + handle redirect for back route (used in front route)

parent 81c61080
No related branches found
No related tags found
No related merge requests found
......@@ -239,6 +239,16 @@
"emailSendInProgress" : "Email send in progress",
"emailSendSuccess" : "Email send success",
"manage_groupsAlt" : "Group(s)",
"groupCreation" : "Add a group"
"groupCreation" : "Add a group",
"manage_usersAdmin" : "Manage users",
"manage_groupsAdmin" : "Manage groups",
"manage_connectionsAdmin" : "Manage connections",
"manage_email_configurationAdmin" : "Manage email server",
"manage_documentsAdmin" : "All access of documents",
"linkedUsers" : "Linked users",
"addUser" : "Add user",
"privileges" : "Privileges",
"groupWarnMsg" : "You may no longer be able to access this page!",
"groupDeleted" : "Group deleted"
}
}
......@@ -240,7 +240,16 @@
"emailSendInProgress" : "En cours d'envoi",
"emailSendSuccess" : "Envoi réussi",
"manage_groupsAlt" : "Groupe(s)",
"groupCreation" : "Ajouter un groupe"
"groupCreation" : "Ajouter un groupe",
"manage_usersAdmin" : "Administrer les utilisateurs",
"manage_groupsAdmin" : "Administrer les groupes",
"manage_connectionsAdmin" : "Administrer les connexions",
"manage_email_configurationAdmin" : "Administrer le serveur courriel",
"manage_documentsAdmin" : "Accéder à tous les documents",
"linkedUsers" : "Utilisateur(s) associé(s)",
"addUser" : "Ajouter un utilisateur",
"privileges" : "Privilèges",
"groupWarnMsg" : "Vous risquez de ne plus pouvoir accéder à cette page !",
"groupDeleted" : "Groupe supprimé"
}
}
<mat-sidenav-container autosize>
<mat-sidenav #snav [disableClose]="!signaturesService.mobileMode"
[mode]="signaturesService.mobileMode ? 'over': 'side'" fixedInViewport="true"
[opened]="!signaturesService.mobileMode" [style.width.px]="350">
<app-admin-sidebar [snavLeftComponent]="this.snav" [snavRightComponent]="this.snavRight"></app-admin-sidebar>
</mat-sidenav>
<mat-sidenav-content class="mainView">
<header class="header">
<div class="header-title">
<button *ngIf="signaturesService.mobileMode" mat-icon-button (click)="this.snav.toggle();">
<mat-icon fontSet="fas" fontIcon="fa-bars" style="font-size: 24px;"></mat-icon>
</button>
<span *ngIf="!loading">{{title}}</span>
</div>
<div *ngIf="!creationMode" style="width: 70px;text-align: center;">
<button mat-icon-button (click)="deleteGroup()">
<mat-icon class="fa fa-trash-alt fa-2x"></mat-icon>
</button>
</div>
</header>
<div class="container">
<div *ngIf="loading" class="loader">
<mat-spinner></mat-spinner>
</div>
<form class="admin-form" *ngIf="!loading" (ngSubmit)="onSubmit()" #adminForm="ngForm">
<mat-form-field class="input-row">
<input name="label" matInput placeholder="{{'lang.label' | translate}}" type="text" [(ngModel)]="group.label"
required>
</mat-form-field>
<fieldset *ngIf="!creationMode">
<legend align="left">{{'lang.privileges' | translate}} :</legend>
<div class="form-container">
<div class="privilegesList" *ngFor="let privilege of group.privileges">
<mat-slide-toggle [name]="privilege.id" color="primary" [(ngModel)]="privilege.checked" (change)="togglePrivilege(privilege)">{{'lang.' + privilege.id + 'Admin' | translate}}</mat-slide-toggle>
</div>
</div>
</fieldset>
<fieldset *ngIf="!creationMode">
<legend align="left">{{'lang.linkedUsers' | translate}} :</legend>
<div class="form-container">
<mat-table #table class="dataTable" [dataSource]="dataSource" matSort matSortActive="lastname"
matSortDirection="asc" [class.emptyTable]="dataSource.filteredData.length === 0">
<ng-container matColumnDef="firstname">
<mat-header-cell *matHeaderCellDef mat-sort-header>{{'lang.firstname' | translate}}</mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.firstname}} </mat-cell>
</ng-container>
<ng-container matColumnDef="lastname">
<mat-header-cell *matHeaderCellDef mat-sort-header>{{'lang.lastname' | translate}}</mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.lastname}} </mat-cell>
</ng-container>
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef style="justify-content: end;">
<mat-paginator #paginator [length]="100" [pageSize]="10"></mat-paginator>
</mat-header-cell>
<mat-cell *matCellDef="let element" style="justify-content: flex-end;">
<button mat-icon-button color="warn" type="button" (click)="$event.stopPropagation();unlinkUser(element)">
<mat-icon class="fa fa-unlink fa-2x"></mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;" routerLink="/administration/users/{{row.id}}"
style="cursor:pointer;"></mat-row>
</mat-table>
<div class="noResult" *ngIf="dataSource.filteredData.length === 0">
{{'lang.noResult' | translate}}
</div>
<div class="addButton">
<button mat-raised-button type="button" color="primary" [matMenuTriggerFor]="menu" [disabled]="usersList.length === group.users.length">{{'lang.addUser' | translate}}</button>
<mat-menu #menu="matMenu">
<ng-container *ngFor="let user of usersList">
<button mat-menu-item *ngIf="!isInGroup(user);" (click)="linkUser(user)">{{user.firstname}} {{user.lastname}}</button>
</ng-container>
</mat-menu>
</div>
</div>
</fieldset>
<div class="actions-form">
<button mat-stroked-button type="submit" class="btn blue"
[disabled]="!adminForm.form.valid || !canValidate()">{{'lang.validate' | translate}}</button>
<button mat-stroked-button type="button" class="btn" (click)="cancel()">{{'lang.cancel' | translate}}</button>
</div>
</form>
</div>
</mat-sidenav-content>
<mat-sidenav #snavRight disableClose [mode]="signaturesService.mobileMode ? 'over': 'side'" [opened]="false"
fixedInViewport="true" position='end'>
</mat-sidenav>
</mat-sidenav-container>
\ No newline at end of file
@import '../../../css/vars.scss';
.privilegesList {
padding-top: 10px;
padding-bottom: 10px;
display: inline-block;
width: 100%;
@media (min-width: 768px) {
width: 50%;
}
@media (min-width: 992px) {
width: 33%;
}
}
fieldset {
border: solid 1px #F1F4F4;
align-items: center;
margin-top: 30px;
}
legend {
color: #F99830;
}
import { Component, OnInit, ViewChild } from '@angular/core';
import { SignaturesContentService } from '../../service/signatures.service';
import { NotificationService } from '../../service/notification.service';
import { HttpClient } from '@angular/common/http';
import { MatDialog, MatTableDataSource, MatPaginator, MatSort } from '@angular/material';
import { map, tap, finalize, startWith } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
import { ConfirmComponent } from '../../plugins/confirm.component';
import { TranslateService } from '@ngx-translate/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
export interface Group {
id: string;
label: string;
users: any[];
privileges: any[];
}
@Component({
selector: 'app-administration-group',
templateUrl: 'group.component.html',
styleUrls: ['../administration.scss', 'group.component.scss'],
})
export class GroupComponent implements OnInit {
creationMode: boolean = true;
loading: boolean = true;
group: Group;
groupClone: Group;
title: string = '';
dataSource: MatTableDataSource<any>;
displayedColumns: string[];
usersList: any[];
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
constructor(public http: HttpClient, private translate: TranslateService, private route: ActivatedRoute, private router: Router, public signaturesService: SignaturesContentService, public notificationService: NotificationService, public dialog: MatDialog) {
this.displayedColumns = ['firstname', 'lastname', 'actions'];
this.group = {
id: '',
label: '',
users: [],
privileges: []
};
this.dataSource = new MatTableDataSource(this.group.users);
}
ngOnInit(): void {
this.route.params.subscribe((params: any) => {
if (params['id'] === undefined) {
this.creationMode = true;
this.title = this.translate.instant('lang.groupCreation');
this.loading = false;
this.groupClone = JSON.parse(JSON.stringify(this.group));
} else {
this.creationMode = false;
this.usersList = [];
this.http.get('../rest/groups/' + params['id'])
.pipe(
map((data: any) => data.group),
finalize(() => this.loading = false)
)
.subscribe({
next: data => {
this.group = data;
// FOR TESTS
this.group.privileges.forEach(element => {
element.checked = true;
});
this.groupClone = JSON.parse(JSON.stringify(this.group));
this.title = this.group.label;
this.updateDataTable();
},
});
this.http.get('../rest/users')
.pipe(
map((data: any) => data.users)
)
.subscribe({
next: data => {
this.usersList = data;
}
});
}
});
}
updateDataTable() {
this.dataSource.data = this.group.users;
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
canValidate() {
if (this.group.label === this.groupClone.label) {
return false;
} else {
return true;
}
}
onSubmit() {
if (this.creationMode) {
this.createGroup();
} else {
this.modifyGroup();
}
}
linkUser(user: any) {
this.http.put('../rest/groups/' + this.group.id + '/users', { userId: user.id })
.subscribe({
next: () => {
this.group.users.push(user);
this.updateDataTable();
this.notificationService.success('lang.userAdded');
},
});
}
unlinkUser(userToDelete: any) {
if (userToDelete.id === this.signaturesService.userLogged.id) {
const dialogRef = this.dialog.open(ConfirmComponent, { autoFocus: false, data: { mode: 'warning', title: 'lang.confirmMsg', msg: 'lang.groupWarnMsg' } });
dialogRef.afterClosed().subscribe(result => {
if (result === 'yes') {
this.deleteUser(userToDelete);
}
});
} else {
this.deleteUser(userToDelete);
}
}
deleteUser(userToDelete: any) {
this.http.delete('../rest/groups/' + this.group.id + '/users/' + userToDelete.id, {})
.subscribe({
next: () => {
const indexToDelete = this.group.users.findIndex((user: any) => user.id === userToDelete.id);
this.group.users.splice(indexToDelete, 1);
this.updateDataTable();
this.notificationService.success('lang.userDeleted');
},
});
}
modifyGroup() {
this.loading = true;
this.http.put('../rest/groups/' + this.group.id, this.group)
.subscribe({
next: () => {
this.router.navigate(['/administration/groups']);
this.notificationService.success('lang.groupUpdated');
},
});
}
createGroup() {
this.loading = true;
this.http.post('../rest/groups', this.group)
.subscribe({
next: () => {
this.router.navigate(['/administration/groups']);
this.notificationService.success('lang.groupAdded');
},
});
}
deleteGroup() {
const dialogRef = this.dialog.open(ConfirmComponent, { autoFocus: false, data: { mode: '', title: 'lang.confirmMsg', msg: '' } });
dialogRef.afterClosed().subscribe(result => {
if (result === 'yes') {
this.loading = true;
this.http.delete('../rest/groups/' + this.group.id)
.subscribe({
next: () => {
this.router.navigate(['/administration/groups']);
this.notificationService.success('lang.groupDeleted');
},
});
}
});
}
togglePrivilege(privilege: any) {
if (privilege.id === 'manage_groups') {
const dialogRef = this.dialog.open(ConfirmComponent, { autoFocus: false, data: { mode: 'warning', title: 'lang.confirmMsg', msg: 'lang.groupWarnMsg' } });
dialogRef.afterClosed().subscribe(result => {
if (result === 'yes') {
this.updatePrivilege(privilege);
} else {
privilege.checked = !privilege.checked;
}
});
} else {
this.updatePrivilege(privilege);
}
}
updatePrivilege(privilege: any) {
this.http.put('../rest/groups/' + this.group.id + '/privilege/' + privilege.id, { checked: privilege.checked })
.subscribe({
next: () => {
this.notificationService.success('lang.privilegeUpdated');
},
});
}
cancel() {
this.router.navigate(['/administration/groups']);
}
isInGroup(user: any) {
if (this.group.users.findIndex((userG: any) => userG.id === user.id) > -1) {
return true;
} else {
return false;
}
}
}
<mat-sidenav-container autosize>
<mat-sidenav #snav [disableClose]="!signaturesService.mobileMode" [mode]="signaturesService.mobileMode ? 'over': 'side'" fixedInViewport="true"
[opened]="!signaturesService.mobileMode" [style.width.px]="350">
<app-admin-sidebar [snavLeftComponent]="this.snav" [snavRightComponent]="this.snavRight"></app-admin-sidebar>
</mat-sidenav>
<mat-sidenav-content class="mainView">
<header class="header">
<div class="header-title">
<button *ngIf="signaturesService.mobileMode" mat-icon-button (click)="this.snav.toggle();">
<mat-icon fontSet="fas" fontIcon="fa-bars" style="font-size: 24px;"></mat-icon>
</button>
<span><b>{{groupList.length}}</b> {{'lang.manage_groupsAlt' | translate}}</span>
</div>
<div>
<mat-paginator #paginator [length]="100" [pageSize]="10"></mat-paginator>
</div>
</header>
<div class="container">
<div *ngIf="loading" class="loader">
<mat-spinner></mat-spinner>
</div>
<mat-table #table class="dataTable" [dataSource]="dataSource" matSort matSortActive="label"
matSortDirection="asc" [class.emptyTable]="dataSource.filteredData.length === 0">
<ng-container matColumnDef="label">
<mat-header-cell *matHeaderCellDef mat-sort-header>{{'lang.label' | translate}}</mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.label}} </mat-cell>
</ng-container>
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef style="justify-content: end;">
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filtrer">
</mat-form-field>
</mat-header-cell>
<mat-cell *matCellDef="let element" style="justify-content: flex-end;">
<button mat-icon-button color="warn" (click)="$event.stopPropagation();delete(element)">
<mat-icon class="fa fa-trash-alt fa-2x"></mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;" routerLink="/administration/groups/{{row.id}}"
style="cursor:pointer;"></mat-row>
</mat-table>
<div class="addButton">
<button mat-stroked-button type="button" class="btn blue" routerLink="/administration/groups/new">{{'lang.groupCreation' | translate}}</button>
</div>
<div class="noResult" *ngIf="dataSource.filteredData.length === 0">
{{'lang.noResult' | translate}}
</div>
</div>
</mat-sidenav-content>
<mat-sidenav #snavRight disableClose [mode]="signaturesService.mobileMode ? 'over': 'side'" [opened]="false"
fixedInViewport="true" position='end'>
</mat-sidenav>
</mat-sidenav-container>
\ No newline at end of file
@import '../../../css/vars.scss';
import { Component, OnInit, ViewChild } from '@angular/core';
import { SignaturesContentService } from '../../service/signatures.service';
import { NotificationService } from '../../service/notification.service';
import { HttpClient } from '@angular/common/http';
import { MatTableDataSource, MatPaginator, MatSort, MatDialog } from '@angular/material';
import { ConfirmComponent } from '../../plugins/confirm.component';
import { TranslateService } from '@ngx-translate/core';
import { map, tap, finalize } from 'rxjs/operators';
import { LatinisePipe } from 'ngx-pipes';
export interface Group {
id: string;
label: string;
}
@Component({
selector: 'app-administration-groups-list',
templateUrl: 'groups-list.component.html',
styleUrls: ['../administration.scss', 'groups-list.component.scss'],
})
export class GroupsListComponent implements OnInit {
groupList: Group[] = [];
dataSource: MatTableDataSource<Group>;
displayedColumns: string[];
loading: boolean = true;
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
constructor(public http: HttpClient, private translate: TranslateService, private latinisePipe: LatinisePipe, public dialog: MatDialog, public signaturesService: SignaturesContentService, public notificationService: NotificationService) {
this.displayedColumns = ['label', 'actions'];
this.dataSource = new MatTableDataSource(this.groupList);
this.dataSource.filterPredicate = (data, filter) => {
let state = false;
this.displayedColumns.forEach(column => {
if (data[column] !== undefined) {
const cleanData = this.latinisePipe.transform(data[column].trim().toLowerCase());
const cleanFilter = this.latinisePipe.transform(filter.trim().toLowerCase());
if (cleanData.indexOf(cleanFilter) !== -1) {
state = true;
}
}
});
return state;
};
}
updateDataTable() {
this.dataSource.data = this.groupList;
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue;
if (this.dataSource.paginator) {
this.dataSource.paginator.firstPage();
}
}
ngOnInit(): void {
this.http.get('../rest/groups')
.pipe(
map((data: any) => data.groups),
finalize(() => this.loading = false)
)
.subscribe({
next: data => {
this.groupList = data;
this.updateDataTable();
},
});
}
delete(groupToDelete: Group) {
const dialogRef = this.dialog.open(ConfirmComponent, { autoFocus: false, data: { mode: '', title: 'lang.confirmMsg', msg: '' } });
dialogRef.afterClosed().subscribe(result => {
if (result === 'yes') {
this.loading = true;
this.http.delete('../rest/groups/' + groupToDelete.id)
.pipe(
finalize(() => this.loading = false)
)
.subscribe({
next: data => {
const indexToDelete = this.groupList.findIndex(group => group.id === groupToDelete.id);
this.groupList.splice(indexToDelete, 1);
this.updateDataTable();
this.notificationService.success('lang.groupDeleted');
},
});
}
});
}
}
......@@ -21,7 +21,8 @@ import {
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatPaginatorIntl
MatPaginatorIntl,
MatAutocompleteModule
} from '@angular/material';
import { MatMenuModule } from '@angular/material/menu';
......@@ -53,7 +54,8 @@ import { getFrenchPaginatorIntl } from './plugins/paginator-fr-intl';
MatBadgeModule,
MatTableModule,
MatPaginatorModule,
MatSortModule
MatSortModule,
MatAutocompleteModule
],
exports: [
MatSidenavModule,
......@@ -77,7 +79,8 @@ import { getFrenchPaginatorIntl } from './plugins/paginator-fr-intl';
MatBadgeModule,
MatTableModule,
MatPaginatorModule,
MatSortModule
MatSortModule,
MatAutocompleteModule
],
providers: [
{ provide: MatPaginatorIntl, useValue: getFrenchPaginatorIntl() },
......
......@@ -62,6 +62,8 @@ import { ConnectionComponent } from './administration/connection/connection.comp
import { LdapListComponent } from './administration/connection/ldap/ldap-list.component';
import { LdapComponent } from './administration/connection/ldap/ldap.component';
import { SendmailComponent } from './administration/sendmail/sendmail.component';
import { GroupsListComponent } from './administration/group/groups-list.component';
import { GroupComponent } from './administration/group/group.component';
// SERVICES
......@@ -109,7 +111,9 @@ import { AlertComponent } from './plugins/alert.component';
ConnectionComponent,
LdapListComponent,
LdapComponent,
SendmailComponent
SendmailComponent,
GroupsListComponent,
GroupComponent
],
imports: [
FormsModule,
......@@ -136,6 +140,9 @@ import { AlertComponent } from './plugins/alert.component';
{ path: 'administration/users', canActivate: [AuthGuard], component: UsersListComponent },
{ path: 'administration/users/new', canActivate: [AuthGuard], component: UserComponent },
{ path: 'administration/users/:id', canActivate: [AuthGuard], component: UserComponent },
{ path: 'administration/groups', canActivate: [AuthGuard], component: GroupsListComponent },
{ path: 'administration/groups/new', canActivate: [AuthGuard], component: GroupComponent },
{ path: 'administration/groups/:id', canActivate: [AuthGuard], component: GroupComponent },
{ path: 'administration/connections', canActivate: [AuthGuard], component: ConnectionComponent },
{ path: 'administration/connections/ldaps', canActivate: [AuthGuard], component: LdapListComponent },
{ path: 'administration/connections/ldaps/new', canActivate: [AuthGuard], component: LdapComponent },
......
......@@ -12,6 +12,8 @@ import { AuthService } from './auth.service';
export class AuthInterceptor implements HttpInterceptor {
excludeUrls: string[] = ['../rest/authenticate', '../rest/authenticate/token', '../rest/authenticationInformations', '../rest/password', '../rest/passwordRules', '../rest/languages/fr', '../rest/languages/en'];
frontUrl: string[] = ['../rest/documents/', '../rest/users/', '../rest/groups/', '../rest/configurations/'];
constructor(public http: HttpClient, private router: Router, public notificationService: NotificationService, public signaturesService: SignaturesContentService, public authService: AuthService) { }
addAuthHeader(request: HttpRequest<any>) {
......@@ -31,7 +33,6 @@ export class AuthInterceptor implements HttpInterceptor {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
// We don't want to intercept some routes
if (this.excludeUrls.indexOf(request.url) > -1 || request.url.indexOf('/password') > -1) {
return next.handle(request);
......@@ -82,9 +83,16 @@ export class AuthInterceptor implements HttpInterceptor {
})
);
} else {
if (request.url.indexOf('../rest/documents/') > -1) {
this.router.navigate(['/documents']);
this.signaturesService.mainDocumentId = null;
if (request.method === 'GET') {
this.frontUrl.forEach(element => {
if (request.url.indexOf(element) > -1) {
if (element === '../rest/documents/') {
this.signaturesService.mainDocumentId = null;
}
this.router.navigate(['/documents']);
return empty();
}
});
}
this.notificationService.handleErrors(error);
return empty();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment