let service = 'open-ils.auth';
let method = 'open-ils.auth.login';
+ if (isOpChange && this.opChangeIsActive()) {
+ // Enforce one op-change at a time.
+ this.undoOpChange();
+ }
+
return this.net.request(
'open-ils.auth_proxy',
'open-ils.auth_proxy.enabled')
permFailed$: EventEmitter<NetRequest>;
authExpired$: EventEmitter<AuthExpiredEvent>;
- // If true, permission failures are emitted via permFailed$
+ // If true, permission failures are emitted via permFailed
// and the active request is marked as superseded.
permFailedHasHandler: Boolean = false;
-import {Component, OnInit, ViewChild} from '@angular/core';
+import {Component, OnInit, OnDestroy, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Location} from '@angular/common';
+import {Subscription} from 'rxjs';
import {OrgService} from '@eg/core/org.service';
import {AuthService} from '@eg/core/auth.service';
import {PcrudService} from '@eg/core/pcrud.service';
import {LocaleService} from '@eg/core/locale.service';
import {PrintService} from '@eg/share/print/print.service';
import {StoreService} from '@eg/core/store.service';
+import {NetRequest, NetService} from '@eg/core/net.service';
+import {OpChangeComponent} from '@eg/staff/share/op-change/op-change.component';
@Component({
selector: 'eg-staff-nav-bar',
templateUrl: 'nav.component.html'
})
-export class StaffNavComponent implements OnInit {
+export class StaffNavComponent implements OnInit, OnDestroy {
// Locales that have Angular staff translations
locales: any[];
// When active, show a link to the experimental Angular staff catalog
showAngularCatalog: boolean;
+ @ViewChild('navOpChange', {static: false}) opChange: OpChangeComponent;
+ permFailedSub: Subscription;
+
constructor(
private router: Router,
private store: StoreService,
+ private net: NetService,
private org: OrgService,
private auth: AuthService,
private pcrud: PcrudService,
.then(settings => this.showAngularCatalog =
Boolean(settings['ui.staff.angular_catalog.enabled']));
}
+
+ // Wire up our op-change component as the general purpose
+ // permission failed handler.
+ this.net.permFailedHasHandler = true;
+ this.permFailedSub =
+ this.net.permFailed$.subscribe(
+ (req: NetRequest) => this.opChange.escalateRequest(req));
+ }
+
+ ngOnDestroy() {
+ if (this.permFailedSub) {
+ this.permFailedSub.unsubscribe();
+ }
}
user() {
-
<eg-string #successMsg
text="Successfully Holdings" i18n-text></eg-string>
<eg-string #errorMsg
text="Failed To Delete Holdings" i18n-text></eg-string>
+<eg-confirm-dialog #confirmOverride
+ i18n-dialogTitle dialogTitle="One or more items could not be deleted. Override?"
+ i18n-dialogBody dialogBody="Reason(s) include: {{deleteEventDesc}}">
+</eg-confirm-dialog>
<ng-template #dialogContent>
- <div class="modal-header bg-info">
- <h4 class="modal-title">
- <span i18n>Delete Holdings</span>
- </h4>
- <button type="button" class="close"
- i18n-aria-label aria-label="Close" (click)="close()">
- <span aria-hidden="true">×</span>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title">
+ <span i18n>Delete Holdings</span>
+ </h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <p i18n>Delete {{numCallNums}} call numbers and {{numCopies}} copies?</p>
+ </div>
+ <div class="modal-footer">
+ <ng-container>
+ <button type="button" class="btn btn-warning"
+ (click)="close()" i18n>Cancel</button>
+ <button type="button" class="btn btn-success"
+ (click)="deleteHoldings()" i18n>
+ Delete Holdings
</button>
- </div>
- <div class="modal-body">
- <p i18n>Delete {{numCallNums}} call numbers and {{numCopies}} copies?</p>
- </div>
- <div class="modal-footer">
- <ng-container>
- <button type="button" class="btn btn-warning"
- (click)="close()" i18n>Cancel</button>
- <button type="button" class="btn btn-success"
- (click)="deleteHoldings()" i18n>
- Delete Holdings
- </button>
- </ng-container>
- </div>
- </ng-template>
-
+ </ng-container>
+ </div>
+</ng-template>
+
import {Observable, throwError} from 'rxjs';
import {IdlObject} from '@eg/core/idl.service';
import {NetService} from '@eg/core/net.service';
-import {EventService} from '@eg/core/event.service';
+import {EgEvent, EventService} from '@eg/core/event.service';
import {PcrudService} from '@eg/core/pcrud.service';
import {ToastService} from '@eg/share/toast/toast.service';
import {AuthService} from '@eg/core/auth.service';
import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
import {DialogComponent} from '@eg/share/dialog/dialog.component';
import {StringComponent} from '@eg/share/string/string.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
/**
numCopies: number;
numSucceeded: number;
numFailed: number;
+ deleteEventDesc: string;
@ViewChild('successMsg', { static: true })
private successMsg: StringComponent;
@ViewChild('errorMsg', { static: true })
private errorMsg: StringComponent;
+ @ViewChild('confirmOverride', {static: false})
+ private confirmOverride: ConfirmDialogComponent;
+
constructor(
private modal: NgbModal, // required for passing to parent
private toast: ToastService,
return super.open(args);
}
- deleteHoldings() {
+ deleteHoldings(override?: boolean) {
+
+ this.deleteEventDesc = '';
- const flags = {
+ const flags: any = {
force_delete_copies: this.forceDeleteCopies
};
+ let method = 'open-ils.cat.asset.volume.fleshed.batch.update';
+ if (override) {
+ method = `${method}.override`;
+ flags.events = ['TITLE_LAST_COPY', 'COPY_DELETE_WARNING'];
+ }
+
this.net.request(
- 'open-ils.cat',
- 'open-ils.cat.asset.volume.fleshed.batch.update.override',
+ 'open-ils.cat', method,
this.auth.token(), this.callNums, 1, flags
).toPromise().then(
result => {
const evt = this.evt.parse(result);
if (evt) {
- console.warn(evt);
- this.errorMsg.current().then(msg => this.toast.warning(msg));
- this.numFailed++;
+ this.handleDeleteEvent(evt, override);
} else {
this.numSucceeded++;
this.close(this.numSucceeded > 0);
}
);
}
+
+ handleDeleteEvent(evt: EgEvent, override?: boolean): Promise<any> {
+
+ if (override) { // override failed
+ console.warn(evt);
+ this.numFailed++;
+ return this.errorMsg.current().then(msg => this.toast.warning(msg));
+ }
+
+ this.deleteEventDesc = evt.desc;
+
+ return this.confirmOverride.open().toPromise().then(confirmed => {
+ if (confirmed) {
+ return this.deleteHoldings(true);
+
+ } else {
+ // User canceled the delete confirmation dialog
+ this.numFailed++;
+ this.errorMsg.current().then(msg => this.toast.warning(msg));
+ this.close(this.numSucceeded > 0);
+ }
+ });
+ }
}
import {AuthService} from '@eg/core/auth.service';
import {DialogComponent} from '@eg/share/dialog/dialog.component';
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
+import {NetRequest, NetService} from '@eg/core/net.service';
@Component({
selector: 'eg-op-change',
@Input() successMessage: string;
@Input() failMessage: string;
+ requestToEscalate: NetRequest;
+
constructor(
private modal: NgbModal, // required for passing to parent
private renderer: Renderer2,
private toast: ToastService,
+ private net: NetService,
private auth: AuthService) {
super(modal);
}
ngOnInit() {
-
// Focus the username any time the dialog is opened.
this.onOpen$.subscribe(
val => this.renderer.selectRootElement('#username').focus()
ok2 => {
this.close();
this.toast.success(this.successMessage);
+ if (this.requestToEscalate) {
+ // Allow a breath for the dialog to clean up.
+ setTimeout(() => this.sendEscalatedRequest());
+ }
}
);
},
err => this.toast.danger(this.failMessage)
);
}
+
+ escalateRequest(req: NetRequest) {
+ this.requestToEscalate = req;
+ this.open({});
+ }
+
+ // Resend a net request using the credentials just created
+ // via operator change.
+ sendEscalatedRequest() {
+ const sourceReq = this.requestToEscalate;
+ delete this.requestToEscalate;
+
+ console.debug('Op-Change escalating request', sourceReq);
+
+ // Clone the source request, modifying the params to
+ // use the op-change'd authtoken
+ const req = new NetRequest(
+ sourceReq.service,
+ sourceReq.method,
+ [this.auth.token()].concat(sourceReq.params.splice(1))
+ );
+
+ // Relay responses received for our escalated request to
+ // the caller via the original request observer.
+ this.net.requestCompiled(req)
+ .subscribe(
+ res => sourceReq.observer.next(res),
+ err => sourceReq.observer.error(err),
+ () => sourceReq.observer.complete()
+ ).add(_ => this.auth.undoOpChange());
+ }
}