Webstaff: implement Operator Change (and Operator Restore)
authorMike Rylander <mrylander@gmail.com>
Thu, 6 Oct 2016 16:00:18 +0000 (12:00 -0400)
committerKathy Lussier <klussier@masslnc.org>
Tue, 22 Nov 2016 19:10:03 +0000 (14:10 -0500)
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Signed-off-by: Kathy Lussier <klussier@masslnc.org>
Open-ILS/src/templates/staff/base_js.tt2
Open-ILS/src/templates/staff/navbar.tt2
Open-ILS/src/templates/staff/share/t_opchange.tt2 [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/services/auth.js
Open-ILS/web/js/ui/default/staff/services/core.js
Open-ILS/web/js/ui/default/staff/services/navbar.js

index af92b80..bf812ba 100644 (file)
@@ -77,6 +77,8 @@
     s.EG_WORK_LOG_REQUESTED_HOLD = '[% l('Hold Request') %]';
     s.EG_CONFIRM_DELETE_RECORD_TITLE = '[% l('Confirm Record Deletion') %]';
     s.EG_CONFIRM_DELETE_RECORD_BODY = "[% l('Delete record {{id}}?') %]";
+    s.OP_CHANGE_SUCCESS = "[% l('Operator Change Succeeded') %]";
+    s.OP_CHANGE_FAILURE = "[% l('Operator Change Failed') %]";
   }]);
 </script>
 
index db4ae08..7430f6a 100644 (file)
     <!-- entries along the right side of the navbar -->
     <ul class="nav navbar-nav navbar-right" style='margin-right: 6px;'>
       <li>
-        <a ng-cloak ng-show="username" 
+        <a ng-cloak ng-show="username" title="{{currentToken()}}"
           ng-init="workstation = '[% l('<no workstation>') %]'">
             [% l('{{username}} @ {{workstation}}') %]
         </a>
         <a href class="glyphicon glyphicon-list" 
           uib-dropdown-toggle></a>
         <ul uib-dropdown-menu>
-          <li class="disabled">
-            <a href="" ng-click="" target="_self">
+          <li ng-if="!op_changed">
+            <a href="" ng-click="changeOperator()">
               <span class="glyphicon glyphicon-random"></span>
               [% l('Change Operator') %]
             </a>
           </li>
+          <li ng-if="op_changed">
+            <a href="" ng-click="changeOperatorUndo()">
+              <span class="glyphicon glyphicon-random"></span>
+              [% l('Restore Operator') %]
+            </a>
+          </li>
           <li>
             <a href="./login" ng-click="logout()" target="_self">
               <span class="glyphicon glyphicon-log-out"></span>
diff --git a/Open-ILS/src/templates/staff/share/t_opchange.tt2 b/Open-ILS/src/templates/staff/share/t_opchange.tt2
new file mode 100644 (file)
index 0000000..030418a
--- /dev/null
@@ -0,0 +1,36 @@
+<!--
+  Username/password prompt for operator change
+-->
+<div>
+  <div class="modal-header">
+    <button type="button" class="close" 
+      ng-click="cancel()" aria-hidden="true">&times;</button>
+    <h4 class="modal-title alert alert-info">[% l('Operator Change') %]</h4> 
+  </div>
+  <div class="modal-body">
+    <div class="row">
+      <div class="col-md-4">
+        [% l('Username:') %]
+      </div>
+      <div class="col-md-1"></div>
+      <div class="col-md-7">
+        <input ng-keyup="$event.keyCode == 13 ? ok() : null" type='text' ng-model="args.username" class="form-control" focus-me="focus"/>
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        [% l('Password:') %]
+      </div>
+      <div class="col-md-1"></div>
+      <div class="col-md-7">
+        <input ng-keyup="$event.keyCode == 13 ? ok() : null" type='password' ng-model="args.password" class="form-control"/>
+      </div>
+    </div>
+  </div>
+  <div class="modal-footer">
+    [% dialog_footer %]
+    <input type="submit" class="btn btn-primary" 
+      ng-click="ok()" value="[% l('OK/Continue') %]"/>
+    <button class="btn btn-warning" ng-click="cancel()">[% l('Cancel') %]</button>
+  </div>
+</div>
index 3446f4a..638ce7d 100644 (file)
@@ -11,10 +11,31 @@ function($q , $timeout , $rootScope , $window , $location , egNet , egHatch) {
 
     var service = {
         // the currently active user (au) object
-        user : function() {
+        user : function(u) {
+            if (u) {
+                this._user = u;
+            }
             return this._user;
         },
 
+        // the user hidden by an operator change
+        OCuser : function(u) {
+            if (u) {
+                this._OCuser = u;
+            }
+            return this._OCuser;
+        },
+
+        // the Op Change hidden auth token string
+        OCtoken : function() {
+            return egHatch.getLoginSessionItem('eg.auth.token.oc');
+        },
+
+        // Op Change hidden authtime in seconds
+        OCauthtime : function() {
+            return egHatch.getLoginSessionItem('eg.auth.time.oc');
+        },
+
         // the currently active auth token string
         token : function() {
             return egHatch.getLoginSessionItem('eg.auth.token');
@@ -47,7 +68,7 @@ function($q , $timeout , $rootScope , $window , $location , egNet , egHatch) {
             .then(function(user) {
                 if (user && user.classname) {
                     // authtoken test succeeded
-                    service._user = user;
+                    service.user(user);
                     service.poll();
                     service.check_workstation(deferred);
 
@@ -156,6 +177,49 @@ function($q , $timeout , $rootScope , $window , $location , egNet , egHatch) {
         return ops.deferred.promise;
     }
 
+    /**
+     * Returns a promise, which is resolved on successful 
+     * login and rejected on failed login.
+     */
+    service.opChange = function(args) {
+        // avoid modifying the caller's data structure.
+        args = angular.copy(args);
+        args.workstation = service.workstation();
+
+        var deferred = $q.defer();
+
+        service.login_api(args).then(function(evt) {
+
+            if (evt.textcode == 'SUCCESS') {
+                service.OCuser(service.user());
+                egHatch.setLoginSessionItem('eg.auth.token.oc', service.token());
+                egHatch.setLoginSessionItem('eg.auth.time.oc', service.authtime());
+                service.handle_login_ok(args, evt);
+                deferred.resolve();
+
+            } else {
+                // note: the likely outcome here is a NO_SESION
+                // server event, which results in broadcasting an 
+                // egInvalidAuth by egNet. 
+                console.error('operator change failed ' + js2JSON(evt));
+                deferred.reject();
+            }
+        });
+
+        return deferred.promise;
+    }
+
+    service.opChangeUndo = function() {
+        if (service.OCtoken()) {
+            service.user(service.OCuser());
+            egHatch.setLoginSessionItem('eg.auth.token', service.OCtoken());
+            egHatch.setLoginSessionItem('eg.auth.time', service.OCauthtime());
+            egHatch.removeLoginSessionItem('eg.auth.token.oc');
+            egHatch.removeLoginSessionItem('eg.auth.time.oc');
+        }
+        return service.testAuthToken();
+    }
+
     service.login_api = function(args) {
         return egNet.request(
             'open-ils.auth',
index dc5ef6c..e61aceb 100644 (file)
@@ -3,4 +3,4 @@
  * egCoreMod houses all of the services, etc. required by all pages
  * for basic functionality.
  */
-angular.module('egCoreMod', ['cfp.hotkeys', 'ngFileSaver', 'ngCookies']);
+angular.module('egCoreMod', ['cfp.hotkeys', 'ngFileSaver', 'ngCookies', 'ngToast']);
index 67c117d..2bc6522 100644 (file)
@@ -26,8 +26,8 @@ angular.module('egCoreMod')
             inspect(element);
         },
 
-        controller:['$scope','$window','$location','$timeout','hotkeys','egCore',
-            function($scope , $window , $location , $timeout , hotkeys , egCore) {
+        controller:['$scope','$window','$location','$timeout','hotkeys','egCore','$uibModal','ngToast',
+            function($scope , $window , $location , $timeout , hotkeys , egCore , $uibModal , ngToast) {
 
                 function navTo(path) {                                           
                     // Strip the leading "./" if any.
@@ -72,6 +72,43 @@ angular.module('egCoreMod')
                         );
                 }
 
+                $scope.changeOperatorUndo = function() {
+                        egCore.auth.opChangeUndo();
+                        $scope.op_changed = false;
+                        ngToast.create(egCore.strings.OP_CHANGE_SUCCESS);
+                }
+
+                $scope.changeOperator = function() {
+                    $uibModal.open({
+                        templateUrl: './share/t_opchange',
+                        controller:
+                            ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
+                            $scope.args = {username : '', password : ''};
+                            $scope.focus = true;
+                            $scope.ok = function() { $uibModalInstance.close($scope.args) }
+                            $scope.cancel = function () { $uibModalInstance.dismiss() }
+                        }]
+                    }).result.then(function (args) {
+                        if (!args || !args.username || !args.password) return;
+                        args.workstation = egCore.auth.workstation();
+                        egCore.auth.opChange(args).then(
+                            function() {
+                                console.log('op change success');
+                                $scope.op_changed = true;
+                                ngToast.create(egCore.strings.OP_CHANGE_SUCCESS);
+                            }, // note success with toast?
+                            function() {
+                                console.log('op change failure');
+                                ngToast.warning(egCore.strings.OP_CHANGE_FAILURE);
+                            }  // note failure with toast?
+                        );
+                    });
+                }
+
+                $scope.currentToken = function () {
+                    return egCore.auth.token();
+                }
+
                 // tied to logout link
                 $scope.logout = function() {
                     egCore.auth.logout();
@@ -81,6 +118,7 @@ angular.module('egCoreMod')
                 egCore.startup.go().then(
                     function() {
                         if (egCore.auth.user()) {
+                            $scope.op_changed = egCore.auth.OCtoken() ? true : false;
                             $scope.username = egCore.auth.user().usrname();
                             $scope.workstation = egCore.auth.workstation();
                         }