--- /dev/null
+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;
+}
+
+
+
--- /dev/null
+[%- 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 %]