LP#1626157 Auth/ws check repairs; ws admin fixes; audio service
authorBill Erickson <berickxx@gmail.com>
Fri, 13 Apr 2018 02:42:52 +0000 (02:42 +0000)
committerBill Erickson <berickxx@gmail.com>
Fri, 13 Apr 2018 02:42:52 +0000 (02:42 +0000)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/common.module.ts
Open-ILS/src/eg2/src/app/core/auth.service.ts
Open-ILS/src/eg2/src/app/share/audio.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.ts
Open-ILS/src/eg2/src/app/staff/resolver.service.ts
Open-ILS/src/eg2/src/app/staff/staff.component.ts

index 20a9b5e..4e2021d 100644 (file)
@@ -14,6 +14,7 @@ import {EgAuthService} from '@eg/core/auth.service';
 import {EgPermService} from '@eg/core/perm.service';
 import {EgPcrudService} from '@eg/core/pcrud.service';
 import {EgOrgService} from '@eg/core/org.service';
+import {EgAudioService} from '@eg/share/audio.service';
 
 @NgModule({
   declarations: [
@@ -44,7 +45,8 @@ export class EgCommonModule {
                 EgAuthService,
                 EgPermService,
                 EgPcrudService,
-                EgOrgService
+                EgOrgService,
+                EgAudioService
             ]
         };
     }
index a14fd9f..7557da2 100644 (file)
@@ -223,7 +223,8 @@ export class EgAuthService {
 
 
     // Resolves if login workstation matches a workstation known to this 
-    // browser instance.
+    // browser instance.  No attempt is made to see if the workstation
+    // is present on the server.  That happens at login time.
     verifyWorkstation(): Promise<void> {
 
         if (!this.user()) {
diff --git a/Open-ILS/src/eg2/src/app/share/audio.service.ts b/Open-ILS/src/eg2/src/app/share/audio.service.ts
new file mode 100644 (file)
index 0000000..971fe7e
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * Plays audio files (alerts, generally) by key name.  Each sound uses a 
+ * dot-path to indicate  the sound.  
+ *
+ * For example:
+ *
+ * this.audio.play('warning.checkout.no_item');
+ *
+ * URLs are tested in the following order until an audio file is found
+ * or no other paths are left to check.
+ *
+ * /audio/notifications/warning/checkout/not_found.wav
+ * /audio/notifications/warning/checkout.wav
+ * /audio/notifications/warning.wav
+ *
+ * Files are only played when sounds are configured to play via 
+ * workstation settings.
+ */
+import {Injectable, EventEmitter} from '@angular/core';
+import {EgStoreService} from '@eg/core/store.service';
+const AUDIO_BASE_URL = '/audio/notifications/';
+
+@Injectable()
+export class EgAudioService {
+
+    // map of requested audio path to resolved path
+    private urlCache: {[path:string] : string} = {};
+
+    constructor(private store: EgStoreService) {}
+
+    play(path: string): void {
+        if (path) {
+            this.playUrl(path, path);
+        }
+    }
+
+    playUrl(path: string, origPath: string): void {
+        //console.debug(`audio: playUrl(${path}, ${origPath})`);
+
+        this.store.getItem('eg.audio.disable').then(audioDisabled => {
+            if (audioDisabled) return;
+        
+            let url = this.urlCache[path] || 
+                AUDIO_BASE_URL + path.replace(/\./g, '/') + '.wav';
+
+            let player = new Audio(url);
+
+            player.onloadeddata = () => {
+                this.urlCache[origPath] = url;
+                player.play();
+                console.debug(`audio: ${url}`);
+            };
+
+            if (this.urlCache[path]) {
+                // when serving from the cache, avoid secondary URL lookups.
+                return;
+            }
+
+            player.onerror = () => {
+                // Unable to play path at the requested URL.
+        
+                if (!path.match(/\./)) {
+                    // all fall-through options have been exhausted.
+                    // No path to play.
+                    console.warn(
+                        "No suitable URL found for path '" + origPath + "'");
+                    return;
+                }
+
+                // Fall through to the next (more generic) option
+                path = path.replace(/\.[^\.]+$/, '');
+                this.playUrl(path, origPath);
+            }
+        });
+    }
+}
+
+
index 6ee04d2..10a8cb6 100644 (file)
@@ -169,7 +169,15 @@ export class WorkstationsComponent implements OnInit {
 
         this.workstations.push(ws);
         this.store.setItem('eg.workstation.all', this.workstations)
-        .then(ok => this.newName = '');
+        .then(ok => {
+            this.newName = '';
+            // when registering our first workstation, mark it as the
+            // default and show it as selected in the ws selector.
+            if (this.workstations.length == 1) {
+                this.selectedName = ws.name;
+                this.setDefault();
+            }
+        });
     }
 }
 
index 530aeb1..8f2fe5a 100644 (file)
@@ -5,7 +5,10 @@ import {Router, Resolve, RouterStateSnapshot,
         ActivatedRoute, ActivatedRouteSnapshot} from '@angular/router';
 import {EgStoreService} from '@eg/core/store.service';
 import {EgNetService} from '@eg/core/net.service';
-import {EgAuthService} from '@eg/core/auth.service';
+import {EgAuthService, EgAuthWsState} from '@eg/core/auth.service';
+
+const LOGIN_PATH = '/staff/login';
+const WS_MANAGE_PATH = '/staff/admin/workstation/workstations/manage';
 
 /**
  * Load data used by all staff modules.
@@ -13,9 +16,6 @@ import {EgAuthService} from '@eg/core/auth.service';
 @Injectable()
 export class EgStaffResolver implements Resolve<Observable<any>> {
 
-    readonly loginPath = '/staff/login';
-    readonly wsRemPath = '/staff/admin/workstation/workstations/remove/';
-
     // Tracks the primary resolve observable.
     observer: Observer<any>;
 
@@ -69,23 +69,19 @@ export class EgStaffResolver implements Resolve<Observable<any>> {
     handleInvalidToken(state: RouterStateSnapshot): void {
         console.debug('EgStaffResolver: authtoken is not valid');
         this.auth.redirectUrl = state.url;
-        this.router.navigate([this.loginPath]);
+        this.router.navigate([LOGIN_PATH]);
         this.observer.error('invalid or no auth token');
     }
 
-    // For the purposes of route resolving, an invalid workstation is
-    // not an error condition when we're on the workstation admin page.
-    // However, it does mean that not all of the usual data will be
-    // loaded and available on the workstation admin page.  Once a valid
-    // workstation is applied and the user re-logs in, the page must be
-    // reloaded to fetch all of the needed staff data.
     handleInvalidWorkstation(path: string): void {
-        if (path.indexOf(this.wsRemPath) < 0) {
-            this.router.navigate([
-                this.wsRemPath + this.auth.workstation()]);
-            this.observer.error('invalid workstation, redirecting');
-        } else {
+
+        if (path.startsWith(WS_MANAGE_PATH)) {
+            // user is navigating to the WS admin page.
             this.observer.complete();
+        } else {
+            this.router.navigate([WS_MANAGE_PATH]);
+            this.observer.error(`Auth session linked to no 
+                workstation or a workstation unknown to this browser`);
         }
     }
 
index 7bdce0b..0084952 100644 (file)
@@ -3,6 +3,10 @@ import {Router, ActivatedRoute, NavigationEnd} from '@angular/router';
 import {EgAuthService, EgAuthWsState} from '@eg/core/auth.service';
 import {EgNetService} from '@eg/core/net.service';
 
+const LOGIN_PATH = '/staff/login';
+const WS_BASE_PATH = '/staff/admin/workstation/workstations/';
+const WS_MANAGE_PATH = '/staff/admin/workstation/workstations/manage';
+
 @Component({
   templateUrl: 'staff.component.html',
   styleUrls: ['staff.component.css']
@@ -10,8 +14,6 @@ import {EgNetService} from '@eg/core/net.service';
 
 export class EgStaffComponent implements OnInit {
 
-    readonly loginPath = '/staff/login';
-    readonly wsAdminBasePath = '/staff/admin/workstation/workstations/';
 
     constructor(
         private router: Router,
@@ -50,7 +52,7 @@ export class EgStaffComponent implements OnInit {
             // with the page.  Fix it by wrapping it in zone.run().
             // This is the only navigate() where I have seen this happen.
             this.zone.run(() => {
-                this.router.navigate([this.loginPath]);
+                this.router.navigate([LOGIN_PATH]);
             });
         });
 
@@ -60,28 +62,30 @@ export class EgStaffComponent implements OnInit {
     }
 
     /**
-     * Verifying the auth token on every route is overkill, since
-     * an expired token will make itself known with the first API
-     * call or when the auth session poll discovers it, but we do
-     * want to prevent navigation from the login or workstation admin
-     * page, since these can be accessed without a valid authtoken or
-     * workstation, respectively, once the initial route resolvers have
-     * done their jobs.
+     * Prevent the user from leaving the login page when they don't have
+     * a valid authoken.
+     *
+     * Prevent the user from leaving the workstation admin page when
+     * they don't have a valid workstation.
+     *
+     * This does not verify auth tokens with the server on every route, 
+     * because that would be overkill.  This is only here to keep
+     * people boxed in.
      */
     basicAuthChecks(url: string): void {
 
-        // Access to login page is always granted
-        if (url == this.loginPath) return;
+        // No auth checks needed for login page.
+        if (url.startsWith(LOGIN_PATH)) return;
 
+        // We lost our authtoken, go back to the login page.
         if (!this.auth.token()) 
-            this.router.navigate([this.loginPath]);
+            this.router.navigate([LOGIN_PATH]);
 
-        // Access to workstation admin page is granted regardless
-        // of workstation validity.
-        if (url.indexOf(this.wsAdminBasePath) >= 0) return;
+        // No workstation checks needed for workstation admin page.
+        if (url.startsWith(WS_BASE_PATH)) return;
 
         if (this.auth.workstationState != EgAuthWsState.VALID)
-            this.router.navigate([this.wsAdminBasePath]);
+            this.router.navigate([WS_MANAGE_PATH]);
     }
 }