</div>
</div>
-<ng-container *ngIf="!loading">
- <h3 i18n>Working Location(s)</h3>
-
- <div class="striped-rows-odd mt-3 mb-3">
- <div class="row mt-2 pt-2 pb-2" *ngFor="let org of workableOrgs">
- <div class="col-lg-12">
- <div class="form-check form-check-inline">
- <input class="form-check-input" type="checkbox" id="work-org-{{org.id()}}"
- [disabled]="!canAssignWorkOrgs[org.id()]" [(ngModel)]="workOuSelector[org.id()]">
- <label class="form-check-label" for="work-org-{{org.id()}}" i18n>
- {{org.shortname()}} ({{org.name()}})
- </label>
+<ul ngbNav #permsNav="ngbNav" class="nav-tabs">
+
+ <li ngbNavItem>
+ <a ngbNavLink i18n>Working Location(s)</a>
+ <ng-template ngbNavContent>
+
+ <div class="striped-rows-odd mt-3 mb-3">
+ <div class="row mt-2 pt-2 pb-2" *ngFor="let org of workableOrgs">
+ <div class="col-lg-12">
+ <div class="form-check form-check-inline">
+ <input class="form-check-input" type="checkbox" id="work-org-{{org.id()}}"
+ [ngModel]="userHasWorkOu(org.id())"
+ (ngModelChange)="userWorkOuChange(org.id(), $event)"
+ [disabled]="!canAssignWorkOrgs[org.id()]"/>
+ <label class="form-check-label" for="work-org-{{org.id()}}" i18n>
+ {{org.shortname()}} ({{org.name()}})
+ </label>
+ </div>
+ </div>
</div>
</div>
- </div>
- </div>
-
- <hr class="mt-2 mb-2"/>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink i18n>User Permissions</a>
+ <ng-template ngbNavContent>
- <h3 i18n>User Permissions</h3>
+ <div class="striped-rows-odd mt-3">
+ <div class="row pt-1 pb-1">
+ <div class="col-lg-5 font-weight-bold" i18n>Permission</div>
+ <div class="col-lg-2 font-weight-bold" i18n>Applied</div>
+ <div class="col-lg-2 font-weight-bold" i18n>Depth</div>
+ <div class="col-lg-2 font-weight-bold" i18n>Grantable</div>
+ </div>
+ <div class="row pt-1 pb-1" *ngFor="let perm of allPerms">
+ <div class="col-lg-5">{{perm.code()}}</div>
+ <div class="col-lg-2">
+ <input class="form-check-input ml-0 pl-0" type="checkbox"
+ [disabled]="!canGrantPerm(perm)"
+ [ngModel]="userHasPerm(perm)"
+ (ngModelChange)="permApplyChanged(perm, $event)"/>
+ </div>
+ <div class="col-lg-2">
+ <select class="form-control"
+ [ngModel]="userHasPermAtDepth(perm)"
+ (ngModelChange)="permDepthChanged(perm, $event)">
+ <ng-container *ngFor="let depth of orgDepths">
+ <option [disabled]="depth < canGrantPermAtDepth(perm)"
+ [value]="depth">{{depth}}</option>
+ </ng-container>
+ </select>
+ </div>
+ <div class="col-lg-2">
+ <input class="form-check-input ml-0 pl-0" type="checkbox"
+ [disabled]="!canGrantPerm(perm)"
+ [ngModel]="userPermIsGrantable(perm)"
+ (ngModelChange)="grantableChanged(perm, $event)"/>
+ </div>
+ </div>
+ </div>
+ </ng-template>
+ </li>
+</ul>
- <div class="striped-rows-odd mt-3">
- <div class="row pt-1 pb-1">
- <div class="col-lg-5 font-weight-bold" i18n>Permission</div>
- <div class="col-lg-1 font-weight-bold" i18n>Applied</div>
- <div class="col-lg-2 font-weight-bold" i18n>Depth</div>
- <div class="col-lg-2 font-weight-bold" i18n>Grantable</div>
+<ng-container *ngIf="!loading">
+ <div class="d-flex w-100 mt-2 mb-2">
+ <div class="ml-auto">
+ <button class="btn btn-success" (click)="save()"
+ [disabled]="cannotSave()" i18n>Apply Changes</button>
</div>
- <div class="row pt-1 pb-1" *ngFor="let perm of allPerms">
- <div class="col-lg-5">{{perm.code()}}</div>
- <div class="col-lg-1">
- <input class="form-check-input ml-0 pl-0" type="checkbox"
- [disabled]="!canGrantPerm(perm)"
- [(ngModel)]="permsApplied[perm.id()]"/>
- </div>
- <div class="col-lg-2">
- <select class="form-control" [(ngModel)]="permDepths[perm.id()]">
- <ng-container *ngFor="let depth of orgDepths">
- <option [disabled]="depth < canGrantPermAtDepth(perm)"
- [value]="depth">{{depth}}</option>
- </ng-container>
- </select>
- </div>
- <div class="col-lg-2">
- <input class="form-check-input ml-0 pl-0" type="checkbox"
- [disabled]="!canGrantPerm(perm)"
- [(ngModel)]="permsGrantable[perm.id()]"/>
- </div>
+ </div>
+ <div [ngbNavOutlet]="permsNav"></div>
+ <div class="d-flex w-100 mb-2 mt-2">
+ <div class="ml-auto">
+ <button class="btn btn-success" (click)="save()"
+ [disabled]="cannotSave()" i18n>Apply Changes</button>
</div>
</div>
</ng-container>
+
import {Component, ViewChild, Input, OnInit, AfterViewInit} from '@angular/core';
import {Router, ActivatedRoute, ParamMap} from '@angular/router';
-import {tap} from 'rxjs/operators';
+import {Observable} from 'rxjs';
+import {concatMap, tap, finalize} from 'rxjs/operators';
import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
import {IdlService, IdlObject} from '@eg/core/idl.service';
import {NetService} from '@eg/core/net.service';
export class PatronPermsComponent implements OnInit, AfterViewInit {
@Input() patronId: number;
- workOuMaps: IdlObject[];
- workOuSelector: {[orgId: number]: boolean} = {};
+
+ myPermMaps: {[permId: number]: IdlObject} = {}
+ userPermMaps: {[permId: number]: IdlObject} = {};
+ userWorkOuMaps: {[orgId: number]: IdlObject} = {};
+
workableOrgs: IdlObject[] = [];
canAssignWorkOrgs: {[orgId: number]: boolean} = {};
- myPermMaps: IdlObject[] = [];
- userPermMaps: IdlObject[] = [];
allPerms: IdlObject[] = [];
loading = true;
- permsApplied: {[id: number]: boolean} = {};
- permDepths: {[id: number]: number} = {};
- permsGrantable: {[id: number]: boolean} = {};
-
- myPermsApplied: {[id: number]: boolean} = {};
- myPermDepths: {[id: number]: number} = {};
- myPermsGrantable: {[id: number]: boolean} = {};
-
orgDepths: number[];
@ViewChild('progress') private progress: ProgressInlineComponent;
const depths = {};
this.org.list().forEach(org => depths[org.ou_type().depth()] = true);
this.orgDepths = Object.keys(depths).map(d => Number(d)).sort();
-
}
ngAfterViewInit() {
- this.progress.update({max: 9, value: 0});
-
- this.net.request(
- 'open-ils.actor',
- 'open-ils.actor.user.get_work_ous',
- this.auth.token(), this.patronId).toPromise()
-
- .then(maps => {
- this.progress.increment();
- this.workOuMaps = maps;
- maps.forEach(map => this.workOuSelector[map.work_ou()] = true);
- })
+ return this.reload().toPromise()
.then(_ => { // All permissions
this.progress.increment();
.pipe(tap(perm => this.allPerms.push(perm))).toPromise();
})
- .then(_ => { // Target user permissions
- this.progress.increment();
- return this.net.request(
- 'open-ils.actor',
- 'open-ils.actor.permissions.user_perms.retrieve',
- this.auth.token(), this.patronId).toPromise()
- .then(maps => {
- this.progress.increment();
- this.userPermMaps = maps;
- maps.forEach(m => {
- this.permsApplied[m.perm()] = true;
- this.permDepths[m.perm()] = m.depth();
- this.permsGrantable[m.perm()] = m.grantable() === 't';
-
- });
- });
- })
-
.then(_ => { // My permissions
this.progress.increment();
return this.net.request(
'open-ils.actor.permissions.user_perms.retrieve',
this.auth.token()).toPromise()
.then(maps => {
- this.myPermMaps = maps
- maps.forEach(m => {
- this.myPermsApplied[m.perm()] = true;
- this.myPermDepths[m.perm()] = m.depth();
- this.myPermsGrantable[m.perm()] = m.grantable() === 't';
- });
+ this.progress.increment();
+ maps.forEach(m => this.myPermMaps[m.perm()] = m);
});
})
.then(_ => this.loading = false);
}
+ reload(): Observable<any> {
+
+ this.userWorkOuMaps = {};
+ this.userPermMaps = {};
+ this.progress.increment();
+
+ return this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.user.get_work_ous',
+ this.auth.token(), this.patronId
+
+ ).pipe(tap(maps => {
+ this.progress.increment();
+ maps.forEach(map => this.userWorkOuMaps[map.work_ou()] = map);
+
+ })).pipe(concatMap(_ => { // User perm maps
+ return this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.permissions.user_perms.retrieve',
+ this.auth.token(), this.patronId
+ );
+
+ })).pipe(tap(maps => {
+ this.progress.increment();
+ maps.forEach(m => this.userPermMaps[m.perm()] = m);
+ }));
+ }
+
+ userHasWorkOu(orgId: number): boolean {
+ return (
+ this.userWorkOuMaps[orgId] &&
+ !this.userWorkOuMaps[orgId].isdeleted()
+ );
+ }
+
+ userWorkOuChange(orgId: number, applied: boolean) {
+ const map = this.userWorkOuMaps[orgId];
+
+ if (map) {
+ map.isdeleted(!applied);
+ } else {
+ const newMap = this.idl.create('puwoum');
+ newMap.isnew(true);
+ newMap.usr(this.patronId);
+ newMap.work_ou(orgId);
+ this.userWorkOuMaps[orgId] = newMap;
+ }
+ }
+
canGrantPerm(perm: IdlObject): boolean {
- return this.myPermsGrantable[perm.id()]
- || this.auth.user().super_user() === 't';
+ if (this.auth.user().super_user() === 't') { return true; }
+ const map = this.myPermMaps[perm.id()];
+ return map && Number(map.grantable()) === 1;
}
canGrantPermAtDepth(perm: IdlObject): number {
if (this.auth.user().super_user() === 't') {
return this.org.root().ou_type().depth();
} else {
- return this.myPermDepths[perm.id()];
+ const map = this.myPermMaps[perm.id()];
+ return map ? map.depth() : NaN;
}
}
+ userHasPerm(perm: IdlObject): boolean {
+ return (
+ this.userPermMaps[perm.id()] &&
+ !this.userPermMaps[perm.id()].isdeleted()
+ );
+ }
+
+ findOrCreatePermMap(perm: IdlObject): IdlObject {
+ if (this.userPermMaps[perm.id()]) {
+ return this.userPermMaps[perm.id()];
+ }
+
+ const map = this.idl.create('pupm');
+ map.isnew(true);
+ map.usr(this.patronId);
+ map.perm(perm.id());
+ return this.userPermMaps[perm.id()] = map;
+ }
+
+ permApplyChanged(perm: IdlObject, applied: boolean) {
+ const map = this.findOrCreatePermMap(perm);
+ map.isdeleted(!applied);
+ map.ischanged(true);
+ }
+
+ userHasPermAtDepth(perm: IdlObject): number {
+ if (this.userPermMaps[perm.id()]) {
+ return this.userPermMaps[perm.id()].depth();
+ } else {
+ return null;
+ }
+ }
+
+ permDepthChanged(perm: IdlObject, depth: number) {
+ const map = this.findOrCreatePermMap(perm);
+ map.depth(depth);
+ map.ischanged(true);
+ }
+
+ userPermIsGrantable(perm: IdlObject): boolean {
+ return (
+ this.userPermMaps[perm.id()] &&
+ Number(this.userPermMaps[perm.id()].grantable()) === 1
+ );
+ }
+
+ grantableChanged(perm: IdlObject, grantable: boolean) {
+ const map = this.findOrCreatePermMap(perm);
+ map.grantable(grantable ? 1 : 0); // API uses 1/0 not t/f
+ map.ischanged(true);
+ }
+
save() {
- // open-ils.actor.user.work_ous.update
+ this.loading = true;
+
+ // Scrub the unmodified values to avoid sending a huge pile
+ // of perms especially.
+
+ const ouMaps = Object.values(this.userWorkOuMaps)
+ .filter(map => map.isnew() || map.ischanged() || map.isdeleted());
+
+ const permMaps = Object.values(this.userPermMaps)
+ .filter(map => map.isnew() || map.ischanged() || map.isdeleted());
+
+ this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.user.work_ous.update',
+ this.auth.token(), ouMaps
+
+ ).pipe(concatMap(_ => {
+
+ this.progress.reset();
+ this.progress.increment();
+
+ return this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.user.permissions.update',
+ this.auth.token(), permMaps
+ )
+ }))
+ .pipe(concatMap(_ => this.reload()))
+ .pipe(finalize(() => this.loading = false)).subscribe();
+ }
+
+ cannotSave(): boolean {
+ return Object.values(this.userPermMaps)
+ .filter(map => map.depth() === null || map.depth() === undefined)
+ .length > 0;
}
}