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

Evergreen Git git at git.evergreen-ils.org
Thu Mar 8 15:59:17 EST 2012


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  aedcb627d299949274743a8dfb7669c8030ff345 (commit)
       via  cd0a6898069afafcf49a7bc59e60dc4927213efd (commit)
       via  aebf4a58fe8f013ce7f0e5a46d920e679287295c (commit)
       via  a7a13b9f0b5ebfaef18004a94ede7e23efa6c5b2 (commit)
       via  f046fa0a17dc74916c62a255f8d08d7e9f3314af (commit)
       via  56e3da24d3c1c4663526e578dc7d342bd4145b09 (commit)
       via  d8d9925b089e56a0ad8c877bd85cabb5fdaf93a8 (commit)
       via  cf198e9279137812800e2722e5e0c56ceaf60881 (commit)
       via  75932d074b19cec1cf954411bb8c138f4cf6414d (commit)
       via  b2d097445cf8c2609bbbd4576a78143658877464 (commit)
      from  e9946e925e33078b6e0a1f40c34ea59d2d7a15d3 (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 aedcb627d299949274743a8dfb7669c8030ff345
Author: Thomas Berezansky <tsbere at mvlc.org>
Date:   Thu Mar 8 15:52:18 2012 -0500

    Stamping User Activity Upgrade Script
    
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index 5793062..07f5392 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -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 ('0680', :eg_version); -- miker/senator
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0681', :eg_version); -- berick/tsbere
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql b/Open-ILS/src/sql/Pg/upgrade/0681.schema.user-activity.sql
similarity index 98%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
rename to Open-ILS/src/sql/Pg/upgrade/0681.schema.user-activity.sql
index 576fe7f..7d0fede 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0681.schema.user-activity.sql
@@ -1,9 +1,9 @@
--- Evergreen DB patch XXXX.schema.user-activity.sql
+-- Evergreen DB patch 0681.schema.user-activity.sql
 --
 BEGIN;
 
 -- check whether patch can be applied
--- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+SELECT evergreen.upgrade_deps_block_check('0681', :eg_version);
 
 -- SCHEMA --
 

commit cd0a6898069afafcf49a7bc59e60dc4927213efd
Author: Bill Erickson <berick at esilibrary.com>
Date:   Thu Mar 8 13:13:25 2012 -0500

    User activity : only delete transient activity for user/type
    
    Repairs a bug spotted by Thomas Berezansky where the addition of a new
    activity for a transient type would delete all existing activity
    entries.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/src/sql/Pg/999.functions.global.sql b/Open-ILS/src/sql/Pg/999.functions.global.sql
index 69d9cbe..c1df4af 100644
--- a/Open-ILS/src/sql/Pg/999.functions.global.sql
+++ b/Open-ILS/src/sql/Pg/999.functions.global.sql
@@ -2097,8 +2097,11 @@ $F$ LANGUAGE SQL;
 -- remove transient activity entries on insert of new entries
 CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
 BEGIN
-    DELETE FROM actor.usr_activity USING config.usr_activity_type atype
-        WHERE atype.transient AND NEW.etype = atype.id;
+    DELETE FROM actor.usr_activity act USING config.usr_activity_type atype
+        WHERE atype.transient AND 
+            NEW.etype = atype.id AND
+            act.etype = atype.id AND
+            act.usr = NEW.usr;
     RETURN NEW;
 END;
 $$ LANGUAGE PLPGSQL;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
index de8cc5e..576fe7f 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
@@ -34,8 +34,11 @@ CREATE TABLE actor.usr_activity (
 -- remove transient activity entries on insert of new entries
 CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
 BEGIN
-    DELETE FROM actor.usr_activity USING config.usr_activity_type atype
-        WHERE atype.transient AND NEW.etype = atype.id;
+    DELETE FROM actor.usr_activity act USING config.usr_activity_type atype
+        WHERE atype.transient AND 
+            NEW.etype = atype.id AND
+            act.etype = atype.id AND
+            act.usr = NEW.usr;
     RETURN NEW;
 END;
 $$ LANGUAGE PLPGSQL;

commit aebf4a58fe8f013ce7f0e5a46d920e679287295c
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Feb 28 14:28:14 2012 -0500

    User Activity : SIP activity tracking
    
    1. Log user activity for all patron-related SIP actions, regardless of
    whether the SIP server verifies the user password.
    
    2. Determine the "ewho" (i.e. 3rd-party) value from configuration.  Each
    SIP login <account> can now specify its own "activity_who" value.
    Additionally, a fall-through <activity_who> element can be added to the
    institution config.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
index d214274..e8369ea 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
@@ -1934,5 +1934,34 @@ sub bib_container_items_via_search {
     return [map { $ordering_hash{$_} } @$id_list];
 }
 
+# returns undef on success, Event on error
+sub log_user_activity {
+    my ($class, $user_id, $who, $what, $e, $async) = @_;
+
+    my $commit = 0;
+    if (!$e) {
+        $e = OpenILS::Utils::CStoreEditor->new(xact => 1);
+        $commit = 1;
+    }
+
+    my $res = $e->json_query({
+        from => [
+            'actor.insert_usr_activity', 
+            $user_id, $who, $what, OpenSRF::AppSession->ingress
+        ]
+    });
+
+    if ($res) { # call returned OK
+
+        $e->commit   if $commit and @$res;
+        $e->rollback if $commit and !@$res;
+
+    } else {
+        return $e->die_event;
+    }
+
+    return undef;
+}
+
 1;
 
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
index 4f62b1b..4774174 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
@@ -29,6 +29,7 @@ my $U = 'OpenILS::Application::AppUtils';
 
 my $editor;
 my $config;
+my $login_account;
 my $target_encoding;    # FIXME: this is configured at the institution level. 
 
 use Digest::MD5 qw(md5_hex);
@@ -38,7 +39,7 @@ sub new {
 	my $type = ref($class) || $class;
 	my $self = {};
 
-	$self->{login} = $login;
+	$self->{login} = $login_account = $login;
 
 	$config = $institution;
 	syslog("LOG_DEBUG", "OILS: new ILS '%s'", $institution->{id});
@@ -95,6 +96,9 @@ sub editor {
 sub config {
 	return $config;
 }
+sub login_account {
+	return $login_account;
+}
 
 sub get_option_value {
     my($self, $option) = @_;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm
index 832c827..538a6a7 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm
@@ -118,9 +118,30 @@ sub new {
     syslog("LOG_DEBUG", "OILS: new OpenILS Patron(%s => %s): found patron : barred=%s, card:active=%s", 
         $key, $patron_id, $user->barred, $user->card->active );
 
+    $U->log_user_activity($user->id, $self->get_act_who, 'verify');
+
     return $self;
 }
 
+sub get_act_who {
+    my $self = shift;
+    my $config = OpenILS::SIP->config();
+    my $login = OpenILS::SIP->login_account();
+
+    my $act_who = $config->{implementation_config}->{default_activity_who};
+    my $force_who = $config->{implementation_config}->{force_activity_who};
+
+    # 1. future: test sip extension for caller-provided ewho and !$force_who
+
+    # 2. See if the login is tagged with an ewho
+    return $login->{activity_who} if $login->{activity_who};
+
+    # 3. if all else fails, see if there is an institution-wide ewho
+    return $config->{activity_who} if $config->{activity_who};
+
+    return undef;
+}
+
 # grab patron penalties.  Only grab non-archived penalties that are for fines,
 # excessive overdues, or otherwise block circluation activity
 sub flesh_user_penalties {

commit a7a13b9f0b5ebfaef18004a94ede7e23efa6c5b2
Author: Bill Erickson <berick at esilibrary.com>
Date:   Mon Feb 27 16:26:48 2012 -0500

    User activity tracking : staff client last activity display
    
    This adds the Last Activity date to the patron summary (horizontal and
    vertical) just below the Expires date.  The Last Activity value will
    come from the most recent activity of any type which is configured to be
    tracked.  A tooltip on the field will show the activity type.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/xul/staff_client/server/locale/en-US/patron.properties b/Open-ILS/xul/staff_client/server/locale/en-US/patron.properties
index bd55f59..55b750c 100644
--- a/Open-ILS/xul/staff_client/server/locale/en-US/patron.properties
+++ b/Open-ILS/xul/staff_client/server/locale/en-US/patron.properties
@@ -369,6 +369,7 @@ staff.patron.summary.retrieve.no_barcode=summary: No barcode or ID
 staff.patron.summary.patron_net_access=Internet
 staff.patron.summary.create_date=Account created on
 staff.patron.summary.expires_on=Expires on
+staff.patron.summary.last_activity=Last Activity
 staff.patron.summary.updated_on=Last updated on
 staff.patron.summary.standing_penalty.remove=Remove
 staff.patron.summary.standing_penalty.none=No Blocks/Penalties
diff --git a/Open-ILS/xul/staff_client/server/patron/summary.js b/Open-ILS/xul/staff_client/server/patron/summary.js
index fd6b91a..7e1272f 100644
--- a/Open-ILS/xul/staff_client/server/patron/summary.js
+++ b/Open-ILS/xul/staff_client/server/patron/summary.js
@@ -556,6 +556,28 @@ patron.summary.prototype = {
                             };
                         }
                     ],
+                    'patron_last_activity_date' : [
+                        ['render'],
+                        function(e) {
+                            return function() { 
+                                var act = obj.patron.usr_activity();
+                                if (act && act.length) {
+                                    act = act[0];
+                                    util.widgets.set_text(e,
+                                        patronStrings.getString('staff.patron.summary.last_activity') + ' ' + 
+                                            util.date.formatted_date( act.event_time(), '%{localized_date}' ) 
+                                    );
+                                    e.setAttribute('tooltiptext', act.etype().label());
+                                } else {
+
+                                    util.widgets.set_text(e,
+                                        patronStrings.getString('staff.patron.summary.last_activity') + ' ' + 
+                                            patronStrings.getString('staff.patron.field.unset') 
+                                    );
+                                }
+                            };
+                        }
+                    ],
                     'patron_date_of_last_update' : [
                         ['render'],
                         function(e) {
diff --git a/Open-ILS/xul/staff_client/server/patron/summary_overlay.xul b/Open-ILS/xul/staff_client/server/patron/summary_overlay.xul
index b93cd38..ae9ff7a 100644
--- a/Open-ILS/xul/staff_client/server/patron/summary_overlay.xul
+++ b/Open-ILS/xul/staff_client/server/patron/summary_overlay.xul
@@ -50,6 +50,9 @@
         <row id="pdsgr5aa">
             <description id="patron_date_of_exp" class="copyable expire_date value"/>
         </row>
+        <row id="pdsgr5aac">
+            <description id="patron_last_activity_date" class="copyable last_activity_date value"/>
+        </row>
         <row id="pdsgr5aaa">
             <description id="patron_date_of_last_update" class="copyable last_update_time value"/>
         </row>
diff --git a/Open-ILS/xul/staff_client/server/patron/summary_overlay_horiz.xul b/Open-ILS/xul/staff_client/server/patron/summary_overlay_horiz.xul
index 544b3a4..ac495ac 100644
--- a/Open-ILS/xul/staff_client/server/patron/summary_overlay_horiz.xul
+++ b/Open-ILS/xul/staff_client/server/patron/summary_overlay_horiz.xul
@@ -90,6 +90,9 @@
             <row id="pdsgr5aa">
                 <description id="patron_date_of_exp" class="copyable expire_date value"/>
             </row>
+            <row id="pdsgr5aac">
+                <description id="patron_last_activity_date" class="copyable last_activity_date value"/>
+            </row>
         </rows>
     </grid>
 </vbox>

commit f046fa0a17dc74916c62a255f8d08d7e9f3314af
Author: Bill Erickson <berick at esilibrary.com>
Date:   Mon Feb 6 12:31:23 2012 -0500

    User activity tracking : user usr_activity field
    
    Added a new virtual field to actor.usr called "usr_activity".  When
    fleshed, the value contains the most recent activities logged for the
    user.  By default, only the most recent activity entry is fetched,
    however this commit also adds an org unit setting
    "circ.patron.usr_activity_retrieve.max" to control the number of entries
    returned for standard patron fleshing calls.
    
    Activity entries are fleshed in the calls:
    
    open-ils.actor.user.fleshed.retrieve
    open-ils.actor.user.fleshed.retrieve_by_barcode
    
    This change also updates the permacrud <retrieve> permission for
    usr_activity form VIEW_USER to RUN_REPORTS, with the assumption that
    any activity data beyond the configured amount (above) should be
    considered historical data that is not as readily accessable.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 237c8c2..2924bfc 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -2703,6 +2703,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<field reporter:label="Circulations Performed as Staff" name="performed_circulations" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Fund Allocation Percentages" name="fund_alloc_pcts" oils_persist:virtual="true" reporter:datatype="link"/>
 			<field reporter:label="Reservations" name="reservations" oils_persist:virtual="true" reporter:datatype="link"/>
+			<field reporter:label="User Activity Entries" name="usr_activity" oils_persist:virtual="true" reporter:datatype="link"/>
 		</fields>
 		<links>
 			<link field="demographic" reltype="might_have" key="id" map="" class="rud"/>
@@ -2734,6 +2735,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<link field="performed_circulations" reltype="has_many" key="circ_staff" map="" class="circ"/>
 			<link field="fund_alloc_pcts" reltype="has_many" key="allocator" map="" class="acqfap"/>
 			<link field="reservations" reltype="has_many" key="usr" map="" class="bresv"/>
+			<link field="usr_activity" reltype="has_many" key="usr" map="" class="auact"/>
 		</links>
 	</class>
 	<class id="cuat" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::usr_activity_type" oils_persist:tablename="config.usr_activity_type" reporter:label="User Activity Type">
@@ -2769,7 +2771,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 		</links>
 		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
 			<actions>
-				<retrieve permission="VIEW_USER">
+				<retrieve permission="RUN_REPORTS">
 					<context link="usr" field="home_ou" />
 				</retrieve>
 			</actions>
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
index 60118d3..627ee87 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
@@ -2955,7 +2955,8 @@ sub user_retrieve_fleshed_by_id {
 		"addresses",
 		"billing_address",
 		"mailing_address",
-		"stat_cat_entries" ];
+		"stat_cat_entries",
+		"usr_activity" ];
 	return new_flesh_user($user_id, $fields, $e);
 }
 
@@ -2972,6 +2973,12 @@ sub new_flesh_user {
         $fetch_penalties = 1;
     }
 
+    my $fetch_usr_act = 0;
+    if(grep {$_ eq 'usr_activity'} @$fields) {
+        $fields = [grep {$_ ne 'usr_activity'} @$fields];
+        $fetch_usr_act = 1;
+    }
+
 	my $user = $e->retrieve_actor_user(
    	[
       	$id,
@@ -3020,6 +3027,31 @@ sub new_flesh_user {
         );
     }
 
+    # retrieve the most recent usr_activity entry
+    if ($fetch_usr_act) {
+
+        # max number to return for simple patron fleshing
+        my $limit = $U->ou_ancestor_setting_value(
+            $e->requestor->ws_ou, 
+            'circ.patron.usr_activity_retrieve.max');
+
+        my $opts = {
+            flesh => 1,
+            flesh_fields => {auact => ['etype']},
+            order_by => {auact => 'event_time DESC'}, 
+        };
+
+        # 0 == none, <0 == return all
+        $limit = 1 unless defined $limit;
+        $opts->{limit} = $limit if $limit > 0;
+
+        $user->usr_activity( 
+            ($limit == 0) ? 
+                [] : # skip the DB call
+                $e->search_actor_usr_activity([{usr => $user->id}, $opts])
+        );
+    }
+
 	$e->rollback;
 	$user->clear_passwd();
 	return $user;
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 2ca7c7b..94e7626 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -11352,3 +11352,23 @@ INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALU
 -- reserve the first 1000 slots
 SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
 
+INSERT INTO config.org_unit_setting_type 
+    (name, label, description, grp, datatype) 
+    VALUES (
+        'circ.patron.usr_activity_retrieve.max',
+         oils_i18n_gettext(
+            'circ.patron.usr_activity_retrieve.max',
+            'Max user activity entries to retrieve (staff client)',
+            'coust', 
+            'label'
+        ),
+        oils_i18n_gettext(
+            'circ.patron.usr_activity_retrieve.max',
+            'Sets the maxinum number of recent user activity entries to retrieve for display in the staff client.  0 means show none, -1 means show all.  Default is 1.',
+            'coust', 
+            'description'
+        ),
+        'gui',
+        'integer'
+    );
+
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
index 30b3396..de8cc5e 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
@@ -126,6 +126,27 @@ INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALU
 -- reserve the first 1000 slots
 SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
 
+INSERT INTO config.org_unit_setting_type 
+    (name, label, description, grp, datatype) 
+    VALUES (
+        'circ.patron.usr_activity_retrieve.max',
+         oils_i18n_gettext(
+            'circ.patron.usr_activity_retrieve.max',
+            'Max user activity entries to retrieve (staff client)',
+            'coust', 
+            'label'
+        ),
+        oils_i18n_gettext(
+            'circ.patron.usr_activity_retrieve.max',
+            'Sets the maxinum number of recent user activity entries to retrieve for display in the staff client.  0 means show none, -1 means show all.  Default is 1.',
+            'coust', 
+            'description'
+        ),
+        'gui',
+        'integer'
+    );
+
+
 COMMIT;
 
 /* 

commit 56e3da24d3c1c4663526e578dc7d342bd4145b09
Author: Bill Erickson <berick at esilibrary.com>
Date:   Mon Feb 6 11:23:44 2012 -0500

    User activity tracking : activity type admin UI
    
    Admin UI for managing user activit types.  Access under Admin -> Server
    Admin -> User Activity Types.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/src/templates/conify/global/config/usr_activity_type.tt2 b/Open-ILS/src/templates/conify/global/config/usr_activity_type.tt2
new file mode 100644
index 0000000..539ae31
--- /dev/null
+++ b/Open-ILS/src/templates/conify/global/config/usr_activity_type.tt2
@@ -0,0 +1,34 @@
+[% WRAPPER base.tt2 %]
+[% ctx.page_title = l('User Activity Type') %]
+<!--
+<script type="text/javascript" src='[% ctx.media_prefix %]/js/ui/default/conify/global/config/sms_carrier.js'> </script>
+-->
+
+<div dojoType="dijit.layout.ContentPane" layoutAlign="client">
+    <div dojoType="dijit.layout.ContentPane" layoutAlign="top" class='oils-header-panel'>
+        <div>[% ctx.page_title %]</div>
+        <div>
+            <button dojoType='dijit.form.Button' onClick='thingGrid.showCreateDialog()'>[% l('New Activity Type') %]</button>
+            <button dojoType='dijit.form.Button' onClick='thingGrid.deleteSelected()'>[% l('Delete Selected') %]</button>
+        </div>
+    </div>
+    <table  jsId="thingGrid"
+            dojoType="openils.widget.AutoGrid"
+            fieldOrder="['id', 'label', 'ewho', 'ewhat', 'ehow', 'egroup', 'enabled', 'transient']"
+            query="{id: '*'}"
+            defaultCellWidth='"15%"'
+            fmClass='cuat'
+            editOnEnter='true'>
+    </table>
+</div>
+<script>
+    dojo.require('openils.widget.AutoGrid');
+    dojo.addOnLoad(
+        function() {
+            thingGrid.loadAll({order_by : {cuat : 'label, ewho'}});
+        }
+    );
+</script>
+[% END %]
+
+
diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd
index 6c6aa19..1fa3359 100644
--- a/Open-ILS/web/opac/locale/en-US/lang.dtd
+++ b/Open-ILS/web/opac/locale/en-US/lang.dtd
@@ -744,6 +744,7 @@
 <!ENTITY staff.main.menu.admin.server_admin.conify.config_weight_assoc "Weights Association">
 <!ENTITY staff.main.menu.admin.server_admin.conify.config_actor_sip_fields "Actor Stat Cat Sip Fields">
 <!ENTITY staff.main.menu.admin.server_admin.conify.config_asset_sip_fields "Asset Stat Cat Sip Fields">
+<!ENTITY staff.main.menu.admin.server_admin.conify.config_usr_activity_type "User Activity Types">
 <!ENTITY staff.main.menu.admin.server_admin.conify.global_flag.label "Global Flags">
 <!ENTITY staff.main.menu.admin.server_admin.conify.circulation_limit_group.label "Circulation Limit Groups">
 
diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu.js b/Open-ILS/xul/staff_client/chrome/content/main/menu.js
index 65d93b5..b9f5a2a 100644
--- a/Open-ILS/xul/staff_client/chrome/content/main/menu.js
+++ b/Open-ILS/xul/staff_client/chrome/content/main/menu.js
@@ -811,6 +811,10 @@ main.menu.prototype = {
                 ['oncommand'],
                 function(event) { open_eg_web_page('conify/global/config/circ_limit_group', null, event); }
             ],
+            'cmd_server_admin_config_usr_activity_type' : [
+                ['oncommand'],
+                function(event) { open_eg_web_page('conify/global/config/usr_activity_type', null, event); }
+            ],
             'cmd_local_admin_external_text_editor' : [
                 ['oncommand'],
                 function() {
diff --git a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
index de03fa6..220c91e 100644
--- a/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
+++ b/Open-ILS/xul/staff_client/chrome/content/main/menu_frame_menus.xul
@@ -246,6 +246,9 @@
     <command id="cmd_server_admin_config_asset_sip_fields"
              perm="CREATE_COPY_STAT_CAT"
              />
+    <command id="cmd_server_admin_config_usr_activity_type"
+             perm="ADMIN_USER_ACTIVITY_TYPE VIEW_USER_ACTIVITY_TYPE"
+             />
 
     <command id="cmd_hotkeys_toggle" />
     <command id="cmd_hotkeys_set" />
@@ -543,6 +546,7 @@
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.config_weight_assoc;" command="cmd_server_admin_config_weight_assoc"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.config_actor_sip_fields;" command="cmd_server_admin_config_actor_sip_fields"/>
                 <menuitem label="&staff.main.menu.admin.server_admin.conify.config_asset_sip_fields;" command="cmd_server_admin_config_asset_sip_fields"/>
+                <menuitem label="&staff.main.menu.admin.server_admin.conify.config_usr_activity_type;" command="cmd_server_admin_config_usr_activity_type"/>
                 <menu id="main.menu.admin.server.acq" label="&staff.main.menu.admin.server_admin.acq.label;" accesskey="&staff.main.menu.admin.server_admin.acq.accesskey;">
                     <menupopup id="main.menu.admin.server.acq.popup">
                         <menuitem label="&staff.main.menu.admin.server_admin.acq.fund.label;" accesskey="&staff.main.menu.admin.server_admin.acq.fund.accesskey;" command="cmd_server_admin_acq_fund" />

commit d8d9925b089e56a0ad8c877bd85cabb5fdaf93a8
Author: Bill Erickson <berick at esilibrary.com>
Date:   Mon Jan 16 16:24:54 2012 -0500

    User activity tracking: ingress, auth.verify, and login agent
    
    Pile of user activity related additions:
    
    * Set the global default Apache ingress value to 'apache'.  Overridable.
    * Set the ingress value for remoteauth.cgi to 'remoteauth'
    * Change remoteauth.cgi to use auth verify instead of true login
    * Set ingress value for SIP to 'sip2'
    * Set the ingress value for XMLRPC to 'xmlrpc'
    * Set auth agent (ewho) to 'opac' for TPAC and JSPAC
    * Set auth agent to 'staffclient' for staff client logins
    * Set auth agent to 'authproxy' to the mod_perl Auth proxy
    * Added support to the openils.User dojo class for auth verify and
      the login/verify "agent" parameter
    * Set the auth agent to 'selfcheck' for the Selfcheck UI.  Also now using
      auth verify instead of login to check the patron username/barcode +
      password combination (when enforced).
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/examples/apache/startup.pl b/Open-ILS/examples/apache/startup.pl
index ee9bb42..bb3fa16 100755
--- a/Open-ILS/examples/apache/startup.pl
+++ b/Open-ILS/examples/apache/startup.pl
@@ -1,4 +1,9 @@
 #!/usr/bin/perl
+use OpenSRF::AppSession;
+
+# default ingress value for all Apache/mod_perl clients
+OpenSRF::AppSession->ingress('apache'); 
+
 use OpenILS::WWW::Exporter qw( /openils/conf/opensrf_core.xml );
 use OpenILS::WWW::SuperCat qw( /openils/conf/opensrf_core.xml );
 use OpenILS::WWW::AddedContent qw( /openils/conf/opensrf_core.xml );
diff --git a/Open-ILS/examples/remoteauth.cgi b/Open-ILS/examples/remoteauth.cgi
index 255f48c..67c7b5c 100755
--- a/Open-ILS/examples/remoteauth.cgi
+++ b/Open-ILS/examples/remoteauth.cgi
@@ -29,16 +29,19 @@ use Digest::MD5 qw(md5_hex);
 
 use OpenSRF::EX qw(:try);
 use OpenSRF::System;
+use OpenSRF::AppSession;
 
 my $bootstrap = '/openils/conf/opensrf_core.xml';
 my $cgi = new CGI;
 my $u = $cgi->param('user');
 my $usrname = $cgi->param('usrname');
 my $barcode = $cgi->param('barcode');
+my $agent = $cgi->param('agent'); # optional, but preferred
 my $p = $cgi->param('passwd');
 
 print $cgi->header(-type=>'text/html', -expires=>'-1d');
 
+OpenSRF::AppSession->ingress('remoteauth');
 OpenSRF::System->bootstrap_client( config_file => $bootstrap );
 
 if (!($u || $usrname || $barcode) || !$p) {
@@ -69,20 +72,17 @@ if (!($u || $usrname || $barcode) || !$p) {
 	if ($seed) {
 		my $response = OpenSRF::AppSession
 			->create('open-ils.auth')
-			->request( 'open-ils.auth.authenticate.complete', { $nametype => $u, password => md5_hex($seed . md5_hex($p)), type => 'opac' })
+			->request( 'open-ils.auth.authenticate.verify', 
+				{ $nametype => $u, password => md5_hex($seed . md5_hex($p)), type => 'opac', agent => $agent })
 			->gather(1);
-		if ($response->{payload}->{authtoken}) {
-			my $user = OpenSRF::AppSession
-				->create('open-ils.auth')
-				->request( 'open-ils.auth.session.retrieve', $response->{payload}->{authtoken} )
-				->gather(1);
-			if (ref($user) eq 'HASH' && $user->{ilsevent} == 1001) {
-				print '+NO';
-			} else {
+		if ($response) {
+			if ($response->{ilsevent} == 0) {
 				print '+VALID';
+			} else {
+				print '+NO';
 			}
 		} else {
-			print '+NO';
+			print '+BACKEND_ERROR';
 		}
 	} else {
 		print '+BACKEND_ERROR';
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm b/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
index 3a82c79..4f62b1b 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/SIP.pm
@@ -17,6 +17,7 @@ use OpenILS::SIP::Transaction::Renew;
 use OpenILS::SIP::Transaction::FeePayment;
 
 use OpenSRF::System;
+use OpenSRF::AppSession;
 use OpenILS::Utils::Fieldmapper;
 use OpenSRF::Utils::SettingsClient;
 use OpenILS::Application::AppUtils;
@@ -47,6 +48,9 @@ sub new {
 	$target_encoding = $institution->{implementation_config}->{encoding} || 'ascii';
 
 	syslog('LOG_DEBUG', "OILS: loading bootstrap config: $bsconfig");
+
+	# ingress will persist throughout
+	OpenSRF::AppSession->ingress('sip2');
 	
 	local $/ = "\n";    # why?
 	OpenSRF::System->bootstrap_client(config_file => $bsconfig);
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
index a0413c6..b1ffd40 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
@@ -330,7 +330,8 @@ sub load_login {
     my $args = {	
         username => $username, 
         password => md5_hex($seed . md5_hex($password)), 
-        type => ($persist) ? 'persist' : 'opac' 
+        type => ($persist) ? 'persist' : 'opac',
+        agent => 'opac'
     };
 
     my $bc_regex = $ctx->{get_org_setting}->($org_unit, 'opac.barcode_regex');
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/Proxy.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/Proxy.pm
index 6c5f3da..ee7b4ef 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/Proxy.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/Proxy.pm
@@ -185,7 +185,7 @@ sub oils_login {
         my $response = OpenSRF::AppSession
 		->create("open-ils.auth")
 		->request( 'open-ils.auth.authenticate.complete',
-			{ $nametype => $username,
+			{ $nametype => $username, agent => 'authproxy',
 			  password => md5_hex($seed . md5_hex($password)),
 			  type => $type })
 		->gather(1);
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/Proxy/Authen.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/Proxy/Authen.pm
index e8962a3..d6ca65e 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/Proxy/Authen.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/Proxy/Authen.pm
@@ -163,7 +163,7 @@ sub oils_login {
         my $response = OpenSRF::AppSession
 		->create("open-ils.auth")
 		->request( 'open-ils.auth.authenticate.complete',
-			{ $nametype => $username,
+			{ $nametype => $username, agent => 'authproxy',
 			  password => md5_hex($seed . md5_hex($password)),
 			  type => $type })
 		->gather(1);
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/XMLRPCGateway.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/XMLRPCGateway.pm
index ea80f1d..26eb0a7 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/XMLRPCGateway.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/XMLRPCGateway.pm
@@ -40,6 +40,7 @@ sub import { $bs_config = $_[1]; }
 # Bootstrap and load config settings
 sub child_init {
 	$__inited = 1;
+	OpenSRF::AppSession->ingress('xmlrpc');
 	OpenSRF::System->bootstrap_client( config_file => $bs_config );
 	my $sclient	= OpenSRF::Utils::SettingsClient->new();
 	my $idl = $sclient->config_value("IDL");
@@ -48,6 +49,7 @@ sub child_init {
 	$logger->debug("XML-RPC: allowed services @$services");
 	OpenILS::Utils::Fieldmapper->require;
 	Fieldmapper->import(IDL => $idl);
+	OpenSRF::AppSession->ingress('apache');
 }
 
 
@@ -82,8 +84,12 @@ sub handler {
 
 sub run_request {
     my( $service, $method, @args ) = @_;
+
+    # since multiple Perl clients run within mod_perl, 
+    # we must set our ingress before each request.
+    OpenSRF::AppSession->ingress('xmlrpc');
+
     my $ses = OpenSRF::AppSession->create( $service );
-    #my $data = $ses->request($method, @args)->gather(1);
 
     my $data = [];
     my $req = $ses->request($method, @args);
@@ -95,6 +101,10 @@ sub run_request {
         push( @$data, $resp->content );
     }
 
+    # recover the default Apache/http ingress to avoid 
+    # polluting other mod_perl clients w/ our ingress value.
+    OpenSRF::AppSession->ingress('apache');
+
     return [] if scalar(@$data) == 0;
     return wrap_perl($$data[0]) 
         if scalar(@$data) == 1 and $method !~ /.atomic$/og;
diff --git a/Open-ILS/web/js/dojo/openils/User.js b/Open-ILS/web/js/dojo/openils/User.js
index bd91ba8..60e5f91 100644
--- a/Open-ILS/web/js/dojo/openils/User.js
+++ b/Open-ILS/web/js/dojo/openils/User.js
@@ -33,6 +33,7 @@ if(!dojo._hasResource["openils.User"]) {
         username : null,
         passwd : null,
         login_type : 'opac',
+        login_agent : null,
         location : null,
         authtoken : null,
         authtime : null,
@@ -48,6 +49,7 @@ if(!dojo._hasResource["openils.User"]) {
             this.authtoken = kwargs.authtoken || openils.User.authtoken;
             this.authtime = kwargs.authtime || openils.User.authtime;
             this.login_type = kwargs.login_type;
+            this.login_agent = kwargs.login_agent || openils.User.default_login_agent || 'staffclient';
             this.location = kwargs.location;
             this.authcookie = kwargs.authcookie || openils.User.authcookie;
             this.permOrgStoreCache = {}; /* permName => permOrgUnitStore map */
@@ -111,6 +113,42 @@ if(!dojo._hasResource["openils.User"]) {
                 return req.recv().content();
             }
         },
+
+        /**
+         * Tests the given username and password.  This version is async only.
+         */
+        auth_verify : function(args, onComplete) {
+            var _u = this;
+            if (!args) args = {};
+            if (!args.username) args.username = _u.username;
+            if (!args.passwd) args.passwd = _u.passwd;
+            if (!args.agent) args.agent = _u.login_agent;
+            if (!args.type) args.type = _u.type;
+
+            var initReq = OpenSRF.CachedClientSession('open-ils.auth').request('open-ils.auth.authenticate.init', args.username);
+    
+            initReq.oncomplete = function(r) {
+                var seed = r.recv().content(); 
+                var loginInfo = {
+                    type : args.type,
+                    username : args.username,
+                    barcode : args.barcode,
+                    password : hex_md5(seed + hex_md5(args.passwd)), 
+                    agent : args.agent,
+                };
+    
+                var authReq = OpenSRF.CachedClientSession('open-ils.auth').request('open-ils.auth.authenticate.verify', loginInfo);
+                authReq.oncomplete = function(rr) {
+                    var data = rr.recv().content();
+                    var evt = openils.Event.parse(data);
+                    if (evt && evt.code == 0) onComplete(true);
+                    else onComplete(false);
+                }
+                authReq.send();
+            }
+    
+            initReq.send();
+        },
     
     
         /**
@@ -123,6 +161,7 @@ if(!dojo._hasResource["openils.User"]) {
             if (!args.username) args.username = _u.username;
             if (!args.passwd) args.passwd = _u.passwd;
             if (!args.type) args.type = _u.login_type;
+            if (!args.agent) args.agent = _u.login_agent;
             if (!args.location) args.location = _u.location;
 
             var initReq = OpenSRF.CachedClientSession('open-ils.auth').request('open-ils.auth.authenticate.init', args.username);
@@ -133,6 +172,7 @@ if(!dojo._hasResource["openils.User"]) {
                     username : args.username,
                     password : hex_md5(seed + hex_md5(args.passwd)), 
                     type : args.type,
+                    agent : args.agent,
                     org : args.location,
                     workstation : args.workstation
                 };
@@ -165,6 +205,7 @@ if(!dojo._hasResource["openils.User"]) {
             if (!args.username) args.username = _u.username;
             if (!args.passwd) args.passwd = _u.passwd;
             if (!args.type) args.type = _u.login_type;
+            if (!args.agent) args.agent = _u.login_agent;
             if (!args.location) args.location = _u.location;
 
             var seed = fieldmapper.standardRequest(
@@ -176,6 +217,7 @@ if(!dojo._hasResource["openils.User"]) {
                 username : args.username,
                 password : hex_md5(seed + hex_md5(args.passwd)), 
                 type : args.type,
+                agent : args.agent,
                 org : args.location,
                 workstation : args.workstation,
             };
@@ -298,6 +340,7 @@ if(!dojo._hasResource["openils.User"]) {
 	openils.User.authtoken = null;
 	openils.User.authtime = null;
     openils.User.authcookie = null;
+    openils.User.default_login_agent = null; // global agent override
     openils.User.localeStrings =
         dojo.i18n.getLocalization("openils.User", "User");
 
diff --git a/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js b/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
index 882c980..886c8c3 100644
--- a/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
+++ b/Open-ILS/web/js/ui/default/circ/selfcheck/selfcheck.js
@@ -27,6 +27,9 @@ const SET_CC_PAYMENT_ALLOWED = 'credit.payments.allow';
 // This setting only comes into play if COPY_NOT_AVAILABLE is in the SET_AUTO_OVERRIDE_EVENTS list
 const SET_BLOCK_CHECKOUT_ON_COPY_STATUS = 'circ.selfcheck.block_checkout_on_copy_status';
 
+// set before the login dialog is rendered
+openils.User.default_login_agent = 'selfcheck';
+
 function SelfCheckManager() {
 
     this.cgi = new openils.CGI();
@@ -329,21 +332,31 @@ SelfCheckManager.prototype.loginPatron = function(barcode_or_usrname, passwd) {
 
         // patron password is required.  Verify it.
 
-        var res = fieldmapper.standardRequest(
-            ['open-ils.actor', 'open-ils.actor.verify_user_password'],
-            {params : [this.authtoken, barcode, usrname, hex_md5(passwd)]}
+        var self = this;
+        new openils.User().auth_verify(
+            {   username : usrname, barcode : barcode, 
+                type : 'opac', passwd : passwd, agent : 'selfcheck' },
+            function(OK) {
+                if (OK) {
+                    self.fetchPatron(barcode, usrname);
+
+                } else {
+                    // auth verify failed
+                    self.handleAlert(
+                        dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode_or_usrname]),
+                        false, 'login-failure'
+                    );
+                    self.drawLoginPage();
+                }
+            }
         );
 
-        if(res == 0) {
-            // user-not-found results in login failure
-            this.handleAlert(
-                dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode_or_usrname]),
-                false, 'login-failure'
-            );
-            this.drawLoginPage();
-            return;
-        }
-    } 
+    } else {
+        this.fetchPatron(barcode, usrname);
+    }
+};
+
+SelfCheckManager.prototype.fetchPatron = function(barcode, usrname) {
 
     var patron_id = fieldmapper.standardRequest(
         ['open-ils.actor', 'open-ils.actor.user.retrieve_id_by_barcode_or_username'],
@@ -373,7 +386,7 @@ SelfCheckManager.prototype.loginPatron = function(barcode_or_usrname, passwd) {
 
     if(evt || inactiveCard) {
         this.handleAlert(
-            dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode_or_usrname]),
+            dojo.string.substitute(localeStrings.LOGIN_FAILED, [barcode || usrname]),
             false, 'login-failure'
         );
         this.drawLoginPage();
diff --git a/Open-ILS/web/opac/common/js/opac_utils.js b/Open-ILS/web/opac/common/js/opac_utils.js
index b699f46..bf1f3d6 100644
--- a/Open-ILS/web/opac/common/js/opac_utils.js
+++ b/Open-ILS/web/opac/common/js/opac_utils.js
@@ -727,7 +727,8 @@ function doLogin(suppressEvents) {
 	var args = {
 		password : hex_md5(seed + hex_md5(passwd)), 
 		type		: "opac", 
-		org		: getOrigLocation()
+		org		: getOrigLocation(),
+		agent : 'opac'
 	};
 
     r = fetchOrgSettingDefault(globalOrgTree.id(), 'opac.barcode_regex');
diff --git a/Open-ILS/xul/staff_client/chrome/content/auth/session.js b/Open-ILS/xul/staff_client/chrome/content/auth/session.js
index c3f9a13..c4f0ae8 100644
--- a/Open-ILS/xul/staff_client/chrome/content/auth/session.js
+++ b/Open-ILS/xul/staff_client/chrome/content/auth/session.js
@@ -38,6 +38,7 @@ auth.session.prototype = {
                         )
                     ),
                     'type' : 'temp',
+                    'agent' : 'staffclient'
                 };
 
                 if (data.ws_info[ this.view.server_prompt.value ]) {

commit cf198e9279137812800e2722e5e0c56ceaf60881
Author: Bill Erickson <berick at esilibrary.com>
Date:   Mon Jan 16 16:24:01 2012 -0500

    User activity tracking : auth verify test for srfsh.py
    
    Added a handler for auth_verify, which calls
    open-ils.auth.authenticate.verify.
    
    Added here (srfsh.py) instead of srfsh(.c) because a) it's quick and b)
    srfsh(.c) already has too much open-ils in it.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/src/python/oils/srfsh.py b/Open-ILS/src/python/oils/srfsh.py
index e5baa26..df8072c 100644
--- a/Open-ILS/src/python/oils/srfsh.py
+++ b/Open-ILS/src/python/oils/srfsh.py
@@ -44,6 +44,34 @@ def handle_login(srfsh, args):
         )
     ])
 
+def handle_auth_verify(srfsh, args):
+    ''' Verify auth w/ args '''
+
+    username = args[0]
+    password = args[1]
+
+    seed = srfsh.handle_request([
+        'open-ils.auth', 
+        'open-ils.auth.authenticate.init', 
+        '"%s"' % username
+    ])
+
+    password = md5sum(seed + md5sum(password))
+
+    response = srfsh.handle_request([
+        'open-ils.auth', 
+        'open-ils.auth.authenticate.verify', 
+
+        osrf.json.to_json( 
+            {   # handle_request accepts json-encoded params
+                'username'    : username,
+                'password'    : password,
+                'type'        : args[2] if len(args) > 2 else None,
+            }
+        )
+    ])
+
+
 def handle_org_setting(srfsh, args):
     ''' Retrieves the requested org setting.
 
@@ -91,6 +119,7 @@ def load(srfsh, config):
 
     # register custom commands
     srfsh.add_command(command = 'login', handler = handle_login)
+    srfsh.add_command(command = 'auth_verify', handler = handle_auth_verify)
     srfsh.add_command(command = 'idl', handler = handle_idl)
     srfsh.add_command(command = 'org_setting', handler = handle_org_setting)
 

commit 75932d074b19cec1cf954411bb8c138f4cf6414d
Author: Bill Erickson <berick at esilibrary.com>
Date:   Mon Jan 16 16:19:41 2012 -0500

    User activity tracking: open-ils.auth additions
    
    * Creates usr_activity entries for login and authentication verification
      requests
    
    * Adds a new parameter to open-ils.auth.authenticate.[complete|verify]
      methods called "agent" which maps to the usr_activity column for "ewho"
      (the UI or 3rd-party that initiated the action).
    
    * Adds a new API call "open-ils.auth.authenticate.verify", which behaves
      almost identically to authenticate.complete, with the exception that it
      does not "log in" (i.e. create an auth token and cache the user object).
      Instead, it simply returns a SUCCESS event if the username/barcode and
      password combination are valid.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/include/openils/oils_utils.h b/Open-ILS/include/openils/oils_utils.h
index 4357a40..3554d21 100644
--- a/Open-ILS/include/openils/oils_utils.h
+++ b/Open-ILS/include/openils/oils_utils.h
@@ -104,6 +104,12 @@ int oilsUtilsIsDBTrue( const char* val );
 
 long oilsUtilsIntervalToSeconds( const char* interval );
 
+/**
+ * Creates actor.usr_activity entries
+ * @return The number of rows created.  0 or 1.
+ */
+int oilsUtilsTrackUserActivity( long usr, const char* ewho, const char* ewhat, const char* ehow );
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Open-ILS/src/c-apps/oils_auth.c b/Open-ILS/src/c-apps/oils_auth.c
index 7c545de..121e5dc 100644
--- a/Open-ILS/src/c-apps/oils_auth.c
+++ b/Open-ILS/src/c-apps/oils_auth.c
@@ -64,6 +64,15 @@ int osrfAppInitialize() {
 
 	osrfAppRegisterMethod(
 		MODULENAME,
+		"open-ils.auth.authenticate.verify",
+		"oilsAuthComplete",
+		"Verifies the user provided a valid username and password."
+		"Params and are the same as open-ils.auth.authenticate.complete."
+		"Returns SUCCESS event on success, failure event on failure", 1, 0);
+
+
+	osrfAppRegisterMethod(
+		MODULENAME,
 		"open-ils.auth.session.retrieve",
 		"oilsAuthSessionRetrieve",
 		"Pass in the auth token and this retrieves the user object.  The auth "
@@ -553,6 +562,7 @@ static oilsEvent* oilsAuthVerifyWorkstation(
 		- "type"
 		- "org"
 		- "workstation"
+		- "agent" (what software/interface/3rd-party is making the request)
 
 	The password is required.  Either a username or a barcode must also be present.
 
@@ -575,6 +585,7 @@ int oilsAuthComplete( osrfMethodContext* ctx ) {
 	int orgloc        = (int) jsonObjectGetNumber(jsonObjectGetKeyConst(args, "org"));
 	const char* workstation = jsonObjectGetString(jsonObjectGetKeyConst(args, "workstation"));
 	const char* barcode     = jsonObjectGetString(jsonObjectGetKeyConst(args, "barcode"));
+	const char* ewho        = jsonObjectGetString(jsonObjectGetKeyConst(args, "agent"));
 
 	const char* ws = (workstation) ? workstation : "";
 
@@ -709,8 +720,23 @@ int oilsAuthComplete( osrfMethodContext* ctx ) {
 		uname = freeable_uname = oilsFMGetString( userObj, "usrname" );
 	}
 
-	if( passOK ) {
-		response = oilsAuthHandleLoginOK( userObj, uname, type, orgloc, workstation );
+	if( passOK ) { // login successful  
+        
+		char* ewhat = "login";
+
+		if (0 == strcmp(ctx->method->name, "open-ils.auth.authenticate.verify")) {
+			response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_SUCCESS );
+			ewhat = "verify";
+
+		} else {
+			response = oilsAuthHandleLoginOK( userObj, uname, type, orgloc, workstation );
+		}
+
+		oilsUtilsTrackUserActivity(
+			oilsFMGetObjectId(userObj), 
+			ewho, ewhat, 
+			osrfAppSessionGetIngress()
+		);
 
 	} else {
 		response = oilsNewEvent( OSRF_LOG_MARK, OILS_EVENT_AUTH_FAILED );
diff --git a/Open-ILS/src/c-apps/oils_utils.c b/Open-ILS/src/c-apps/oils_utils.c
index 2be8420..cb334ac 100644
--- a/Open-ILS/src/c-apps/oils_utils.c
+++ b/Open-ILS/src/c-apps/oils_utils.c
@@ -120,6 +120,50 @@ long oilsFMGetObjectId( const jsonObject* obj ) {
 	return id;
 }
 
+int oilsUtilsTrackUserActivity(long usr, const char* ewho, const char* ewhat, const char* ehow) {
+    if (!usr && !(ewho || ewhat || ehow)) return 0;
+    int rowcount = 0;
+
+    jsonObject* params = jsonParseFmt(
+        "{\"from\":[\"actor.insert_usr_activity\", %ld, \"%s\", \"%s\", \"%s\"]}",
+        usr, 
+        (NULL == ewho)  ? "" : ewho, 
+        (NULL == ewhat) ? "" : ewhat, 
+        (NULL == ehow)  ? "" : ehow
+    );
+
+	osrfAppSession* session = osrfAppSessionClientInit("open-ils.cstore");
+    osrfAppSessionConnect(session);
+    int reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.begin", 1);
+	osrfMessage* omsg = osrfAppSessionRequestRecv(session, reqid, 60);
+
+    if(omsg) {
+        osrfMessageFree(omsg);
+        reqid = osrfAppSessionSendRequest(session, params, "open-ils.cstore.json_query", 1);
+	    omsg = osrfAppSessionRequestRecv(session, reqid, 60);
+
+        if(omsg) {
+            const jsonObject* rows = osrfMessageGetResult(omsg);
+            if (rows) rowcount = rows->size;
+            osrfMessageFree(omsg); // frees rows
+            if (rowcount) {
+                reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.commit", 1);
+	            omsg = osrfAppSessionRequestRecv(session, reqid, 60);
+                osrfMessageFree(omsg);
+            } else {
+                reqid = osrfAppSessionSendRequest(session, NULL, "open-ils.cstore.transaction.rollback", 1);
+	            omsg = osrfAppSessionRequestRecv(session, reqid, 60);
+                osrfMessageFree(omsg);
+            }
+        }
+    }
+
+    osrfAppSessionFree(session); // calls disconnect internally
+    jsonObjectFree(params);
+    return rowcount;
+}
+
+
 
 oilsEvent* oilsUtilsCheckPerms( int userid, int orgid, char* permissions[], int size ) {
 	if (!permissions) return NULL;

commit b2d097445cf8c2609bbbd4576a78143658877464
Author: Bill Erickson <berick at esilibrary.com>
Date:   Mon Jan 16 16:18:49 2012 -0500

    User activity tracking: schema and IDL
    
    * Adds 2 new tables and IDL classes.  The first is a configuration table
      used for defining activity types (config.usr_activity_type).  The
      second is for tracking activity events.  A user activity event is
      defined as a combination of user, action (e.g. login), the interface
      or 3rd-party responsible for the action (e.g. opac, staffclient,
      libraryelf), and the OpenSRF ingress (i.e. the mechanism through which
      the action was delivered: e.g. gateway, translator, xmlrpc).
    
    * Includes a front-facing stored procedure (actor.insert_usr_activity),
      used for creating new activity entries.
    
    * Adds seed data for some default activity types and reserves the first
      1000 IDs for system use.
    
    Current default values for "ewho":
    
    opac
    staffclient
    selfcheck
    authproxy
    ums
    libraryelf
    ezproxy
    
    Current default values for "ehow" (ingress, some inherited from
    opensrf):
    
    opensrf (default)
    gateway-v1
    translator-v1
    srfsh
    --
    sip2
    xmlrpc
    remoteauth
    apache (default mod_perl/apache mod entry point)
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Thomas Berezansky <tsbere at mvlc.org>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 5260a76..237c8c2 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -2736,7 +2736,45 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 			<link field="reservations" reltype="has_many" key="usr" map="" class="bresv"/>
 		</links>
 	</class>
-
+	<class id="cuat" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::usr_activity_type" oils_persist:tablename="config.usr_activity_type" reporter:label="User Activity Type">
+		<fields oils_persist:primary="id" oils_persist:sequence="config.usr_activity_type_id_seq">
+			<field name="id" reporter:label="ID" reporter:datatype="id" />
+			<field name="ewho" reporter:label="Event Caller" reporter:datatype="text"/>
+			<field name="ewhat" reporter:label="Event Type" reporter:datatype="text"/>
+			<field name="ehow" reporter:label="Event Mechanism" reporter:datatype="text"/>
+			<field name="label" reporter:label="Label" reporter:datatype="text"/>
+			<field name="egroup" reporter:label="Activity Group" reporter:datatype="text"/>
+			<field name="enabled" reporter:label="Enabled" reporter:datatype="bool"/>
+			<field name="transient" reporter:label="Transient" reporter:datatype="bool"/>
+        </fields>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<create permission="ADMIN_USER_ACTIVITY_TYPE" global_required="true"/>
+				<retrieve/>
+				<update permission="ADMIN_USER_ACTIVITY_TYPE" global_required="true"/>
+				<delete permission="ADMIN_USER_ACTIVITY_TYPE" global_required="true"/>
+			</actions>
+		</permacrud>
+	</class>
+	<class id="auact" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::usr_activity" oils_persist:tablename="actor.usr_activity" reporter:label="User Activity">
+		<fields oils_persist:primary="id" oils_persist:sequence="actor.usr_activity_id_seq">
+			<field name="id" reporter:label="ID" reporter:datatype="id" />
+			<field name="usr" reporter:label="User" reporter:datatype="link" />
+			<field name="etype" reporter:label="Activity Type" reporter:datatype="link" />
+			<field name="event_time" reporter:label="Event Time" reporter:datatype="timestamp" />
+        </fields>
+        <links>
+			<link field="usr" reltype="has_a" key="id" map="" class="au"/>
+			<link field="etype" reltype="has_a" key="id" map="" class="cuat"/>
+		</links>
+		<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+			<actions>
+				<retrieve permission="VIEW_USER">
+					<context link="usr" field="home_ou" />
+				</retrieve>
+			</actions>
+		</permacrud>
+	</class>
 	<class id="csg" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="config::settings_group" oils_persist:tablename="config.settings_group" reporter:label="Settings Group">
 		<fields oils_persist:primary="name">
 			<field name="name" reporter:datatype="text"/>
diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index 112a50b..5793062 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -951,4 +951,22 @@ CREATE TABLE config.sms_carrier (
     active          BOOLEAN DEFAULT TRUE
 );
 
+CREATE TYPE config.usr_activity_group AS ENUM ('authen','authz','circ','hold','search');
+
+CREATE TABLE config.usr_activity_type (
+    id          SERIAL                      PRIMARY KEY, 
+    ewho        TEXT,
+    ewhat       TEXT,
+    ehow        TEXT,
+    label       TEXT                        NOT NULL, -- i18n
+    egroup      config.usr_activity_group   NOT NULL,
+    enabled     BOOL                        NOT NULL DEFAULT TRUE,
+    transient   BOOL                        NOT NULL DEFAULT FALSE,
+    CONSTRAINT  one_of_wwh CHECK (COALESCE(ewho,ewhat,ehow) IS NOT NULL)
+);
+
+CREATE UNIQUE INDEX unique_wwh ON config.usr_activity_type 
+    (COALESCE(ewho,''), COALESCE (ewhat,''), COALESCE(ehow,''));
+
+
 COMMIT;
diff --git a/Open-ILS/src/sql/Pg/005.schema.actors.sql b/Open-ILS/src/sql/Pg/005.schema.actors.sql
index fbe0504..9c219e0 100644
--- a/Open-ILS/src/sql/Pg/005.schema.actors.sql
+++ b/Open-ILS/src/sql/Pg/005.schema.actors.sql
@@ -623,4 +623,11 @@ CREATE TABLE actor.address_alert (
     billing_address BOOL    NOT NULL DEFAULT FALSE
 );
 
+CREATE TABLE actor.usr_activity (
+    id          BIGSERIAL   PRIMARY KEY,
+    usr         INT         REFERENCES actor.usr (id) ON DELETE SET NULL,
+    etype       INT         NOT NULL REFERENCES config.usr_activity_type (id),
+    event_time  TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
 COMMIT;
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 4896475..2ca7c7b 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -11312,4 +11312,43 @@ INSERT INTO vandelay.merge_profile (owner, name, replace_spec)
 INSERT INTO vandelay.merge_profile (owner, name, preserve_spec) 
     VALUES (1, 'Full Overlay', '901c');
 
+-- user activity seed data --
+
+INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALUES
+
+     -- authen/authz actions
+     -- note: "opensrf" is the default ingress/ehow
+     (1,  NULL, 'login',  'opensrf',      'authen', oils_i18n_gettext(1 , 'Login via opensrf', 'cuat', 'label'))
+    ,(2,  NULL, 'login',  'srfsh',        'authen', oils_i18n_gettext(2 , 'Login via srfsh', 'cuat', 'label'))
+    ,(3,  NULL, 'login',  'gateway-v1',   'authen', oils_i18n_gettext(3 , 'Login via gateway-v1', 'cuat', 'label'))
+    ,(4,  NULL, 'login',  'translator-v1','authen', oils_i18n_gettext(4 , 'Login via translator-v1', 'cuat', 'label'))
+    ,(5,  NULL, 'login',  'xmlrpc',       'authen', oils_i18n_gettext(5 , 'Login via xmlrpc', 'cuat', 'label'))
+    ,(6,  NULL, 'login',  'remoteauth',   'authen', oils_i18n_gettext(6 , 'Login via remoteauth', 'cuat', 'label'))
+    ,(7,  NULL, 'login',  'sip2',         'authen', oils_i18n_gettext(7 , 'SIP2 Proxy Login', 'cuat', 'label'))
+    ,(8,  NULL, 'login',  'apache',       'authen', oils_i18n_gettext(8 , 'Login via Apache module', 'cuat', 'label'))
+
+    ,(9,  NULL, 'verify', 'opensrf',      'authz',  oils_i18n_gettext(9 , 'Verification via opensrf', 'cuat', 'label'))
+    ,(10, NULL, 'verify', 'srfsh',        'authz',  oils_i18n_gettext(10, 'Verification via srfsh', 'cuat', 'label'))
+    ,(11, NULL, 'verify', 'gateway-v1',   'authz',  oils_i18n_gettext(11, 'Verification via gateway-v1', 'cuat', 'label'))
+    ,(12, NULL, 'verify', 'translator-v1','authz',  oils_i18n_gettext(12, 'Verification via translator-v1', 'cuat', 'label'))
+    ,(13, NULL, 'verify', 'xmlrpc',       'authz',  oils_i18n_gettext(13, 'Verification via xmlrpc', 'cuat', 'label'))
+    ,(14, NULL, 'verify', 'remoteauth',   'authz',  oils_i18n_gettext(14, 'Verification via remoteauth', 'cuat', 'label'))
+    ,(15, NULL, 'verify', 'sip2',         'authz',  oils_i18n_gettext(15, 'SIP2 User Verification', 'cuat', 'label'))
+
+     -- authen/authz actions w/ known uses of "who"
+    ,(16, 'opac',        'login',  'gateway-v1',   'authen', oils_i18n_gettext(16, 'OPAC Login (jspac)', 'cuat', 'label'))
+    ,(17, 'opac',        'login',  'apache',       'authen', oils_i18n_gettext(17, 'OPAC Login (tpac)', 'cuat', 'label'))
+    ,(18, 'staffclient', 'login',  'gateway-v1',   'authen', oils_i18n_gettext(18, 'Staff Client Login', 'cuat', 'label'))
+    ,(19, 'selfcheck',   'login',  'translator-v1','authen', oils_i18n_gettext(19, 'Self-Check Proxy Login', 'cuat', 'label'))
+    ,(20, 'ums',         'login',  'xmlrpc',       'authen', oils_i18n_gettext(20, 'Unique Mgt Login', 'cuat', 'label'))
+    ,(21, 'authproxy',   'login',  'apache',       'authen', oils_i18n_gettext(21, 'Apache Auth Proxy Login', 'cuat', 'label'))
+    ,(22, 'libraryelf',  'login',  'xmlrpc',       'authz',  oils_i18n_gettext(22, 'LibraryElf Login', 'cuat', 'label'))
+
+    ,(23, 'selfcheck',   'verify', 'translator-v1','authz',  oils_i18n_gettext(23, 'Self-Check User Verification', 'cuat', 'label'))
+    ,(24, 'ezproxy',     'verify', 'remoteauth',   'authz',  oils_i18n_gettext(24, 'EZProxy Verification', 'cuat', 'label'))
+    -- ...
+    ;
+
+-- reserve the first 1000 slots
+SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
 
diff --git a/Open-ILS/src/sql/Pg/999.functions.global.sql b/Open-ILS/src/sql/Pg/999.functions.global.sql
index 1acd06a..69d9cbe 100644
--- a/Open-ILS/src/sql/Pg/999.functions.global.sql
+++ b/Open-ILS/src/sql/Pg/999.functions.global.sql
@@ -2092,4 +2092,61 @@ CREATE OR REPLACE FUNCTION evergreen.coded_value_map_normalizer( input TEXT, cty
             WHERE ctype = $2 AND code = $1;
 $F$ LANGUAGE SQL;
 
+-- user activity functions --
 
+-- remove transient activity entries on insert of new entries
+CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
+BEGIN
+    DELETE FROM actor.usr_activity USING config.usr_activity_type atype
+        WHERE atype.transient AND NEW.etype = atype.id;
+    RETURN NEW;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER remove_transient_usr_activity
+    BEFORE INSERT ON actor.usr_activity
+    FOR EACH ROW EXECUTE PROCEDURE actor.usr_activity_transient_trg();
+
+-- given a set of activity criteria, find the most approprate activity type
+CREATE OR REPLACE FUNCTION actor.usr_activity_get_type (
+        ewho TEXT, 
+        ewhat TEXT, 
+        ehow TEXT
+    ) RETURNS SETOF config.usr_activity_type AS $$
+SELECT * FROM config.usr_activity_type 
+    WHERE 
+        enabled AND 
+        (ewho  IS NULL OR ewho  = $1) AND
+        (ewhat IS NULL OR ewhat = $2) AND
+        (ehow  IS NULL OR ehow  = $3) 
+    ORDER BY 
+        -- BOOL comparisons sort false to true
+        COALESCE(ewho, '')  != COALESCE($1, ''),
+        COALESCE(ewhat,'')  != COALESCE($2, ''),
+        COALESCE(ehow, '')  != COALESCE($3, '') 
+    LIMIT 1;
+$$ LANGUAGE SQL;
+
+-- given a set of activity criteria, finds the best
+-- activity type and inserts the activity entry
+CREATE OR REPLACE FUNCTION actor.insert_usr_activity (
+        usr INT,
+        ewho TEXT, 
+        ewhat TEXT, 
+        ehow TEXT
+    ) RETURNS SETOF actor.usr_activity AS $$
+DECLARE
+    new_row actor.usr_activity%ROWTYPE;
+BEGIN
+    SELECT id INTO new_row.etype FROM actor.usr_activity_get_type(ewho, ewhat, ehow);
+    IF FOUND THEN
+        new_row.usr := usr;
+        INSERT INTO actor.usr_activity (usr, etype) 
+            VALUES (usr, new_row.etype)
+            RETURNING * INTO new_row;
+        RETURN NEXT new_row;
+    END IF;
+END;
+$$ LANGUAGE plpgsql;
+
+-- user activity functions --
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
new file mode 100644
index 0000000..30b3396
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.user-activity.sql
@@ -0,0 +1,144 @@
+-- Evergreen DB patch XXXX.schema.user-activity.sql
+--
+BEGIN;
+
+-- check whether patch can be applied
+-- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+-- SCHEMA --
+
+CREATE TYPE config.usr_activity_group AS ENUM ('authen','authz','circ','hold','search');
+
+CREATE TABLE config.usr_activity_type (
+    id          SERIAL                      PRIMARY KEY, 
+    ewho        TEXT,
+    ewhat       TEXT,
+    ehow        TEXT,
+    label       TEXT                        NOT NULL, -- i18n
+    egroup      config.usr_activity_group   NOT NULL,
+    enabled     BOOL                        NOT NULL DEFAULT TRUE,
+    transient   BOOL                        NOT NULL DEFAULT FALSE,
+    CONSTRAINT  one_of_wwh CHECK (COALESCE(ewho,ewhat,ehow) IS NOT NULL)
+);
+
+CREATE UNIQUE INDEX unique_wwh ON config.usr_activity_type 
+    (COALESCE(ewho,''), COALESCE (ewhat,''), COALESCE(ehow,''));
+
+CREATE TABLE actor.usr_activity (
+    id          BIGSERIAL   PRIMARY KEY,
+    usr         INT         REFERENCES actor.usr (id) ON DELETE SET NULL,
+    etype       INT         NOT NULL REFERENCES config.usr_activity_type (id),
+    event_time  TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+-- remove transient activity entries on insert of new entries
+CREATE OR REPLACE FUNCTION actor.usr_activity_transient_trg () RETURNS TRIGGER AS $$
+BEGIN
+    DELETE FROM actor.usr_activity USING config.usr_activity_type atype
+        WHERE atype.transient AND NEW.etype = atype.id;
+    RETURN NEW;
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE TRIGGER remove_transient_usr_activity
+    BEFORE INSERT ON actor.usr_activity
+    FOR EACH ROW EXECUTE PROCEDURE actor.usr_activity_transient_trg();
+
+-- given a set of activity criteria, find the most approprate activity type
+CREATE OR REPLACE FUNCTION actor.usr_activity_get_type (
+        ewho TEXT, 
+        ewhat TEXT, 
+        ehow TEXT
+    ) RETURNS SETOF config.usr_activity_type AS $$
+SELECT * FROM config.usr_activity_type 
+    WHERE 
+        enabled AND 
+        (ewho  IS NULL OR ewho  = $1) AND
+        (ewhat IS NULL OR ewhat = $2) AND
+        (ehow  IS NULL OR ehow  = $3) 
+    ORDER BY 
+        -- BOOL comparisons sort false to true
+        COALESCE(ewho, '')  != COALESCE($1, ''),
+        COALESCE(ewhat,'')  != COALESCE($2, ''),
+        COALESCE(ehow, '')  != COALESCE($3, '') 
+    LIMIT 1;
+$$ LANGUAGE SQL;
+
+-- given a set of activity criteria, finds the best
+-- activity type and inserts the activity entry
+CREATE OR REPLACE FUNCTION actor.insert_usr_activity (
+        usr INT,
+        ewho TEXT, 
+        ewhat TEXT, 
+        ehow TEXT
+    ) RETURNS SETOF actor.usr_activity AS $$
+DECLARE
+    new_row actor.usr_activity%ROWTYPE;
+BEGIN
+    SELECT id INTO new_row.etype FROM actor.usr_activity_get_type(ewho, ewhat, ehow);
+    IF FOUND THEN
+        new_row.usr := usr;
+        INSERT INTO actor.usr_activity (usr, etype) 
+            VALUES (usr, new_row.etype)
+            RETURNING * INTO new_row;
+        RETURN NEXT new_row;
+    END IF;
+END;
+$$ LANGUAGE plpgsql;
+
+-- SEED DATA --
+
+INSERT INTO config.usr_activity_type (id, ewho, ewhat, ehow, egroup, label) VALUES
+
+     -- authen/authz actions
+     -- note: "opensrf" is the default ingress/ehow
+     (1,  NULL, 'login',  'opensrf',      'authen', oils_i18n_gettext(1 , 'Login via opensrf', 'cuat', 'label'))
+    ,(2,  NULL, 'login',  'srfsh',        'authen', oils_i18n_gettext(2 , 'Login via srfsh', 'cuat', 'label'))
+    ,(3,  NULL, 'login',  'gateway-v1',   'authen', oils_i18n_gettext(3 , 'Login via gateway-v1', 'cuat', 'label'))
+    ,(4,  NULL, 'login',  'translator-v1','authen', oils_i18n_gettext(4 , 'Login via translator-v1', 'cuat', 'label'))
+    ,(5,  NULL, 'login',  'xmlrpc',       'authen', oils_i18n_gettext(5 , 'Login via xmlrpc', 'cuat', 'label'))
+    ,(6,  NULL, 'login',  'remoteauth',   'authen', oils_i18n_gettext(6 , 'Login via remoteauth', 'cuat', 'label'))
+    ,(7,  NULL, 'login',  'sip2',         'authen', oils_i18n_gettext(7 , 'SIP2 Proxy Login', 'cuat', 'label'))
+    ,(8,  NULL, 'login',  'apache',       'authen', oils_i18n_gettext(8 , 'Login via Apache module', 'cuat', 'label'))
+
+    ,(9,  NULL, 'verify', 'opensrf',      'authz',  oils_i18n_gettext(9 , 'Verification via opensrf', 'cuat', 'label'))
+    ,(10, NULL, 'verify', 'srfsh',        'authz',  oils_i18n_gettext(10, 'Verification via srfsh', 'cuat', 'label'))
+    ,(11, NULL, 'verify', 'gateway-v1',   'authz',  oils_i18n_gettext(11, 'Verification via gateway-v1', 'cuat', 'label'))
+    ,(12, NULL, 'verify', 'translator-v1','authz',  oils_i18n_gettext(12, 'Verification via translator-v1', 'cuat', 'label'))
+    ,(13, NULL, 'verify', 'xmlrpc',       'authz',  oils_i18n_gettext(13, 'Verification via xmlrpc', 'cuat', 'label'))
+    ,(14, NULL, 'verify', 'remoteauth',   'authz',  oils_i18n_gettext(14, 'Verification via remoteauth', 'cuat', 'label'))
+    ,(15, NULL, 'verify', 'sip2',         'authz',  oils_i18n_gettext(15, 'SIP2 User Verification', 'cuat', 'label'))
+
+     -- authen/authz actions w/ known uses of "who"
+    ,(16, 'opac',        'login',  'gateway-v1',   'authen', oils_i18n_gettext(16, 'OPAC Login (jspac)', 'cuat', 'label'))
+    ,(17, 'opac',        'login',  'apache',       'authen', oils_i18n_gettext(17, 'OPAC Login (tpac)', 'cuat', 'label'))
+    ,(18, 'staffclient', 'login',  'gateway-v1',   'authen', oils_i18n_gettext(18, 'Staff Client Login', 'cuat', 'label'))
+    ,(19, 'selfcheck',   'login',  'translator-v1','authen', oils_i18n_gettext(19, 'Self-Check Proxy Login', 'cuat', 'label'))
+    ,(20, 'ums',         'login',  'xmlrpc',       'authen', oils_i18n_gettext(20, 'Unique Mgt Login', 'cuat', 'label'))
+    ,(21, 'authproxy',   'login',  'apache',       'authen', oils_i18n_gettext(21, 'Apache Auth Proxy Login', 'cuat', 'label'))
+    ,(22, 'libraryelf',  'login',  'xmlrpc',       'authz',  oils_i18n_gettext(22, 'LibraryElf Login', 'cuat', 'label'))
+
+    ,(23, 'selfcheck',   'verify', 'translator-v1','authz',  oils_i18n_gettext(23, 'Self-Check User Verification', 'cuat', 'label'))
+    ,(24, 'ezproxy',     'verify', 'remoteauth',   'authz',  oils_i18n_gettext(24, 'EZProxy Verification', 'cuat', 'label'))
+    -- ...
+    ;
+
+-- reserve the first 1000 slots
+SELECT SETVAL('config.usr_activity_type_id_seq'::TEXT, 1000);
+
+COMMIT;
+
+/* 
+-- UNDO SQL --
+BEGIN;
+DELETE FROM actor.usr_activity;
+DELETE FROM config.usr_activity_type;
+DROP TRIGGER remove_transient_usr_activity ON actor.usr_activity;
+DROP FUNCTION actor.usr_activity_transient_trg();
+DROP FUNCTION actor.insert_usr_activity(INT, TEXT, TEXT, TEXT);
+DROP FUNCTION actor.usr_activity_get_type(TEXT, TEXT, TEXT);
+DROP TABLE actor.usr_activity;
+DROP TABLE config.usr_activity_type;
+DROP TYPE config.usr_activity_group;
+COMMIT;
+*/

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

Summary of changes:
 Open-ILS/examples/apache/startup.pl                |    5 +
 Open-ILS/examples/fm_IDL.xml                       |   42 +++++-
 Open-ILS/examples/remoteauth.cgi                   |   20 ++--
 Open-ILS/include/openils/oils_utils.h              |    6 +
 Open-ILS/src/c-apps/oils_auth.c                    |   30 ++++-
 Open-ILS/src/c-apps/oils_utils.c                   |   44 +++++
 .../src/perlmods/lib/OpenILS/Application/Actor.pm  |   34 ++++-
 .../perlmods/lib/OpenILS/Application/AppUtils.pm   |   29 ++++
 Open-ILS/src/perlmods/lib/OpenILS/SIP.pm           |   10 +-
 Open-ILS/src/perlmods/lib/OpenILS/SIP/Patron.pm    |   21 +++
 .../src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm    |    3 +-
 Open-ILS/src/perlmods/lib/OpenILS/WWW/Proxy.pm     |    2 +-
 .../src/perlmods/lib/OpenILS/WWW/Proxy/Authen.pm   |    2 +-
 .../src/perlmods/lib/OpenILS/WWW/XMLRPCGateway.pm  |   12 ++-
 Open-ILS/src/python/oils/srfsh.py                  |   29 ++++
 Open-ILS/src/sql/Pg/002.schema.config.sql          |   20 +++-
 Open-ILS/src/sql/Pg/005.schema.actors.sql          |    7 +
 Open-ILS/src/sql/Pg/950.data.seed-values.sql       |   59 +++++++
 Open-ILS/src/sql/Pg/999.functions.global.sql       |   60 +++++++
 .../sql/Pg/upgrade/0681.schema.user-activity.sql   |  168 ++++++++++++++++++++
 .../conify/global/config/usr_activity_type.tt2     |   34 ++++
 Open-ILS/web/js/dojo/openils/User.js               |   43 +++++
 .../web/js/ui/default/circ/selfcheck/selfcheck.js  |   41 +++--
 Open-ILS/web/opac/common/js/opac_utils.js          |    3 +-
 Open-ILS/web/opac/locale/en-US/lang.dtd            |    1 +
 .../staff_client/chrome/content/auth/session.js    |    1 +
 .../xul/staff_client/chrome/content/main/menu.js   |    4 +
 .../chrome/content/main/menu_frame_menus.xul       |    4 +
 .../server/locale/en-US/patron.properties          |    1 +
 Open-ILS/xul/staff_client/server/patron/summary.js |   22 +++
 .../staff_client/server/patron/summary_overlay.xul |    3 +
 .../server/patron/summary_overlay_horiz.xul        |    3 +
 32 files changed, 728 insertions(+), 35 deletions(-)
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/0681.schema.user-activity.sql
 create mode 100644 Open-ILS/src/templates/conify/global/config/usr_activity_type.tt2


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list