[open-ils-commits] [GIT] Evergreen ILS branch master updated. f749f0bc16585a481e246b0326a8c22d97288e27

Evergreen Git git at git.evergreen-ils.org
Wed Mar 13 13:04:40 EDT 2013


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Evergreen ILS".

The branch, master has been updated
       via  f749f0bc16585a481e246b0326a8c22d97288e27 (commit)
       via  094172bec1a395eb747f62ca1777393932ecfaba (commit)
       via  1bebc3203acb4b2d481c7c30f8b089a5f2d680f4 (commit)
       via  af56c5c5cf0fde61544175edbf901b5313498ea2 (commit)
       via  9ba7516ab4195b940e9d1eb49d51133082770b93 (commit)
       via  19a144fb990564f8278ca7640c7abc91767f464c (commit)
       via  2076647ce42c417a4d6b5efcce75c2edbc2885b0 (commit)
       via  ffb1d58274054fb56e1a4dcdd877beecca350f7d (commit)
       via  39fbd09f6121c5dc9014c52bc47b8e74c31461aa (commit)
      from  8aea98c37f73c6b2b58c79d48cb0e2d13134b2df (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit f749f0bc16585a481e246b0326a8c22d97288e27
Author: Mike Rylander <mrylander at gmail.com>
Date:   Wed Mar 13 12:52:45 2013 -0400

    Stamping upgrade script for user-barred A/T hooks

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index ba740d7..b087bfd 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -90,7 +90,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 ('0770', :eg_version); -- mrpeters/bshum
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0771', :eg_version); -- berick/miker
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.user_barred_hook.sql b/Open-ILS/src/sql/Pg/upgrade/0771.data.user_barred_hook.sql
similarity index 92%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.data.user_barred_hook.sql
rename to Open-ILS/src/sql/Pg/upgrade/0771.data.user_barred_hook.sql
index 20ea301..8852af6 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.user_barred_hook.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0771.data.user_barred_hook.sql
@@ -1,6 +1,8 @@
 
 BEGIN;
 
+SELECT evergreen.upgrade_deps_block_check('0771', :eg_version);
+
 INSERT INTO action_trigger.hook (
         key,
         core_type,

commit 094172bec1a395eb747f62ca1777393932ecfaba
Author: Bill Erickson <berick at esilibrary.com>
Date:   Thu Dec 20 16:38:16 2012 -0500

    A/T aggregator --granularity option
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/support-scripts/action_trigger_aggregator.pl b/Open-ILS/src/support-scripts/action_trigger_aggregator.pl
old mode 100644
new mode 100755
index e526bee..aebf235
--- a/Open-ILS/src/support-scripts/action_trigger_aggregator.pl
+++ b/Open-ILS/src/support-scripts/action_trigger_aggregator.pl
@@ -27,6 +27,7 @@ my $osrf_config = '/openils/conf/opensrf_core.xml';
 my $start_date  = DateTime->now->strftime('%F');
 my $end_date    = '';
 my $event_defs  = '';
+my $granularity = '';
 my $output_file = '';
 my $local_dir   = '/tmp'; # where to keep local copies of generated files
 my $remote_acct = '';
@@ -39,6 +40,7 @@ GetOptions(
     'start-date=s'      => \$start_date,
     'end-date=s'        => \$end_date,
     'event-defs=s'      => \$event_defs,
+    'granularity=s'     => \$granularity,
     'output-file=s'     => \$output_file,
     'remote-acct=s'     => \$remote_acct,
     'local-dir=s'       => \$local_dir,
@@ -76,7 +78,11 @@ $0 \
 Options
 
     --event-defs 
-        action_trigger.event_definition IDs
+        action_trigger.event_definition IDs to include
+
+    --granularity
+        Process all event definitions that match this granularity.  If used in
+        conjunction with --event-defs, the union of the two sets is used.
     
     --start-date 
         Only collect output for events whose run_time is on or after this ISO date
@@ -102,7 +108,8 @@ HELP
 help() if $help;
 
 my @event_defs = split(/,/, $event_defs);
-die "--event-defs required\n" unless @event_defs;
+die "--event-defs or --granularity required\n" 
+    unless @event_defs or $granularity;
 
 my $local_file = $output_file ? "$local_dir/$output_file" : '&STDOUT';
 
@@ -110,12 +117,30 @@ open(OUTFILE, ">$local_file") or
     die "unable to open out-file '$local_file' for writing: $!\n";
 binmode(OUTFILE, ":utf8");
 
+print "Output will be written to $local_file\n" if $verbose;
+
 OpenSRF::System->bootstrap_client(config_file => $osrf_config);
 Fieldmapper->import(IDL => 
     OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
 OpenILS::Utils::CStoreEditor::init();
 my $editor = OpenILS::Utils::CStoreEditor->new;
 
+# if granularity is set, append all event defs with the 
+# selected granularity to the set of event-defs to process.
+if ($granularity) {
+    my $defs = $editor->search_action_trigger_event_definition(
+        {granularity => $granularity},
+        {idlist => 1}
+    );
+
+    for my $id (@$defs) {
+        push(@event_defs, $id) 
+            unless grep { $_ eq $id} @event_defs;
+    }
+}
+
+print "Processing event-defs @event_defs\n" if $verbose;
+
 my %date_filter;
 $date_filter{run_time} = {'>=' => $start_date} if $start_date;
 $date_filter{run_time} = {'<' => $end_date} if $end_date;

commit 1bebc3203acb4b2d481c7c30f8b089a5f2d680f4
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Dec 11 12:09:42 2012 -0500

    Batch CSV notifications release notes
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/docs/RELEASE_NOTES_NEXT/notify_csv.txt b/docs/RELEASE_NOTES_NEXT/notify_csv.txt
new file mode 100644
index 0000000..0529726
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/notify_csv.txt
@@ -0,0 +1,78 @@
+New Feature: Generic CVS Notification Generator/Receiver
+========================================================
+
+New Action/Trigger template and sample event definitions for creating a CSV
+export file for various patron actions, primarily for the purpose of creating
+patron notices via external notification mechanisms.
+
+The reference implementation for this development is the TalkingTech iTiva
+product, which consumes CSV files for generating phone/text notifications and
+produces CSV results files for informing the ILS of notification statuses.  
+The template and send/receive scripts, however, should be generic enough to 
+create CSV for any type of 3rd-party notification product.
+
+Action/Trigger Event Definition Configuration
+---------------------------------------------
+
+  * Supported hook core types include *circ*, *ahr*, *ausp*, and *au*
+  * Reactor is *ProcessTemplate*
+
+Event Environment Requirements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  * Patron object with card
+  * copy object
+    ** circ.target_copy
+    ** hold.current_copy
+  * Org unit
+    ** circ.circ_lib
+    ** ahr.pickup_lib
+    ** ausp.org_unit
+    ** patron.home_ou
+
+Not all fields are relevent to all notice types.
+
+Required Event Parameters
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  * notify_media (e.g. phone)
+  * notify_type  (e.g. overdue)
+  * notify_level (e.g. "1" -- first overdue)
+
+The set of options for each event parameter is dependent on the 3rd-party
+processing the CSV file.  
+
+iTiva Event Parameter Options
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  * notify_media
+    ** V (voice)
+    ** T (text)
+  * notify_level
+    ** 1 (1st notice)
+    ** 2 (2nd notice)
+    ** 3 (3rd notice)
+  * notify_type 
+    ** FINES 
+    ** OVERDUE 
+    ** PREOVERDUE 
+    ** PRERESERVE 
+    ** RECALL 
+    ** RESERVE 
+    ** RESERVECANCEL 
+    ** RESERVEEXPIRE 
+    ** SUSPEND
+
+Push/Fetch Scripts
+------------------
+
+  * action_trigger_aggregator.pl collects event output from requested event 
+    definitions and stitches them together into a single file, which is sent 
+    via (s)FTP to the 3rd party.
+  * Why don't we use the SendFile reactor directly?
+    ** The final file is meant to be a collection of event-def outputs, not
+       the output from a single event def
+    ** The final file may be too large to reasonably store directly in a 
+       single action/trigger event_output row.
+  * csv_notify_fetcher.pl retrieves responses from the 3rd party and applies
+    the statuses to the async_output of each notified event.

commit af56c5c5cf0fde61544175edbf901b5313498ea2
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Dec 11 12:07:37 2012 -0500

    Script to collect CSV notification status responses
    
    For 3rd-party services which provide batch notifications status updates
    via CSV file.  The script assumes the action/trigger event definition ID
    is included in the response and, from there, applyes the "status" value
    to the async_output of the event definition in question.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/support-scripts/csv_notify_fetcher.pl b/Open-ILS/src/support-scripts/csv_notify_fetcher.pl
new file mode 100755
index 0000000..e144bb7
--- /dev/null
+++ b/Open-ILS/src/support-scripts/csv_notify_fetcher.pl
@@ -0,0 +1,198 @@
+#!/usr/bin/perl
+use strict; 
+use warnings;
+use DateTime;
+use Getopt::Long;
+use OpenSRF::AppSession;
+use OpenILS::Utils::CStoreEditor;
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Utils::RemoteAccount;
+use OpenSRF::Utils::Logger qw/$logger/;
+
+my $osrf_config     = '/openils/conf/opensrf_core.xml';
+my $remote_account  = '';
+my $remote_file     = '';
+my $local_dir       = '/tmp'; # locally stored file directory
+my $local_file      = ''; # locally stored file name
+my $response_file   = ''; # local file with response data
+my $delete_file     = 0;
+my $verbose         = 0;
+my $help            = 0;
+my $remote_conn     = undef;
+
+GetOptions(
+    'osrf-config=s'     => \$osrf_config,
+    'remote-account=s'  => \$remote_account,
+    'remote-file=s'     => \$remote_file,
+    'local-file=s'      => \$local_file,
+    'local-dir=s'       => \$local_dir,
+    'response-file=s'   => \$response_file,
+    'delete-file'       => \$delete_file,
+    'help'              => \$help,
+    'verbose'           => \$verbose
+);
+
+sub help {
+    print <<HELP;
+
+Collect CSV notification results file and update affected events.  The assumed
+format is "event-id","event-status".
+
+# Fetch notification response file from a remote site and delete the file when
+# done.
+
+$0 \
+    --osrf-config /openils/conf/opensrf_core.xml \
+    --remote-account 1 \
+    --remote-file some-file.csv \
+    --local-dir /tmp \
+    --local-file csv-result-file.csv \
+    --delete-file \
+    --verbose
+
+Options
+
+    --osrf-config [/openils/conf/opensrf_core.xml]
+        Full path to opensrf_core.xml configuration file
+
+    --remote-account
+        Identifier of the config.remote_account entry from which files should
+        be retrieved
+
+    --remote-file
+        Name of file on the remote site to retrieve and process
+
+    --local-file
+        Name given to local copy of retrieved files
+
+    --local-dir [/tmp]
+        Directory to store retrieved files
+
+    --response-file
+        Name of local file which contains results.  
+
+    --delete-file
+        Delete the remote file after processing.  If the user did not specify
+        a value for --local-file, the local file will be deleted as well.
+
+    --help
+        Print this help
+
+    --verbose
+        Display debug information during execution
+
+HELP
+    exit;
+}
+
+help() if $help;
+
+die "--response-file OR --remote-account and --remote-file required\n"
+    unless $response_file or ($remote_account and $remote_file);
+
+OpenSRF::System->bootstrap_client(config_file => $osrf_config);
+Fieldmapper->import(IDL => 
+    OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
+OpenILS::Utils::CStoreEditor::init();
+my $editor = OpenILS::Utils::CStoreEditor->new;
+
+if (!$response_file and $remote_account) {
+
+    # fetch data remotely and push the data into $response_file
+
+    my $racct = $editor->retrieve_config_remote_account($remote_account);
+    die "No such remote account $remote_account" unless $racct;
+
+    my $type;
+    my $host = $racct->host;
+    ($host =~ s/^(S?FTP)://i and $type = uc($1)) or                   
+    ($host =~ s/^(SSH|SCP)://i and $type = 'SCP');
+    $host =~ s#//##;
+
+    if ($local_file) {
+        # user specified local file name
+        $response_file = "$local_dir/$local_file";
+    } else {
+        $response_file = File::Temp->new()->filename;
+    }
+
+    print "Connecting to host $host\n" if $verbose;
+
+    $remote_conn = OpenILS::Utils::RemoteAccount->new(
+        type            => $type,
+        remote_host     => $host,
+        account_object  => $racct,
+        local_file      => $response_file,
+        remote_file     => $remote_file
+    );
+
+    my $res = $remote_conn->get;
+
+    die "Unable to fetch from  remote server [$remote_account] : " . 
+        $remote_conn->error . "\n" unless $res;
+
+    print "Fetched file $remote_file => $response_file\n" if $verbose;
+}
+
+# at this point, $file contains CSV, because it was already 
+# there or because we just fetched it from the remote account
+open(FILE, $response_file) or 
+    die "Unable to open response file: '$response_file' : $!\n";
+
+binmode(FILE, ":utf8");
+    
+while (<FILE>) {
+    chomp;
+
+    my ($id, $stat) = /"(.+)","(.+)"/g;
+    next unless $id and $stat;
+
+    $logger->info("csv: processing event $id; stat $stat");
+
+    my $event = $editor->retrieve_action_trigger_event($id);
+
+    if (!$event) {
+        $logger->warn("csv: unable to find event $id");
+        next;
+    }
+
+    if ($event->async_output) {
+        $logger->info("csv: skipping event $id; async_output already set");
+        next;
+    }
+
+    $editor->xact_begin;
+
+    # store the response output
+    my $output = Fieldmapper::action_trigger::event_output->new;
+    $output->data($stat);
+
+    unless ($editor->create_action_trigger_event_output($output)) {
+        $logger->warn("csv: error creating event ".
+            "output for event $id: ". $editor->die_event);
+        next;
+    }
+
+    # link the async response output to the original event
+    $event->async_output($output->id);
+
+    unless ($editor->update_action_trigger_event($event)) {
+        $logger->warn("csv: error updating event $id: ". $editor->die_event);
+        next;
+    }
+
+    $editor->xact_commit;
+}
+
+$editor->disconnect;
+
+if ($delete_file) {
+    # after we have successfully processed the file, 
+    # delete it from the remote server.
+
+    $remote_conn->delete($remote_file);
+
+    # delete the local file unless the user 
+    # specified a location to save the file
+    unlink($response_file) unless $local_file;
+}

commit 9ba7516ab4195b940e9d1eb49d51133082770b93
Author: Bill Erickson <berick at esilibrary.com>
Date:   Thu Jan 10 12:25:09 2013 -0500

    Support FTP/SCP delete operation in RemoteAccount
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/RemoteAccount.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/RemoteAccount.pm
index 164e227..34a7257 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Utils/RemoteAccount.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/RemoteAccount.pm
@@ -226,7 +226,6 @@ sub key_check {
 
 
 # TOP LEVEL methods
-# TODO: delete for both FTP and SSH2
 
 sub get {
     my $self   = shift;
@@ -322,6 +321,31 @@ sub ls {
     }
 }
 
+sub delete {
+    my $self   = shift;
+    my $params = shift;
+
+    $params = {remote_file => $params} unless ref $params;
+    $self->init($params); # secondary init
+
+    my $file = $params->{remote_file};
+
+    if (!$file) {
+        $logger->warn("No file specified for deletion");
+        return undef;
+    }
+
+    $logger->info("Deleting remote file '$file'");
+
+    if ($self->type eq "FTP") {
+        return $self->delete_ftp($file);
+    } else {
+        my %keys = $self->key_check($params);
+        return $self->delete_ssh2(\%keys, $file);
+    }
+}
+
+
 # Checks if the filename part of a pathname has one or more glob characters
 # We split out the filename portion of the path
 # Detect glob or no glob.
@@ -499,6 +523,14 @@ sub ls_ssh2_full {
 
 }
 
+sub delete_ssh2 {
+    my $self = shift;
+    my $keys = shift;
+    my $file = shift;
+    my $sftp = $self->_ssh2($keys)->sftp;
+    return $sftp->unlink($file);
+}
+
 sub _slash_path {
     my $self = shift;
     my $dir  = shift || '.';
@@ -634,8 +666,10 @@ sub ls_ftp {   # returns full path like: dir/path/file.ext
     return @list;
 }
 
-sub delete_ftp { # XXX not yet used
-    $_[0]->_ftp->delete($_[1]);
+sub delete_ftp { 
+    my $self = shift;
+    my $file = shift;
+    return $self->_ftp->delete($file);
 }
 
 sub _pkg {      # Not OO

commit 19a144fb990564f8278ca7640c7abc91767f464c
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Dec 11 12:03:27 2012 -0500

    Action/Trigger template output aggregator
    
    Script to collect template output data from action-trigger events,
    potentially spanning multiple event definitions, and stitching the
    output together into a single file / output.  The file may then be
    delivered to a 3rd party via sFTP/SCP using config.remote_account.
    
    The primary use case of the script is for aggregating CSV, XML, etc.
    output to deliver to a 3rd party for notification purposes (e.g. phone,
    print notices).  This is useful because a) no one event definition can
    cover all notification types and b) template output files can become too
    large to practically store in the DB and deliver to/from opensrf
    services.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/support-scripts/action_trigger_aggregator.pl b/Open-ILS/src/support-scripts/action_trigger_aggregator.pl
new file mode 100644
index 0000000..e526bee
--- /dev/null
+++ b/Open-ILS/src/support-scripts/action_trigger_aggregator.pl
@@ -0,0 +1,179 @@
+#!/usr/bin/perl
+# ---------------------------------------------------------------
+# Copyright (C) 2012 Equinox Software, Inc
+# Author: Bill Erickson <berick at esilibrary.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# ---------------------------------------------------------------
+use strict; 
+use warnings;
+use DateTime;
+use Getopt::Long;
+use OpenSRF::System;
+use OpenSRF::AppSession;
+use OpenILS::Utils::CStoreEditor;
+use OpenILS::Utils::RemoteAccount;
+use OpenILS::Utils::Fieldmapper;
+
+my $osrf_config = '/openils/conf/opensrf_core.xml';
+my $start_date  = DateTime->now->strftime('%F');
+my $end_date    = '';
+my $event_defs  = '';
+my $output_file = '';
+my $local_dir   = '/tmp'; # where to keep local copies of generated files
+my $remote_acct = '';
+my $cleanup     = 0; # cleanup generated files
+my $verbose     = 0;
+my $help        = 0;
+
+GetOptions(
+    'osrf-config=s'     => \$osrf_config,
+    'start-date=s'      => \$start_date,
+    'end-date=s'        => \$end_date,
+    'event-defs=s'      => \$event_defs,
+    'output-file=s'     => \$output_file,
+    'remote-acct=s'     => \$remote_acct,
+    'local-dir=s'       => \$local_dir,
+    'cleanup'           => \$cleanup,
+    'verbose'           => \$verbose,
+    'help'              => \$help
+);
+
+sub help {
+    print <<HELP;
+
+Action/Trigger Aggregator Script
+
+Collect template output from one or more event-definitions and stitch the 
+results together in a single file.  The file may be optionally stored locally
+and/or delivered to a remote ftp/scp account.
+
+This script is useful for generating a single mass notification (e.g. overdue)
+file and sending the file to a 3rd party for processing and/or for local 
+processing.  The anticipated use case would be to stitch together lines of CSV 
+or chunks of XML.
+
+Example
+
+# Collect a week of notifications for 3 event definitions
+
+$0 \
+    --event-defs 104,105,106 \
+    --start-date 2012-01-01 \
+    --end-date 2012-01-07 \
+    --output-file 2012-01-07.notify.csv \
+    --local-dir /var/run/evergreen/csv \
+    --remote-account 6
+
+Options
+
+    --event-defs 
+        action_trigger.event_definition IDs
+    
+    --start-date 
+        Only collect output for events whose run_time is on or after this ISO date
+
+    --end-date 
+        Only collect output for events whose run_time occurred before this IDO date
+
+    --output-file [default STDOUT]
+        Output goes to this file.  
+
+    --local-dir [default /tmp]
+        Local directory where the output-file is placed.  If --cleanup is 
+        set, this is used as the tmp directory for file generation
+
+    --remote-account
+        Evergreen config.remote_account ID
+        If set, the output-file will be sent via sFTP/SCP to this server.
+
+HELP
+    exit;
+}
+
+help() if $help;
+
+my @event_defs = split(/,/, $event_defs);
+die "--event-defs required\n" unless @event_defs;
+
+my $local_file = $output_file ? "$local_dir/$output_file" : '&STDOUT';
+
+open(OUTFILE, ">$local_file") or 
+    die "unable to open out-file '$local_file' for writing: $!\n";
+binmode(OUTFILE, ":utf8");
+
+OpenSRF::System->bootstrap_client(config_file => $osrf_config);
+Fieldmapper->import(IDL => 
+    OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
+OpenILS::Utils::CStoreEditor::init();
+my $editor = OpenILS::Utils::CStoreEditor->new;
+
+my %date_filter;
+$date_filter{run_time} = {'>=' => $start_date} if $start_date;
+$date_filter{run_time} = {'<' => $end_date} if $end_date;
+
+# collect the event tempate output data
+# use a real session here so we can stream results directly to the output file
+my $ses = OpenSRF::AppSession->create('open-ils.cstore');
+my $req = $ses->request(
+    'open-ils.cstore.json_query', {
+        select => {ateo => ['data']},
+        from => {ateo => { atev => {
+            filter => {state => 'complete', %date_filter},
+            join => {atevdef => {filter => {
+                id => \@event_defs,
+                active => 't'
+            }}}
+        }}}
+    }
+);
+
+# use a large timeout since this is likely to be a hefty query
+while (my $resp = $req->recv(timeout => 3600)) {
+    die $req->failed . "\n" if $req->failed;
+    my $content = $resp->content or next;
+    print OUTFILE $content->{data};
+}
+
+if ($remote_acct) {
+    # send the file to the remote account
+
+    my $racct = $editor->retrieve_config_remote_account($remote_acct);
+    die "No such remote account $remote_acct" unless $racct;
+
+    my $type;
+    my $host = $racct->host;
+    ($host =~ s/^(S?FTP)://i and $type = uc($1)) or                   
+    ($host =~ s/^(SSH|SCP)://i and $type = 'SCP');
+    $host =~ s#//##;
+
+    my $acct = OpenILS::Utils::RemoteAccount->new(
+        type            => $type,
+        remote_host     => $host,
+        account_object  => $racct,
+        local_file      => $local_file,
+        remote_file     => $output_file
+    );
+
+    my $res = $acct->put;
+
+    die "Unable to push to remote server [$remote_acct] : " . 
+        $acct->error . "\n" unless $res;
+
+    print "Pushed file to $res\n" if $verbose;
+}
+
+if ($cleanup) {
+    unlink($local_file) or 
+        die "Unable to clean up file '$local_file' : $!\n";
+}
+
+

commit 2076647ce42c417a4d6b5efcce75c2edbc2885b0
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Dec 11 11:59:50 2012 -0500

    Seed data for CSV notification export
    
    Full set of seed data for CSV action/trigger notification batch exports.
    The seed data is added to its own file and not automatically loaded into
    new installs (or via upgrade script) since it adds a significant number
    of new event definitions, probably more than most installs need by
    default.
    
    Review the event defs. and their parameters prior to loading this seed
    data, as it only represents one possible set of CSV notification
    options.  For example, it assumes one type of "notify media" for all
    created notices.  It may necessary to add additional event defs to cover
    additional media, etc.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/sql/Pg/notify-csv-action-trigger.sql b/Open-ILS/src/sql/Pg/notify-csv-action-trigger.sql
new file mode 100644
index 0000000..a656ad0
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/notify-csv-action-trigger.sql
@@ -0,0 +1,342 @@
+BEGIN;
+
+-- Build the event defintions, environment, and params, then apply the same 
+-- template to all CSV definitions.
+
+-- All definitions assume a notify media of "V" (voice).
+
+---------------------------------------------------
+-- 1st overdue 
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', '1st Overdue CSV', 1, 'checkout.due','CircIsOverdue', 
+    'ProcessTemplate', '7 days', 'due_date', 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'circ_lib'), 
+(currval('action_trigger.event_definition_id_seq'), 'target_copy'), 
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '1'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''OVERDUE''')
+;
+
+---------------------------------------------------
+-- 2nd overdue 
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', '2nd Overdue CSV', 1, 'checkout.due','CircIsOverdue', 
+    'ProcessTemplate', '14 days', 'due_date', 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'circ_lib'), 
+(currval('action_trigger.event_definition_id_seq'), 'target_copy'), 
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '2'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''OVERDUE''')
+;
+
+---------------------------------------------------
+-- 3rd overdue 
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', '3rd Overdue CSV', 1, 'checkout.due','CircIsOverdue', 
+    'ProcessTemplate', '28 days', 'due_date', 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'circ_lib'), 
+(currval('action_trigger.event_definition_id_seq'), 'target_copy'), 
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '3'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''OVERDUE''')
+;
+
+---------------------------------------------------
+-- predue 
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', '3-Day Predue CSV', 1, 'checkout.due','CircIsOpen', 
+    'ProcessTemplate', '-3 days', 'due_date', 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'circ_lib'), 
+(currval('action_trigger.event_definition_id_seq'), 'target_copy'), 
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '1'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''PREOVERDUE''')
+;
+
+---------------------------------------------------
+-- hold ready for pickup 
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', 'Hold Ready CSV', 1, 'hold.available','HoldIsAvailable', 
+    'ProcessTemplate', '30 minutes', 'shelf_time', 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'pickup_lib'), 
+(currval('action_trigger.event_definition_id_seq'), 'current_copy'), 
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '1'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''RESERVE''')
+;
+
+---------------------------------------------------
+-- hold expires on shelf soon
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', 'Hold Expires On Shelf Soon CSV', 1, 
+    'hold_request.shelf_expires_soon',
+    'HoldIsAvailable', 'ProcessTemplate', '-1 days', 
+    'shelf_expire_time', 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'pickup_lib'), 
+(currval('action_trigger.event_definition_id_seq'), 'current_copy'), 
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '2'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''PRERESERVE''')
+;
+
+---------------------------------------------------
+-- hold expired on shelf
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', 'Hold Expired On Shelf CSV', 1, 
+    'hold_request.cancel.expire_holds_shelf',
+    'HoldIsCancelled', 'ProcessTemplate', '30 minutes', 
+    'cancel_time', 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'pickup_lib'), 
+(currval('action_trigger.event_definition_id_seq'), 'current_copy'),
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '1'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''RESERVEEXPIRE''')
+;
+
+---------------------------------------------------
+-- hold cancelled
+-- see also hooks hold_request.cancel.staff and
+-- hold_request.cancel.patron
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', 'Hold Cancelled (no target) CSV', 1, 
+    'hold_request.cancel.expire_no_target',
+    'HoldIsCancelled', 'ProcessTemplate', '30 minutes', 
+    'cancel_time', 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'pickup_lib'), 
+(currval('action_trigger.event_definition_id_seq'), 'current_copy'),
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '1'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''RESERVECANCEL''')
+;
+
+---------------------------------------------------
+-- recall
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', 'Copy Recall CSV', 1, 
+    'circ.recall.target',
+    'NOOP_True', 'ProcessTemplate', DEFAULT,
+    NULL, 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'circ_lib'), 
+(currval('action_trigger.event_definition_id_seq'), 'target_copy'), 
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '1'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''RECALL''')
+;
+
+---------------------------------------------------
+-- patron exceeds fines threshold
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', 'Patron Exceeds Fines CSV', 1, 
+    'penalty.PATRON_EXCEEDS_FINES',
+    'NOOP_True', 'ProcessTemplate', DEFAULT,
+    NULL, 'usr', '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'org_unit'),
+(currval('action_trigger.event_definition_id_seq'), 'usr.card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '1'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''FINES''')
+;
+
+---------------------------------------------------
+-- patron barred
+INSERT INTO action_trigger.event_definition (active, name, owner, hook, 
+    validator, reactor, delay, delay_field, group_field, template, granularity)
+VALUES (
+    'f', 'Patron Barred CSV', 1, 
+    'au.barred',
+    'PatronBarred', 'ProcessTemplate', DEFAULT,
+    NULL, NULL, '', 'notify-csv'
+);
+
+INSERT INTO action_trigger.environment (event_def, path) VALUES 
+(currval('action_trigger.event_definition_id_seq'), 'home_ou'),
+(currval('action_trigger.event_definition_id_seq'), 'card')
+;
+
+INSERT INTO action_trigger.event_params (event_def, param, value) VALUES
+(currval('action_trigger.event_definition_id_seq'), 'notify_media', '''V'''),
+(currval('action_trigger.event_definition_id_seq'), 'notify_level', '1'),
+(currval('action_trigger.event_definition_id_seq'), 'notify_type', '''SUSPEND''')
+;
+
+
+
+---------------------------------------------------
+-- apply the generic CVS template to all event defs
+UPDATE action_trigger.event_definition SET template = $$
+[%-
+    USE date;
+
+    # accommodate grouped events
+    SET event = event.0 UNLESS event.id;
+    SET target = [target] UNLESS event.event_def.group_field;
+
+    core_type = event.event_def.hook.core_type;
+    notice_org_unit = helpers.get_org_unit(event.event_def.owner);
+
+    FOR target_obj IN target;
+
+        # Mangle the data into a consistent shape
+        circ = '';
+        hold = '';
+        copy = '';
+        user = '';
+        title = '';
+        org_unit = '';
+        date_info = '';
+
+        IF core_type == 'circ';
+            # e.g. overdue circ
+            circ = target_obj;
+            user = circ.usr;
+            copy = circ.target_copy;
+            org_unit = circ.circ_lib;
+            date_info = circ.due_date;
+
+        ELSIF core_type == 'ahr';
+            # e.g. hold ready for pickup
+            hold = target_obj;
+            user = hold.usr;
+            copy = hold.current_copy;
+            org_unit = hold.pickup_lib;
+            date_info = hold.shelf_expire_time;
+
+        ELSIF core_type == 'ausp';
+            # e.g. max fines
+            user = target_obj.usr;
+            org_unit = target_obj.org_unit;
+
+        ELSIF core_type == 'au';
+            # e.g. barred
+            user = target_obj;
+            org_unit = user.home_ou;
+        END;
+
+        user_locale = helpers.get_user_locale(user.id);
+        user_lang = user_locale | replace('-.*', ''); # ISO 639-1 language
+        user_phone = helpers.get_user_setting(
+            user.id, 'opac.default_phone') || user.day_phone;
+
+        IF copy;
+            bib_data = helpers.get_copy_bib_basics(copy.id);
+            title = bib_data.title;
+        END;
+
+        IF date_info;
+            date_info = date.format(
+                helpers.format_date(date_info), '%d/%m/%Y');
+        END;
+
+        # Print the data for each target object as CSV
+-%]
+[%- '"' _ helpers.escape_csv(params.notify_media) _ '",' -%]
+[%- '"' _ helpers.escape_csv(user_lang) _ '",' -%]
+[%- '"' _ helpers.escape_csv(params.notify_type) _ '",' -%]
+[%- '"' _ helpers.escape_csv(params.notify_level) _ '",' -%]
+[%- '"' _ helpers.escape_csv(user.card.barcode) _ '",' -%]
+[%- '"' _ helpers.escape_csv(user.prefix) _ '",' -%]
+[%- '"' _ helpers.escape_csv(user.first_given_name) _ '",' -%]
+[%- '"' _ helpers.escape_csv(user.family_name) _ '",' -%]
+[%- '"' _ helpers.escape_csv(user_phone) _ '",' -%]
+[%- '"' _ helpers.escape_csv(user.email) _ '",' -%]
+[%- '"' _ helpers.escape_csv(notice_org_unit.shortname) _ '",' -%]
+[%- '"' _ helpers.escape_csv(org_unit.shortname) _ '",' -%]
+[%- '"' _ helpers.escape_csv(org_unit.name) _ '",' -%]
+[%- '"' _ helpers.escape_csv(copy.barcode) _ '",' -%]
+[%- '"' _ helpers.escape_csv(date_info) _ '",' -%]
+[%- '"' _ helpers.escape_csv(title) _ '",' -%]
+[%- '"' _ helpers.escape_csv(event.id) _ '"'  %]
+[% END -%]
+$$
+WHERE granularity = 'notify-csv';
+
+COMMIT;

commit ffb1d58274054fb56e1a4dcdd877beecca350f7d
Author: Bill Erickson <berick at esilibrary.com>
Date:   Thu Dec 27 10:58:01 2012 -0500

    Action/trigger user barred/unbarred hooks
    
    New active A/T hooks 'au.barred' and 'au.unbarred', plus PatronBarred
    and PatronNotBarred validators, plus code to make the hooks dance.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    
    Conflicts:
    	Open-ILS/src/sql/Pg/950.data.seed-values.sql
    
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
index 5f6073f..d818454 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
@@ -373,6 +373,7 @@ sub update_patron {
     # if we want to represent the old patron.
 
     my $old_patron;
+    my $barred_hook = '';
 
 	if($patron->isnew()) {
 		( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
@@ -392,6 +393,9 @@ sub update_patron {
         if($U->is_true($old_patron->barred) != $U->is_true($new_patron->barred)) {
             $evt = $U->check_perms($user_obj->id, $patron->home_ou, $U->is_true($old_patron->barred) ? 'UNBAR_PATRON' : 'BAR_PATRON');
             return $evt if $evt;
+
+            $barred_hook = $U->is_true($new_patron->barred) ? 
+                'au.barred' : 'au.unbarred';
         }
     }
 
@@ -429,6 +433,9 @@ sub update_patron {
         $tses->request('open-ils.trigger.event.autocreate', 'au.create', $new_patron, $new_patron->home_ou);
 	} else {
         $tses->request('open-ils.trigger.event.autocreate', 'au.update', $new_patron, $new_patron->home_ou);
+
+        $tses->request('open-ils.trigger.event.autocreate', $barred_hook, 
+            $new_patron, $new_patron->home_ou) if $barred_hook;
     }
 
 	return flesh_user($new_patron->id(), new_editor(requestor => $user_obj, xact => 1));
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm
index f3a701a..6b5dc3c 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Validator.pm
@@ -151,4 +151,14 @@ sub HoldNotifyCheck {
     return 1;
 }
 
+# core_type au
+sub PatronBarred {
+    my ($self, $env) = @_;
+    return $U->is_true($env->{target}->barred);
+}
+
+sub PatronNotBarred {
+    return !PatronBarred(@_);
+}
+
 1;
diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
index 3beb825..04fd4e0 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -12528,3 +12528,43 @@ INSERT INTO actor.org_unit_setting (
     '"6 months"'
 );
 
+INSERT INTO action_trigger.hook (
+        key,
+        core_type,
+        description,
+        passive
+    ) VALUES (
+        'au.barred',
+        'au',
+        'A user was barred by staff',
+        FALSE
+    );
+
+INSERT INTO action_trigger.hook (
+        key,
+        core_type,
+        description,
+        passive
+    ) VALUES (
+        'au.unbarred',
+        'au',
+        'A user was un-barred by staff',
+        FALSE
+    );
+
+INSERT INTO action_trigger.validator (
+        module, 
+        description
+    ) VALUES (
+        'PatronBarred',
+        'Tests if a patron is currently marked as barred'
+    );
+
+INSERT INTO action_trigger.validator (
+        module, 
+        description
+    ) VALUES (
+        'PatronNotBarred',
+        'Tests if a patron is currently not marked as barred'
+    );
+
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.user_barred_hook.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.user_barred_hook.sql
new file mode 100644
index 0000000..20ea301
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.user_barred_hook.sql
@@ -0,0 +1,44 @@
+
+BEGIN;
+
+INSERT INTO action_trigger.hook (
+        key,
+        core_type,
+        description,
+        passive
+    ) VALUES (
+        'au.barred',
+        'au',
+        'A user was barred by staff',
+        FALSE
+    );
+
+INSERT INTO action_trigger.hook (
+        key,
+        core_type,
+        description,
+        passive
+    ) VALUES (
+        'au.unbarred',
+        'au',
+        'A user was un-barred by staff',
+        FALSE
+    );
+
+INSERT INTO action_trigger.validator (
+        module, 
+        description
+    ) VALUES (
+        'PatronBarred',
+        'Tests if a patron is currently marked as barred'
+    );
+
+INSERT INTO action_trigger.validator (
+        module, 
+        description
+    ) VALUES (
+        'PatronNotBarred',
+        'Tests if a patron is currently not marked as barred'
+    );
+
+COMMIT;

commit 39fbd09f6121c5dc9014c52bc47b8e74c31461aa
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Dec 11 11:57:42 2012 -0500

    Additional action/trigger helper functions
    
    Added to the 'helpers' environment for action/trigger templates
    
     * get_org_unit -- get org object from id
     * escape_csv -- escapes strings ia csv values
     * get_user_setting
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Mike Rylander <mrylander at gmail.com>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
index 62ca21b..7be1993 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm
@@ -98,6 +98,12 @@ $_TT_helpers = {
         return $U->get_copy_price(new_editor(xact=>1), $copy_id);
     },
 
+    get_org_unit => sub {
+        my $org_id = shift;
+        return $org_id if ref $org_id;
+        return new_editor()->retrieve_actor_org_unit($org_id);
+    },
+
     # given a copy, returns the title and author in a hash
     get_copy_bib_basics => sub {
         my $copy_id = shift;
@@ -165,6 +171,14 @@ $_TT_helpers = {
         return $U->ou_ancestor_setting_value($org_id, $setting);
     },
 
+    get_user_setting => sub {
+        my ($user_id, $setting) = @_;
+        my $val = new_editor()->search_actor_user_setting(
+            {usr => $user_id, name => $setting})->[0];
+        return undef unless $val; 
+        return OpenSRF::Utils::JSON->JSON2perl($val->value);  
+    },
+
     # This basically greps/maps out ths isbn string values, but also promotes the first isbn-13 to the
     # front of the line (so that the EDI translator takes it as primary) if there is one.
     get_li_isbns => sub {
@@ -372,6 +386,13 @@ $_TT_helpers = {
         my $unapi = new_editor()->json_query($query);
         return undef unless @$unapi;
         return $_TT_helpers->{xml_doc}->($unapi->[0]->{'unapi.bre'});
+    },
+
+    # escapes quotes in csv string values
+    escape_csv => sub {
+        my $string = shift;
+        $string =~ s/"/""/og;
+        return $string;
     }
 };
 

-----------------------------------------------------------------------

Summary of changes:
 .../src/perlmods/lib/OpenILS/Application/Actor.pm  |    7 +
 .../lib/OpenILS/Application/Trigger/Reactor.pm     |   21 ++
 .../lib/OpenILS/Application/Trigger/Validator.pm   |   10 +
 .../perlmods/lib/OpenILS/Utils/RemoteAccount.pm    |   40 +++-
 Open-ILS/src/sql/Pg/002.schema.config.sql          |    2 +-
 Open-ILS/src/sql/Pg/950.data.seed-values.sql       |   40 +++
 Open-ILS/src/sql/Pg/notify-csv-action-trigger.sql  |  342 ++++++++++++++++++++
 .../sql/Pg/upgrade/0771.data.user_barred_hook.sql  |   46 +++
 .../support-scripts/action_trigger_aggregator.pl   |  204 ++++++++++++
 Open-ILS/src/support-scripts/csv_notify_fetcher.pl |  198 +++++++++++
 docs/RELEASE_NOTES_NEXT/notify_csv.txt             |   78 +++++
 11 files changed, 984 insertions(+), 4 deletions(-)
 create mode 100644 Open-ILS/src/sql/Pg/notify-csv-action-trigger.sql
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/0771.data.user_barred_hook.sql
 create mode 100755 Open-ILS/src/support-scripts/action_trigger_aggregator.pl
 create mode 100755 Open-ILS/src/support-scripts/csv_notify_fetcher.pl
 create mode 100644 docs/RELEASE_NOTES_NEXT/notify_csv.txt


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list