lp1846354 staff client changes
authorJason Etheridge <jason@EquinoxInitiative.org>
Wed, 8 Jan 2020 14:05:58 +0000 (09:05 -0500)
committerJason Etheridge <jason@EquinoxInitiative.org>
Thu, 6 Aug 2020 17:30:18 +0000 (13:30 -0400)
References to Message or Messages in the UI, including the button bar that
spawns the interface, will be changed to Note and Notes.

The Alert Message field in the patron editor will be removed.

The Notes interface under Other -> Notes will be removed.

The Notes (and count) indicator in the patron summary sidebar will be removed.
The note count will instead be presented as part of the Notes nav button.

The patron summary sidebar and the "stop sign page" will be modified to
retrieve and display user messages linked to standing penalties that are
flagged as staff alerting.  It will retain the same styling (i.e. red text) by
default.

The Messages interface, including the archived view, will be populated with a
combination of user messages and user penalties, which may or may not be linked
in pairs via a new foreign key on the penalties.  For messages without
associated standing penalties, the sending_lib column will be used for the
implicit filtering on org units that standing penalties get now, based on the
workstation library.  This combined view will result in new columns for the
interface.  At minimum the following columns (subject to label changes) will be
displayed by default: Title, Message, Create Date, Creator, Library, and Patron
Visible.  Columns such as Staff Alert, Org Depth, Block List, Ignore Proximity,
and Penalty Label will still be available.

The Apply Penalty / Message dialog will gain a new widget for toggling whether
or not a message is intended to be visible to the patron.  The Edit message
version of the dialog will also show when/if a patron has read and/or deleted
the message.  An entry field for the message title will be added.  The dialog
will also gain an org selector to the right of the penalty type selector, which
will default to the workstation library, and will change based on the org depth
of any standing penalty type selected in the UI (either via the
Note/Alert/Block buttons or the penalty menu).  This will be passed as the
org_unit field for a penalty instead of the workstation library and org depth
that is implicitly used today  The sending_lib field for the user message will
always be set to the workstation library.

The Archive Penalty / Message action will be modified to work with both
penalties and user messages depending on what it is selected.  If an archived
penalty has a linked user message, then both will have their archive fields
set.  An archived user message may still be otherwise visible to the patron if
it is not marked as deleted or staff only.

The Remove Penalty / Message action will behave as normal for a selected
penalty.  For a user message, linked or unlinked, the message will be flagged
as deleted and, even if public, will no longer show up in the patron's Message
Center.

Signed-off-by: Jason Etheridge <jason@EquinoxInitiative.org>
17 files changed:
Open-ILS/src/templates/actor/user/register_table.tt2
Open-ILS/src/templates/staff/circ/patron/index.tt2
Open-ILS/src/templates/staff/circ/patron/t_alerts.tt2
Open-ILS/src/templates/staff/circ/patron/t_edit.tt2
Open-ILS/src/templates/staff/circ/patron/t_messages.tt2
Open-ILS/src/templates/staff/circ/patron/t_summary.tt2
Open-ILS/src/templates/staff/circ/share/t_new_message_dialog.tt2
Open-ILS/src/templates/staff/css/circ.css.tt2
Open-ILS/src/templates/staff/share/print_templates/t_patron_data.tt2
Open-ILS/web/js/ui/default/actor/user/register.js
Open-ILS/web/js/ui/default/staff/admin/workstation/app.js
Open-ILS/web/js/ui/default/staff/circ/patron/app.js
Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js
Open-ILS/web/js/ui/default/staff/circ/services/circ.js
Open-ILS/web/js/ui/default/staff/services/auth.js
Open-ILS/web/js/ui/default/staff/services/patron_search.js
Open-ILS/web/js/ui/default/staff/services/ui.js

index 8235c65..37ff893 100644 (file)
@@ -60,7 +60,6 @@
     <tr fmclass='au' fmfield='master_account'></tr>
     <tr fmclass='au' fmfield='claims_returned_count' wclass='dijit.form.NumberSpinner' wconstraints="{min:0,places:0}" wvalue='0'></tr>
     <tr fmclass='au' fmfield='claims_never_checked_out_count' wclass='dijit.form.NumberSpinner' wconstraints="{min:0,places:0}" wvalue='0'></tr>
-    <tr fmclass='au' fmfield='alert_message' wclass='dijit.form.Textarea' wstyle='height:5em'></tr>
 
     <tr class='divider hidden' id='uedit-settings-divider'><td colspan='0' id='userSettings'></td></tr>
     <tr class='hidden' id='uedit-user-setting-template'>
index a36c758..bee3b2a 100644 (file)
@@ -70,7 +70,7 @@ angular.module('egCoreMod').run(['egStrings', function(s) {
   s.PAGE_TITLE_PATRON_SEARCH = "[% l('Patron Search') %]";
   s.PAGE_TITLE_PATRON_NAME = "[% l('[_1], [_2] [_3]', '{{lname}}','{{fname}}','{{mname}}') %]";
   s.PAGE_TITLE_PATRON_CHECKOUT = "[% l('Checkout') %]";
-  s.PAGE_TITLE_PATRON_MESSAGES = "[% l('Messages') %]";
+  s.PAGE_TITLE_PATRON_MESSAGES = "[% l('Notes') %]";
   /* TODO: The "Other" page title could be smarter.. */
   s.PAGE_TITLE_PATRON_OTHER = "[% l('Other') %]";
   s.PAGE_TITLE_PATRON_BILLS = "[% l('Bills') %]";
@@ -107,7 +107,6 @@ angular.module('egCoreMod').run(['egStrings', function(s) {
                <span ng-if="patron().name_keywords()"> <a title="[% l('Name keywords: ') %]{{patron().name_keywords()}}" class="glyphicon glyphicon-tags"></a>
           </div>
 
-          <p ng-show="patron().notes().length > 0"><a class='patron-summary-has-notes' href="./circ/patron/{{patron().id()}}/notes"><span class="label label-warning">Notes &nbsp;{{patron().notes().length}}</span></a></p>
           <div ng-show="tab != 'search'">
             <a href ng-click="toggle_expand_summary()"
               title="[% l('Collapse Patron Summary Display') %]"
@@ -155,7 +154,11 @@ angular.module('egCoreMod').run(['egStrings', function(s) {
         </a>
       </li>
       <li ng-class="{active : tab == 'messages', disabled : !patron()}">
-        <a a-disabled="!patron()" href="./circ/patron/{{patron().id()}}/messages">[% l('Messages') %]</a>
+        <a a-disabled="!patron()" href="./circ/patron/{{patron().id()}}/messages">[% l('Notes') %]
+            <span ng-if="visible_notes().length > 0">
+                ({{visible_notes().length}})
+            </span>
+        </a>
       </li>
       <li ng-class="{active : tab == 'edit', disabled : !patron()}">
         <a href="./circ/patron/{{patron().id()}}/edit">[% l('Edit') %]</a>
@@ -168,12 +171,7 @@ angular.module('egCoreMod').run(['egStrings', function(s) {
         <ul uib-dropdown-menu>
           <li>
             <a href="./circ/patron/{{patron().id()}}/alerts">
-              [% l('Display Alert and Messages') %]
-            </a>
-          </li>
-          <li>
-            <a href="./circ/patron/{{patron().id()}}/notes">
-              [% l('Notes') %]
+              [% l('Display Alerts') %]
             </a>
           </li>
           <li>
index 3c7929e..4e4e692 100644 (file)
     [% l('Patron account has invalid addresses.') %]
   </div>
 
-  <!-- alert message -->
-  <div class="row" ng-if="patron().alert_message()">
-    <div class="col-md-12">
-      <div class="panel panel-warning">
-        <div class="panel-heading">
-          <div class="panel-title text-center">[% l('Alert Message') %]</div>
-        </div>
-        <div class="panel-body">
-          {{patron().alert_message()}}
-        </div>
-      </div>
-    </div>
-  </div>
-
   <!-- penalties -->
   <div class="row" ng-if="alert_penalties().length">
     <div class="col-md-12">
       <div class="panel panel-warning">
         <div class="panel-heading">
-          <div class="panel-title text-center">[% l('Penalties') %]</div>
+          <div class="panel-title text-center">[% l('Alerts') %]</div>
         </div>
         <div class="panel-body">
           <div class="row" 
               {{penalty.org_unit().shortname()}}
             </div>
             <div class="col-md-8"
-              title="{{penalty.standing_penalty().name()}}">
-              {{penalty.standing_penalty().label()}}
-              <div>{{penalty.note()}}</div><!-- force newline -->
+              title="{{penalty.standing_penalty().name()}} (id {{penalty.id()}})">
+              {{penalty.usr_message().title() || penalty.standing_penalty().label()}}
+              <div>{{penalty.usr_message().message()}}</div><!-- force newline -->
+              <div>&nbsp;</div><!-- should use CSS for this, but spacing out the notes -->
             </div>
             <div class="col-md-2">
               {{penalty.set_date() | date:$root.egDateFormat}}
index 2fff639..9f9a0f9 100644 (file)
@@ -671,24 +671,6 @@ within the "form" by name for validation.
   </div>
 </div>
 
-<!-- ALERT_MESSAGE -->
-
-<div class="row reg-field-row" ng-show="show_field('au.alert_message')">
-  [% draw_field_label('au', 'alert_message') %]
-  <div class="col-md-3 reg-field-input">
-    <textarea 
-      class="form-control" 
-      ng-model="patron.alert_message"
-      ng-pattern="field_pattern('au', 'alert_message')"
-      ng-change="field_modified()" 
-      ng-blur="handle_field_changed(patron, 'alert_message')">
-    </textarea>
-  </div>
-  <div class="col-md-6 patron-reg-example">
-    [% draw_example_text('au', 'alert_message') %]
-  </div>
-</div>
-
 <div ng-if="!offline">
 
 <div class="alert alert-success row" role="alert">
index 081d325..017cbf4 100644 (file)
@@ -1,37 +1,46 @@
-
-<div class="strong-text-2">[% l('Penalties and Messages') %]</div>
+<div class="strong-text-2">[% l('Notes') %]</div>
 <div class="pad-vert"></div>
 <eg-grid
-  idl-class="ausp"
+  idl-class="aump"
   grid-controls="activeGridControls"
   dateformat="{{$root.egDateAndTimeFormat}}"
   persist-key="circ.patron.staff_messages">
 
   <eg-grid-menu-item handler="createPenalty"
-    label="[% l('Apply Penalty / Message') %]"></eg-grid-menu-item>
+    label="[% l('Create Note') %]"></eg-grid-menu-item>
 
-  <eg-grid-action label="[% l('Remove Penalty / Message') %]" 
-    handler="removePenalty"></eg-grid-action>
-  <eg-grid-action label="[% l('Modify Penalty / Message') %]" 
+  <eg-grid-action label="[% l('Remove Note') %]" 
+    disabled="test_for_disable_remove_penalty" handler="removePenalty"></eg-grid-action>
+  <eg-grid-action label="[% l('Edit Note') %]" 
     handler="editPenalty"></eg-grid-action>
-  <eg-grid-action label="[% l('Archive Penalty / Message') %]" 
+  <eg-grid-action label="[% l('Archive Note') %]" 
     handler="archivePenalty"></eg-grid-action>
 
-  <eg-grid-field path="set_date" label="[% l('Applied On') %]" datatype="timestamp"></eg-grid-field>
-  <eg-grid-field path="standing_penalty.label"></eg-grid-field>
-  <eg-grid-field path="org_unit.shortname" label="[% l('Library') %]"></eg-grid-field>
-  <eg-grid-field path="note"></eg-grid-field>
-  <eg-grid-field path="id" required hidden></eg-grid-field>
+  <eg-grid-field path="read_date"></eg-grid-field>
+  <eg-grid-field path="deleted" required hidden></eg-grid-field>
+  <eg-grid-field path="usr.usrname" label="[% l('User') %]" required hidden></eg-grid-field>
+  <eg-grid-field path="message" label="[% l('Note Text') %]"></eg-grid-field>
+  <eg-grid-field path="title"></eg-grid-field>
+  <eg-grid-field path="pub"></eg-grid-field>
+  <eg-grid-field path="stop_date" hidden></eg-grid-field>
+  <eg-grid-field path="editor.usrname" label="[% l('Editor') %]" required hidden></eg-grid-field>
+  <eg-grid-field path="edit_date" hidden></eg-grid-field>
+  <eg-grid-field path="staff.usrname" label="[% l('Staff') %]" required></eg-grid-field>
+  <eg-grid-field path="standing_penalty.name" required label="[% l('Penalty Name') %]"></eg-grid-field>
   <eg-grid-field path="standing_penalty.block_list" required hidden></eg-grid-field>
   <eg-grid-field path="standing_penalty.*" hidden></eg-grid-field>
-
+  <eg-grid-field path="org_unit.shortname" label="[% l('Location') %]" required></eg-grid-field>
+  <eg-grid-field path="create_date"></eg-grid-field>
+  <eg-grid-field path="ausp_id" required hidden></eg-grid-field>
+  <eg-grid-field path="aum_id" required hidden></eg-grid-field>
+  <eg-grid-field path="id" required hidden></eg-grid-field>
 </eg-grid>
 
 <div class="pad-vert"><hr/></div>
 
 <div class="pad-vert row padded">
   <div class="col-md-4">
-    <div class="strong-text-2">[% l('Archived Penalties / Messages') %]</div>
+    <div class="strong-text-2">[% l('Archived Notes') %]</div>
   </div>
   <div class="col-md-4">
     <label>[% l('Set Date Start:') %]</label>
   </div>
 </div>
 <eg-grid
-  idl-class="ausp"
+  idl-class="aump"
   grid-controls="archiveGridControls"
   dateformat="{{$root.egDateAndTimeFormat}}"
+  features="-multiselect"
   persist-key="circ.patron.archived_messages">
 
-  <eg-grid-field path="set_date" label="[% l('Applied On') %]" datatype="timestamp"></eg-grid-field>
-  <eg-grid-field path="standing_penalty.label"></eg-grid-field>
-  <eg-grid-field path="org_unit.shortname" label="[% l('Library') %]"></eg-grid-field>
-  <eg-grid-field path="note"></eg-grid-field>
-  <eg-grid-field path="id" required hidden></eg-grid-field>
+  <eg-grid-field path="read_date"></eg-grid-field>
+  <eg-grid-field path="deleted" required hidden></eg-grid-field>
+  <eg-grid-field path="usr.usrname" label="[% l('User') %]" required hidden></eg-grid-field>
+  <eg-grid-field path="message" label="[% l('Note Text') %]"></eg-grid-field>
+  <eg-grid-field path="title"></eg-grid-field>
+  <eg-grid-field path="pub"></eg-grid-field>
+  <eg-grid-field path="stop_date" hidden></eg-grid-field>
+  <eg-grid-field path="editor.usrname" label="[% l('Editor') %]" required hidden></eg-grid-field>
+  <eg-grid-field path="edit_date" hidden></eg-grid-field>
+  <eg-grid-field path="staff.usrname" label="[% l('Staff') %]" required></eg-grid-field>
+  <eg-grid-field path="standing_penalty.name" required label="[% l('Penalty Name') %]"></eg-grid-field>
   <eg-grid-field path="standing_penalty.block_list" required hidden></eg-grid-field>
   <eg-grid-field path="standing_penalty.*" hidden></eg-grid-field>
-
+  <eg-grid-field path="org_unit.shortname" label="[% l('Location') %]" required></eg-grid-field>
+  <eg-grid-field path="create_date"></eg-grid-field>
+  <eg-grid-field path="ausp_id" required hidden></eg-grid-field>
+  <eg-grid-field path="aum_id" required hidden></eg-grid-field>
+  <eg-grid-field path="id" required hidden></eg-grid-field>
 </eg-grid>
 
 
index 11df48d..e863030 100644 (file)
       ng-repeat="penalty in alert_penalties()">
       <div 
         class="col-md-9 patron-summary-alert"
-        title="{{penalty.standing_penalty().name()}}">
-        {{penalty.note() || penalty.standing_penalty().label()}}
+        title="{{penalty.standing_penalty().name()}} (id {{penalty.id()}}): {{penalty.usr_message().message()}}">
+        {{penalty.usr_message().title() || penalty.standing_penalty().label() | limitTo: 40}}
       </div>
       <div class="col-md-3">
         {{penalty.set_date() | date:$root.egDateFormat}}
       </div>
     </div>
-    <div class="row patron-summary-divider">
-      <div
-        class="col-md-9 patron-summary-alert"
-        title="[% l('Patron Alert Message') %]"
-        ng-if="patron().alert_message()">
-        {{patron().alert_message()}}
-      </div>
-    </div>
     <div ng-if="patron().photo_url()" class="row">
          <div class="col-md-8 patron_photo_wrap"><img class="img-responsive img-rounded" src="{{patron().photo_url()}}" alt=""></div>
     </div>
     <div class="row" 
-      ng-class="{'patron-summary-divider' : alert_penalties().length || patron().alert_message()}">
+      ng-class="{'patron-summary-divider' : alert_penalties().length}">
       <div class="col-md-5">[% l('Profile') %]</div>
       <div class="col-md-7">{{patron().profile().name()}}</div>
     </div>
index d588f8b..0637a63 100644 (file)
@@ -1,39 +1,93 @@
-<form ng-submit="ok(args)" role="form">
+<form id="patron-notes-container" ng-submit="ok(args)" role="form">
     <div class="modal-header">
       <button type="button" class="close" ng-click="cancel()" 
         aria-hidden="true">&times;</button>
-      <h4 class="modal-title">[% l('Apply Standing Penalty / Message') %]</h4>
+      <h4 class="modal-title">[% l('Create or Edit Note') %]</h4>
     </div>
     <div class="modal-body">
       <div class="row">
-        <div class="col-md-8">
+        <div class="col-md-6 pull-left">
           <ul class="nav nav-pills">
             <!-- 21 == SILENT_NOTE -->
             <li ng-class="{active : args.penalty == 21}">
-              <a href ng-click="args.penalty=21">[% l('Note') %]</a>
+              <a href ng-click="set_penalty(21);">[% l('Note') %]</a>
             </li>
             <!-- 20 == ALERT_NOTE -->
             <li ng-class="{active : args.penalty == 20}">
-              <a href ng-click="args.penalty=20">[% l('Alert') %]</a>
+              <a href ng-click="set_penalty(20);">[% l('Alert') %]</a>
             </li>
             <!-- 25 == STAFF_CHR -->
             <li ng-class="{active : args.penalty == 25}">
-              <a href ng-click="args.penalty=25">[% l('Block') %]</a>
+              <a href ng-click="set_penalty(25);">[% l('Block') %]</a>
             </li>
           </ul>
         </div>
-        <div class="col-md-4 pull-right nullable">
-          <select class="form-control"
-                  ng-model="args.custom_penalty"
-                  ng-options="penalty.id() as penalty.label() for penalty in penalties">
-            <option value="">[% l('Penalty Type') %]</option>
-          </select>
+        <div class="col-md-6 pull-left">
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-6 nullable">
+            <label>[% l('Penalty Type:') %]
+              <select class="form-control"
+                      ng-model="args.custom_penalty"
+                      ng-disabled="(args.pub && args.read_date) || args.deleted"
+                      ng-options="penalty.id() as penalty.label() for penalty in penalties">
+              </select>
+            </label>
+        </div>
+        <div class="col-md-6 pull-right">
+            <div>
+                <label ng-disabled="(args.pub && args.read_date) || args.deleted">[% l('Depth:') %]
+                    <eg-share-depth-selector id="org_depth" useOpacLabel maxDepth="{{args.max_depth}}" ng-model="args.custom_depth"></eg-share-depth-selector>
+                </label>
+            </div>
+            <div>
+                <label>[% l('Location:') %]</label><span>&nbsp;{{args.org.shortname()}}</span>
+                    <!--<eg-org-selector selected="args.org" onchange="update_org"
+                        disable-test="cant_use_org"></eg-org-selector>-->
+            </div>
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-3 pull-left">
+            <label>
+                <input type="checkbox" ng-model="args.pub"
+                      ng-disabled="(args.pub && args.read_date) || args.deleted"/>
+                    [% l('Patron Visible') %]
+            </label>
+        </div>
+        <div class="col-md-3 pull-left">
+            <label ng-if="args.pub && args.read_date">
+                    [% l('Read on [_1]', '{{args.read_date | date:$root.egDateAndTimeFormat}}') %]
+            </label>
+            <label ng-if="args.pub && !args.read_date">
+                    [% l('Unread') %]
+            </label>
+        </div>
+        <div class="col-md-3 pull-left">
+            <label ng-if="args.edit_date">
+                    [% l('Edited on [_1] by [_2]', '{{args.edit_date | date:$root.egDateAndTimeFormat}}', '{{args.editor.usrname()}}') %]
+            </label>
+        </div>
+        <div class="col-md-3 pull-left">
+            <label class="patron-summary-alert" ng-if="args.deleted">
+                    [% l('Deleted') %]
+            </label>
+        </div>
+      </div>
+      <div class="form-group row pad-vert">
+        <div class="col-md-12">
+          <textarea class="form-control" 
+            ng-model="args.title" placeholder="[% l('Title...') %]"
+            ng-required="true" ng-disabled="(args.pub && args.read_date) || args.deleted">
+          </textarea>
         </div>
       </div>
       <div class="form-group row pad-vert">
         <div class="col-md-12">
           <textarea class="form-control" 
-            ng-model="args.note" placeholder="[% l('Note...') %]">
+            ng-model="args.note" placeholder="[% l('Note Text...') %]"
+            ng-disabled="(args.pub && args.read_date) || args.deleted">
           </textarea>
         </div>
       </div>
       <div class="row">
         <div class="col-md-2">
           <input type="text" class="form-control" ng-hide="!require_initials" 
+            ng-disabled="(args.pub && args.read_date) || args.deleted"
             ng-model="args.initials" placeholder="[% l('Initials') %]" ng-required="require_initials"/>
         </div>
         <div class="col-md-10 pull-right">
-          <input type="submit" class="btn btn-primary" value="[% l('OK') %]"/>
+          <input type="submit" class="btn btn-primary"
+            ng-disabled="(args.pub && args.read_date) || args.deleted || !args.org" value="[% l('OK') %]"/>
           <button class="btn btn-warning" ng-click="cancel($event)">[% l('Cancel') %]</button>
         </div>
       </div>
index 2ac4d9b..5a92101 100644 (file)
@@ -167,6 +167,7 @@ but the ones I'm finding aren't quite cutting it..*/
 
 /* Angular applies these classes based on the field's 
  * required and pattern settings */
+#patron-notes-container .ng-invalid-required,
 #patron-reg-container .ng-invalid,
 #patron-reg-container .ng-invalid-required,
 #patron-pay-by-credit-form .ng-invalid {
index 48d6152..619ab0e 100644 (file)
@@ -32,7 +32,6 @@ Template for printing a patron's data, including addresses and statistical categ
   <div>Is Group Lead Account: {{patron.master_account}}</div>
   <div>Claims-Returned Count: {{patron.claims_returned_count}}</div>
   <div>Claims-Never-Checked-Out Count: {{patron.claims_never_checked_out_count}}</div>
-  <div>Alert Message: {{patron.alert_message}}</div>
 
   <div>
     <div ng-repeat="address in patron.addresses">
index 0e9ae91..bc1acd1 100644 (file)
@@ -185,8 +185,6 @@ function load() {
         'ui.patron.edit.au.claims_returned_count.suggest',
         'ui.patron.edit.au.claims_never_checked_out_count.show',
         'ui.patron.edit.au.claims_never_checked_out_count.suggest',
-        'ui.patron.edit.au.alert_message.show',
-        'ui.patron.edit.au.alert_message.suggest',
         'ui.patron.edit.aua.post_code.regex',
         'ui.patron.edit.aua.post_code.example',
         'ui.patron.edit.aua.county.require',
index d01ccd5..cbf2894 100644 (file)
@@ -412,7 +412,6 @@ function($scope , $q , egCore , ngToast) {
         master_account : 'f',
         claims_returned_count : '0',
         claims_never_checked_out_count : '0',
-        alert_message : 'Coat is in the lost-and-found behind the circ desk',
         ident_type: {name: function() {return 'Drivers License'}},
         ident_value: '11332445',
         ident_type2: {name: function() {return 'Other'}},
index 4409cb1..7fab0ea 100644 (file)
@@ -15,6 +15,10 @@ angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap', 'egUserBucketMod',
     });
 }])
 
+.factory("hasPermAt",function(){
+    return {};
+})
+
 .config(function($routeProvider, $locationProvider, $compileProvider) {
     $locationProvider.html5Mode(true);
     $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|mailto|blob):/); // grid export
@@ -22,7 +26,7 @@ angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap', 'egUserBucketMod',
     // data loaded at startup which only requires an authtoken goes
     // here. this allows the requests to be run in parallel instead of
     // waiting until startup has completed.
-    var resolver = {delay : ['egCore','egUser', function(egCore , egUser) {
+    var resolver = {delay : ['egCore','egUser','hasPermAt', function(egCore , egUser , hasPermAt) {
 
         // fetch the org settings we care about during egStartup
         // and toss them into egCore.env as egCore.env.aous[name] = value.
@@ -61,7 +65,16 @@ angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap', 'egUserBucketMod',
             ]);
         }
 
-        return egCore.startup.go();
+        return egCore.startup.go().then(function(go_promise) {
+            // FIXME: the following is really just for PatronMessagesCtrl
+            // and PatronCtrl, so we could refactor to avoid calling it
+            // for every controller
+            return egCore.perm.hasPermFullPathAt('VIEW_USER')
+            .then(function(orgList) {
+                hasPermAt['VIEW_USER'] = orgList;
+                return go_promise;
+            });
+        });
     }]};
 
     $routeProvider.when('/circ/patron/search', {
@@ -219,8 +232,8 @@ angular.module('egPatronApp', ['ngRoute', 'ui.bootstrap', 'egUserBucketMod',
  *
  * */
 .controller('PatronCtrl',
-       ['$scope','$q','$location','$filter','egCore','egNet','egUser','egAlertDialog','egConfirmDialog','egPromptDialog','patronSvc',
-function($scope,  $q , $location , $filter , egCore , egNet , egUser , egAlertDialog , egConfirmDialog , egPromptDialog , patronSvc) {
+       ['$scope','$q','$location','$filter','egCore','egNet','egUser','egAlertDialog','egConfirmDialog','egPromptDialog','patronSvc','hasPermAt',
+function($scope,  $q , $location , $filter , egCore , egNet , egUser , egAlertDialog , egConfirmDialog , egPromptDialog , patronSvc , hasPermAt) {
 
     $scope.is_patron_edit = function() {
         return Boolean($location.path().match(/patron\/\d+\/edit$/));
@@ -323,6 +336,15 @@ function($scope,  $q , $location , $filter , egCore , egNet , egUser , egAlertDi
     }
 
     $scope.patron = function() { return patronSvc.current }
+    $scope.visible_notes = function() {
+        var p = patronSvc.current;
+        if (p) {
+            var org_ids = hasPermAt['VIEW_USER'];
+            var filtered_notes = p.notes().filter(function(n) { return org_ids.indexOf(n.org_unit()) > -1; });
+            return filtered_notes;
+        }
+        return [];
+    }
     $scope.patron_stats = function() { return patronSvc.patron_stats }
     $scope.summary_stat_cats = function() { return patronSvc.summary_stat_cats }
     $scope.hasAlerts = function() { return patronSvc.hasAlerts }
@@ -731,11 +753,11 @@ function($scope,  $q,  $routeParams,  $timeout,  $window,  $location,  egCore ,
  * Manages messages
  */
 .controller('PatronMessagesCtrl',
-       ['$scope','$q','$routeParams','egCore','$uibModal','patronSvc','egCirc',
-function($scope , $q , $routeParams,  egCore , $uibModal , patronSvc , egCirc) {
+       ['$scope','$q','$routeParams','egCore','$uibModal','patronSvc','egCirc','hasPermAt',
+function($scope , $q , $routeParams,  egCore , $uibModal , patronSvc , egCirc , hasPermAt ) {
     $scope.initTab('messages', $routeParams.id);
     var usr_id = $routeParams.id;
-    var org_ids = egCore.org.fullPath(egCore.auth.user().ws_ou(), true);
+    var org_ids = hasPermAt.VIEW_USER;
 
     // setup date filters
     var start = new Date(); // now - 1 year
@@ -757,7 +779,7 @@ function($scope , $q , $routeParams,  egCore , $uibModal , patronSvc , egCirc) {
    
     var activeGrid = $scope.activeGridControls = {
         setSort : function() {
-            return ['set_date'];
+            return ['create_date'];
         },
         setQuery : function() {
             return {
@@ -773,53 +795,120 @@ function($scope , $q , $routeParams,  egCore , $uibModal , patronSvc , egCirc) {
 
     var archiveGrid = $scope.archiveGridControls = {
         setSort : function() {
-            return ['set_date'];
+            return ['create_date'];
         },
         watchQuery : function() {
             return {
                 usr : usr_id, 
                 org_unit : org_ids,
                 stop_date : {'<=' : 'now'},
-                set_date : {between : date_range()}
+                create_date : {between : date_range()}
             };
         }
     };
 
+    $scope.test_for_disable_remove_penalty = function() {
+        var selected = $scope.activeGridControls.selectedItems();
+        var found_pub_and_read_and_not_deleted = false;
+        angular.forEach(selected, function(s) {
+            if (Boolean(s.pub == 't') && Boolean(s.read_date) && !Boolean(s.deleted == 't')) {
+                found_pub_and_read_and_not_deleted = true;
+            }
+        });
+        return found_pub_and_read_and_not_deleted;
+    }
+
     $scope.removePenalty = function(selected) {
-        // the grid stores flattened penalties.  Fetch penalty objects first
+        if (selected.length == 0) return;
 
-        var ids = selected.map(function(s){ return s.id });
-        egCore.pcrud.search('ausp', 
-            {id : ids}, {}, 
-            {atomic : true, authoritative : true}
+        // TODO: need confirmation dialog
 
-        // then delete them
-        ).then(function(penalties) {
-            return egCore.pcrud.remove(penalties);
+        var promises = [];
+        // figure out the view components
+        var aum_ids = [];
+        var ausp_ids = [];
+        angular.forEach(selected, function(s) {
+            if (s.aum_id) { aum_ids.push(s.aum_id); }
+            if (s.ausp_id) { ausp_ids.push(s.ausp_id); }
+        });
 
-        // then refresh the grid
-        }).then(function() {
+        // fetch all of them since trying to pull them
+        // off of patronSvc.current isn't reliable
+        if (ausp_ids.length > 0) {
+            promises.push(
+                egCore.pcrud.search('ausp',
+                    {id : ausp_ids}, {},
+                    {atomic : true, authoritative : true}
+                ).then(function(penalties) {
+                    return egCore.pcrud.remove(penalties);
+                })
+            );
+        }
+        if (aum_ids.length > 0) {
+            promises.push(
+                egCore.pcrud.search('aum',
+                    {id : aum_ids}, {},
+                    {atomic : true, authoritative : true}
+                ).then(function(messages) {
+                    return egCore.pcrud.remove(messages);
+                })
+            );
+        }
+        $q.all(promises).then(function() {
             activeGrid.refresh();
+            archiveGrid.refresh();
+            // force a refresh of the user
+            patronSvc.setPrimary(patronSvc.current.id(), null, true);
         });
     }
 
     $scope.archivePenalty = function(selected) {
-        // the grid stores flattened penalties.  Fetch penalty objects first
+        if (selected.length == 0) return;
 
-        var ids = selected.map(function(s){ return s.id });
-        egCore.pcrud.search('ausp', 
-            {id : ids}, {}, 
-            {atomic : true, authoritative : true}
+        // TODO: need confirmation dialog
 
-        // then delete them
-        ).then(function(penalties) {
-            angular.forEach(penalties, function(p){ p.stop_date('now') });
-            return egCore.pcrud.update(penalties);
+        var promises = [];
+        // figure out the view components
+        var aum_ids = [];
+        var ausp_ids = [];
+        angular.forEach(selected, function(s) {
+            if (s.aum_id) { aum_ids.push(s.aum_id); }
+            if (s.ausp_id) { ausp_ids.push(s.ausp_id); }
+        });
 
-        // then refresh the grid
-        }).then(function() {
+        // fetch all of them since trying to pull them
+        // off of patronSvc.current isn't reliable
+        if (ausp_ids.length > 0) {
+            promises.push(
+                egCore.pcrud.search('ausp',
+                    {id : ausp_ids}, {},
+                    {atomic : true, authoritative : true}
+                ).then(function(penalties) {
+                    angular.forEach(penalties, function(p) {
+                        p.stop_date('now');
+                    });
+                    return egCore.pcrud.update(penalties);
+                })
+            );
+        }
+        if (aum_ids.length > 0) {
+            promises.push(
+                egCore.pcrud.search('aum',
+                    {id : aum_ids}, {},
+                    {atomic : true, authoritative : true}
+                ).then(function(messages) {
+                    angular.forEach(messages, function(m) {
+                        m.stop_date('now');
+                    });
+                    return egCore.pcrud.update(messages);
+                })
+            );
+        }
+        $q.all(promises).then(function() {
             activeGrid.refresh();
             archiveGrid.refresh();
+            // force a refresh of the user
+            patronSvc.setPrimary(patronSvc.current.id(), null, true);
         });
     }
 
@@ -848,15 +937,60 @@ function($scope , $q , $routeParams,  egCore , $uibModal , patronSvc , egCirc) {
     $scope.editPenalty = function(selected) {
         if (selected.length == 0) return;
 
-        // grab the penalty from the user object
-        var penalty = patronSvc.current.standing_penalties().filter(
-            function(p) {return p.id() == selected[0].id})[0];
+        var promises = [];
+        // figure out the view components
+        var aum_ids = []; var aum_objs = {};
+        var ausp_ids = []; var ausp_objs = {};
+        var pairs = [];
+        angular.forEach(selected, function(s) {
+            if (s.aum_id) { aum_ids.push(s.aum_id); }
+            if (s.ausp_id) { ausp_ids.push(s.ausp_id); }
+            pairs.push( { aum_id : s.aum_id, ausp_id : s.ausp_id } );
+        });
 
-        egCirc.edit_penalty(penalty).then(function() {
-            activeGrid.refresh();
-            // force a refresh of the user, since they may now
-            // have blocking penalties, etc.
-            patronSvc.setPrimary(patronSvc.current.id(), null, true);
+        // fetch all of them since trying to pull them
+        // off of patronSvc.current isn't reliable
+        // (we want deleted user messages too)
+        if (ausp_ids.length > 0) {
+            promises.push(
+                egCore.pcrud.search('ausp',
+                    {id : ausp_ids}, {},
+                    {atomic : true, authoritative : true}
+                ).then(function(penalties) {
+                    angular.forEach(penalties, function(p) {
+                        ausp_objs[p.id()] = p;
+                    });
+                    return $q.when();
+                })
+            );
+        }
+        if (aum_ids.length > 0) {
+            promises.push(
+                egCore.pcrud.search('aum',
+                    {id : aum_ids}, {
+                        flesh : 1,
+                        flesh_fields : {
+                            aum : ['editor']
+                        }
+                    },
+                    {atomic : true, authoritative : true}
+                ).then(function(messages) {
+                    angular.forEach(messages, function(m) {
+                        aum_objs[m.id()] = m;
+                    });
+                    return $q.when();
+                })
+            );
+        }
+        $q.all(promises).then(function() {
+            angular.forEach(pairs, function(pair) {
+                egCirc.edit_penalty(ausp_objs[pair.ausp_id],aum_objs[pair.aum_id]).then(function() {
+                    activeGrid.refresh();
+                    // force a refresh of the user, since they may now
+                    // have blocking penalties, etc.
+                    patronSvc.setPrimary(patronSvc.current.id(), null, true);
+                });
+            });
         });
     }
 }])
index 17c908b..00d84c3 100644 (file)
@@ -390,8 +390,6 @@ angular.module('egCoreMod')
             'ui.patron.edit.au.claims_returned_count.suggest',
             'ui.patron.edit.au.claims_never_checked_out_count.show',
             'ui.patron.edit.au.claims_never_checked_out_count.suggest',
-            'ui.patron.edit.au.alert_message.show',
-            'ui.patron.edit.au.alert_message.suggest',
             'ui.patron.edit.aua.post_code.regex',
             'ui.patron.edit.aua.post_code.example',
             'ui.patron.edit.aua.county.require',
index 54aa9f3..6f6df2c 100644 (file)
@@ -605,15 +605,14 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
         });
     }
 
-    service.get_staff_penalty_types = function() {
+    service.get_all_penalty_types = function() {
         if (egCore.env.csp) 
             return $q.when(egCore.env.csp.list);
-        return egCore.pcrud.search(
-            // id <= 100 are reserved for system use
-            'csp', {id : {'>': 100}}, {}, {atomic : true})
-        .then(function(penalties) {
-            return egCore.env.absorbList(penalties, 'csp').list;
-        });
+        return egCore.pcrud.retrieveAll('csp', {}, {atomic : true}).then(
+            function(penalties) {
+                return egCore.env.absorbList(penalties, 'csp').list;
+            }
+        );
     }
 
     // ideally all of these data should be returned with the response,
@@ -2072,34 +2071,96 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
         });
     }
 
+    function generate_penalty_dialog_watch_callback($scope,egCore,allPenalties) {
+        return function(newval) {
+            if (newval) {
+                var selected_penalty = allPenalties.filter(function(p) {
+                        return p.id() == newval; })[0];
+                var penalty_id = selected_penalty.id();
+                if (penalty_id == 20 || penalty_id == 21 || penalty_id == 25) {
+                    $scope.args.custom_penalty = penalty_id;
+                    $scope.args.penalty = penalty_id;
+                }
+                if (penalty_id > 100) {
+                    $scope.args.custom_penalty = penalty_id;
+                    $scope.args.penalty = null;
+                }
+                // there's a $watch on custom_depth
+                if (selected_penalty.org_depth() || selected_penalty.org_depth() == 0) {
+                    $scope.args.custom_depth = selected_penalty.org_depth();
+                } else {
+                    $scope.args.custom_depth = $scope.args.org.ou_type().depth();
+                }
+            }
+        };
+    }
+
     service.create_penalty = function(user_id) {
         return $uibModal.open({
             templateUrl: './circ/share/t_new_message_dialog',
             backdrop: 'static',
             controller: 
-                   ['$scope','$uibModalInstance','staffPenalties',
-            function($scope , $uibModalInstance , staffPenalties) {
+                   ['$scope','$uibModalInstance','allPenalties','goodOrgs',
+            function($scope , $uibModalInstance , allPenalties , goodOrgs) {
                 $scope.focusNote = true;
-                $scope.penalties = staffPenalties;
+                $scope.penalties = allPenalties.filter(
+                    function(p) { return p.id() > 100 || p.id() == 20 || p.id() == 21 || p.id() == 25; });
+                $scope.set_penalty = function(id) {
+                    if (!($scope.args.pub && $scope.args.read_date) && !$scope.args.deleted) {
+                        $scope.args.penalty = id;
+                    }
+                }
                 $scope.require_initials = service.require_initials;
-                $scope.args = {penalty : 21}; // default to Note
-                $scope.setPenalty = function(id) {
-                    args.penalty = id;
+                $scope.update_org = function(org) {
+                    if (!($scope.args.pub && $scope.args.read_date) && !$scope.args.deleted) {
+                        $scope.args.org = org;
+                    }
+                }
+                $scope.cant_use_org = function(org_id) {
+                    return ($scope.args.pub && $scope.args.read_date) || $scope.args.deleted || goodOrgs.indexOf(org_id) == -1;
                 }
+                $scope.args = {
+                    pub : false,
+                    penalty : 21, // default to Note
+                    org : egCore.org.get(egCore.auth.user().ws_ou())
+                };
+                $scope.args.max_depth = $scope.args.org.ou_type().depth();
                 $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
                 $scope.cancel = function($event) { 
                     $uibModalInstance.dismiss();
                     $event.preventDefault();
                 }
+                $scope.$watch('args.penalty', generate_penalty_dialog_watch_callback($scope,egCore,allPenalties));
+                $scope.$watch('args.custom_penalty', generate_penalty_dialog_watch_callback($scope,egCore,allPenalties));
+                $scope.$watch('args.custom_depth', function(org_depth) {
+                    if (org_depth || org_depth == 0) {
+                        egCore.net.request(
+                            'open-ils.actor',
+                            'open-ils.actor.org_unit.ancestor_at_depth.retrieve',
+                            egCore.auth.token(), egCore.auth.user().ws_ou(), org_depth
+                        ).then(function(ctx_org) {
+                            if (ctx_org) {
+                                $scope.args.org = egCore.org.get(ctx_org);
+                            }
+                        });
+                    }
+                });
             }],
-            resolve : { staffPenalties : service.get_staff_penalty_types }
+            resolve : {
+                allPenalties : service.get_all_penalty_types,
+                goodOrgs : egCore.perm.hasPermAt('UPDATE_USER', true)
+            }
         }).result.then(
             function(args) {
                 var pen = new egCore.idl.ausp();
+                var msg = {
+                    pub : args.pub,
+                    title : args.title,
+                    message : args.note ? args.note : ''
+                };
                 pen.usr(user_id);
-                pen.org_unit(egCore.auth.user().ws_ou());
-                pen.note(args.note);
-                if (args.initials) pen.note(args.note + ' [' + args.initials + ']');
+                pen.org_unit(args.org.id());
+                if (args.initials) msg.message = (args.note ? args.note : '') + ' [' + args.initials + ']';
                 if (args.custom_penalty) {
                     pen.standing_penalty(args.custom_penalty);
                 } else {
@@ -2111,41 +2172,133 @@ function($uibModal , $q , egCore , egAlertDialog , egConfirmDialog,  egAddCopyAl
                 return egCore.net.request(
                     'open-ils.actor',
                     'open-ils.actor.user.penalty.apply',
-                    egCore.auth.token(), pen
+                    egCore.auth.token(), pen, msg
                 );
             }
         );
     }
 
     // assumes, for now anyway,  penalty type is fleshed onto usr_penalty.
-    service.edit_penalty = function(usr_penalty) {
+    service.edit_penalty = function(pen,aum) {
         return $uibModal.open({
             templateUrl: './circ/share/t_new_message_dialog',
             backdrop: 'static',
             controller: 
-                   ['$scope','$uibModalInstance','staffPenalties',
-            function($scope , $uibModalInstance , staffPenalties) {
+                   ['$scope','$uibModalInstance','allPenalties','goodOrgs',
+            function($scope , $uibModalInstance , allPenalties , goodOrgs) {
+                // We may need to vivicate usr_penalty (pen) or usr_message (aum)
+                if (!pen) {
+                    pen = new egCore.idl.ausp();
+                    pen.usr(aum.usr());
+                    pen.org_unit(aum.sending_lib()); // FIXME: preserve sending_lib or use ws_ou?
+                    pen.staff(egCore.auth.user().id());
+                    pen.set_date('now');
+                    pen.usr_message(aum.id());
+                    pen.isnew(true);
+                    aum.ischanged(true);
+                }
+                if (!aum) {
+                    aum = new egCore.idl.aum();
+                    aum.create_date('now');
+                    aum.sending_lib(pen.org_unit());
+                    aum.pub(false);
+                    aum.usr(pen.usr());
+                    aum.isnew(true);
+                    pen.ischanged(true);
+                }
+
                 $scope.focusNote = true;
-                $scope.penalties = staffPenalties;
+                $scope.penalties = allPenalties.filter(
+                    function(p) { return p.id() > 100 || p.id() == 20 || p.id() == 21 || p.id() == 25; });
+                $scope.set_penalty = function(id) {
+                    if (!($scope.args.pub && $scope.args.read_date) && !$scope.args.deleted) {
+                        $scope.args.penalty = id;
+                    }
+                }
                 $scope.require_initials = service.require_initials;
+                $scope.update_org = function(org) {
+                    if (!($scope.args.pub && $scope.args.read_date) && !$scope.args.deleted) {
+                        $scope.args.org = org;
+                    }
+                }
+                $scope.cant_use_org = function(org_id) {
+                    return ($scope.args.pub && $scope.args.read_date) || $scope.args.deleted || goodOrgs.indexOf(org_id) == -1;
+                }
+                var penalty_id = pen.standing_penalty();
                 $scope.args = {
-                    penalty : usr_penalty.standing_penalty().id(),
-                    note : usr_penalty.note()
+                    penalty : pen.isnew()
+                        ? 21 // default to Note
+                        : penalty_id,
+                    pub : typeof aum.pub() == 'boolean'
+                        ? aum.pub()
+                        : aum.pub() == 't',
+                    title : aum.title(),
+                    note : aum.message() ? aum.message() : '',
+                    org : egCore.org.get(pen.org_unit()),
+                    deleted : typeof aum.deleted() == 'boolean'
+                        ? aum.deleted()
+                        : aum.deleted() == 't',
+                    read_date : aum.read_date(),
+                    edit_date : aum.edit_date(),
+                    editor : aum.editor()
+                }
+                $scope.args.max_depth = $scope.args.org.ou_type().depth();
+                $scope.original_org = $scope.args.org;
+                $scope.workstation_depth = egCore.org.get(egCore.auth.user().ws_ou()).ou_type().depth();
+                if (penalty_id == 20 || penalty_id == 21 || penalty_id == 25) {
+                    $scope.args.custom_penalty = penalty_id;
+                }
+                if (penalty_id > 100) {
+                    $scope.args.custom_penalty = penalty_id;
+                    $scope.args.penalty = null;
                 }
-                $scope.setPenalty = function(id) { args.penalty = id; }
                 $scope.ok = function(count) { $uibModalInstance.close($scope.args) }
                 $scope.cancel = function($event) { 
                     $uibModalInstance.dismiss();
                     $event.preventDefault();
                 }
+                $scope.$watch('args.penalty', generate_penalty_dialog_watch_callback($scope,egCore,allPenalties));
+                $scope.$watch('args.custom_penalty', generate_penalty_dialog_watch_callback($scope,egCore,allPenalties));
+                $scope.$watch('args.custom_depth', function(org_depth) {
+                    if (org_depth || org_depth == 0) {
+                        if (org_depth > $scope.workstation_depth) {
+                            $scope.args.org = $scope.original_org;
+                        } else {
+                            egCore.net.request(
+                                'open-ils.actor',
+                                'open-ils.actor.org_unit.ancestor_at_depth.retrieve',
+                                egCore.auth.token(), egCore.auth.user().ws_ou(), org_depth
+                            ).then(function(ctx_org) {
+                                if (ctx_org) {
+                                    $scope.args.org = egCore.org.get(ctx_org);
+                                }
+                            });
+                        }
+                    }
+                });
             }],
-            resolve : { staffPenalties : service.get_staff_penalty_types }
+            resolve : {
+                allPenalties : service.get_all_penalty_types,
+                goodOrgs : egCore.perm.hasPermAt('UPDATE_USER', true)
+            }
         }).result.then(
             function(args) {
-                usr_penalty.note(args.note);
-                if (args.initials) usr_penalty.note(args.note + ' [' + args.initials + ']');
-                usr_penalty.standing_penalty(args.penalty);
-                return egCore.pcrud.update(usr_penalty);
+                aum.pub(args.pub);
+                aum.title(args.title);
+                aum.message(args.note);
+                aum.sending_lib(egCore.org.get(egCore.auth.user().ws_ou()).id());
+                pen.org_unit(egCore.org.get(args.org).id());
+                if (args.initials) aum.message((args.note ? args.note : '') + ' [' + args.initials + ']');
+                if (args.custom_penalty) {
+                    pen.standing_penalty(args.custom_penalty);
+                } else {
+                    pen.standing_penalty(args.penalty);
+                }
+                return egCore.net.request(
+                    'open-ils.actor',
+                    'open-ils.actor.user.penalty.modify',
+                    egCore.auth.token(), pen, aum
+                );
             }
         );
     }
index 50818e5..6a5643a 100644 (file)
@@ -467,6 +467,34 @@ function($q , egNet , egAuth , egOrg) {
         });
     }
 
+    /*
+     * Returns a union of the full org path of each org unit at which the
+     * currently logged in user has the selected permissions.
+     * @permList - list or string.  Unlike hasPermAt, the response object
+     * is always a list of org ids (or an empty list).
+     */
+    service.hasPermFullPathAt = function(permList) {
+        return service.hasPermAt(permList, true)
+        .then(function(orgs) {
+            var orgHash = {};
+            if (permList.constructor != Array) {
+                orgHash[permList] = orgs;
+            } else {
+                orgHash = orgs;
+            }
+            var org_seen = {};
+            angular.forEach(orgHash, function(orgList) {
+                angular.forEach(orgList, function(org) {
+                    var full_path = egOrg.fullPath(org,true);
+                    angular.forEach(full_path, function(org2) {
+                        org_seen[org2] = true;
+                    });
+                });
+            });
+            return Object.keys(org_seen).map(function(o) { return Number(o); });
+        });
+    }
+
     return service;
 }])
 
index 07e961e..136fcdc 100644 (file)
@@ -331,7 +331,6 @@ function($q , $timeout , $location , egCore,  egUser , egConfirmDialog , $locale
         var p = service.current;
 
         if (service.alert_penalties.length ||
-            p.alert_message() ||
             p.active() == 'f' ||
             p.barred() == 't' ||
             service.patron_stats.holds.ready) {
index f8f7cd1..c903811 100644 (file)
@@ -1484,6 +1484,8 @@ https://stackoverflow.com/questions/24764802/angular-js-automatically-focus-inpu
         transclude : true,
         scope : {
             ngModel : '=',
+            useOpacLabel : '@',
+            maxDepth : '@',
         },
         require: 'ngModel',
         templateUrl : './share/t_share_depth_selector',
@@ -1497,17 +1499,26 @@ https://stackoverflow.com/questions/24764802/angular-js-automatically-focus-inpu
                 var scratch = [];
                 angular.forEach(list, function(aout) {
                     var depth = parseInt(aout.depth());
-                    if (depth in scratch) {
-                        scratch[depth].push(aout.name());
-                    } else {
-                        scratch[depth] = [ aout.name() ]
+                    if (typeof $scope.maxDepth == 'undefined' || depth <= $scope.maxDepth) {
+                        var text = $scope.useOpacLabel ? aout.opac_label() : aout.name();
+                        if (depth in scratch) {
+                            scratch[depth].push(text);
+                        } else {
+                            scratch[depth] = [ text ]
+                        }
                     }
                 });
                 scratch.forEach(function(val, idx) {
                     $scope.values.push({ id : idx,  name : scratch[idx].join(' / ') });
                 });
             });
-        }]
+        }],
+        link : function(scope, elm, attrs) {
+            if ('useOpacLabel' in attrs)
+                scope.useOpacLabel = true;
+            if ('maxDepth' in attrs) // I feel like I'm doing this wrong :)
+                scope.maxDepth = parseInt(attrs.maxdepth);
+        }
     }
 })