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

Evergreen Git git at git.evergreen-ils.org
Fri Aug 23 19:35:15 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  3613e273d1d0d399e35389a009affc7d31b83f79 (commit)
       via  7df7e939c02a61bba1c87bfd8bc9b417adb8828a (commit)
       via  cb2ad9df75ec15d6587dc0b27dc5b409f6b2d49e (commit)
       via  5981733f695d4b2ac8c17e5c229184b2f77fa88e (commit)
       via  a14e0b45a3f084e17c5d2817553d4acf67999fb0 (commit)
       via  6dd7c5dfc73bf5ebbc52caac578ce4309161a05d (commit)
       via  4a3a7193dbe5388b5672f29108c59dedeee65cc4 (commit)
       via  26b04ccb853a4edd71bd820231061d4eeec16f53 (commit)
       via  97b3335282973d0577a521d1415b079ec88671ff (commit)
      from  f9a2646c51ad3b71c3af0dd422dd0ab32bd5f363 (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 3613e273d1d0d399e35389a009affc7d31b83f79
Author: Dan Wells <dbw2 at calvin.edu>
Date:   Fri Aug 23 19:33:58 2013 -0400

    Stamping upgrade for patron self-reg
    
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql
index 62e2edd..cb7fb8c 100644
--- a/Open-ILS/src/sql/Pg/002.schema.config.sql
+++ b/Open-ILS/src/sql/Pg/002.schema.config.sql
@@ -91,7 +91,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 ('0820', :eg_version); -- callender/miker
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0823', :eg_version); -- berick/dbwells
 
 CREATE TABLE config.bib_source (
 	id		SERIAL	PRIMARY KEY,
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql b/Open-ILS/src/sql/Pg/upgrade/0823.schema.patron-self-reg.sql
similarity index 97%
rename from Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
rename to Open-ILS/src/sql/Pg/upgrade/0823.schema.patron-self-reg.sql
index b14f1ea..9fe041c 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/0823.schema.patron-self-reg.sql
@@ -1,6 +1,8 @@
 
 BEGIN;
 
+SELECT evergreen.upgrade_deps_block_check('0823', :eg_version);
+
 -- Track the requesting user
 ALTER TABLE staging.user_stage
     ADD COLUMN requesting_usr INTEGER 

commit 7df7e939c02a61bba1c87bfd8bc9b417adb8828a
Author: Dan Wells <dbw2 at calvin.edu>
Date:   Fri Aug 23 19:25:06 2013 -0400

    Trivial changes
    
    -Fix (conform) whitespace in IDL
    -Fix "county" typo in settings label
    -Restore mysteriously absent "Bottom Link 2" :)
    
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index 656f1d1..f4c2440 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -8958,9 +8958,9 @@ SELECT  usr,
             <field reporter:label="Complete" name="complete" reporter:datatype="bool"/>
             <field reporter:label="Requesting User" name="requesting_usr" reporter:datatype="link"/>
         </fields>
-		<links>
-			<link field="requesting_usr" reltype="has_a" key="id" map="" class="au"/>
-		</links>
+        <links>
+            <link field="requesting_usr" reltype="has_a" key="id" map="" class="au"/>
+        </links>
     </class>
 
     <class id="stgc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="staging::card_stage" oils_persist:tablename="staging.card_stage" reporter:label="Card Stage">
@@ -8981,7 +8981,7 @@ SELECT  usr,
             <field reporter:label="Street (1)" name="street1" reporter:datatype="text"/>
             <field reporter:label="Street (2)" name="street2" reporter:datatype="text"/>
             <field reporter:label="City" name="city" reporter:datatype="text"/>
-			<field reporter:label="County" name="county"  reporter:datatype="text"/>
+            <field reporter:label="County" name="county"  reporter:datatype="text"/>
             <field reporter:label="State" name="state" reporter:datatype="text"/>
             <field reporter:label="Country" name="country" reporter:datatype="text"/>
             <field reporter:label="Postal Code" name="post_code" reporter:datatype="text"/>
@@ -8997,7 +8997,7 @@ SELECT  usr,
             <field reporter:label="Street (1)" name="street1" reporter:datatype="text"/>
             <field reporter:label="Street (2)" name="street2" reporter:datatype="text"/>
             <field reporter:label="City" name="city" reporter:datatype="text"/>
-			<field reporter:label="County" name="county"  reporter:datatype="text"/>
+            <field reporter:label="County" name="county"  reporter:datatype="text"/>
             <field reporter:label="State" name="state" reporter:datatype="text"/>
             <field reporter:label="Country" name="country" reporter:datatype="text"/>
             <field reporter:label="Postal Code" name="post_code" reporter:datatype="text"/>
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 ae8a119..e571b8d 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -13229,7 +13229,7 @@ VALUES (
     'bool',
     oils_i18n_gettext(
         'ui.patron.edit.aua.county.require',
-        'Show count field on patron registration',
+        'Show county field on patron registration',
         'coust',
         'label'
     ),
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
index a08d957..b14f1ea 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
@@ -85,7 +85,7 @@ VALUES (
     'bool',
     oils_i18n_gettext(
         'ui.patron.edit.aua.county.require',
-        'Show count field on patron registration',
+        'Show county field on patron registration',
         'coust',
         'label'
     ),
diff --git a/Open-ILS/src/templates/opac/parts/footer.tt2 b/Open-ILS/src/templates/opac/parts/footer.tt2
index cf43f7c..83d63e5 100644
--- a/Open-ILS/src/templates/opac/parts/footer.tt2
+++ b/Open-ILS/src/templates/opac/parts/footer.tt2
@@ -6,8 +6,9 @@
     <a href="[% mkurl(ctx.opac_root _ '/register') %]">[% 
         l('Request Library Card') %]</a> &nbsp;|&nbsp;
     [% ELSE %]
-    <a href="http://example.com">[% l('Bottom Link 3') %]</a> &nbsp;|&nbsp;
+    <a href="http://example.com">[% l('Bottom Link 2') %]</a> &nbsp;|&nbsp;
     [% END %]
+    <a href="http://example.com">[% l('Bottom Link 3') %]</a> &nbsp;|&nbsp;
     <a href="http://example.com">[% l('Bottom Link 4') %]</a> &nbsp;|&nbsp;
     <a href="http://example.com">[% l('Bottom Link 5') %]</a>
     [% IF ctx.timing %]

commit cb2ad9df75ec15d6587dc0b27dc5b409f6b2d49e
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Aug 6 12:08:00 2013 -0400

    LP1207396 Patron Self-Register Release Notes
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/docs/RELEASE_NOTES_NEXT/OPAC/patron_self_reg.txt b/docs/RELEASE_NOTES_NEXT/OPAC/patron_self_reg.txt
new file mode 100644
index 0000000..1ce6367
--- /dev/null
+++ b/docs/RELEASE_NOTES_NEXT/OPAC/patron_self_reg.txt
@@ -0,0 +1,53 @@
+New Feature: Web-Based Patron Self-Registration
+===============================================
+
+Feature Summary
+---------------
+
+Patrons may now fill out a web-based form from within the TPAC to create 
+pending user accounts.  The goal is to make the registration process more 
+efficient by allowing the patron to provide much of the registration 
+details in advance of registering with staff.  
+
+Pending user accounts have no privileges.  
+
+The form supports hiding fields, requiring fields, applying format
+validation, and displaying example text for selected fields by inspecting the 
+relevant patron registration org unit settings.
+
+The "Request Library Card" link appears as the second default "bottom link"
+in the TPAC.  
+
+If a user is logged in when clicking the register link, the logged in
+user will be tracked as the requesting user for the pending account.
+Additionally, the home org unit and some address fields will be pre-populated
+for convenience (with the assumption that these will likely be the same for
+the pending user -- they can of course be changed).  When a requesting user
+is present on the pending user, a link to the requesting user will be 
+displayed within the patron registration form in the staff client.
+
+Pending patron accounts which sit unattended in the database for too long
+are purged via a regularly running (CRON) script.
+
+Technical Details
+-----------------
+
+  * To activate the web form and allow pending patrons to be created, set
+    the 'opac.allow_pending_user' org unit setting to true where appropriate.
+  * To purge old pending user accounts, set an interval value for the new
+    'opac.pending_user_expire_interval' org unit setting.
+  * The org unit settings to indicate show/require/regex/example are 
+    loaded dynamically, so any similar org unit settings added in the future
+    will also be honored.  Any setting matching the following format is used:
+    ** ui.patron.edit.[au|aua].*.show
+    ** ui.patron.edit.[au|aua].*.require
+    ** ui.patron.edit.[au|aua].*.regex
+    ** ui.patron.edit.[au|aua].*.example
+
+Upgrade Notes
+-------------
+
+  * If a value is set for the 'opac.pending_user_expire_interval' for
+    any org units, the new /openils/bin/purge_pending_users.srfsh script 
+    should also be added the opensrf user's crontab.  Running the script once 
+    per day should suffice.

commit 5981733f695d4b2ac8c17e5c229184b2f77fa88e
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Aug 6 13:52:24 2013 -0400

    LP1207396 pending patron reg show requestor
    
    Show a link to the requesting user, when present, for pending patrons in
    the staff client patron registration form.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/templates/actor/user/register.tt2 b/Open-ILS/src/templates/actor/user/register.tt2
index e17328e..95be962 100644
--- a/Open-ILS/src/templates/actor/user/register.tt2
+++ b/Open-ILS/src/templates/actor/user/register.tt2
@@ -22,8 +22,14 @@
         <a href='javascript:uEditToggleRequired(2);' id='uedit-show-suggested'>[% l('Show Suggested Fields') %]</a><br id='uedit-show-suggested-br'/>
         <a href='javascript:uEditToggleRequired(0);' id='uedit-show-all' class='hidden'>[% l('Show All Fields') %]</a>
     </div>
+
+    <!-- link to requesting user account for pending users -->
+    <div class='hidden' id='uedit-requesting-user-div'>
+        <a href='javascript:;' id='uedit-requesting-user'></a>
+    </div>
 </div>
 
+
 <!-- context help widget -->
 <a class='hidden' id='uedit-help-template'><img src='/opac/images/advancedsearch-icon.png'></img></a>
 <fieldset id='uedit-help-div' class='hidden'>
diff --git a/Open-ILS/web/css/skin/default/register.css b/Open-ILS/web/css/skin/default/register.css
index 582975b..3ea7309 100644
--- a/Open-ILS/web/css/skin/default/register.css
+++ b/Open-ILS/web/css/skin/default/register.css
@@ -106,4 +106,5 @@
     margin-bottom:-5px;
 }
 
+#uedit-requesting-user-div { margin: 8px; }
 
diff --git a/Open-ILS/web/js/dojo/openils/actor/nls/register.js b/Open-ILS/web/js/dojo/openils/actor/nls/register.js
index eefeb94..f7d13b6 100644
--- a/Open-ILS/web/js/dojo/openils/actor/nls/register.js
+++ b/Open-ILS/web/js/dojo/openils/actor/nls/register.js
@@ -39,5 +39,6 @@
     "INVALIDATE": "Invalidate",
     "HOLD_NOTIFY_PHONE": "Phone: ",
     "HOLD_NOTIFY_EMAIL": "Email: ",
-    "HOLD_NOTIFY_SMS": "SMS: "
+    "HOLD_NOTIFY_SMS": "SMS: ",
+    "REQUESTING_USER" : "Account requested by ${0} ${1}"
 }
diff --git a/Open-ILS/web/js/ui/default/actor/user/register.js b/Open-ILS/web/js/ui/default/actor/user/register.js
index 7280a10..aabdc3c 100644
--- a/Open-ILS/web/js/ui/default/actor/user/register.js
+++ b/Open-ILS/web/js/ui/default/actor/user/register.js
@@ -509,6 +509,40 @@ function uEditLoadStageUser(stageUname) {
     if(!stageUser) 
         return patron;
 
+    /* if we know who requested this pending account, show the requestor's
+     * name and create a link to open the requestor in a new tab */
+    if (stageUser.requesting_usr()) {
+        fieldmapper.standardRequest(
+            ['open-ils.actor', 'open-ils.actor.user.retrieve.parts'],
+            {   params : [
+                    openils.User.authtoken, 
+                    stageUser.requesting_usr(), 
+                    ['first_given_name', 'family_name']
+                ],
+                oncomplete : function(r) {
+                    var res = openils.Util.readResponse(r);
+                    if (!res) return;
+
+                    var link = dojo.byId('uedit-requesting-user');
+                    link.innerHTML = dojo.string.substitute(
+                        localeStrings.REQUESTING_USER, res);
+                    openils.Util.show(link.parentNode);
+
+                    link.onclick = function() {
+                        window.xulG.new_patron_tab(                
+                            {   'tab_name' : '' }, // tab name is set on draw
+                            {   'id' : stageUser.requesting_usr(),
+                                'url_prefix' : xulG.url_prefix,    
+                                'new_tab' : xulG.new_tab,          
+                                'set_tab' : xulG.set_tab           
+                            }                                      
+                        ); 
+                    };
+                }
+            }
+        );
+    }
+
     // copy the data into our new user object
     for(var key in fieldmapper.IDL.fmclasses.stgu.field_map) {
         if(fieldmapper.IDL.fmclasses.au.field_map[key] && !fieldmapper.IDL.fmclasses.stgu.field_map[key].virtual) {

commit a14e0b45a3f084e17c5d2817553d4acf67999fb0
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Aug 6 09:31:11 2013 -0400

    LP1207396 TPAC org_selector supports org filter
    
    When building and org unit selector via the build_org_selector MARCO,
    the caller can now pass in a "valid_org_list" array which contains org
    unit IDs.  When such a list is provided, any org units in the selector
    which are not in the list are marke as disabled.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/templates/opac/parts/org_selector.tt2 b/Open-ILS/src/templates/opac/parts/org_selector.tt2
index 262fe40..4625023 100644
--- a/Open-ILS/src/templates/opac/parts/org_selector.tt2
+++ b/Open-ILS/src/templates/opac/parts/org_selector.tt2
@@ -2,7 +2,7 @@
 # Org Unit Selector Widget :
 #   INCLUDE build_org_selector id='selector-id' name='selector-name' 
 #       value=org_id show_loc_groups=1/0 can_have_vols_only=1/0
-#       can_have_users_only=1/0
+#       can_have_users_only=1/0 valid_org_list=[1,2,3]
 #
 # NOTE: DO NOT USE PROCESS
 # Use of PROCESS results in internal variables, such as value or org_unit, to "leak" out
@@ -97,6 +97,14 @@ BLOCK build_org_selector;
                 selected = 'selected="selected"';
             END; 
 
+            # caller provided a list of valid org units.
+            # all orgs not in the list are marked invalid.
+            IF valid_org_list AND 
+                    !valid_org_list.grep('^' _ org_unit.id _ '$').size;
+                disabled = 'disabled="disabled"';
+                selected = '';
+            END;
+
             pad_depth = 0;
 
             # copy loc groups appear as children of the owning org unit

commit 6dd7c5dfc73bf5ebbc52caac578ce4309161a05d
Author: Bill Erickson <berick at esilibrary.com>
Date:   Thu Aug 1 16:52:18 2013 -0400

    LP1207396 Patron self-registration web form
    
    Web form for allowing patrons to submit thier own library registration
    requests.  The form collects various bits of user data and creates a
    pending user account, which has no privileges.  These pending accounts
    must be approved and completed by staff from the staff client "Pending
    Patrons" action in the Cirulation menu.
    
    Control of whether to show a field, treat a field as required, show
    example text, and validate the field format is managed with existing org
    unit settings.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
index 210a3c0..48931e8 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
@@ -24,6 +24,7 @@ use OpenILS::WWW::EGCatLoader::Search;
 use OpenILS::WWW::EGCatLoader::Record;
 use OpenILS::WWW::EGCatLoader::Container;
 use OpenILS::WWW::EGCatLoader::SMS;
+use OpenILS::WWW::EGCatLoader::Register;
 
 my $U = 'OpenILS::Application::AppUtils';
 
@@ -140,6 +141,7 @@ sub load {
     return $self->redirect_ssl unless $self->cgi->https;
     return $self->load_password_reset if $path =~ m|opac/password_reset|;
     return $self->load_logout if $path =~ m|opac/logout|;
+    return $self->load_patron_reg if $path =~ m|opac/register|;
 
     if($path =~ m|opac/login|) {
         return $self->load_login unless $self->editor->requestor; # already logged in?
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Register.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Register.pm
new file mode 100644
index 0000000..cdbe9a4
--- /dev/null
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Register.pm
@@ -0,0 +1,242 @@
+package OpenILS::WWW::EGCatLoader;
+use strict; use warnings;
+use Apache2::Const -compile => qw(OK FORBIDDEN HTTP_INTERNAL_SERVER_ERROR);
+use OpenSRF::Utils::Logger qw/$logger/;
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Application::AppUtils;
+use OpenILS::Utils::CStoreEditor qw/:funcs/;
+use OpenILS::Event;
+use Data::Dumper;
+$Data::Dumper::Indent = 0;
+my $U = 'OpenILS::Application::AppUtils';
+
+sub load_patron_reg {
+    my $self = shift;
+    my $ctx = $self->ctx;
+    my $cgi = $self->cgi;
+    $ctx->{register} = {};
+    $self->collect_register_validation_settings;
+    $self->collect_requestor_info;
+
+    # in the home org unit selector, we only want to present 
+    # org units to the patron which support self-registration.
+    # all other org units will be disabled
+    $ctx->{register}{valid_orgs} = 
+        $self->setting_is_true_for_orgs('opac.allow_pending_user');
+
+    # just loading the form
+    return Apache2::Const::OK
+        unless $cgi->request_method eq 'POST';
+
+    my $user = Fieldmapper::staging::user_stage->new;
+    my $addr = Fieldmapper::staging::mailing_address_stage->new;
+
+    # user
+    foreach (grep /^stgu\./, $cgi->param) {
+        my $val = $cgi->param($_);
+        $self->inspect_register_value($_, $val);
+        s/^stgu\.//g;
+        $user->$_($val);
+    }
+
+    # requestor is logged in, capture who is making this request
+    $user->requesting_usr($ctx->{user}->id) if $ctx->{user};
+
+    # make sure the selected home org unit is in the list 
+    # of valid orgs.  This can happen if the selector 
+    # defaults to CONS, for example.
+    $ctx->{register}{invalid}{bad_home_ou} = 1 unless
+        grep {$_ eq $user->home_ou} @{$ctx->{register}{valid_orgs}};
+
+    # address
+    my $has_addr = 0;
+    foreach (grep /^stgma\./, $cgi->param) {
+        my $val = $cgi->param($_);
+        $self->inspect_register_value($_, $val);
+        s/^stgma\.//g;
+        $addr->$_($val);
+        $has_addr = 1;
+    }
+
+    # if the form contains no address fields, do not 
+    # attempt to create a pending address
+    $addr = undef unless $has_addr;
+
+    # At least one value was invalid. Exit early and re-render.
+    return Apache2::Const::OK if $ctx->{register}{invalid};
+
+    $self->test_requested_username($user);
+
+    # user.stage.create will generate a temporary usrname and 
+    # link the user and address objects via this username in the DB.
+    my $resp = $U->simplereq(
+        'open-ils.actor', 
+        'open-ils.actor.user.stage.create',
+        $user, $addr
+    );
+
+    if (!$resp or ref $resp) {
+
+        $logger->warn("Patron self-reg failed ".Dumper($resp));
+        $ctx->{register}{error} = 1;
+
+    } else {
+
+        $logger->info("Patron self-reg success; usrname $resp");
+        $ctx->{register}{success} = 1;
+    }
+
+    return Apache2::Const::OK;
+}
+
+# if the pending account is requested by an existing user account,
+# load the existing user's data to pre-populate some fields.
+sub collect_requestor_info {
+    my $self = shift;
+    return unless $self->ctx->{user};
+
+    my $user = $self->editor->retrieve_actor_user([
+        $self->ctx->{user}->id,
+        {flesh => 1, flesh_fields => {
+            au => [qw/mailing_address billing_address/]}
+        }
+    ]);
+
+
+    my $vhash = $self->ctx->{register}{values} = {};
+    my $addr = $user->mailing_address || $user->billing_address;
+    $vhash->{stgu}{home_ou} = $user->home_ou;
+
+    if ($addr) {
+        $vhash->{stgma}{city} = $addr->city;
+        $vhash->{stgma}{county} = $addr->county;
+        $vhash->{stgma}{state} = $addr->state;
+        $vhash->{stgma}{post_code} = $addr->post_code;
+    }
+}
+
+# if the username is in use by an actor.usr OR a 
+# pending user treat it as taken and warn the user.
+sub test_requested_username {
+    my ($self, $user) = @_;
+    my $uname = $user->usrname || return;
+    my $e = $self->editor;
+
+    my $taken = $e->search_actor_user(
+        {usrname => $uname, deleted => 'f'}, 
+        {idlist => 1}
+    )->[0];
+
+    $taken = $e->search_staging_user_stage(
+        {usrname => $uname}, 
+        {idlist => 1}
+    )->[0] unless $taken;
+
+    if ($taken) {
+        $self->ctx->{register}{username_taken} = 1;
+        $user->clear_usrname;
+    }
+}
+
+sub collect_register_validation_settings {
+    my $self = shift;
+    my $ctx = $self->ctx;
+    my $e = new_editor();
+    my $ctx_org = $ctx->{physical_loc} || $self->_get_search_lib;
+    my $shash = $self->{register}{settings} = {};
+
+    # retrieve the org unit setting types and values
+    # that are relevant to our validation tasks.
+
+    my $settings = $e->json_query({
+        select => {coust => ['name']},
+        from => 'coust',
+        where => {name => {like => 'ui.patron.edit.%.%.%'}}
+    });
+
+    # load org setting values for all of the regex, 
+    # example, show, and require settings
+    for my $set (@$settings) {
+        $set = $set->{name};
+        next unless $set =~ /regex$|show$|require$|example$/;
+
+        my $val = $ctx->{get_org_setting}->($ctx_org, $set);
+        next unless $val; # no configured org setting
+
+        # extract the field class, name, and 
+        # setting type from the setting name
+        my (undef, undef, undef, $cls, $field, $type) = split(/\./, $set);
+
+        # translate classes into stage classes
+        my $scls = ($cls eq 'au') ? 'stgu' : 'stgma';
+
+        $shash->{$scls}{$field}{$type} = $val;
+    }
+
+    # use the generic phone settings where none are provided for day_phone.
+
+    $shash->{stgu}{day_phone}{example} =
+        $ctx->{get_org_setting}->($ctx_org, 'ui.patron.edit.phone.example')
+        unless $shash->{stgu}{day_phone}{example};
+
+    $shash->{stgu}{day_phone}{regex} =
+        $ctx->{get_org_setting}->($ctx_org, 'ui.patron.edit.phone.regex')
+        unless $shash->{stgu}{day_phone}{regex};
+
+    # some fields are assumed to be visible / required even without the            
+    # presence of org unit settings.  E.g. we obviously want the user to 
+    # enter a name, since a name is required for ultimately creating a user 
+    # account.  We can mimic that by forcing some org unit setting values
+    
+    $shash->{stgu}{first_given_name}{require} = 1
+        unless defined $shash->{stgu}{first_given_name}{require};
+    $shash->{stgu}{second_given_name}{show} = 1
+        unless defined $shash->{stgu}{second_given_name}{show};
+    $shash->{stgu}{family_name}{require} = 1
+        unless defined $shash->{stgu}{family_name}{require};
+    $shash->{stgma}{street1}{require} = 1
+        unless defined $shash->{stgma}{street1}{require};
+    $shash->{stgma}{street2}{show} = 1
+        unless defined $shash->{stgma}{street2}{show};
+    $shash->{stgma}{city}{require} = 1
+        unless defined $shash->{stgma}{city}{require};
+    $shash->{stgma}{post_code}{require} = 1
+        unless defined $shash->{stgma}{post_code}{require};
+    $shash->{stgu}{usrname}{show} = 1
+        unless defined $shash->{stgu}{usrname}{show};
+
+    $ctx->{register}{settings} = $shash;
+}
+
+# inspects each value and determines, based on org unit settings, 
+# if the value is invalid.  Invalid is defined as not providing 
+# a value when one is required or not matching the configured regex.
+sub inspect_register_value {
+    my ($self, $field_path, $value) = @_;
+    my $ctx = $self->ctx;
+    my ($scls, $field) = split(/\./, $field_path);
+
+    if (!$value) {
+
+        if ($self->{register}{settings}{$scls}{$field}{require}) {
+            $ctx->{register}{invalid}{$scls}{$field}{require} = 1;
+
+            $logger->info("patron register field $field ".
+                "requires a value, but none was entered");
+        }
+        return;
+    }
+
+    my $regex = $self->{register}{settings}{$scls}{$field}{regex};
+    return if !$regex or $value =~ /$regex/; # field is valid
+
+    $logger->info("invalid value was provided for patron ".
+        "register field=$field; pattern=$regex; value=$value");
+
+    $ctx->{register}{invalid}{$scls}{$field}{regex} = 1;
+
+    return;
+}
+
+
+
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
index 6382210..142f537 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
@@ -753,7 +753,24 @@ sub load_org_util_funcs {
 
 }
 
+# returns the list of org unit IDs for which the 
+# selected org unit setting returned a true value
+sub setting_is_true_for_orgs {
+    my ($self, $setting) = @_;
+    my $ctx = $self->ctx;
+    my @valid_orgs;
+
+    my $test_org;
+    $test_org = sub {
+        my $org = shift;
+        push (@valid_orgs, $org->id) if
+            $ctx->{get_org_setting}->($org->id, $setting);
+        $test_org->($_) for @{$org->children};
+    };
 
+    $test_org->($ctx->{aou_tree}->());
+    return \@valid_orgs;
+}
     
 
 
diff --git a/Open-ILS/src/templates/opac/css/style.css.tt2 b/Open-ILS/src/templates/opac/css/style.css.tt2
index 39cd573..3d827d5 100644
--- a/Open-ILS/src/templates/opac/css/style.css.tt2
+++ b/Open-ILS/src/templates/opac/css/style.css.tt2
@@ -1584,3 +1584,28 @@ a.preflib_change {
 .bib_peer_type {
     font-weight: bold;
 }
+
+#main-content-register {
+    margin-left: 40px;
+    font-size: 120%;
+}
+
+#main-content-register table { 
+    padding: 20px; 
+    margin-top: 18px; 
+    border-collapse: collapse;
+}
+
+#main-content-register td {
+    text-align: left;
+}
+
+#main-content-register td:not(:first-child) {
+    padding-left: 20px;
+}
+
+.patron-reg-invalid {
+    font-weight: bold;
+    color: red;
+    padding-right: 10px;
+}
diff --git a/Open-ILS/src/templates/opac/parts/footer.tt2 b/Open-ILS/src/templates/opac/parts/footer.tt2
index 2368055..cf43f7c 100644
--- a/Open-ILS/src/templates/opac/parts/footer.tt2
+++ b/Open-ILS/src/templates/opac/parts/footer.tt2
@@ -1,8 +1,13 @@
 <div id="footer-wrap">
 <div id="footer">
     <a href="/">[% l('Dynamic catalog') %]</a> &nbsp;|&nbsp;
-    <a href="http://example.com">[% l('Bottom Link 2') %]</a> &nbsp;|&nbsp;
+    [% IF ctx.get_org_setting(
+        ctx.physical_loc || ctx.aou_tree.id, 'opac.allow_pending_user') %]
+    <a href="[% mkurl(ctx.opac_root _ '/register') %]">[% 
+        l('Request Library Card') %]</a> &nbsp;|&nbsp;
+    [% ELSE %]
     <a href="http://example.com">[% l('Bottom Link 3') %]</a> &nbsp;|&nbsp;
+    [% END %]
     <a href="http://example.com">[% l('Bottom Link 4') %]</a> &nbsp;|&nbsp;
     <a href="http://example.com">[% l('Bottom Link 5') %]</a>
     [% IF ctx.timing %]
diff --git a/Open-ILS/src/templates/opac/parts/org_selector.tt2 b/Open-ILS/src/templates/opac/parts/org_selector.tt2
index d82933d..262fe40 100644
--- a/Open-ILS/src/templates/opac/parts/org_selector.tt2
+++ b/Open-ILS/src/templates/opac/parts/org_selector.tt2
@@ -2,6 +2,7 @@
 # Org Unit Selector Widget :
 #   INCLUDE build_org_selector id='selector-id' name='selector-name' 
 #       value=org_id show_loc_groups=1/0 can_have_vols_only=1/0
+#       can_have_users_only=1/0
 #
 # NOTE: DO NOT USE PROCESS
 # Use of PROCESS results in internal variables, such as value or org_unit, to "leak" out
@@ -90,6 +91,12 @@ BLOCK build_org_selector;
                 selected = 'selected="selected"';
             END; 
 
+            IF can_have_users_only AND org_unit.ou_type.can_have_users != 't';
+                disabled = 'disabled="disabled"';
+            ELSIF node_value == value;
+                selected = 'selected="selected"';
+            END; 
+
             pad_depth = 0;
 
             # copy loc groups appear as children of the owning org unit
diff --git a/Open-ILS/src/templates/opac/register.tt2 b/Open-ILS/src/templates/opac/register.tt2
new file mode 100644
index 0000000..a1c7b0e
--- /dev/null
+++ b/Open-ILS/src/templates/opac/register.tt2
@@ -0,0 +1,177 @@
+[%- PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/org_selector.tt2";
+    WRAPPER "opac/parts/base.tt2";
+    INCLUDE "opac/parts/topnav.tt2";
+    ctx.page_title = l("Request Library Card");
+
+# some useful variables and MACROs for display, 
+# field validation, and added info display
+
+ctx_org = ctx.physical_loc || ctx.search_ou || ctx.aou_tree.id;
+
+# list of the registration fields to (potentially) 
+# display in the order they should be shown
+
+# post_code is the only field below that is required in the database and
+# post_code is only required if an address is created.
+# To prevent any of these fields from showing locally, regardless org unit
+# settings, simply remove the fields from this list.  In the case of 
+# addresses, if all address fields are removed, no attempt at creating
+# an address will be made (and post_code will no longer be required).
+
+register_fields = [
+    {class => 'stgu',  name = 'first_given_name', label => l('First Name')},
+    {class => 'stgu',  name = 'second_given_name', label => l('Middle Name')},
+    {class => 'stgu',  name = 'family_name', label => l('Last Name')},
+    {class => 'stgma', name = 'street1', label => l('Street Address')},
+    {class => 'stgma', name = 'street2', label => l('Street Address (2)')},
+    {class => 'stgma', name = 'city', label => l('City')},
+    {class => 'stgma', name = 'county', label => l('County')},
+    {class => 'stgma', name = 'state', label => l('State')},
+    {class => 'stgma', name = 'post_code', label => l('Zip Code')},
+    {class => 'stgu',  name = 'dob', label => l('Date of Birth')},
+    {class => 'stgu',  name = 'day_phone', label => l('Phone Number')},
+    {class => 'stgu',  name = 'email', label => l('Email Address')}
+    {class => 'stgu',  name = 'usrname', label => l('Requested Username')}
+];
+%]
+
+<div id="content-wrapper">
+    <div id="main-content-register">
+        <div class="common-full-pad"></div>
+        <h1>[% l('Request a Library Card')%]</h1>
+        <hr/>
+
+        [% IF ctx.register.success %]
+            <h3>[% l('Registration successful!') %]<h3>
+            <h4>[% l('Please see library staff to complete your registration.') %]</h4>
+
+            [% IF ctx.register.username_taken %]
+            <p>
+                [% |l %]
+                Note: The selected username may be in use by another patron.  
+                You may select another username when finalizing your 
+                registration or in the online catalog.
+                [% END %]
+            </p>
+            [% END %]
+
+            <br/>
+            <p>
+                <a href="[% ctx.opac_root %]/home" 
+                    class="opac-button">[% l('Return to the Catalog') %]</a>
+            </p>
+
+        [% ELSIF ctx.register.error %]
+            <h3>[% l('A registration error has occurred') %]</h3>
+            <h4>[% l('Please see library staff to complete your registration.') %]</h4>
+
+            <br/>
+            <p>
+                <a href="[% ctx.opac_root %]/home" 
+                    class="opac-button">[% l('Return to the Catalog') %]</a>
+            </p>
+
+        [% ELSE %]
+
+        [% IF ctx.user %]
+            <!-- if the user is logged in, make it 
+                clear we are tracking the requestor -->
+            <h4>[% l('New account requested by [_1] [_2] [_3] [_4] [_5]',
+                    ctx.user.prefix, ctx.user.first_given_name,
+                    ctx.user.second_given_name, ctx.user.family_name,
+                    ctx.user.suffix
+                ) | html %]</h4>
+        [% END %]
+
+        <form method='POST'>
+            <table>
+                <tr>
+                    <td>
+                        <label for='stgu.home_ou'>[% l('Home Library') %]</label>
+                    </td>
+                    <td>[% INCLUDE build_org_selector 
+                            name='stgu.home_ou' 
+                            value=value || ctx_org
+                            can_have_users_only=1
+                            valid_org_list=ctx.register.valid_orgs
+                        %]
+                    </td>
+                    <td>
+                        [% IF ctx.register.invalid.bad_home_ou %]
+                        <span class='patron-reg-invalid'>
+                            [% l('Please select a valid library') %]
+                        </span>
+                        [% END %]
+                </tr>
+[%
+# <=== shifting code left for readability
+
+# render the table row for each of the register fields
+FOR field_def IN register_fields;
+    fclass = field_def.class;
+    fname = field_def.name;
+    field_path = fclass _ "." _ fname;
+
+    show = ctx.register.settings.$fclass.$fname.show;
+    require = ctx.register.settings.$fclass.$fname.require;
+    example = ctx.register.settings.$fclass.$fname.example;
+    value = ctx.register.values.$fclass.$fname;
+
+    invalid_require = ctx.register.invalid.$fclass.$fname.require;
+    invalid_regex = ctx.register.invalid.$fclass.$fname.regex;
+
+    NEXT UNLESS require OR show;
+%]
+<tr>
+    <td>
+        <label for='[% field_path %]'>[% field_def.label | html %]</label>
+    </td>
+    <td>
+        <input 
+            type='text' 
+            name='[% field_path %]'
+            value='[% value || CGI.param(field_path) | html %]'/>
+        [% IF require %]
+        <span class='patron-reg-invalid'>*</span>
+        [% END %]
+    </td>
+    <td>
+
+    <!-- display errors and example text -->
+
+    [% IF invalid_require %]
+        <span class='patron-reg-invalid'>
+            [% l('This field is required') %]
+        </span>
+    [% ELSIF invalid_regex %]
+        <span class='patron-reg-invalid'>
+            [% l('The value entered does not have the correct format') %]
+        </span>
+    [% END %]
+    [% IF example %]
+        <span class='patron-reg-extra'>
+            [% l('(Example: [_1])', example) %]
+        </span>
+    [% END %]
+
+    </td>
+</tr>
+[% END %]
+<!-- ====> shifting the code back to the right for context -->
+                    <tr>
+                        <td colspan='3'>
+                            <a href="[% ctx.opac_root %]/home" 
+                                class="opac-button">[% l('Go Back') %]</a>
+                            <input type="submit" 
+                                value="[% l('Submit Registration') %]" 
+                                class="opac-button" />
+                        </td>
+                    </tr>
+                </table>
+            </form>
+            [% END %]
+            <div class="common-full-pad"></div>	
+        </div>
+    </div>
+[%- END %]

commit 4a3a7193dbe5388b5672f29108c59dedeee65cc4
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Aug 6 10:10:19 2013 -0400

    LP1207396 user stage allows username selection
    
    The caller may now pass a value for username.  If unset, it still falls
    back to using a uuid.  If the username clashes with an existing staged
    username, a USERNAME_EXISTS event is returned.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/Stage.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/Stage.pm
index ff60879..8f094b7 100644
--- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/Stage.pm
+++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/Stage.pm
@@ -38,9 +38,13 @@ sub create_user_stage {
 
     my $e = new_editor(xact => 1);
 
-    my $uname = $U->create_uuid_string;
+    my $uname = $user->usrname || $U->create_uuid_string;
     $user->usrname($uname);
 
+    # see if this username is already taken
+    return OpenILS::Event->new('USERNAME_EXISTS') if
+        $e->search_staging_user_stage({usrname => $uname})->[0];
+
     $e->create_staging_user_stage($user) or return $e->die_event;
 
     if($mail_addr) {

commit 26b04ccb853a4edd71bd820231061d4eeec16f53
Author: Bill Erickson <berick at esilibrary.com>
Date:   Tue Aug 6 11:09:45 2013 -0400

    LP1207396 purge old pending users
    
    Script to delete pending user accounts based on the interval defined in
    the opac.pending_user_expire_interval org unit setting.
    
    Add /openils/bin/purge_pending_users.srfsh to opensrf's CRON.
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/examples/crontab.example b/Open-ILS/examples/crontab.example
index 1cd1db3..5838ff4 100644
--- a/Open-ILS/examples/crontab.example
+++ b/Open-ILS/examples/crontab.example
@@ -42,6 +42,9 @@ EG_BIN_DIR = /openils/bin
 # Run the reshelving completer
 2  0  * * *   . ~/.bashrc && $EG_BIN_DIR/reshelving_complete.srfsh
 
+# Run the pending user purger
+2  30  * * *   . ~/.bashrc && $EG_BIN_DIR/purge_pending_users.srfsh
+
 # create the list of blocked patrons for offline use
 # Note: The resulting list.txt file needs to be copied to all Apache servers
 30 6  * * *   . ~/.bashrc && $EG_BIN_DIR/offline-blocked-list.pl $SRF_CORE > $OPENILS/var/web/standalone/list.txt
diff --git a/Open-ILS/src/sql/Pg/015.schema.staging.sql b/Open-ILS/src/sql/Pg/015.schema.staging.sql
index 83bc697..3f558e1 100644
--- a/Open-ILS/src/sql/Pg/015.schema.staging.sql
+++ b/Open-ILS/src/sql/Pg/015.schema.staging.sql
@@ -60,5 +60,29 @@ CREATE TABLE staging.statcat_stage (
         complete        BOOL DEFAULT FALSE
 );
 
+-- stored procedure for deleting expired pending patrons
+CREATE OR REPLACE FUNCTION staging.purge_pending_users() RETURNS VOID AS $$
+DECLARE
+    org_id INT;
+    intvl TEXT;
+BEGIN
+    FOR org_id IN SELECT DISTINCT(home_ou) FROM staging.user_stage LOOP
+
+        SELECT INTO intvl value FROM 
+            actor.org_unit_ancestor_setting(
+                'opac.pending_user_expire_interval', org_id);
+
+        CONTINUE WHEN intvl IS NULL OR intvl ILIKE 'null';
+
+        -- de-JSON-ify the string
+        SELECT INTO intvl TRIM(BOTH '"' FROM intvl);
+
+        DELETE FROM staging.user_stage 
+            WHERE home_ou = org_id AND row_date + intvl::INTERVAL < NOW();
+
+    END LOOP;
+END;
+$$ LANGUAGE PLPGSQL;
+
 COMMIT;
 
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
index 9f6a574..a08d957 100644
--- a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
@@ -18,6 +18,31 @@ ALTER TABLE staging.billing_address_stage
     ALTER COLUMN state DROP DEFAULT,
     ALTER COLUMN state DROP NOT NULL;
 
+-- stored procedure for deleting expired pending patrons
+CREATE OR REPLACE FUNCTION staging.purge_pending_users() RETURNS VOID AS $$
+DECLARE
+    org_id INT;
+    intvl TEXT;
+BEGIN
+    FOR org_id IN SELECT DISTINCT(home_ou) FROM staging.user_stage LOOP
+
+        SELECT INTO intvl value FROM 
+            actor.org_unit_ancestor_setting(
+                'opac.pending_user_expire_interval', org_id);
+
+        CONTINUE WHEN intvl IS NULL OR intvl ILIKE 'null';
+
+        -- de-JSON-ify the string
+        SELECT INTO intvl TRIM(BOTH '"' FROM intvl);
+
+        DELETE FROM staging.user_stage 
+            WHERE home_ou = org_id AND row_date + intvl::INTERVAL < NOW();
+
+    END LOOP;
+END;
+$$ LANGUAGE PLPGSQL;
+
+
 INSERT INTO config.org_unit_setting_type
     (name, grp, datatype, label, description)
 VALUES (
diff --git a/Open-ILS/src/support-scripts/purge_pending_users.srfsh b/Open-ILS/src/support-scripts/purge_pending_users.srfsh
new file mode 100755
index 0000000..56d337a
--- /dev/null
+++ b/Open-ILS/src/support-scripts/purge_pending_users.srfsh
@@ -0,0 +1,7 @@
+#!/openils/bin/srfsh
+open open-ils.cstore
+request open-ils.cstore open-ils.cstore.transaction.begin
+request open-ils.cstore open-ils.cstore.json_query {"from":["staging.purge_pending_users"]}
+request open-ils.cstore open-ils.cstore.transaction.commit
+close open-ils.cstore
+

commit 97b3335282973d0577a521d1415b079ec88671ff
Author: Bill Erickson <berick at esilibrary.com>
Date:   Thu Aug 1 13:30:52 2013 -0400

    LP1207396 DB / IDL for patron self-reg
    
    Updating pending addr tables to track requesting user and address county.
    
    Adding org settings:
    
    * opac.allow_pending_user -- allow self-registration
    * opac.pending_user_expire_interval - delete pending user interval
    * ui.patron.edit.aua.county.require - indicate if county is required
    
    Signed-off-by: Bill Erickson <berick at esilibrary.com>
    Signed-off-by: Dan Wells <dbw2 at calvin.edu>

diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml
index d270341..656f1d1 100644
--- a/Open-ILS/examples/fm_IDL.xml
+++ b/Open-ILS/examples/fm_IDL.xml
@@ -8956,7 +8956,11 @@ SELECT  usr,
             <field reporter:label="Home Library" name="home_ou" reporter:datatype="int"/>
             <field reporter:label="Date of Birth" name="dob" reporter:datatype="text"/>
             <field reporter:label="Complete" name="complete" reporter:datatype="bool"/>
+            <field reporter:label="Requesting User" name="requesting_usr" reporter:datatype="link"/>
         </fields>
+		<links>
+			<link field="requesting_usr" reltype="has_a" key="id" map="" class="au"/>
+		</links>
     </class>
 
     <class id="stgc" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="staging::card_stage" oils_persist:tablename="staging.card_stage" reporter:label="Card Stage">
@@ -8977,6 +8981,7 @@ SELECT  usr,
             <field reporter:label="Street (1)" name="street1" reporter:datatype="text"/>
             <field reporter:label="Street (2)" name="street2" reporter:datatype="text"/>
             <field reporter:label="City" name="city" reporter:datatype="text"/>
+			<field reporter:label="County" name="county"  reporter:datatype="text"/>
             <field reporter:label="State" name="state" reporter:datatype="text"/>
             <field reporter:label="Country" name="country" reporter:datatype="text"/>
             <field reporter:label="Postal Code" name="post_code" reporter:datatype="text"/>
@@ -8992,6 +8997,7 @@ SELECT  usr,
             <field reporter:label="Street (1)" name="street1" reporter:datatype="text"/>
             <field reporter:label="Street (2)" name="street2" reporter:datatype="text"/>
             <field reporter:label="City" name="city" reporter:datatype="text"/>
+			<field reporter:label="County" name="county"  reporter:datatype="text"/>
             <field reporter:label="State" name="state" reporter:datatype="text"/>
             <field reporter:label="Country" name="country" reporter:datatype="text"/>
             <field reporter:label="Postal Code" name="post_code" reporter:datatype="text"/>
diff --git a/Open-ILS/src/sql/Pg/015.schema.staging.sql b/Open-ILS/src/sql/Pg/015.schema.staging.sql
index 893650d..83bc697 100644
--- a/Open-ILS/src/sql/Pg/015.schema.staging.sql
+++ b/Open-ILS/src/sql/Pg/015.schema.staging.sql
@@ -6,7 +6,7 @@ CREATE SCHEMA staging;
 
 CREATE TABLE staging.user_stage (
         row_id                  BIGSERIAL PRIMARY KEY,
-        row_date                            TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
+        row_date                TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
         usrname                 TEXT NOT NULL,
         profile                 TEXT,
         email                   TEXT,
@@ -19,7 +19,8 @@ CREATE TABLE staging.user_stage (
         evening_phone           TEXT,
         home_ou                 INT DEFAULT 2,
         dob                     TEXT,
-        complete                BOOL DEFAULT FALSE
+        complete                BOOL DEFAULT FALSE,
+        requesting_usr          INT REFERENCES actor.usr(id) ON DELETE SET NULL
 );
 
 CREATE TABLE staging.card_stage ( -- for new library barcodes
@@ -32,12 +33,13 @@ CREATE TABLE staging.card_stage ( -- for new library barcodes
 
 CREATE TABLE staging.mailing_address_stage (
         row_id          BIGSERIAL PRIMARY KEY,
-        row_date            TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
+        row_date        TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
         usrname         TEXT NOT NULL,  -- user's SIS barcode, for linking
         street1         TEXT,
         street2         TEXT,
         city            TEXT NOT NULL DEFAULT '',
-        state           TEXT    NOT NULL DEFAULT 'OK',
+        county          TEXT,
+        state           TEXT,
         country         TEXT NOT NULL DEFAULT 'US',
         post_code       TEXT NOT NULL,
         complete        BOOL DEFAULT FALSE
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 1f046df..ae8a119 100644
--- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql
+++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql
@@ -13186,3 +13186,57 @@ INSERT INTO action_trigger.environment (event_def, path) VALUES
     (50, 'circ_lib.billing_address'),
     (50, 'target_copy.location');
 
+-- OUS's for patron self-reg
+INSERT INTO config.org_unit_setting_type
+    (name, grp, datatype, label, description)
+VALUES (
+    'opac.allow_pending_user',
+    'opac',
+    'bool',
+    oils_i18n_gettext(
+        'opac.allow_pending_user',
+        'Allow Patron Self-Registration',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'opac.allow_pending_user',
+        'Allow patrons to self-register, creating pending user accounts',
+        'coust',
+        'description'
+    )
+), (
+    'opac.pending_user_expire_interval',
+    'opac',
+    'interval',
+    oils_i18n_gettext(
+        'opac.pending_user_expire_interval',
+        'Patron Self-Reg. Expire Interval',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'opac.pending_user_expire_interval',
+        'If set, this is the amount of time a pending user account will ' ||
+        'be allowed to sit in the database.  After this time, the pending ' ||
+        'user information will be purged',
+        'coust',
+        'description'
+    )
+), (
+    'ui.patron.edit.aua.county.show',
+    'gui',
+    'bool',
+    oils_i18n_gettext(
+        'ui.patron.edit.aua.county.require',
+        'Show count field on patron registration',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ui.patron.edit.aua.county.require',
+        'The county field will be shown on the patron registration screen',
+        'coust',
+        'description'
+    )
+);
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
new file mode 100644
index 0000000..9f6a574
--- /dev/null
+++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.patron-self-reg.sql
@@ -0,0 +1,75 @@
+
+BEGIN;
+
+-- Track the requesting user
+ALTER TABLE staging.user_stage
+    ADD COLUMN requesting_usr INTEGER 
+        REFERENCES actor.usr(id) ON DELETE SET NULL;
+
+-- add county column to staged address tables and 
+-- drop state requirement to match actor.usr_address
+ALTER TABLE staging.mailing_address_stage 
+    ADD COLUMN county TEXT,
+    ALTER COLUMN state DROP DEFAULT,
+    ALTER COLUMN state DROP NOT NULL;
+
+ALTER TABLE staging.billing_address_stage 
+    ADD COLUMN county TEXT,
+    ALTER COLUMN state DROP DEFAULT,
+    ALTER COLUMN state DROP NOT NULL;
+
+INSERT INTO config.org_unit_setting_type
+    (name, grp, datatype, label, description)
+VALUES (
+    'opac.allow_pending_user',
+    'opac',
+    'bool',
+    oils_i18n_gettext(
+        'opac.allow_pending_user',
+        'Allow Patron Self-Registration',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'opac.allow_pending_user',
+        'Allow patrons to self-register, creating pending user accounts',
+        'coust',
+        'description'
+    )
+), (
+    'opac.pending_user_expire_interval',
+    'opac',
+    'interval',
+    oils_i18n_gettext(
+        'opac.pending_user_expire_interval',
+        'Patron Self-Reg. Expire Interval',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'opac.pending_user_expire_interval',
+        'If set, this is the amount of time a pending user account will ' ||
+        'be allowed to sit in the database.  After this time, the pending ' ||
+        'user information will be purged',
+        'coust',
+        'description'
+    )
+), (
+    'ui.patron.edit.aua.county.show',
+    'gui',
+    'bool',
+    oils_i18n_gettext(
+        'ui.patron.edit.aua.county.require',
+        'Show count field on patron registration',
+        'coust',
+        'label'
+    ),
+    oils_i18n_gettext(
+        'ui.patron.edit.aua.county.require',
+        'The county field will be shown on the patron registration screen',
+        'coust',
+        'description'
+    )
+);
+
+COMMIT;

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

Summary of changes:
 Open-ILS/examples/crontab.example                  |    3 +
 Open-ILS/examples/fm_IDL.xml                       |    6 +
 .../lib/OpenILS/Application/Actor/Stage.pm         |    6 +-
 .../src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm    |    2 +
 .../lib/OpenILS/WWW/EGCatLoader/Register.pm        |  242 ++++++++++++++++++++
 .../perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm   |   17 ++
 Open-ILS/src/sql/Pg/002.schema.config.sql          |    2 +-
 Open-ILS/src/sql/Pg/015.schema.staging.sql         |   34 +++-
 Open-ILS/src/sql/Pg/950.data.seed-values.sql       |   54 +++++
 .../sql/Pg/upgrade/0823.schema.patron-self-reg.sql |  102 ++++++++
 ...irculations.srfsh => purge_pending_users.srfsh} |    2 +-
 Open-ILS/src/templates/actor/user/register.tt2     |    6 +
 Open-ILS/src/templates/opac/css/style.css.tt2      |   25 ++
 Open-ILS/src/templates/opac/parts/footer.tt2       |    6 +
 Open-ILS/src/templates/opac/parts/org_selector.tt2 |   15 ++
 Open-ILS/src/templates/opac/register.tt2           |  177 ++++++++++++++
 Open-ILS/web/css/skin/default/register.css         |    1 +
 Open-ILS/web/js/dojo/openils/actor/nls/register.js |    3 +-
 Open-ILS/web/js/ui/default/actor/user/register.js  |   34 +++
 docs/RELEASE_NOTES_NEXT/OPAC/patron_self_reg.txt   |   53 +++++
 20 files changed, 782 insertions(+), 8 deletions(-)
 create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Register.pm
 create mode 100644 Open-ILS/src/sql/Pg/upgrade/0823.schema.patron-self-reg.sql
 copy Open-ILS/src/support-scripts/{purge_circulations.srfsh => purge_pending_users.srfsh} (66%)
 create mode 100644 Open-ILS/src/templates/opac/register.tt2
 create mode 100644 docs/RELEASE_NOTES_NEXT/OPAC/patron_self_reg.txt


hooks/post-receive
-- 
Evergreen ILS


More information about the open-ils-commits mailing list