LP1840773 SCKO Angular
authorBill Erickson <berickxx@gmail.com>
Tue, 14 Jun 2022 20:33:08 +0000 (16:33 -0400)
committerBill Erickson <berickxx@gmail.com>
Tue, 14 Jun 2022 20:33:08 +0000 (16:33 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/core/auth.service.ts
Open-ILS/src/eg2/src/app/scko/banner.component.html
Open-ILS/src/eg2/src/app/scko/banner.component.ts
Open-ILS/src/eg2/src/app/scko/scko.component.ts
Open-ILS/src/eg2/src/app/scko/scko.service.ts

index 682b730..f549cba 100644 (file)
@@ -41,7 +41,7 @@ export enum AuthWsState {
 export class AuthService {
 
     // Override this to store authtokens, etc. in a different location
-    storePrefix = 'eg.auth';
+    authDomain = 'eg.auth';
 
     private authChannel: any;
 
@@ -65,7 +65,7 @@ export class AuthService {
 
     // Returns true if we are currently in op-change mode.
     opChangeIsActive(): boolean {
-        return Boolean(this.store.getLoginSessionItem(`${this.storePrefix}.time.oc`));
+        return Boolean(this.store.getLoginSessionItem(`${this.authDomain}.time.oc`));
     }
 
     // - Accessor functions always refer to the active user.
@@ -95,8 +95,8 @@ export class AuthService {
             // Only necessary on new page loads.  During op-change,
             // for example, we already have an activeUser.
             this.activeUser = new AuthUser(
-                this.store.getLoginSessionItem(`${this.storePrefix}.token`),
-                this.store.getLoginSessionItem(`${this.storePrefix}.time`)
+                this.store.getLoginSessionItem(`${this.authDomain}.token`),
+                this.store.getLoginSessionItem(`${this.authDomain}.time`)
             );
         }
 
@@ -178,8 +178,8 @@ export class AuthService {
     handleLoginOk(args: AuthLoginArgs, evt: EgEvent, isOpChange: boolean): Promise<void> {
 
         if (isOpChange) {
-            this.store.setLoginSessionItem(`${this.storePrefix}.token.oc`, this.token());
-            this.store.setLoginSessionItem(`${this.storePrefix}.time.oc`, this.authtime());
+            this.store.setLoginSessionItem(`${this.authDomain}.token.oc`, this.token());
+            this.store.setLoginSessionItem(`${this.authDomain}.time.oc`, this.authtime());
         }
 
         this.activeUser = new AuthUser(
@@ -188,8 +188,8 @@ export class AuthService {
             args.workstation
         );
 
-        this.store.setLoginSessionItem(`${this.storePrefix}.token`, this.token());
-        this.store.setLoginSessionItem(`${this.storePrefix}.time`, this.authtime());
+        this.store.setLoginSessionItem(`${this.authDomain}.token`, this.token());
+        this.store.setLoginSessionItem(`${this.authDomain}.time`, this.authtime());
 
         return Promise.resolve();
     }
@@ -198,14 +198,14 @@ export class AuthService {
         if (this.opChangeIsActive()) {
             this.deleteSession();
             this.activeUser = new AuthUser(
-                this.store.getLoginSessionItem(`${this.storePrefix}.token.oc`),
-                this.store.getLoginSessionItem(`${this.storePrefix}.time.oc`),
+                this.store.getLoginSessionItem(`${this.authDomain}.token.oc`),
+                this.store.getLoginSessionItem(`${this.authDomain}.time.oc`),
                 this.activeUser.workstation
             );
-            this.store.removeLoginSessionItem(`${this.storePrefix}.token.oc`);
-            this.store.removeLoginSessionItem(`${this.storePrefix}.time.oc`);
-            this.store.setLoginSessionItem(`${this.storePrefix}.token`, this.token());
-            this.store.setLoginSessionItem(`${this.storePrefix}.time`, this.authtime());
+            this.store.removeLoginSessionItem(`${this.authDomain}.token.oc`);
+            this.store.removeLoginSessionItem(`${this.authDomain}.time.oc`);
+            this.store.setLoginSessionItem(`${this.authDomain}.token`, this.token());
+            this.store.setLoginSessionItem(`${this.authDomain}.time`, this.authtime());
         }
         // Re-fetch the user.
         return this.testAuthToken();
index 61e6aa9..1645045 100644 (file)
 
             <input type="text" class="form-control border border-dark shadow-rounded" 
               autocomplete="off" id="patron-username" required 
-              [(ngModel)]="patronUsername" placeholder="Username or Barcode" i18n-placeholder>
+              [(ngModel)]="patronUsername" name="patron-username"
+              placeholder="Username or Barcode" i18n-placeholder>
 
             <label class="sr-only" for="patron-password" i18n>Password</label>
 
             <input type="password" class="form-control shadow border border-dark rounded ml-2" 
               autocomplete="off" id="patron-password" required
-              [(ngModel)]="patronUsername" placeholder="Password" i18n-placeholder>
+              [(ngModel)]="patronUsername" name="patron-password"
+              placeholder="Password" i18n-placeholder>
 
             <button type="submit" class="btn btn-dark ml-2">Submit</button>
           </form>
index 8bd9a56..833d46f 100644 (file)
@@ -14,15 +14,16 @@ import {OrgService} from '@eg/core/org.service';
 export class SckoBannerComponent implements OnInit {
 
     workstations: any[];
-    loginFailed = false;
     workstationNotFound = false;
 
     patronUsername: string;
     patronPassword: string;
+    patronLoginFailed = false;
 
     staffUsername: string;
     staffPassword: string;
     staffWorkstation: string;
+    staffLoginFailed = false;
 
     constructor(
         private route: ActivatedRoute,
@@ -37,10 +38,6 @@ export class SckoBannerComponent implements OnInit {
 
         const node = document.getElementById('staff-username');
 
-        console.log('PFX', this.scko.auth.storePrefix);
-        console.log(this.scko.auth.token());
-        console.log(this.scko.auth.user());
-
         // NOTE: Displaying a list of workstations will not work for users
         // of Hatch until the extension is updated to support /eg2/*/scko
         this.store.getWorkstations()
@@ -67,7 +64,7 @@ export class SckoBannerComponent implements OnInit {
 
     submitStaffLogin() {
 
-        this.loginFailed = false;
+        this.staffLoginFailed = false;
 
         const args = {
             type: 'persistent',
@@ -76,14 +73,14 @@ export class SckoBannerComponent implements OnInit {
             workstation: this.staffWorkstation
         };
 
-        this.loginFailed = false;
+        this.staffLoginFailed = false;
         this.workstationNotFound = false;
 
         this.auth.login(args).then(
             ok => {
 
                 if (this.auth.workstationState === AuthWsState.NOT_FOUND_SERVER) {
-                    this.loginFailed = true;
+                    this.staffLoginFailed = true;
                     this.workstationNotFound = true;
 
                 } else {
@@ -93,8 +90,6 @@ export class SckoBannerComponent implements OnInit {
                     this.org.clearCachedSettings().then(_ => {
 
                         // Force reload of the app after a successful login.
-                        // This allows the route resolver to re-run with a
-                        // valid auth token and workstation.
                         window.location.href =
                             this.ngLocation.prepareExternalUrl('/scko');
 
@@ -102,12 +97,17 @@ export class SckoBannerComponent implements OnInit {
                 }
             },
             notOk => {
-                this.loginFailed = true;
+                this.staffLoginFailed = true;
             }
         );
     }
 
     submitPatronLogin() {
+        this.patronLoginFailed = false;
+        this.scko.loadPatron(this.patronUsername, this.patronPassword)
+        .finally(() => {
+            this.patronLoginFailed = this.scko.patron === null;
+        });
     }
 }
 
index e3f90a5..31a5bf8 100644 (file)
@@ -2,6 +2,7 @@ import {Component, OnInit, ViewEncapsulation} from '@angular/core';
 import {Router, ActivatedRoute, NavigationEnd} from '@angular/router';
 import {AuthService} from '@eg/core/auth.service';
 import {SckoService} from './scko.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
 
 @Component({
   templateUrl: 'scko.component.html',
@@ -18,6 +19,7 @@ export class SckoComponent implements OnInit {
     ) {}
 
     ngOnInit() {
+        this.scko.load();
     }
 }
 
index cdfc91d..c085461 100644 (file)
@@ -11,12 +11,97 @@ export class SckoService {
 
     // Currently active patron account object.
     patron: IdlObject;
+    barcodeRegex: RegExp;
+    patronPasswordRequired = false;
 
     constructor(
         private net: NetService,
+        private evt: EventService,
+        private serverStore: ServerStoreService,
         public auth: AuthService
-    ) {
-        this.auth.storePrefix = 'eg.scko';
+    ) {}
+
+    load(): Promise<any> {
+        this.auth.authDomain = 'eg.scko';
+
+        return this.auth.testAuthToken()
+
+        .then(_ => {
+
+            return this.serverStore.getItemBatch([
+                'opac.barcode_regex',
+                'circ.selfcheck.patron_login_timeout',
+                'circ.selfcheck.auto_override_checkout_events',
+                'circ.selfcheck.patron_password_required',
+                'circ.checkout_auto_renew_age',
+                'circ.selfcheck.workstation_required',
+                'circ.selfcheck.alert.popup',
+                'circ.selfcheck.alert.sound',
+                'credit.payments.allow',
+                'circ.selfcheck.block_checkout_on_copy_status'
+            ]);
+
+        }).then(sets => {
+            const regPattern = sets['opac.barcode_regex'] || /^\d/;
+            this.barcodeRegex = new RegExp(regPattern);
+            this.patronPasswordRequired =
+                sets['circ.selfcheck.patron_password_required'];
+        });
+    }
+
+    loadPatron(username: string, password: string): Promise<any> {
+        this.patron = null;
+
+        if (!username) { return; }
+
+        let barcode;
+
+        if (username.match(this.barcodeRegex)) {
+            barcode = username;
+            username = null;
+        }
+
+        if (this.patronPasswordRequired) {
+            // TODO verify password
+
+        } else {
+
+            return this.fetchPatron(username, barcode);
+        }
+    }
+
+    fetchPatron(username: string, barcode: string): Promise<any> {
+
+        return this.net.request(
+            'open-ils.actor',
+            'open-ils.actor.user.retrieve_id_by_barcode_or_username',
+            this.auth.token(), barcode, username).toPromise()
+
+        .then(patronId => {
+
+            const evt = this.evt.parse(patronId);
+
+            if (evt || !patronId) {
+                console.error("Cannot find user: ", evt);
+                return Promise.reject('User not found');
+            }
+
+            return this.net.request(
+                'open-ils.actor',
+                'open-ils.actor.user.fleshed.retrieve.authoritative',
+                this.auth.token(), patronId).toPromise()
+
+        }).then(patron => {
+
+            const evt = this.evt.parse(patron);
+
+            if (evt) {
+                console.error('fetchPatron()', evt);
+                return Promise.reject('User not found');
+            }
+
+            this.patron = patron;
+        });
     }
 }