Merge branch 'master' of git.evergreen-ils.org:Evergreen into template-toolkit-opac...
authorBill Erickson <berick@esilibrary.com>
Thu, 8 Sep 2011 19:55:36 +0000 (15:55 -0400)
committerBill Erickson <berick@esilibrary.com>
Thu, 8 Sep 2011 19:55:36 +0000 (15:55 -0400)
34 files changed:
Open-ILS/examples/fm_IDL.xml
Open-ILS/examples/oils_sip.xml.example
Open-ILS/src/Makefile.am
Open-ILS/src/perlmods/lib/OpenILS/Application/Booking.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/action.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor/AstCall.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/Configure.pm
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/020.schema.functions.sql
Open-ILS/src/sql/Pg/095.schema.booking.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/999.functions.global.sql
Open-ILS/src/sql/Pg/upgrade/0615.schema.generic-mapping-index-normalizer.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/0616.schema.actor_org_unit_trigger_prox_update.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/0617.schema.add-reservation-email-notify.sql [new file with mode: 0644]
Open-ILS/src/sql/Pg/upgrade/0618.data.org-setting-phone_pw.sql [new file with mode: 0644]
Open-ILS/src/support-scripts/eg_db_config.pl
Open-ILS/src/templates/booking/reservation.tt2
Open-ILS/src/templates/conify/global/acq/provider.tt2
Open-ILS/web/js/dojo/openils/XUL.js
Open-ILS/web/js/dojo/openils/booking/nls/reservation.js
Open-ILS/web/js/ui/default/actor/user/register.js
Open-ILS/web/js/ui/default/booking/reservation.js
Open-ILS/web/js/ui/default/conify/global/acq/provider.js
Open-ILS/web/js/ui/default/vandelay/vandelay.js
Open-ILS/xul/staff_client/chrome/content/OpenILS/util_overlay_chrome.xul
Open-ILS/xul/staff_client/chrome/content/OpenILS/util_overlay_offline.xul
Open-ILS/xul/staff_client/chrome/content/auth/controller.js
Open-ILS/xul/staff_client/chrome/content/main/main.js
Open-ILS/xul/staff_client/server/admin/org_unit_settings.js
Open-ILS/xul/staff_client/server/cat/volume_copy_creator.js
Open-ILS/xul/staff_client/server/patron/summary.js

index 2f9eb59..f94d73f 100644 (file)
@@ -3568,6 +3568,7 @@ SELECT  usr,
                        <field reporter:label="Request Library" name="request_lib" reporter:datatype="link"/>
                        <field reporter:label="Pickup Library" name="pickup_lib" reporter:datatype="link"/>
                        <field reporter:label="Capture Staff" name="capture_staff" reporter:datatype="link"/>
+                       <field reporter:label="Notify by Email?" name="email_notify" reporter:datatype="bool"/>
                        <field reporter:label="Attribute Value Maps" name="attr_val_maps" oils_persist:virtual="true" reporter:datatype="link"/>
                </fields>
                <links>
index 14446aa..5d4ebd3 100644 (file)
                     -->
                                        <path>LOCALSTATEDIR/</path>
                     <path>LOCALSTATEDIR/catalog/</path>
+                   <path>LIBDIR/javascript/</path>
                                        <item_config>circ/circ_item_config.js</item_config>
                                </scripts>
 
index 3ebbda7..a70bbcc 100644 (file)
@@ -173,6 +173,7 @@ ilscore-install:
        cp -r @srcdir@/templates/strings $(DESTDIR)$(TEMPLATEDIR)
        sed -i 's|LOCALSTATEDIR|@localstatedir@|g' '$(DESTDIR)@sysconfdir@/oils_sip.xml.example'
        sed -i 's|SYSCONFDIR|@sysconfdir@|g' '$(DESTDIR)@sysconfdir@/oils_sip.xml.example'
+       sed -i 's|LIBDIR|@libdir@|g' '$(DESTDIR)@sysconfdir@/oils_sip.xml.example'
        sed -i 's|LOCALSTATEDIR|@localstatedir@|g' '$(DESTDIR)@sysconfdir@/opensrf_core.xml.example'
        sed -i 's|SYSCONFDIR|@sysconfdir@|g' '$(DESTDIR)@sysconfdir@/opensrf_core.xml.example'
        sed -i 's|LOCALSTATEDIR|@localstatedir@|g' '$(DESTDIR)@sysconfdir@/opensrf.xml.example'
index 5e75fa8..781bded 100644 (file)
@@ -192,7 +192,7 @@ __PACKAGE__->register_method(
 sub create_bresv {
     my ($self, $client, $authtoken,
         $target_user_barcode, $datetime_range, $pickup_lib,
-        $brt, $brsrc_list, $attr_values) = @_;
+        $brt, $brsrc_list, $attr_values, $email_notify) = @_;
 
     $brsrc_list = [ undef ] if not defined $brsrc_list;
     return undef if scalar(@$brsrc_list) < 1; # Empty list not ok.
@@ -212,6 +212,7 @@ sub create_bresv {
         $bresv->pickup_lib($pickup_lib);
         $bresv->start_time($datetime_range->[0]);
         $bresv->end_time($datetime_range->[1]);
+        $bresv->email_notify(1) if $email_notify;
 
         # A little sanity checking: don't agree to put a reservation on a
         # brsrc and a brt when they don't match.  In fact, bomb out of
@@ -304,6 +305,7 @@ __PACKAGE__->register_method(
             {type => 'int', desc => 'Booking resource type'},
             {type => 'list', desc => 'Booking resource (undef ok; empty not ok)'},
             {type => 'array', desc => 'Attribute values selected'},
+            {type => 'bool', desc => 'Email notification?'},
         ],
         return => { desc => "A hash containing the new bresv and a list " .
             "of new bravm"}
@@ -1097,6 +1099,12 @@ sub capture_reservation {
 
     $e->commit or return $e->die_event;
 
+    # create action trigger event to notify that reservation is available
+    if ($reservation->email_notify) {
+        my $ses = OpenSRF::AppSession->create('open-ils.trigger');
+        $ses->request('open-ils.trigger.event.autocreate', 'reservation.available', $reservation, $reservation->pickup_lib);
+    }
+
     # XXX I'm not sure whether these last two elements of the payload
     # actually get used anywhere.
     $ret->{"resource"} = $reservation->current_resource;
index 5f8f74a..f5cb67d 100644 (file)
@@ -286,7 +286,7 @@ sub initialize_filter_normalizers {
     my $tree = shift; # open-ils.cstore.direct.config.record_attr_index_norm_map.search.atomic { "id" : { "!=" : null } }, { "flesh" : 1, "flesh_fields" : { "crainm" : ["norm"] }, "order_by" : [{ "class" : "crainm", "field" : "pos" }] }
 
     for my $crainm ( @$tree ) {
-        __PACKAGE__->add_filter_normalizer( $crainm->name, $crainm->norm->func, OpenSRF::Utils::JSON->JSON2perl($crainm->params) );
+        __PACKAGE__->add_filter_normalizer( $crainm->attr, $crainm->norm->func, OpenSRF::Utils::JSON->JSON2perl($crainm->params) );
     }
 }
 
index d54575d..6a2b6d6 100644 (file)
@@ -12,6 +12,7 @@ use OpenILS::Utils::PermitHold;
 use DateTime;
 use DateTime::Format::ISO8601;
 use OpenILS::Utils::Penalty;
+use POSIX qw(ceil);
 
 sub isTrue {
        my $v = shift;
@@ -920,10 +921,8 @@ sub generate_fines {
                        }
 
             next if ($last_fine > $now);
-            my $pending_fine_count = int( ($now - $last_fine) / $fine_interval ); 
-
-            # Generate fines for the interval we are currently inside, when the fine interval is some multiple of 1d
-            $pending_fine_count++ if ($fine_interval && ($fine_interval % 86400 == 0));
+            # Generate fines for each past interval, including the one we are inside
+            my $pending_fine_count = ceil( ($now - $last_fine) / $fine_interval );
 
             if ( $last_fine == $due                         # we have no fines yet
                  && $grace_period                           # and we have a grace period
index e664da6..bddd7e7 100644 (file)
@@ -14,8 +14,6 @@ $Data::Dumper::Indent = 0;
 
 my $U = 'OpenILS::Application::AppUtils';
 
-my $e = new_editor(xact => 1);
-
 # $last_channel_used is:
 # ~ index (not literal value) of last channel used in a callfile
 # ~ index is of position in @channels (zero-based)
@@ -114,6 +112,8 @@ sub rpc_client {
 sub handler {
     my ($self, $env) = @_;
 
+    my $e = new_editor(xact => 1);
+
     $logger->info(__PACKAGE__ . ": entered handler");
 
     # assignment, not comparison
@@ -297,64 +297,69 @@ sub cleanup {
 }
 
 sub retrieve {
-    my $self   = shift or return;
-    my $client = rpc_client();
-    my $resp   = $client->send_request('retrieve');
-    unless ($resp and ref $resp) {
-         $logger->error(
-             __PACKAGE__ . ": Mediator Error: " .
-             ($resp ? 'Bad' : 'No') . " response to retrieve request"
-         );
-         return;
-    }
-
-    # my $count   = $resp{match_count}; # how many files we should have
-    # my @rm_list = ();
-    my @files   = _files($resp);
-    foreach (@files) {
-        my $content  = $resp->{$_}->content;
-        my $filename = $resp->{$_}->filename;
-        unless ($content) {
-            $logger->error(__PACKAGE__ .
-                ": Mediator sent incomplete/unintelligible message for " .
-                "filename " . ($filename || 'UNKNOWN'));
-            next;
-        }
-        my $feedback = feedback_hash($content);
-        my $output   = $e->retrieve_action_trigger_event_output(
-            $feedback->{event_output}
-        );
-        if ($content == $output->data) {
-            $logger->error(
-                __PACKAGE__ . ": Mediator sent duplicate file "
-                . $resp->{$_}->filename . " for event_output " .
-                $feedback->{event_output}
-            );
-        } else {
-            $output->data($content);
-        }
-        $e->commit;     # defer until after loop? probably not
-        my $clean = $client->send_request('cleanup', $filename);
-        # TODO: deletion by (comma-separated) filenames in chunks
-        # instead of individually?
-        # push @rm_list, $_; $client->send_request('cleanup', join(',',@rm_list));
-        unless ($clean and ref $clean) {
-            $logger->error(
-                __PACKAGE__ . ": Mediator Error: " .
-                ($clean ? 'Bad' : 'No') .
-                " response to cleanup $filename request");
-            next;
-        }
-        unless ($clean->{code}->value == 200 and $clean->{delete_count}) {
-            $logger->error(__PACKAGE__ . ": cleanup $filename returned " . (
-                $resp->{faultcode} ? $resp->{faultcode}->value :
-                    $resp->{     code} ? $resp->{     code}->value :
-                        " -- UNKNOWN response '$resp'"
-            ) . " with delete_count " .
-            (defined $clean->{delete_count} ? $clean->{delete_count} : 'UNDEF'));
-        }
-    }
-    return @files;
+       $logger->info("retrieve() not implemented. how'd we get here?"); # XXX
+       return;
 }
 
+#sub retrieve {
+#    my $self   = shift or return;
+#    my $client = rpc_client();
+#    my $resp   = $client->send_request('retrieve');
+#    unless ($resp and ref $resp) {
+#         $logger->error(
+#             __PACKAGE__ . ": Mediator Error: " .
+#             ($resp ? 'Bad' : 'No') . " response to retrieve request"
+#         );
+#         return;
+#    }
+#
+#    # my $count   = $resp{match_count}; # how many files we should have
+#    # my @rm_list = ();
+#    my @files   = _files($resp);
+#    foreach (@files) {
+#        my $content  = $resp->{$_}->content;
+#        my $filename = $resp->{$_}->filename;
+#        unless ($content) {
+#            $logger->error(__PACKAGE__ .
+#                ": Mediator sent incomplete/unintelligible message for " .
+#                "filename " . ($filename || 'UNKNOWN'));
+#            next;
+#        }
+#        my $feedback = feedback_hash($content);
+#        my $output   = $e->retrieve_action_trigger_event_output(
+#            $feedback->{event_output}
+#        );
+#        if ($content == $output->data) {
+#            $logger->error(
+#                __PACKAGE__ . ": Mediator sent duplicate file "
+#                . $resp->{$_}->filename . " for event_output " .
+#                $feedback->{event_output}
+#            );
+#        } else {
+#            $output->data($content);
+#        }
+#        $e->commit;     # defer until after loop? probably not
+#        my $clean = $client->send_request('cleanup', $filename);
+#        # TODO: deletion by (comma-separated) filenames in chunks
+#        # instead of individually?
+#        # push @rm_list, $_; $client->send_request('cleanup', join(',',@rm_list));
+#        unless ($clean and ref $clean) {
+#            $logger->error(
+#                __PACKAGE__ . ": Mediator Error: " .
+#                ($clean ? 'Bad' : 'No') .
+#                " response to cleanup $filename request");
+#            next;
+#        }
+#        unless ($clean->{code}->value == 200 and $clean->{delete_count}) {
+#            $logger->error(__PACKAGE__ . ": cleanup $filename returned " . (
+#                $resp->{faultcode} ? $resp->{faultcode}->value :
+#                    $resp->{     code} ? $resp->{     code}->value :
+#                        " -- UNKNOWN response '$resp'"
+#            ) . " with delete_count " .
+#            (defined $clean->{delete_count} ? $clean->{delete_count} : 'UNDEF'));
+#        }
+#    }
+#    return @files;
+#}
+
 1;
index 98e18cc..54847b3 100644 (file)
@@ -84,6 +84,19 @@ sub HoldIsAvailable {
     return 0;
 }
 
+sub ReservationIsAvailable {
+    my $self = shift;
+    my $env = shift;
+    my $reservation = $env->{target};
+
+    return 1 if
+        !$reservation->cancel_time and
+        $reservation->capture_time and
+        $reservation->current_resource;
+
+    return 0;
+}
+
 sub HoldIsCancelled {
     my $self = shift;
     my $env = shift;
index b324ac2..2501f59 100644 (file)
@@ -91,7 +91,7 @@ sub org_tree_js {
     foreach my $locale (@$locales) {
         warn "removing OrgTree from the cache for locale " . $locale->code . "...\n";
         my $cache = OpenSRF::Utils::Cache->new;
-        $cache->delete_cache("orgtree.$locale->code");
+        $cache->delete_cache("orgtree.".$locale->code);
 
         # fetch the org_unit's and org_unit_type's
         my $e = OpenILS::Utils::CStoreEditor->new;
index 1e5427f..bc00d9b 100644 (file)
@@ -86,7 +86,7 @@ CREATE TRIGGER no_overlapping_deps
     BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
     FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
 
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0614', :eg_version); -- miker/phasefx
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0618', :eg_version); -- phasefx/tsbere
 
 CREATE TABLE config.bib_source (
        id              SERIAL  PRIMARY KEY,
index 1f78468..8c6a0c8 100644 (file)
@@ -414,3 +414,44 @@ Given user input, find an appropriate barcode in the proper class.
 Will add prefix/suffix information to do so, and return all results.
 $$;
 
+CREATE OR REPLACE FUNCTION actor.org_unit_prox_update () RETURNS TRIGGER as $$
+BEGIN
+
+
+IF TG_OP = 'DELETE' THEN
+
+    DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
+
+END IF;
+
+IF TG_OP = 'UPDATE' THEN
+
+    IF NEW.parent_ou <> OLD.parent_ou THEN
+
+        DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
+            INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
+            SELECT  l.id, r.id, actor.org_unit_proximity(l.id,r.id)
+                FROM  actor.org_unit l, actor.org_unit r
+                WHERE (l.id = NEW.id or r.id = NEW.id);
+
+    END IF;
+
+END IF;
+
+IF TG_OP = 'INSERT' THEN
+
+     INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
+     SELECT  l.id, r.id, actor.org_unit_proximity(l.id,r.id)
+         FROM  actor.org_unit l, actor.org_unit r
+         WHERE (l.id = NEW.id or r.id = NEW.id);
+
+END IF;
+
+RETURN null;
+
+END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE TRIGGER proximity_update_tgr AFTER INSERT OR UPDATE OR DELETE ON actor.org_unit FOR EACH ROW EXECUTE PROCEDURE actor.org_unit_prox_update ();
+
index 6d75582..453057b 100644 (file)
@@ -128,7 +128,8 @@ CREATE TABLE booking.reservation (
        pickup_lib       INT            REFERENCES actor.org_unit(id)
                                        DEFERRABLE INITIALLY DEFERRED,
        capture_staff    INT            REFERENCES actor.usr(id)
-                                       DEFERRABLE INITIALLY DEFERRED
+                                       DEFERRABLE INITIALLY DEFERRED,
+       email_notify     BOOLEAN        NOT NULL DEFAULT FALSE
 ) INHERITS (money.billable_xact);
 
 ALTER TABLE booking.reservation ADD PRIMARY KEY (id);
index f6984df..b77364d 100644 (file)
@@ -2478,7 +2478,7 @@ INSERT into config.org_unit_setting_type
 
 ( 'patron.password.use_phone',
     oils_i18n_gettext('patron.password.use_phone', 'Patron: password from phone #', 'coust', 'label'),
-    oils_i18n_gettext('patron.password.use_phone', 'Use the last 4 digits of the patrons phone number as the default password when creating new users', 'coust', 'description'),
+    oils_i18n_gettext('patron.password.use_phone', 'By default, use the last 4 alphanumeric characters of the patrons phone number as the default password when creating new users.  The exact characters used may be configured via the "GUI: Regex for day_phone field on patron registration" setting.', 'coust', 'description'),
     'bool'),
 
 ( 'circ.charge_on_damaged',
@@ -3157,7 +3157,7 @@ INSERT into config.org_unit_setting_type
     'string'),
 ( 'ui.patron.edit.au.day_phone.regex',
     oils_i18n_gettext('ui.patron.edit.au.day_phone.regex', 'GUI: Regex for day_phone field on patron registration', 'coust', 'label'),
-    oils_i18n_gettext('ui.patron.edit.au.day_phone.regex', 'The Regular Expression for validation on the day_phone field in patron registration.', 'coust', 'description'),
+    oils_i18n_gettext('ui.patron.edit.au.day_phone.regex', E'The Regular Expression for validation on the day_phone field in patron registration. Note: The first capture group will be used for the "last 4 digits of phone number" feature, if enabled. Ex: "[2-9]\\d{2}-\\d{3}-(\\d{4})( x\\d+)?" will ignore the extension on a NANP number.', 'coust', 'description'),
     'string'),
 ( 'ui.patron.edit.au.day_phone.require',
     oils_i18n_gettext('ui.patron.edit.au.day_phone.require', 'GUI: Require day_phone field on patron registration', 'coust', 'label'),
@@ -3317,7 +3317,7 @@ INSERT into config.org_unit_setting_type
     'string'),
 ( 'ui.patron.edit.phone.regex',
     oils_i18n_gettext('ui.patron.edit.phone.regex', 'GUI: Regex for phone fields on patron registration', 'coust', 'label'),
-    oils_i18n_gettext('ui.patron.edit.phone.regex', 'The Regular Expression for validation on phone fields in patron registration. Applies to all phone fields without their own setting.', 'coust', 'description'),
+    oils_i18n_gettext('ui.patron.edit.phone.regex', 'The Regular Expression for validation on phone fields in patron registration. Applies to all phone fields without their own setting. NOTE: See description of the day_phone regex for important information about capture groups with it.', 'coust', 'description'),
     'string');
 
 -- *** Has to go below coust definition to satisfy referential integrity ***
@@ -6051,6 +6051,13 @@ INSERT INTO config.index_normalizer (name, description, func, param_count) VALUE
        0
 );
 
+INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
+    'Generic Mapping Normalizer', 
+    'Map values or sets of values to new values',
+    'generic_map_normalizer', 
+    1
+);
+
 -- make use of the index normalizers
 
 INSERT INTO config.metabib_field_index_norm_map (field,norm)
@@ -9796,3 +9803,21 @@ INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
     'bool'
 );
 
+INSERT INTO action_trigger.hook ( key, core_type, description, passive ) VALUES (
+    'reservation.available',
+    'bresv',
+    'A reservation is available for pickup',
+    false
+);
+
+INSERT INTO action_trigger.validator ( module, description ) VALUES (
+    'ReservationIsAvailable',
+    'Checked that a reserved resource is available for checkout'
+);
+
+INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
+    'booking.allow_email_notify',
+    'booking.allow_email_notify',
+    'Permit email notification when a reservation is ready for pickup.',
+    'bool'
+);
index 034b9e9..25dfb6a 100644 (file)
@@ -1897,3 +1897,29 @@ CREATE TRIGGER ingest_item_trigger
     AFTER INSERT OR UPDATE ON vandelay.queued_bib_record
     FOR EACH ROW EXECUTE PROCEDURE vandelay.ingest_bib_items();
 
+
+-- evergreen.generic_map_normalizer 
+
+CREATE OR REPLACE FUNCTION evergreen.generic_map_normalizer ( TEXT, TEXT ) RETURNS TEXT AS $f$
+my $string = shift;
+my %map;
+
+my $default = $string;
+
+$_ = shift;
+while (/^\s*?(.*?)\s*?=>\s*?(\S+)\s*/) {
+    if ($1 eq '') {
+        $default = $2;
+    } else {
+        $map{$2} = [split(/\s*,\s*/, $1)];
+    }
+    $_ = $';
+}
+
+for my $key ( keys %map ) {
+    return $key if (grep { $_ eq $string } @{ $map{$key} });
+}
+
+return $default;
+
+$f$ LANGUAGE PLPERLU;
diff --git a/Open-ILS/src/sql/Pg/upgrade/0615.schema.generic-mapping-index-normalizer.sql b/Open-ILS/src/sql/Pg/upgrade/0615.schema.generic-mapping-index-normalizer.sql
new file mode 100644 (file)
index 0000000..3c63dcf
--- /dev/null
@@ -0,0 +1,43 @@
+-- Evergreen DB patch XXXX.schema.generic-mapping-index-normalizer.sql
+--
+BEGIN;
+
+-- check whether patch can be applied
+SELECT evergreen.upgrade_deps_block_check('0615', :eg_version);
+
+-- evergreen.generic_map_normalizer 
+
+CREATE OR REPLACE FUNCTION evergreen.generic_map_normalizer ( TEXT, TEXT ) RETURNS TEXT AS $f$
+my $string = shift;
+my %map;
+
+my $default = $string;
+
+$_ = shift;
+while (/^\s*?(.*?)\s*?=>\s*?(\S+)\s*/) {
+    if ($1 eq '') {
+        $default = $2;
+    } else {
+        $map{$2} = [split(/\s*,\s*/, $1)];
+    }
+    $_ = $';
+}
+
+for my $key ( keys %map ) {
+    return $key if (grep { $_ eq $string } @{ $map{$key} });
+}
+
+return $default;
+
+$f$ LANGUAGE PLPERLU;
+
+-- evergreen.generic_map_normalizer 
+
+INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
+    'Generic Mapping Normalizer', 
+    'Map values or sets of values to new values',
+    'generic_map_normalizer', 
+    1
+);
+
+COMMIT;
diff --git a/Open-ILS/src/sql/Pg/upgrade/0616.schema.actor_org_unit_trigger_prox_update.sql b/Open-ILS/src/sql/Pg/upgrade/0616.schema.actor_org_unit_trigger_prox_update.sql
new file mode 100644 (file)
index 0000000..c3aab88
--- /dev/null
@@ -0,0 +1,46 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('0616', :eg_version);
+
+CREATE OR REPLACE FUNCTION actor.org_unit_prox_update () RETURNS TRIGGER as $$
+BEGIN
+
+
+IF TG_OP = 'DELETE' THEN
+
+    DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
+
+END IF;
+
+IF TG_OP = 'UPDATE' THEN
+
+    IF NEW.parent_ou <> OLD.parent_ou THEN
+
+        DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
+            INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
+            SELECT  l.id, r.id, actor.org_unit_proximity(l.id,r.id)
+                FROM  actor.org_unit l, actor.org_unit r
+                WHERE (l.id = NEW.id or r.id = NEW.id);
+
+    END IF;
+
+END IF;
+
+IF TG_OP = 'INSERT' THEN
+
+     INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
+     SELECT  l.id, r.id, actor.org_unit_proximity(l.id,r.id)
+         FROM  actor.org_unit l, actor.org_unit r
+         WHERE (l.id = NEW.id or r.id = NEW.id);
+
+END IF;
+
+RETURN null;
+
+END;
+$$ LANGUAGE plpgsql;
+
+
+CREATE TRIGGER proximity_update_tgr AFTER INSERT OR UPDATE OR DELETE ON actor.org_unit FOR EACH ROW EXECUTE PROCEDURE actor.org_unit_prox_update ();
+
+COMMIT;
diff --git a/Open-ILS/src/sql/Pg/upgrade/0617.schema.add-reservation-email-notify.sql b/Open-ILS/src/sql/Pg/upgrade/0617.schema.add-reservation-email-notify.sql
new file mode 100644 (file)
index 0000000..9ca561c
--- /dev/null
@@ -0,0 +1,19 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('0617', :eg_version);
+
+-- add notify columns to booking.reservation
+ALTER TABLE booking.reservation
+  ADD COLUMN email_notify BOOLEAN NOT NULL DEFAULT FALSE;
+
+-- create the hook and validator
+INSERT INTO action_trigger.hook (key, core_type, description, passive)
+  VALUES ('reservation.available', 'bresv', 'A reservation is available for pickup', false);
+INSERT INTO action_trigger.validator (module, description)
+  VALUES ('ReservationIsAvailable','Checked that a reserved resource is available for checkout');
+
+-- create org unit setting to toggle checkbox display
+INSERT INTO config.org_unit_setting_type (name, label, description, datatype)
+  VALUES ('booking.allow_email_notify', 'booking.allow_email_notify', 'Permit email notification when a reservation is ready for pickup.', 'bool');
+
+COMMIT;
diff --git a/Open-ILS/src/sql/Pg/upgrade/0618.data.org-setting-phone_pw.sql b/Open-ILS/src/sql/Pg/upgrade/0618.data.org-setting-phone_pw.sql
new file mode 100644 (file)
index 0000000..d3f74d2
--- /dev/null
@@ -0,0 +1,11 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('0618', :eg_version);
+
+UPDATE config.org_unit_setting_type SET description = E'The Regular Expression for validation on the day_phone field in patron registration. Note: The first capture group will be used for the "last 4 digits of phone number" feature, if enabled. Ex: "[2-9]\\d{2}-\\d{3}-(\\d{4})( x\\d+)?" will ignore the extension on a NANP number.' WHERE name = 'ui.patron.edit.au.day_phone.regex';
+
+UPDATE config.org_unit_setting_type SET description = 'The Regular Expression for validation on phone fields in patron registration. Applies to all phone fields without their own setting. NOTE: See description of the day_phone regex for important information about capture groups with it.' WHERE name = 'ui.patron.edit.phone.regex';
+
+UPDATE config.org_unit_setting_type SET description = oils_i18n_gettext('patron.password.use_phone', 'By default, use the last 4 alphanumeric characters of the patrons phone number as the default password when creating new users.  The exact characters used may be configured via the "GUI: Regex for day_phone field on patron registration" setting.', 'coust', 'description') WHERE name = 'patron.password.use_phone';
+
+COMMIT;
index 7a3c2a8..894f716 100755 (executable)
@@ -194,7 +194,7 @@ GetOptions("create-schema" => \$cschema,
                "build-db-file=s" => \$build_db_sh,
                "pg-contrib-dir=s" => \$pg_contribdir,
                "create-db-sql=s" => \$create_db_sql,
-               "pg-config" => \$pgconfig,
+               "pg-config=s" => \$pgconfig,
                "admin-user=s" => \$admin_user,
                "admin-password=s" => \$admin_pw,
                "service=s" => \@services,
index 5888049..ece7c58 100644 (file)
                         id="pickup_lib_selector" jsId="pickup_lib_selector"
                         searchAttr="shortname" labelAttr="shortname"></select>
                 </div>
+                <div id="contain_email_notify" class="nice_vertical_padding">
+                    <input type="checkbox" name="email_notify" id="email_notify" />
+                    <label class="AUTO_email_notify" for="email_notify"></label>
+                </div>
                 <div class="nice_vertical_padding">
                     <span class="two_buttons">
                         <input type="button"
index 117f8e2..655d838 100644 (file)
         </div>
     </div>
     <div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+        <span>Context Org Unit</span>
+        <select dojoType="openils.widget.OrgUnitFilteringSelect"
+            jsId='contextOrgSelector'
+            searchAttr='shortname'
+            labelAttr='shortname'>
+        </select>
+    </div>
+    <div dojoType="dijit.layout.ContentPane" layoutAlign="client">
         <table  jsId="pListGrid"
                 dojoType="openils.widget.AutoGrid"
                 fieldOrder="['id', 'name', 'code', 'owner', 'currency_type']"
index 8e367da..9d93f2e 100644 (file)
@@ -148,7 +148,7 @@ if(!dojo._hasResource["openils.XUL"]) {
         }
     };
 
-    openils.XUL.contentToFileSaveDialog = function(content, windowTitle) {
+    openils.XUL.contentToFileSaveDialog = function(content, windowTitle, dispositionArgs) {
         var api = new openils.XUL.SimpleXPCOM();
         api.getPrivilegeManager().enablePrivilege("UniversalXPConnect");
 
@@ -156,6 +156,29 @@ if(!dojo._hasResource["openils.XUL"]) {
         picker.init(
             window, windowTitle || "Save File", api.FP.iface.modeSave
         );
+
+        if (dispositionArgs) {
+            /**
+             * https://developer.mozilla.org/En/NsIFilePicker
+             * Example: 
+             * { defaultString : 'MyExport.csv',
+                 defaultExtension : '.csv',
+                 filterName : 'CSV',
+                 filterExtension : '*.csv',
+                 filterAll : true } */
+
+            picker.defaultString = dispositionArgs.defaultString;
+            picker.defaultExtension = dispositionArgs.defaultExtension;
+            if (dispositionArgs.filterName) {
+                picker.appendFilter(
+                    dispositionArgs.filterName,
+                    dispositionArgs.filterExtension
+                );
+            }
+            if (dispositionArgs.filterAll) 
+                picker.appendFilters(picker.filterAll)
+        }
+
         var result = picker.show();
         if (picker.file &&
                 (result == api.FP.iface.returnOK ||
index 8c39198..9b23c58 100644 (file)
@@ -56,5 +56,6 @@
     "AUTO_arbitrary_resource": "Enter the barcode of a cataloged, bookable resource:",
     "AUTO_explain_bookable": "To reserve an item that is not yet registered as a bookable resource, find it in the catalog or under <em>Display Item</em>, and select <em>Make Item Bookable</em> or <em>Book Item Now</em> there.",
     "AUTO_pickup_lib_selector": "Choose the pickup library for this reservation:",
+    "AUTO_email_notify": "Send email notification when resource is available for pickup.",
     "AUTO_or": "- Or -"
 }
index ac9b45c..3fe41b8 100644 (file)
@@ -1078,14 +1078,23 @@ function attachWidgetEvents(fmcls, fmfield, widget) {
 
             case 'day_phone':
                 // if configured, use the last four digits of the day phone number as the password
+                // Alt, use the first capture group of the validator regex
                 if(uEditUsePhonePw && patron.isnew()) {
-                    dojo.connect(widget.widget, 'onChange',
+                    dojo.connect(widget.widget, 'onChange', widget.widget,
                         function(newVal) {
-                            if(newVal && newVal.length >= 4) {
+                            var newPw = false;
+                            if(this.regExp) {
+                                matches = RegExp(this.regExp).exec(newVal);
+                                if(matches.length > 1) newPw = matches[1];
+                            }
+                            if(!newPw && newVal && newVal.length >= 4) {
+                                newPw = newVal.substring(newVal.length - 4);
+                            }
+                            if(newPw) {
                                 var pw1 = findWidget('au', 'passwd').widget;
                                 var pw2 = findWidget('au', 'passwd2').widget;
-                                pw1.attr('value', newVal.substring(newVal.length - 4));
-                                pw2.attr('value', newVal.substring(newVal.length - 4));
+                                pw1.attr('value', newPw);
+                                pw2.attr('value', newPw);
                             }
                         }
                     );
index 7893384..fcaf572 100644 (file)
@@ -293,6 +293,7 @@ function create_bresv(resource_list) {
         alert(localeStrings.INVALID_TS_RANGE);
         return;
     }
+    var email_notify = document.getElementById("email_notify").checked ? true : false;
     var results;
     try {
         results = fieldmapper.standardRequest(
@@ -304,7 +305,8 @@ function create_bresv(resource_list) {
                 pickup_lib_selected,
                 our_brt.id(),
                 resource_list,
-                attr_value_table.get_all_values()
+                attr_value_table.get_all_values(),
+                email_notify
             ]
         );
     } catch (E) {
@@ -590,6 +592,10 @@ function init_resv_iface_sel() {
 }
 
 function init_reservation_interface(widget) {
+    /* Show or hide the email notification checkbox depending on org unit setting. */
+    if (!aous_cache["booking.allow_email_notify"]) {
+        hide_dom_element(document.getElementById("contain_email_notify"));
+    }
     /* Save a global reference to the brt we're going to reserve */
     if (widget && (widget.selectedIndex != undefined)) {
         our_brt = brt_list[widget.selectedIndex];
@@ -804,7 +810,7 @@ function init_aous_cache() {
     /* The following method call could be given a longer
      * list of OU settings to fetch in the future if needed. */
     var results = fieldmapper.aou.fetchOrgSettingBatch(
-        openils.User.user.ws_ou(), ["booking.require_successful_targeting"]
+        openils.User.user.ws_ou(), ["booking.require_successful_targeting", "booking.allow_email_notify"]
     );
     if (results && !is_ils_event(results)) {
         for (var k in results) {
index 76c63a5..2dbe1da 100644 (file)
@@ -3,6 +3,7 @@ dojo.require('openils.widget.AutoGrid');
 dojo.require('dijit.form.FilteringSelect');
 dojo.require('openils.PermaCrud');
 dojo.require('openils.MarcXPathParser');
+dojo.require('openils.widget.OrgUnitFilteringSelect');
 
 
 var provider;
@@ -10,6 +11,19 @@ var xpathParser = new openils.MarcXPathParser();
 var subFields= [];
 
 function draw() {
+    var org_id = openils.User.user.ws_ou();
+    var list = fieldmapper.aou.findOrgUnit(org_id).orgNodeTrail().map(
+        function (i) {return i.id() }
+    );
+
+    new openils.User().buildPermOrgSelector(
+        'VIEW_PROVIDER', contextOrgSelector, null,
+        function() {
+            dojo.connect(contextOrgSelector, 'onChange', filterGrid);
+        }
+    );
+
+
     if(providerId) {
         openils.Util.addCSSClass(dojo.byId('provider-list-div'), 'hidden');
        
@@ -31,7 +45,7 @@ function draw() {
     } else {
         openils.Util.addCSSClass(dojo.byId('provider-details-div'), 'hidden');       
         console.log('in else block');
-        pListGrid.loadAll({order_by:{acqpro : 'name'}});       
+        pListGrid.loadAll({order_by:{acqpro : 'name'}},{'owner':list});
         pListGrid.onPostCreate = function(fmObject) {
             location.href = location.href + '/' + fmObject.id();
         }
@@ -120,4 +134,16 @@ function getParsedSubf(rowIndex, item) {
     }
     return'';
 }
+
+function filterGrid() {
+    pListGrid.resetStore();
+    var unit = contextOrgSelector.getValue();
+    var list = fieldmapper.aou.findOrgUnit(unit).orgNodeTrail().map( function (i) {return i.id() } );
+
+    if(unit){
+        pListGrid.loadAll({order_by:{acqpro : 'name'}}, { 'owner' : list });
+    } else {
+        pListGrid.loadAll({order_by:{acqpro : 'name'}});
+    }
+}
 openils.Util.addOnLoad(draw);
index 75da815..e166db7 100644 (file)
@@ -462,7 +462,13 @@ function exportHandler(type, response) {
             break;
             case 'csv':
                 //content = content.replace(/\\t/g,'\t'); // if we really wanted to do .tsv instead
-                openils.XUL.contentToFileSaveDialog(content);
+                openils.XUL.contentToFileSaveDialog(content, null, {
+                    defaultString : 'VandelayExport.csv',
+                    defaultExtension : '.csv',
+                    filterName : 'CSV',
+                    filterExtension : '*.csv',
+                    filterAll : true
+                } );
             break;
             default:
                 alert('response = ' + response + '\tcontent:\n' + content);
index 32f6ee9..fdfb446 100644 (file)
         ]]>
         </script>
         <script type="text/javascript" src="../main/constants.js" />
+        <script>
+        <![CDATA[
+            try {
+                if (typeof xulG.url_prefix != 'undefined') {
+                    dump('pulling in custom.js in util_overlay_chrome.xul\n');
+                    // Pull in local customizations
+                    var r = new XMLHttpRequest();
+                    r.open("GET", xulG.url_prefix('/xul/server/skin/custom.js'), false);
+                    r.send(null);
+                    if (r.status == 200) {
+                        dump('Evaluating /xul/server/skin/custom.js\n');
+                        eval( r.responseText );
+                    }
+                } else {
+                    dump('cannot pull in custom.js in util_overlay_chrome.xul\n');
+                }
+            } catch(E) {
+                dump('cannot pull in custom.js in util_overlay_chrome.xul: ' + E + '\n');
+            }
+        ]]>
+        </script>
         <script type="text/javascript" src="util/utils.js" />
         <script type="text/javascript" src="util/CGI.js" />
         <script type="text/javascript" src="util/md5.js" />
index 3fb8de2..207469c 100644 (file)
     </script>
     <scripts id="openils_util_scripts">
         <script type="text/javascript" src="../main/constants.js" />
+        <script>
+        <![CDATA[
+            try {
+                if (typeof xulG.url_prefix != 'undefined') {
+                    dump('pulling in custom.js in util_overlay_offline.xul\n');
+                    // Pull in local customizations
+                    var r = new XMLHttpRequest();
+                    r.open("GET", xulG.url_prefix('/xul/server/skin/custom.js'), false);
+                    r.send(null);
+                    if (r.status == 200) {
+                        dump('Evaluating /xul/server/skin/custom.js\n');
+                        eval( r.responseText );
+                    }
+                } else {
+                    dump('cannot pull in custom.js in util_overlay_offline.xul\n');
+                }
+            } catch(E) {
+                dump('cannot pull in custom.js in util_overlay_offline.xul: ' + E + '\n');
+            }
+        ]]>
+        </script>
         <script type="text/javascript" src="util/utils.js" />
         <script type="text/javascript" src="util/CGI.js" />
         <script type="text/javascript" src="util/md5.js" />
index 2b3a14a..35d885c 100644 (file)
@@ -132,7 +132,7 @@ auth.controller.prototype = {
                                 '', 
                                 'chrome,centerscreen,modal', 
                                 { 
-                                    'location' : 'https://' + obj.controller.view.server_prompt.value, 
+                                    'location' : 'https://' + obj.controller.view.server_prompt.value.match(/^[^\/]*/)
                                     'prefetchCert' : true 
                                 } 
                             );
@@ -291,6 +291,7 @@ auth.controller.prototype = {
                 obj.controller.view.server_prompt.value = url;
             }
         }
+        url = url.match(/^[^\/]*/).toString(); // Only test the pre-slash URL
         obj.controller.view.submit_button.disabled = true;
         obj.controller.view.server_prompt.disabled = true;
         var s = document.getElementById('status');
@@ -427,7 +428,7 @@ auth.controller.prototype = {
         this.controller.view.password_prompt.disabled = true;
         this.controller.view.submit_button.disabled = true;
         this.controller.view.apply_locale_btn.disabled = true;
-        XML_HTTP_SERVER = this.controller.view.server_prompt.value;
+        XML_HTTP_SERVER = this.controller.view.server_prompt.value.match(/^[^\/]*/).toString();
 
         try {
 
index 77e72af..123e3de 100644 (file)
@@ -342,7 +342,7 @@ function main_init() {
 
         G.auth.on_login = function() {
 
-            var url = G.auth.controller.view.server_prompt.value || urls.remote;
+            var url = G.auth.controller.view.server_prompt.value.match(/^[^\/]*/).toString() || urls.remote;
 
             G.data.server_unadorned = url; G.data.stash('server_unadorned'); G.data.stash_retrieve();
 
index 82f0475..31a6a4a 100644 (file)
@@ -22,6 +22,7 @@ var user;
 var osSettings = {};
 var ouSettingValues = {};
 var osEditAutoWidget;
+var perm_codes = {};
 
 function osInit(data) {
     authtoken = new openils.CGI().param('ses') || dojo.cookie('ses');
@@ -43,6 +44,20 @@ function osInit(data) {
     };
 
     new openils.User().buildPermOrgSelector('VIEW_ORG_SETTINGS', osContextSelector, null, connect);
+
+    fieldmapper.standardRequest(
+        [   'open-ils.actor',
+            'open-ils.actor.permissions.retrieve'],
+        {   async: true,
+            oncomplete: function(r) {
+                var data = r.recv().content();
+                if(e = openils.Event.parse(data))
+                    return alert(e);
+                for(var key in data)
+                    perm_codes[data[key].id()] = data[key].code();
+            }
+        }
+    );
 }
 dojo.addOnLoad(osInit);
 
@@ -65,7 +80,8 @@ function osDraw(specific_setting) {
                     label : type.label(),
                     desc : type.description(),
                     type : type.datatype(),
-                    fm_class : type.fm_class()
+                    fm_class : type.fm_class(),
+                    update_perm : type.update_perm()
                 }
             }
         );
@@ -183,8 +199,12 @@ function osFormatEditLink(name) {
 function osLaunchEditor(name) {
     osEditDialog._osattr = name;
     osEditDialog.show();
+    var perms = ['UPDATE_ORG_UNIT_SETTING_ALL'];
+    if(osSettings[name].update_perm && perm_codes[osSettings[name].update_perm]) {
+        perms.push(perm_codes[osSettings[name].update_perm]);
+    }
     user.buildPermOrgSelector(
-        ['UPDATE_ORG_UNIT_SETTING.' + name, 'UPDATE_ORG_UNIT_SETTING_ALL'],
+        perms,
         osEditContextSelector, osSettings[name].context
     );
     dojo.byId('os-edit-name').innerHTML = osSettings[name].label;
index aecfe20..1ec3d0e 100644 (file)
@@ -421,6 +421,7 @@ g.render_callnumber_copy_count_entry = function(row,ou_id,count) {
         dump('\tcomposite_key = ' + callnumber_composite_key + '\n');
 
         _call_number_column_textbox.setAttribute('callkey',callnumber_composite_key);
+        //_call_number_column_textbox.setAttribute('tooltiptext',callnumber_composite_key);
         _call_number_column_textbox.setAttribute('acnc_id',acnc_id);
         _call_number_column_textbox.setAttribute('acnp_id',acnp_id);
         _call_number_column_textbox.setAttribute('acns_id',acns_id);
@@ -489,25 +490,17 @@ g.render_callnumber_copy_count_entry = function(row,ou_id,count) {
                     call_number_column_textbox.addEventListener( 'focus', function(ev) { g.last_focus = ev.target; }, false );
 
                     /**** CLASSIFICATION COLUMN revisited ****/
-                    var classification_column_menulist = g.render_class_menu(call_number_column_textbox);
-                    classification_column_menulist.addEventListener(
-                        'command',
-                        function() {
-                            handle_change_to_callnumber_data({'target':call_number_column_textbox});
-                        }
-                        ,false
+                    var classification_column_menulist = g.render_class_menu(
+                        call_number_column_textbox,
+                        handle_change_to_callnumber_data
                     );
                     classification_column_box.appendChild(classification_column_menulist);
                     classification_column_menulist.value = g.label_class;
 
                     /**** PREFIX COLUMN revisited ****/
-                    var prefix_column_menulist = g.render_prefix_menu(call_number_column_textbox);
-                    prefix_column_menulist.addEventListener(
-                        'command',
-                        function() {
-                            handle_change_to_callnumber_data({'target':call_number_column_textbox});
-                        }
-                        ,false
+                    var prefix_column_menulist = g.render_prefix_menu(
+                        call_number_column_textbox,
+                        handle_change_to_callnumber_data
                     );
 
                     prefix_column_box.appendChild(prefix_column_menulist);
@@ -517,13 +510,9 @@ g.render_callnumber_copy_count_entry = function(row,ou_id,count) {
             suffix_column_box.setAttribute('class','cn_suffix');
             r.appendChild(suffix_column_box);
             suffix_column_box.width = $('batch_suffix').parentNode.boxObject.width;
-                var suffix_column_menulist = g.render_suffix_menu(call_number_column_textbox);
-                suffix_column_menulist.addEventListener(
-                    'command',
-                    function() {
-                        handle_change_to_callnumber_data({'target':call_number_column_textbox});
-                    }
-                    ,false
+                var suffix_column_menulist = g.render_suffix_menu(
+                    call_number_column_textbox,
+                    handle_change_to_callnumber_data
                 );
                 suffix_column_box.appendChild(suffix_column_menulist);
 
@@ -587,9 +576,25 @@ g.render_callnumber_copy_count_entry = function(row,ou_id,count) {
                             var acnp_id = callnumber_composite_key.split(/:/)[1];
                             var acns_id = callnumber_composite_key.split(/:/).slice(-1)[0];
                             call_number_column_textbox.value = acn_label;
-                            classification_column_menulist.value = acnc_id;
-                            prefix_column_menulist.value = acnp_id;
-                            suffix_column_menulist.value = acns_id;
+
+                            var _call_number_column_box = call_number_column_textbox.parentNode;
+
+                            var _classification_column_box =
+                                _call_number_column_box.previousSibling.previousSibling; /* two over to the left */
+                            var _classification_column_menulist =
+                                _classification_column_box.firstChild;
+                            var _prefix_column_box =
+                                _call_number_column_box.previousSibling; /* one over to the left */
+                            var _prefix_column_menulist =
+                                _prefix_column_box.firstChild;
+                            var _suffix_column_box =
+                                _call_number_column_box.nextSibling; /* one over to the right */
+                            var _suffix_column_menulist =
+                                _suffix_column_box.firstChild;
+
+                            _classification_column_menulist.value = acnc_id;
+                            _prefix_column_menulist.value = acnp_id;
+                            _suffix_column_menulist.value = acns_id;
                             dump('\tacn_label = ' + acn_label + ' acnc_id = ' + acnc_id + ' acnp_id = ' + acnp_id + ' acns_id = ' + acns_id + '\n');
                             handle_change_to_callnumber_data({'target':call_number_column_textbox});
                         } else {
@@ -728,6 +733,7 @@ g.render_barcode_entry = function(node,callnumber_composite_key,count,ou_id) {
             }
             tb.setAttribute('ou_id',ou_id);
             tb.setAttribute('callkey',callnumber_composite_key);
+            //tb.setAttribute('tooltiptext',callnumber_composite_key);
             tb.setAttribute('rel_vert_pos',rel_vert_pos_barcode);
             part_menu.firstChild.setAttribute('rel_vert_pos',rel_vert_pos_part);
             if (!tb.value && g.org_label_existing_copy_map[ ou_id ]) {
@@ -1264,20 +1270,21 @@ g.save_prefs = function () {
     }
 }
 
-g.render_class_menu = function(call_number_tb) {
+g.render_class_menu = function(call_number_tb,update_func) {
     var ml = cat.util.render_cn_class_menu();
     ml.setAttribute('rel_vert_pos',rel_vert_pos_call_number_classification);
     ml.addEventListener(
         'command',
         function() {
             call_number_tb.setAttribute('acnc_id',ml.value);
+            update_func({'target':call_number_tb});
         },
         false
     );
     return ml;
 }
 
-g.render_prefix_menu = function(call_number_tb) {
+g.render_prefix_menu = function(call_number_tb,update_func) {
     var ou_id = call_number_tb.getAttribute('ou_id');
     var menulist = cat.util.render_cn_prefix_menu([ou_id]);
     menulist.setAttribute('rel_vert_pos',rel_vert_pos_call_number_prefix);
@@ -1285,13 +1292,14 @@ g.render_prefix_menu = function(call_number_tb) {
         'command',
         function() {
             call_number_tb.setAttribute('acnp_id',menulist.value);
+            update_func({'target':call_number_tb});
         },
         false
     );
     return menulist;
 }
 
-g.render_suffix_menu = function(call_number_tb) {
+g.render_suffix_menu = function(call_number_tb,update_func) {
     var ou_id = call_number_tb.getAttribute('ou_id');
     var menulist = cat.util.render_cn_suffix_menu([ou_id]);
     menulist.setAttribute('rel_vert_pos',rel_vert_pos_call_number_suffix);
@@ -1299,6 +1307,7 @@ g.render_suffix_menu = function(call_number_tb) {
         'command',
         function() {
             call_number_tb.setAttribute('acns_id',menulist.value);
+            update_func({'target':call_number_tb});
         },
         false
     );
index 55ad634..5eab31e 100644 (file)
@@ -81,7 +81,9 @@ patron.summary.prototype = {
                     obj.stat_cat_list.fm_columns( 'actsc', {
                         'actsc_id' : { 'hidden' : true },
                         'actsc_opac_visible' : { 'hidden' : true },
-                        'actsc_usr_summary' : { 'hidden' : true }
+                        'actsc_usr_summary' : { 'hidden' : true },
+                        'actsc_sip_format' : { 'hidden' : true },
+                        'astsc_sip_field' : { 'hidden' : true }
                     } )
                 ).concat(
                     obj.stat_cat_list.fm_columns( 'actscecm', {
@@ -969,6 +971,45 @@ patron.summary.prototype = {
                 if ($('stat_cat_tab')) {
                     util.widgets.dispatch('command','stat_cat_tab'); 
                 }
+                if ($('pdcgpr')) {
+                    try {
+                        var rows = $('pdcgpr');
+                        var entries = obj.patron.stat_cat_entries();
+                        for (var i = 0; i < entries.length; i++) {
+                            var stat_cat = obj.OpenILS.data.hash.my_actsc[ entries[i].stat_cat() ];
+                            if (!stat_cat) {
+                                stat_cat = obj.OpenILS.data.lookup('actsc',entries[i].stat_cat());
+                            }
+                            if (!stat_cat) { continue; }
+                            // Only a proud few share the Patron Info pane
+                            if (rows && get_bool( stat_cat.usr_summary() )) {
+                                var row_id = 'stat_cat_id_' + stat_cat.id();
+                                var row; var label1; var label2;
+                                if ($(row_id)) {
+                                    row = $(row_id);
+                                    row.setAttribute('class','stat_cat_summary_row');
+                                    label1 = row.firstChild;
+                                    label2 = row.lastChild;
+                                } else {
+                                    row = document.createElement('row');
+                                    row.setAttribute('id',row_id);
+                                    row.setAttribute('class','stat_cat_summary_row');
+                                    label1 = document.createElement('label');
+                                    label2 = document.createElement('label');
+                                    row.appendChild(label1);
+                                    row.appendChild(label2);
+                                    // Place before the spacer at the end
+                                    rows.insertBefore(row, rows.lastChild);
+                                }
+                                label1.setAttribute('value',stat_cat.name());
+                                label1.setAttribute('tooltiptext','stat cat id ' + stat_cat.id());
+                                label2.setAttribute('value',entries[i].stat_cat_entry());
+                            }
+                        }
+                    } catch(E) {
+                        alert('Error in summary.js: ' + E);
+                    }
+                }
             } );
 
             // On Complete