use OpenILS::WWW::EGCatLoader::SMS;
use OpenILS::WWW::EGCatLoader::Register;
use OpenILS::WWW::EGCatLoader::OpenAthens;
+use OpenILS::WWW::EGCatLoader::Ecard;
my $U = 'OpenILS::Application::AppUtils';
$self->load_simple("myopac") if $path =~ m:opac/myopac:; # A default page for myopac parts
+ # maybe make these optional parts of load_patron_reg?
+ #return $self->load_ecard_form if $path =~ m|opac/ecard/form|;
+ return $self->load_ecard_submit if $path =~ m|opac/ecard/submit|;
+ return $self->load_ecard_verify if $path =~ m|opac/ecard/verify|;
+
if($path =~ m|opac/login|) {
return $self->load_login unless $self->editor->requestor; # already logged in?
--- /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 OpenSRF::Utils::JSON;
+use OpenSRF::Utils qw/:datetime/;
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Application::AppUtils;
+use OpenILS::Utils::CStoreEditor qw/:funcs/;
+use OpenILS::Event;
+use Data::Dumper;
+use LWP::UserAgent;
+use DateTime;
+use Digest::MD5 qw(md5_hex);
+$Data::Dumper::Indent = 0;
+my $U = 'OpenILS::Application::AppUtils';
+
+my @api_fields = (
+ {name => 'vendor_username', required => 1},
+ {name => 'vendor_password', required => 1},
+ {name => 'first_given_name', class => 'au', required => 1},
+ {name => 'second_given_name', class => 'au'},
+ {name => 'family_name', class => 'au', required => 1},
+ {name => 'suffix', class => 'au'},
+ {name => 'email', class => 'au', required => 1},
+ {name => 'passwd', class => 'au', required => 1},
+ {name => 'day_phone', class => 'au', required => 1},
+ {name => 'dob', class => 'au', required => 1},
+ {name => 'home_ou', class => 'au', required => 1},
+ {name => 'ident_type', class => 'au', required => 1},
+ {name => 'ident_value', class => 'au', required => 1},
+ {name => 'ident_value2',
+ class => 'au',
+ notes => "AKA parent/guardian",
+ required_if => 'Patron is less than 18 years old'
+ },
+ {name => 'pref_first_given_name', class => 'au'},
+ {name => 'pref_second_given_name', class => 'au'},
+ {name => 'pref_family_name', class => 'au'},
+ {name => 'pref_suffix', class => 'au'},
+ {name => 'physical_street1', class => 'aua', required => 1},
+ {name => 'physical_street1_name'},
+ {name => 'physical_street2', class => 'aua'},
+ {name => 'physical_city', class => 'aua', required => 1},
+ {name => 'physical_post_code', class => 'aua', required => 1},
+ {name => 'physical_county', class => 'aua', required => 1},
+ {name => 'physical_state', class => 'aua', required => 1},
+ {name => 'physical_country', class => 'aua', required => 1},
+ {name => 'mailing_street1', class => 'aua', required => 1},
+ {name => 'mailing_street1_name'},
+ {name => 'mailing_street2', class => 'aua'},
+ {name => 'mailing_city', class => 'aua', required => 1},
+ {name => 'mailing_post_code', class => 'aua', required => 1},
+ {name => 'mailing_county', class => 'aua', required => 1},
+ {name => 'mailing_state', class => 'aua', required => 1},
+ {name => 'mailing_country', class => 'aua', required => 1},
+ {name => 'voter_registration', class => 'asvr', required => 1},
+ {name => 'in_house_registration', required => 1},
+);
+
+
+# TODO: wrap the following in a check for a library setting as to whether or not
+# to require emailed verification
+
+## Random 6-character alpha-numeric code that avoids look-alike characters
+## https://ux.stackexchange.com/questions/53341/are-there-any-letters-numbers-that-should-be-avoided-in-an-id
+## Also exclude vowels to avoid creating any real (potentially offensive) words.
+#my @code_chars = ('C','D','F','H','J'..'N','P','R','T','V','W','X','3','4','7','9');
+#sub generate_verify_code {
+# my $string = '';
+# $string .= $code_chars[rand @code_chars] for 1..6;
+# return $string;
+#}
+#
+#
+## only if we're verifying the card via email
+#sub load_ecard_verify {
+# my $self = shift;
+# my $cgi = $self->cgi;
+# $self->collect_header_footer;
+#
+# # Loading the form.
+# return Apache2::Const::OK if $cgi->request_method eq 'GET';
+#
+# #$self->verify_ecard;
+# return Apache2::Const::OK;
+#}
+#
+#sub verify_ecard {
+# my $self = shift;
+# my $cgi = $self->cgi;
+# my $ctx = $self->ctx;
+# $self->log_params;
+#
+# my $verify_code = $ctx->{verify_code} = $cgi->param('verification_code');
+# my $barcode = $ctx->{barcode} = $cgi->param('barcode');
+#
+# $ctx->{verify_failed} = 1;
+#
+# my $e = new_editor();
+#
+# my $au = $e->search_actor_user({
+# profile => $PROVISIONAL_ECARD_GRP,
+# ident_type => $ECARD_VERIFY_IDENT,
+# ident_value => $verify_code
+# })->[0];
+#
+# if (!$au) {
+# $logger->warn(
+# "ECARD: No provisional ecard found with code $verify_code");
+# sleep 2; # Mitigate brute-force attacks
+# return;
+# }
+#
+# my $card = $e->search_actor_card({
+# usr => $au->id,
+# barcode => $barcode
+# })->[0];
+#
+# if (!$card) {
+# $logger->warn("ECARD: Failed to match verify code ".
+# "($verify_code) with provided barcode ($barcode)");
+# sleep 2; # Mitigate brute-force attacks
+# return;
+# }
+#
+# # Verification looks good. Update the account.
+#
+# my $grp = new_editor()->retrieve_permission_grp_tree($FULL_ECARD_GRP);
+#
+# $au->profile($grp->id);
+# $au->expire_date(
+# DateTime->now(time_zone => 'local')->add(
+# seconds => interval_to_seconds($grp->perm_interval))->iso8601()
+# );
+#
+# $e->xact_begin;
+#
+# unless ($e->update_actor_user($au)) {
+# $logger->error("ECARD update failed for $barcode: " . $e->die_event);
+# return;
+# }
+#
+# $e->commit;
+# $logger->info("ECARD: Update to full ecard succeeded for $barcode");
+#
+# $ctx->{verify_success} = 1;
+# $ctx->{verify_failed} = 0;
+#
+# return;
+#}
+
+
+sub log_params {
+ my $self = shift;
+ my $cgi = $self->cgi;
+ my @params = $cgi->param;
+
+ my $msg = '';
+ for my $p (@params) {
+ next if $p =~ /pass/;
+ $msg .= "|" if $msg;
+ $msg .= "$p=".$cgi->param($p);
+ }
+
+ $logger->info("ECARD: Submit params: $msg");
+}
+
+sub handle_testmode_api {
+ my $self = shift;
+ my $ctx = $self->ctx;
+
+ # Strip data we don't want to publish.
+ my @doc_fields;
+ for my $field_info (@api_fields) {
+ my $doc_info = {};
+ for my $info_key (keys %$field_info) {
+ $doc_info->{$info_key} = $field_info->{$info_key}
+ unless $info_key eq 'class';
+ }
+ push(@doc_fields, $doc_info);
+ }
+
+ $ctx->{response}->{messages} = [fields => \@doc_fields];
+ $ctx->{response}->{status} = 'API_OK';
+ return $self->compile_response;
+}
+
+sub handle_datamode_api {
+ my $self = shift;
+ my $datamode = shift;
+ my $ctx = $self->ctx;
+
+ if ($datamode =~ /org_units/) {
+ my $orgs = new_editor()->search_actor_org_unit({opac_visible => 't'});
+ my $list = [
+ map {
+ {name => $_->name, id => $_->id, parent_ou => $_->parent_ou}
+ } @$orgs
+ ];
+ $ctx->{response}->{messages} = [org_units => $list];
+ }
+
+ $ctx->{response}->{status} = 'DATA_OK';
+ return $self->compile_response;
+}
+
+sub load_ecard_submit {
+ my $self = shift;
+ my $ctx = $self->ctx;
+ my $cgi = $self->cgi;
+
+ $self->log_params;
+
+ my $testmode = $cgi->param('testmode') || '';
+ my $datamode = $cgi->param('datamode') || '';
+
+ my $e = $ctx->{editor} = new_editor();
+ $ctx->{response} = {messages => []};
+
+ if ($testmode eq 'CONNECT') {
+ $ctx->{response}->{status} = 'CONNECT_OK';
+ return $self->compile_response;
+ }
+
+ return Apache2::Const::FORBIDDEN unless
+ $cgi->request_method eq 'POST' &&
+ $self->verify_vendor_host &&
+ $self->login_vendor;
+
+ if ($testmode eq 'AUTH') {
+ # If we got this far, the caller is authorized.
+ $ctx->{response}->{status} = 'AUTH_OK';
+ return $self->compile_response;
+ }
+
+ return $self->handle_testmode_api if $testmode eq 'API';
+ return $self->handle_datamode_api($datamode) if $datamode;
+
+ return $self->compile_response unless $self->make_user;
+ return $self->compile_response unless $self->add_addresses;
+ return $self->compile_response unless $self->check_dupes;
+ return $self->compile_response unless $self->add_card;
+ return $self->compile_response unless $self->add_survey_responses;
+ return $self->compile_response unless $self->save_user;
+ return $self->compile_response unless $self->add_usr_settings;
+ return $self->compile_response if $ctx->{response}->{status};
+
+ $U->create_events_for_hook(
+ 'au.create.ecard', $ctx->{user}, $ctx->{user}->home_ou);
+
+ $ctx->{response}->{status} = 'OK';
+ $ctx->{response}->{barcode} = $ctx->{user}->card->barcode;
+
+ return $self->compile_response;
+}
+
+# E-card vendor is not a regular account. They must have an entry in
+# the password table with password type ecard_vendor.
+sub login_vendor {
+ my $self = shift;
+ my $username = $self->cgi->param('vendor_username');
+ my $password = $self->cgi->param('vendor_password');
+
+ my $e = new_editor();
+ my $vendor = $e->search_actor_user({usrname => $username})->[0];
+ return 0 unless $vendor;
+
+ return unless $U->verify_user_password(
+ $e, $vendor->id, $password, 'ecard_vendor');
+
+ # Auth checks out OK. Manually create an authtoken
+
+ my $auth = $U->simplereq(
+ 'open-ils.auth_internal',
+ 'open-ils.auth_internal.session.create',
+ {user_id => 1, org_unit => 394, login_type => 'temp'}
+ );
+
+ return unless $auth && $auth->{textcode} eq 'SUCCESS';
+
+ $self->ctx->{authtoken} = $auth->{payload}->{authtoken};
+
+ return 1;
+}
+
+sub verify_vendor_host {
+ my $self = shift;
+ # TODO
+ # Confirm calling host matches AOUS ecard.vendor.host
+ # NOTE: we may not have that information inside the firewall.
+ return 1;
+}
+
+
+sub compile_response {
+ my $self = shift;
+ my $ctx = $self->ctx;
+ $self->apache->content_type("application/json; charset=utf-8");
+ $ctx->{response} = OpenSRF::Utils::JSON->perl2JSON($ctx->{response});
+ $logger->info("ECARD responding with " . $ctx->{response});
+ return Apache2::Const::OK;
+}
+
+my %keep_case = (usrname => 1, passwd => 1, email => 1);
+sub upperclense {
+ my $self = shift;
+ my $field = shift;
+ my $value = shift;
+ $value = uc($value) unless $keep_case{$field};
+ $value = lc($value) if $field eq 'email'; # force it
+ $value =~ s/(^\s*|\s*$)//g;
+ return $value;
+}
+
+# Create actor.usr perl object and populate column data
+sub make_user {
+ my $self = shift;
+ my $ctx = $self->ctx;
+ my $cgi = $self->cgi;
+
+ my $au = Fieldmapper::actor::user->new;
+ my $in_house = $cgi->param('in_house_registration');
+
+ $au->isnew(1);
+ $au->net_access_level(1); # Filtered
+ $au->name_keywords($in_house ? 'quipu_inhouse' : 'quipu_remote');
+ my $home_ou = $cgi->param('home_ou');
+
+ my $perm_grp = $U->ou_ancestor_setting_value(
+ $home_ou,
+ 'lib.ecard_patron_profile'
+ );
+
+ $au->profile($perm_grp);
+ my $grp = new_editor()->retrieve_permission_grp_tree($perm_grp);
+
+ $au->expire_date(
+ DateTime->now(time_zone => 'local')->add(
+ seconds => interval_to_seconds($grp->perm_interval))->iso8601()
+ );
+
+ for my $field_info (@api_fields) {
+ my $field = $field_info->{name};
+ next unless $field_info->{class} eq 'au';
+
+ my $val = $cgi->param($field);
+
+ $field = 'guardian' if $field eq 'ident_value2' && $val;
+ $au->juvenile(1) if $field eq 'guardian' && $val;
+
+ if ($field_info->{required} && !$val) {
+ my $msg = "Value required for field: '$field'";
+ $ctx->{response}->{status} = 'INVALID_PARAMS';
+ push(@{$ctx->{response}->{messages}}, $msg);
+ $logger->error("ECARD $msg");
+ }
+
+ $val = undef if $field eq 'day_phone' && $val eq '--';
+ $self->verify_dob($val) if $field eq 'dob' && $val;
+ $au->$field($val);
+ }
+
+ # Usename defaults to the user barcode
+ return undef if $ctx->{response}->{status};
+ return $ctx->{user} = $au;
+}
+
+# Card generation must occur after the user is saved in the DB.
+sub add_card {
+ my $self = shift;
+ my $ctx = $self->ctx;
+ my $cgi = $self->cgi;
+ my $user = $ctx->{user};
+ my $home_ou = $cgi->param('home_ou');
+ my $prefix = $U->ou_ancestor_setting_value(
+ $home_ou,
+ 'lib.ecard_barcode_prefix'
+ ) || 'AUTO';
+
+ my $bc = new_editor()->json_query({from => [
+ 'actor.generate_barcode',
+ $prefix, # ecard prefix
+ 8, # length of autogenated portion
+ 'actor.auto_barcode_ecard_seq' # base sequence for autogeneration.
+ ]})->[0];
+
+ my $barcode = $bc->{'actor.generate_barcode'};
+
+ $logger->info("ECARD using generated barcode: $barcode");
+
+ my $card = Fieldmapper::actor::card->new;
+ $card->id(-1);
+ $card->isnew(1);
+ $card->usr($user->id);
+ $card->barcode($barcode);
+
+ # username defaults to barcode
+ $user->usrname($barcode);
+ $user->card($card);
+ $user->cards([$card]);
+
+ return 1;
+}
+
+# Returns 1 on success, undef on error.
+sub verify_dob {
+ my $self = shift;
+ my $dob = shift;
+ my $ctx = $self->ctx;
+ my $cgi = $self->cgi;
+
+ my @parts = split(/-/, $dob);
+ my $dob_date;
+
+ eval { # avoid dying on funky dates
+ $dob_date = DateTime->new(
+ year => $parts[0], month => $parts[1], day => $parts[2]);
+ };
+
+ if (!$dob_date || $dob_date > DateTime->now) {
+ my $msg = "Invalid dob: '$dob'";
+ $ctx->{response}->{status} = 'INVALID_PARAMS';
+ push(@{$ctx->{response}->{messages}}, $msg);
+ $logger->error("ECARD $msg");
+ return undef;
+ }
+
+ my $comp_date = DateTime->now;
+ $comp_date->set_hour(0);
+ $comp_date->set_minute(0);
+ $comp_date->set_second(0);
+ $comp_date->subtract(years => 18); # juv age
+
+ if (
+ $dob_date > $comp_date # less than 18 years old
+ && !$cgi->param('ident_value2')) {
+
+ my $msg = "Parent/Guardian (ident_value2) is required for patrons ".
+ "under 18 years of age. dob=$dob";
+ $ctx->{response}->{status} = 'INVALID_PARAMS';
+ push(@{$ctx->{response}->{messages}}, $msg);
+ $logger->error("ECARD $msg");
+ return undef;
+ }
+
+ return 1;
+}
+
+# returns true if the addresses contain all of the same values.
+sub addrs_match {
+ my ($self, $addr1, $addr2) = @_;
+ for my $field ($addr1->real_fields) {
+ return 0 if ($addr1->$field() || '') ne ($addr2->$field() || '');
+ }
+ return 1;
+}
+
+
+sub add_addresses {
+ my $self = shift;
+ my $cgi = $self->cgi;
+ my $ctx = $self->ctx;
+ my $e = $ctx->{editor};
+ my $user = $ctx->{user};
+
+ my $physical_addr = Fieldmapper::actor::user_address->new;
+ $physical_addr->isnew(1);
+ $physical_addr->usr($user->id);
+ $physical_addr->address_type('PHYSICAL');
+ $physical_addr->within_city_limits('f');
+
+ my $mailing_addr = Fieldmapper::actor::user_address->new;
+ $mailing_addr->isnew(1);
+ $mailing_addr->usr($user->id);
+ $mailing_addr->address_type('MAILING');
+ $mailing_addr->within_city_limits('f');
+
+ # Use as both billing and mailing via virtual ID.
+ $physical_addr->id(-1);
+ $mailing_addr->id(-2);
+ $user->billing_address(-1);
+ $user->mailing_address(-2);
+
+ # Confirm we have values for all of the required fields.
+ # Apply values to our in-progress address object.
+ for my $field_info (@api_fields) {
+ my $field = $field_info->{name};
+ next unless $field =~ /physical|mailing/;
+ next if $field =~ /street1_/;
+
+ my $val = $cgi->param($field);
+
+ if ($field_info->{required} && !$val) {
+ my $msg = "Value required for field: '$field'";
+ $ctx->{response}->{status} = 'INVALID_PARAMS';
+ push(@{$ctx->{response}->{messages}}, $msg);
+ $logger->error("ECARD $msg");
+ }
+
+ if ($field =~ /physical/) {
+ (my $col_field = $field) =~ s/physical_//g;
+ $physical_addr->$col_field($val) if $val;
+ } else {
+ (my $col_field = $field) =~ s/mailing_//g;
+ $mailing_addr->$col_field($val) if $val;
+ }
+
+ }
+
+ # exit if there were any errors above.
+ return undef if $ctx->{response}->{status};
+
+ $user->billing_address($physical_addr);
+ $user->mailing_address($mailing_addr);
+ $user->addresses([$physical_addr, $mailing_addr]);
+
+ return 1;
+}
+
+sub add_usr_settings {
+ my $self = shift;
+ my $cgi = $self->cgi;
+ my $ctx = $self->ctx;
+ my $user = $ctx->{user};
+ my %settings = (
+ 'opac.hold_notify' => 'email'
+ );
+
+ $U->simplereq(
+ 'open-ils.actor',
+ 'open-ils.actor.patron.settings.update',
+ $self->ctx->{authtoken}, $user->id, \%settings);
+
+ return 1;
+}
+
+sub add_survey_responses {
+ my $self = shift;
+ my $cgi = $self->cgi;
+ my $user = $self->ctx->{user};
+ my $answer = $cgi->param('voter_registration');
+
+ my $survey_response = Fieldmapper::action::survey_response->new;
+ $survey_response->id(-1);
+ $survey_response->isnew(1);
+ $survey_response->survey(1); # voter registration survey
+ $survey_response->question(1);
+ $survey_response->answer($answer);
+
+ $user->survey_responses([$survey_response]);
+ return 1;
+}
+
+# TODO: this is KCLS-specific, but maybe we can make it something
+# generic for adding stat cats to the patron
+
+#sub add_stat_cats {
+# my $self = shift;
+# my $cgi = $self->cgi;
+# my $user = $self->ctx->{user};
+#
+# my $ds_map = Fieldmapper::actor::stat_cat_entry_user_map->new;
+# $ds_map->isnew(1);
+# $ds_map->stat_cat(12);
+# $ds_map->stat_cat_entry('KCLS');
+#
+# my $events = $cgi->param('events_mailing');
+# my $em_map = Fieldmapper::actor::stat_cat_entry_user_map->new;
+# $em_map->isnew(1);
+# $em_map->stat_cat(3);
+# $em_map->stat_cat_entry($events ? 'Y' : 'N');
+#
+# my $foundation = $cgi->param('foundation_mailing');
+# my $fm_map = Fieldmapper::actor::stat_cat_entry_user_map->new;
+# $fm_map->isnew(1);
+# $fm_map->stat_cat(4);
+# $fm_map->stat_cat_entry($foundation ? 'Y' : 'N');
+#
+# $user->stat_cat_entries([$ds_map, $em_map, $fm_map]);
+# return 1;
+#}
+
+# Returns true if no dupes found, false if dupes are found.
+sub check_dupes {
+ my $self = shift;
+ my $ctx = $self->ctx;
+ my $user = $ctx->{user};
+ my $addr = $user->addresses->[0];
+ my $e = new_editor();
+
+ my @dupe_patron_fields =
+ qw/first_given_name family_name dob/;
+
+ my $search = {
+ first_given_name => {value => $user->first_given_name, group => 0},
+ family_name => {value => $user->family_name, group => 0},
+ dob => {value => $user->dob, group => 0}
+ };
+
+ my $root_org = $e->search_actor_org_unit({parent_ou => undef})->[0];
+
+ my $ids = $U->storagereq(
+ "open-ils.storage.actor.user.crazy_search",
+ $search,
+ 1000, # search limit
+ undef, # sort
+ 1, # include inactive
+ $root_org->id, # ws_ou
+ $root_org->id # search_ou
+ );
+
+ return 1 if @$ids == 0;
+
+ $logger->info("ECARD found potential duplicate patrons: @$ids");
+
+# if (my $streetname = $self->cgi->param('physical_street1_name')) {
+# # We found matching patrons. Perform a secondary check on the
+# # address street name only.
+#
+# $logger->info("ECARD secondary search on street name: $streetname");
+#
+# my $addr_ids = $e->search_actor_user_address(
+# { usr => $ids,
+# street1 => {'~*' => "(^| )$streetname( |\$)"}
+# }, {idlist => 1}
+# );
+#
+# if (@$addr_ids) {
+# # we don't really care what patrons match at this point,
+# # only whether a match is found.
+# $ids = [1];
+# $logger->info("ECARD secondary address check match(es) ".
+# "found on address(es) @$addr_ids");
+#
+# } else {
+# $ids = [];
+# $logger->info(
+# "ECARD secondary address check found no matches");
+# }
+#
+# } else {
+# $ids = [];
+# # unclear if this is a possibility -- err on the side of allowing
+# # the registration.
+# $logger->info("ECARD found possible patron match but skipping ".
+# "secondary street name check -- no street name was provided");
+# }
+#
+# return 1 if @$ids == 0;
+
+ $ctx->{response}->{status} = 'DUPLICATE';
+ $ctx->{response}->{messages} = ['first_given_name',
+ 'family_name', 'dob'];
+ return undef;
+}
+
+
+sub save_user {
+ my $self = shift;
+ my $ctx = $self->ctx;
+ my $cgi = $self->cgi;
+ my $user = $ctx->{user};
+
+ my $resp = $U->simplereq(
+ 'open-ils.actor',
+ 'open-ils.actor.patron.update',
+ $self->ctx->{authtoken}, $user
+ );
+
+ $resp = {textcode => 'UNKNOWN_ERROR'} unless $resp;
+
+ if ($U->is_event($resp)) {
+
+ my $msg = "Error creating user account: " . $resp->{textcode};
+ $logger->error("ECARD: $msg");
+
+ $ctx->{response}->{status} = 'CREATE_ERR';
+ $ctx->{response}->{messages} = [{msg => $msg, pid => $$}];
+
+ return 0;
+ }
+
+ $ctx->{user} = $resp;
+ return 1;
+}
+
+1;
+
CREATE INDEX actor_usr_setting_usr_idx ON actor.usr_setting (usr);
+-- Start at 100 to avoid barcodes with long stretches of zeros early on.
+-- eCard barcodes have 7 auto-generated digits.
+CREATE SEQUENCE actor.auto_barcode_ecard_seq START 100 MAXVALUE 9999999;
+
+CREATE OR REPLACE FUNCTION actor.generate_barcode
+ (prefix TEXT, numchars INTEGER, seqname TEXT) RETURNS TEXT AS
+$$
+/*
+Generate a barcode starting with 'prefix' and followed by 'numchars'
+numbers. The auto portion numbers are generated from the provided
+sequence, guaranteeing uniquness across all barcodes generated with
+the same sequence. The number is left-padded with zeros to meet the
+numchars size requirement. Returns NULL if the sequnce value is
+higher than numchars can accommodate .*/
+ SELECT NEXTVAL($3); -- bump the sequence up 1
+ SELECT CASE
+ WHEN LENGTH(CURRVAL($3)::TEXT) > $2 THEN NULL
+ ELSE $1 || LPAD(CURRVAL($3)::TEXT, $2, '0')
+ END;
+$$ LANGUAGE SQL;
+
CREATE TABLE actor.stat_cat_sip_fields (
field CHAR(2) PRIMARY KEY,
name TEXT NOT NULL,
-- Admin user account
INSERT INTO actor.passwd_type
- (code, name, login, crypt_algo, iter_count)
- VALUES ('main', 'Main Login Password', TRUE, 'bf', 10);
+ (code, name, login, crypt_algo, iter_count) VALUES
+ ('main', 'Main Login Password', TRUE, 'bf', 10)
+ ,('ecard_vendor', 'eCard Vendor Password', TRUE, 'bf', 10);
INSERT INTO actor.usr ( profile, card, usrname, passwd, first_given_name, family_name, dob, master_account, super_user, ident_type, ident_value, home_ou ) VALUES ( 1, 1, md5(random()::text), md5(random()::text), 'Administrator', 'System Account', '1979-01-22', TRUE, TRUE, 1, 'identification', 1 );
'coust', 'description'),
'string', null)
+,( 'lib.ecard_barcode_prefix', 'lib',
+ oils_i18n_gettext('lib.ecard_barcode_prefix',
+ 'Barcode prefix for Quipu eCard feature',
+ 'coust', 'label'),
+ oils_i18n_gettext('lib.ecard_barcode_prefix',
+ 'Set the barcode prefix for new Quipu eCard users',
+ 'coust', 'description'),
+ 'string', null)
+
+,( 'lib.ecard_patron_profile', 'lib',
+ oils_i18n_gettext('lib.ecard_patron_profile',
+ 'Patron permission profile for Quipu eCard feature',
+ 'coust', 'label'),
+ oils_i18n_gettext('lib.ecard_barcode_prefix',
+ 'Patron permission profile for Quipu eCard feature',
+ 'coust', 'description'),
+ 'link', 'pgt')
+
,( 'lib.info_url', 'lib',
oils_i18n_gettext('lib.info_url',
'Library information URL (such as "http://example.com/about.html")',
--- /dev/null
+BEGIN;
+
+-- Thank you, berick :-)
+-- Start at 100 to avoid barcodes with long stretches of zeros early on.
+-- eCard barcodes have 7 auto-generated digits.
+CREATE SEQUENCE actor.auto_barcode_ecard_seq START 100 MAXVALUE 9999999;
+
+CREATE OR REPLACE FUNCTION actor.generate_barcode
+ (prefix TEXT, numchars INTEGER, seqname TEXT) RETURNS TEXT AS
+$$
+/*
+Generate a barcode starting with 'prefix' and followed by 'numchars'
+numbers. The auto portion numbers are generated from the provided
+sequence, guaranteeing uniquness across all barcodes generated with
+the same sequence. The number is left-padded with zeros to meet the
+numchars size requirement. Returns NULL if the sequnce value is
+higher than numchars can accommodate .*/
+ SELECT NEXTVAL($3); -- bump the sequence up 1
+ SELECT CASE
+ WHEN LENGTH(CURRVAL($3)::TEXT) > $2 THEN NULL
+ ELSE $1 || LPAD(CURRVAL($3)::TEXT, $2, '0')
+ END;
+$$ LANGUAGE SQL;
+
+INSERT INTO actor.passwd_type
+ (code, name, login, crypt_algo, iter_count)
+ VALUES ('ecard_vendor', 'eCard Vendor Password', TRUE, 'bf', 10);
+
+INSERT into config.org_unit_setting_type
+( name, grp, label, description, datatype, fm_class ) VALUES
+
+( 'lib.ecard_barcode_prefix', 'lib',
+ oils_i18n_gettext('lib.ecard_barcode_prefix',
+ 'Barcode prefix for Quipu eCard feature',
+ 'coust', 'label'),
+ oils_i18n_gettext('lib.ecard_barcode_prefix',
+ 'Set the barcode prefix for new Quipu eCard users',
+ 'coust', 'description'),
+ 'string', null)
+
+,( 'lib.ecard_patron_profile', 'lib',
+ oils_i18n_gettext('lib.ecard_patron_profile',
+ 'Patron permission profile for Quipu eCard feature',
+ 'coust', 'label'),
+ oils_i18n_gettext('lib.ecard_barcode_prefix',
+ 'Patron permission profile for Quipu eCard feature',
+ 'coust', 'description'),
+ 'link', 'pgt')
+;
+
+COMMIT;
#course_material_table td, #course_material_table th {
padding: 4px;
+
+/* QUIPU CUSTOMIZATIONS */
+.radioLabel {
+ text-decoration: none;
+ color: #000;
+ padding-right: 10px;
+}
+
+.eCARDPreferredNameDivClass {
+ padding: 10px;
+ font-size: .9em;
+}
+
+.eCARDPatronNumber {
+ border:3px solid forestgreen;
+ padding:20px;max-width:700px;
+ font-weight:bold;
+ text-align:center;
}
.form-control {
--- /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("Apply for a PINES Library Card");
+%]
+
+
+
+<h2 class="sr-only">[% l('Apply for a PINES Library Card') %]</h2>
+<div id="content-wrapper">
+ <div id="main-content-register">
+ <div class="common-full-pad"></div>
+ <p style="font-size:.8em;">(<a href="/eg/opac/register">English</a> | Español)</p>
+ <iframe id="eCARDFrame" style="border:none;" src="https://ecard.quipugroup.net/Libraries/48/eCARD.php?language=es" allowfullscreen="" width="100%" height="1900px;"></iframe>
+ <!-- eCARD requires JavaScript in order to display the registration form -->
+ <!-- The following will detect if JavaScript is enabled on the patron's browser -->
+ <noscript>
+ <h2 style="color:red;">Warning - JavaScript Required</h2>
+ <p>For full functionality of this web page it is necessary to enable JavaScript in your browser. For more information on most browsers, try <a href="http://www.enable-javascript.com/" target="_blank">How to enable JavaScript</a> OR <a href="http://activatejavascript.org/en/instructions" target="_blank">activatejavascript.org</a><
+ </p>
+ </noscript>
+ </div>
+
+ <div class="common-full-pad"></div>
+ </div>
+</div>
+[%- END %]
WRAPPER "opac/parts/base.tt2";
INCLUDE "opac/parts/topnav.tt2";
ctx.page_title = l("Request Library Card");
-
-# for privacy, reload the page after (default) 5 minutes
-refresh_time = ctx.register.settings.refresh_timeout || 300;
-ctx.refresh = refresh_time _ '; ' _ ctx.opac_root _ '/home';
-
-# 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 => 'stgu', name = 'pref_first_given_name', label => l('Preferred First Name')},
- {class => 'stgu', name = 'pref_second_given_name', label => l('Preferred Middle Name')},
- {class => 'stgu', name = 'pref_family_name', label => l('Preferred 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')}
-];
+%]
-%]
-
<h2 class="sr-only">[% l('Account Registration') %]</h2>
-<div class="container">
+<div id="content-wrapper">
<div id="main-content-register">
- <h1>[% l('Request a Library Card')%]</h1>
- <span class="validate">★ = Required Field</span>
- <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="btn btn-confirm">[% l('Return to the Catalog') %]</a>
+ <div class="common-full-pad"></div>
+ <p style="font-size:.8em;">(English | <a href="/eg/opac/register-sp">Español</a>)</p>
+ <iframe id="eCARDFrame" height="1900px;" width="100%" style="border:none;" src="https://ecard.quipugroup.net/Libraries/48/eCARD.php" allowfullscreen=""></iframe>
+ <!-- eCARD requires JavaScript in order to display the registration form -->
+ <!-- The following will detect if JavaScript is enabled on the patron's browser -->
+ <noscript>
+ <h2 style="color:red;">Warning - JavaScript Required</h2>
+ <p>For full functionality of this web page it is necessary to enable JavaScript in your browser. For more information on most browsers, try <a href="http://www.enable-javascript.com/" target="_blank">How to enable JavaScript</a> OR <a href="http://activatejavascript.org/en/instructions" target="_blank">activatejavascript.org</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="btn btn-confirm">[% 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' class="needs-validation" novalidate>
- <div class="form-group row">
- <label class="control-label col-md-2" for='stgu.home_ou'>[% l('Home Library') %]</label>
- <div class="col-md-6">
- [% INCLUDE build_org_selector
- name='stgu.home_ou'
- value=value || ctx_org
- can_have_users_only=1
- valid_org_list=ctx.register.valid_orgs
- %]
- </div>
- <div class="col-md-4">
- [% IF ctx.register.invalid.bad_home_ou %]
- <span class='patron-reg-invalid'>
- [% l('Please select a valid library') %]
- </span>
- [% END %]
- </div>
+ </noscript>
</div>
-
-[%
-# <=== shifting code left for readability
-
-# render a row for each of the register fields
-FOR field_def IN register_fields;
- fclass = field_def.class;
- fname = field_def.name;
- orig_name = fname;
-
- field_path = fclass _ "." _ fname;
-
- IF fname.match('^pref_');
- # Preferred name fields adopt most visibility, etc.
- # settings from the primary name counterparts.
- fname = fname.remove('^pref_');
- END;
-
- 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;
-
- IF orig_name.match('^pref_');
- show = show || require;
- require = 0; # pref name values never required
- END;
-
- NEXT UNLESS require OR show;
-%]
-
-<div class="form-group row">
- <label class="control-label col-md-2" for='[% field_path %]'>[% field_def.label | html %]
- [% IF require %]
- <span class="validate">★</span>
- [% END %]
- </label>
- <div class="col-md-6">
- [% IF fname == "dob"; %]
- <div class="input-group date" data-provide="datepicker-inline">
- <input type="text" class="form-control datepicker" id='[% field_path %]'
- name='[% field_path %]' value='[% value || CGI.param(field_path) | html %]' [% IF require %]required[% END %] data-date-format="yyyy-mm-dd" />
- <div class="input-group-addon">
- <span class="glyphicon glyphicon-th"></span>
- </div>
- </div>
- [% ELSE; %]
- <input
- class='form-control'
- type='text'
- id='[% field_path %]'
- name='[% field_path %]'
- value='[% value || CGI.param(field_path) | html %]' [% IF require %]required[% END %]/>
- [% END %]
- <div class="invalid-feedback">
- Please enter a [% field_def.label | html %]
- </div>
- [% IF example %]
- <span class='patron-reg-extra'>
- [% l('(Example: [_1])', example) %]
- </span>
- [% END %]
-
- </div>
-
- <div class="col-md-4">
- <!-- 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 %]
+
+ <div class="common-full-pad"></div>
</div>
</div>
-[% END %]
-
-
-<!-- ====> shifting the code back to the right for context -->
- [% IF ctx.register.opt_in_settings.size > 0 %]
- [% FOR optin IN ctx.register.opt_in_settings %]
- <div class="form-group row">
- <label class="control-label col-md-2" for="stgs.[% optin.name | uri %]'">[% optin.label | html %]</label>
- <div class="col-md-6">
- <input type='checkbox'
- name='stgs.[% optin.name | uri %]'
- id='stgs.[% optin.name | uri %]'
- title="[% optin.label | html %]"
- ></input>
- </div>
- <div class="col-md-4">
- <!-- display errors and example text -->
- </div>
- </div>
- [% END %]
- [% END %]
-
- <div class="form-group row">
- <div class="col-md-6 offset-md-2">
- <a href="[% ctx.opac_root %]/home"
- class="btn btn-confirm">[% l('Go Back') %]</a>
- <input type="submit"
- value="[% l('Submit Registration') %]"
- class="btn btn-confirm" />
- </div>
- </div>
- </form>
- [% END %]
- </div>
- </div>
[%- END %]
-<script>
-(function() {
- 'use strict';
- window.addEventListener('load', function() {
- // Fetch all the forms we want to apply custom Bootstrap validation styles to
- var forms = document.getElementsByClassName('needs-validation');
- // Loop over them and prevent submission
- var validation = Array.prototype.filter.call(forms, function(form) {
- form.addEventListener('submit', function(event) {
- if (form.checkValidity() === false) {
- event.preventDefault();
- event.stopPropagation();
- }
- form.classList.add('was-validated');
- }, false);
- });
- }, false);
-})();
-
-$(document).ready(function(){
- $('.datepicker').datepicker({
- weekStart: 1,
- autoclose: true,
- todayHighlight: true,
- });
- $('.datepicker').datepicker("setDate", "");
-});
-</script>
color: #00593d;
}
+
+/*
+ Fake tables for form-rows
+*/
+
+div.egtable { display:table; }
+form.egtr, div.egtr { display:table-row; }
+span.egth { display:table-cell; font-weight: bold; }
+span.egtd { display:table-cell; }
+
+/* Example:
+
+<div class="egtable">
+ <form class="egtr" method="post" action="blah.html">
+ <span class="egtd"><input type="text"/></span>
+ <span class="egtd"><input type="text"/></span>
+ </form>
+ <div class="egtr">
+ <span class="egtd">(cell data)</span>
+ <span class="egtd">(cell data)</span>
+ </div>
+ ...
+</div>
+
+*/
+
+
+/* QUIPU CUSTOMIZATIONS */
+.radioLabel {
+ text-decoration: none;
+ color: #000;
+ padding-right: 10px;
+}
+
+.eCARDPreferredNameDivClass {
+ padding: 10px;
+ font-size: .9em;
+}
+
+.eCARDPatronNumber {
+ border:3px solid forestgreen;
+ padding:20px;max-width:700px;
+ font-weight:bold;
+ text-align:center;
+}
+
+
+
+
@media only screen and (max-width: 1200px) {
.carousel {
margin-left:430px;
--- /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");
+%]
+
+<h2 class="sr-only">[% l('Account Registration') %]</h2>
+<div id="content-wrapper">
+ <div id="main-content-register">
+ <div class="common-full-pad"></div>
+ <div id="eCARDFiles">
+
+ <link href="https://ecard.quipugroup.net/css/eCARD.css" rel="stylesheet">
+
+ <link href="https://ecard.quipugroup.net/js/jqueryUI/css/ui-lightness/jquery-ui-1.10.4.custom.min.css" rel="stylesheet">
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jquery-1.11.1.min.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jqueryUI/js/jquery-ui-1.10.4.custom.min.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jquery.xdomainrequest.min.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jquery.PrintArea.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jQuery-Mask-Plugin-master/dist/jquery.mask.min.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/eCARDMain2.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/Libraries/48/eCARDLibrary.js"></script>
+
+ </div>
+
+ <div id="pines_intro_inhouse" style="display:none;">
+ <h1>Welcome to PINES!</h1>
+ <p>Please fill out the form below to register for a PINES e-card. An e-card provides immediate access to your library's online resources.</p>
+
+ <p>If you'd like to upgrade to a full PINES card in order to check out library books and other materials, please bring your e-card account number and your photo ID to the library's circulation desk to complete the registration process.</p>
+ </div>
+
+ <div id="pines_intro_remote" style="display:none;">
+ <h1>Welcome to PINES!</h1>
+ <p>Please fill out the form below to register for a PINES e-card. An e-card provides immediate access to your library's online resources.</p>
+
+ <p>If you'd like to upgrade to a full PINES card in order to check out library books and other materials, please bring your e-card account number and your photo ID to any PINES library to complete the registration process. If you have any questions, please call your local library for assistance.</p>
+ </div>
+
+ <div id="eCARD" data-language="en" data-branchid="">
+
+
+ <!-- eCARD requires JavaScript in order to display the registration form -->
+ <!-- The following will detect if JavaScript is enabled on the patron's browser -->
+ <noscript>
+ <h2 style="color:red;">Warning - JavaScript Required</h2>
+ <p>For full functionality of this web page it is necessary to enable JavaScript in your browser. For more information on most browsers, try <a href="http://www.enable-javascript.com/" target="_blank">How to enable JavaScript</a> OR <a href="http://activatejavascript.org/en/instructions" target="_blank">activatejavascript.org</a><
+ </p>
+ </noscript>
+ </div>
+
+ <div class="common-full-pad"></div>
+ </div>
+</div>
+[%- END %]
--- /dev/null
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>[% l('Get an eCard') %]</title>
+ [% INCLUDE 'opac/parts/googalytics_new.tt2' %]
+ [% INCLUDE 'opac/parts/goog_tag_manager.tt2' %]
+
+ <!-- NOTE: BootstrapCSS v4 does not play nicely with BC headers -->
+ <link rel="stylesheet" href="/js/ui/default/staff/build/css/bootstrap.min.css" />
+
+ <!-- QUIPU CSS -->
+ <link rel="stylesheet" href="https://ecard.quipugroup.net/css/eCARD.css">
+ <link rel="stylesheet"
+ href="https://ecard.quipugroup.net/js/jqueryUI/css/ui-lightness/jquery-ui-1.10.4.custom.min.css">
+ <!-- END QUIPU CSS -->
+
+ <!-- local CSS -->
+ <style>
+ /* BC screen reader links are not correctly hidden via their API.
+ Add some additional CSS to hide them */
+ .screen_reader_nav {
+ position: absolute;
+ top: -1000px;
+ left: -1000px;
+ z-index: 0;
+ }
+ #ecard-container-wrapper {
+ width: 98%;
+ }
+ #ecard-container {
+ margin-bottom: 20px;
+ color: #585d5e;
+ font-family: 'Open Sans', sans-serif;
+ letter-spacing: .5pt;
+ font-size: 15px;
+ width: 900px; /* to match bibliocms */
+ margin-left: auto;
+ margin-right: auto;
+ }
+ </style>
+
+ <!-- BC CSS -->
+ [% ctx.bc_css %]
+ <!-- BC END CSS -->
+
+ </head>
+ <body>
+
+ [% INCLUDE 'opac/parts/goog_tag_manager_noscript.tt2' %]
+
+ <!-- BC SCREEN READER NAVIGATION -->
+ [% ctx.bc_screen_reader_navigation %]
+ <!-- BC END SCREEN READER NAVIGATION -->
+
+ <!-- BC HEADER -->
+ [% ctx.bc_header %]
+ <!-- BC END HEADER -->
+
+ <div id='ecard-container-wrapper'>
+ <div id='ecard-container'>
+
+ <div id='ecard-preamble'>
+
+ <h1>Get a KCLS eCard</h1>
+
+ <p>
+ Please fill out the application below to get immediate 24/7
+ access to King County Library System’s online services
+ including e-books and audiobooks, movies and music, online
+ classes, exam prep and research databases, and magazines.
+ </p>
+ <!--
+ <p>
+ Please apply <a title="Get A Library Card"
+ href="https://w3.kcls.org/get-a-library-card">here</a>
+ if you would prefer a card with full library privileges
+ including check out, computer use and printing.
+ </p>
+ -->
+ <p>
+ If your eCard application is successful we will mail
+ a confirmation letter to you in order to verify your
+ address. You must follow the instructions in the letter so
+ that the account remains open.
+ </p>
+ <p>
+ Want more information about the eCard and who qualifies to
+ use it? Read the <a href="https://kcls.org/faq/ecards/#ecards">FAQ</a>.
+ </p>
+ <p><b>KCLS eCard is not available to City of Seattle residents.</b></p>
+ </div>
+
+ <div id="eCARD" data-language="en" data-branchid="">
+ <!-- eCARD requires JavaScript in order to display the registration form -->
+ <!-- The following will detect if JavaScript is enabled on the patron's browser -->
+ <noscript>
+ <h2 style="color:red;">Warning - JavaScript Required</h2>
+ <p>
+ For full functionality of this web page it is necessary to enable
+ JavaScript in your browser. For more information on most browsers, try
+ <a href="http://www.enable-javascript.com/" target="_blank">How to enable JavaScript</a>
+ OR <a href="http://activatejavascript.org/en/instructions" target="_blank">activatejavascript.org</a>
+ </p>
+ </noscript>
+ </div>
+ </div>
+ </div>
+
+ <!-- BC FOOTER -->
+ [% ctx.bc_footer %]
+ <!-- BC END FOOTER -->
+
+
+ <!-- QUIPU JS -->
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jquery-1.11.1.min.js"></script>
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jqueryUI/js/jquery-ui-1.10.4.custom.min.js"></script>
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jquery.xdomainrequest.min.js"></script>
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jquery.PrintArea.js"></script>
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/eCARDMain.js"></script>
+ <script type="text/javascript" src="https://ecard.quipugroup.net/Libraries/22/eCARDLibrary.js"></script>
+ <!-- END QUIPU JS -->
+
+ <!-- BC requires jquery, loaded from quipu (above) in this form -->
+ <!-- BC JS -->
+ [% ctx.bc_js %]
+ <!-- BC END JS -->
+
+ </body>
+</html>
+
--- /dev/null
+[% ctx.response %]
--- /dev/null
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>[% l('Confirm an eCard Account') %]</title>
+ [% INCLUDE 'opac/parts/googalytics_new.tt2' %]
+
+ <link rel="stylesheet" href="/js/ui/default/staff/build/css/bootstrap.min.css" />
+
+ <!-- local CSS -->
+ <style>
+ /* BC screen reader links are not correctly hidden via their API.
+ Add some additional CSS to hide them */
+ .screen_reader_nav {
+ position: absolute;
+ top: -1000px;
+ left: -1000px;
+ z-index: 0;
+ }
+ #ecard-container-wrapper {
+ width: 98%;
+ }
+ #ecard-container {
+ margin-bottom: 20px;
+ color: #585d5e;
+ font-family: 'Open Sans', sans-serif;
+ letter-spacing: .5pt;
+ font-size: 15px;
+ width: 900px; /* to match bibliocms */
+ margin-left: auto;
+ margin-right: auto;
+ }
+ </style>
+
+ <script>
+ function handleSubmit() {
+ if (typeof ga === 'function') {
+ ga('send', 'event', 'Ecard Verify', 'submit', 'Forms');
+ }
+ return true;
+ }
+
+ function handleCancel() {
+ // unused at time of writing, keep around just in case.
+ if (typeof ga === 'function') {
+ ga('send', 'event', 'Ecard Verify', 'cancel', 'Forms');
+ }
+ return false; // avoid submit
+ }
+ </script>
+
+ <!-- BC CSS -->
+ [% ctx.bc_css %]
+ <!-- BC END CSS -->
+
+ </head>
+ <body>
+
+ <!-- BC SCREEN READER NAVIGATION -->
+ [% ctx.bc_screen_reader_navigation %]
+ <!-- BC END SCREEN READER NAVIGATION -->
+
+ <!-- BC HEADER -->
+ [% ctx.bc_header %]
+ <!-- BC END HEADER -->
+
+ <div id='ecard-container-wrapper' role="main">
+ <div id='ecard-container'>
+
+ <div id='ecard-preamble'>
+ <h1>Confirm Your KCLS eCard</h1>
+ <p>
+ Please fill out the form below to confirm your eCard account.
+ </p>
+ [% IF ctx.verify_failed %]
+ <div id='ecard-verify-failed'>
+ <div class="alert alert-warning" role="alert">
+ <p>
+ It appears that your confirmation did not go through.
+ This could happen for a variety of reasons:
+ </p>
+ <br/>
+ <ul>
+ <li>You mistyped the confirmation code</li>
+ <li>
+ You typed in the code, but you provided it more than 30
+ days after your application. If so, please feel free to reapply.
+ </li>
+ <li>You already confirmed, so your eCard is ready to use!</li>
+ </ul>
+ <br/>
+ <p>
+ If you have any questions about how to confirm your eCard
+ please contact <a href="https://kcls.org/ask/">Ask KCLS</a>
+ or staff at <a href="https://kcls.bibliocommons.com/locations">
+ your neighborhood library</a>.
+ </p>
+ </div>
+ </div>
+ [% END %]
+ </div>
+
+ [% IF ctx.verify_success %]
+ <div id='ecard-verify-success'>
+ <div class="alert alert-success" role="alert">
+ <p>
+ Congratulations! You have successfully confirmed your KCLS eCard!
+ </p>
+ <p>
+ <b>If you have not yet had a chance to find out what you can do with
+ your eCard, go to <a href="https://kcls.org/onlinelibrary">KCLS Online Resources</a>.
+ Read, stream, listen, find information, advance your studies, and enjoy!</b>
+ </p>
+ </div>
+ </div>
+ [% ELSE %]
+ <div id='ecard-verify-form' class='col-md-6'>
+ <form method='POST' onsubmit="return handleSubmit()">
+ <div class="form-group">
+ <label for="barcode">eCard Number</label>
+ <input type="text" class="form-control" id="barcode"
+ name="barcode" placeholder="eCard Number"
+ value="[% ctx.barcode | html %]"/>
+ </div>
+ <div class="form-group">
+ <label for="verification_code">6-Character Confirmation Code</label>
+ <input type="text" class="form-control" id="verification_code"
+ name="verification_code" placeholder="Confirmation Code"
+ value="[% ctx.verify_code | html %]"/>
+ </div>
+ <button type="submit" class="btn btn-default">Submit</button>
+ </form>
+ </div>
+ <div style="clear:both"></div>
+ [% END %]
+
+ </div>
+ </div>
+
+ <!-- BC FOOTER -->
+ [% ctx.bc_footer %]
+ <!-- BC END FOOTER -->
+
+ <!-- unlike the quipu form page, we have to manaully load jquery here -->
+ <script src="[% ctx.media_prefix %]/js/ui/default/common/build/js/jquery.min.js"></script>
+
+ <!-- BC JS -->
+ [% ctx.bc_js %]
+ <!-- BC END JS -->
+
+ </body>
+</html>
+
[% IF !ctx.is_staff %]
<div id="footer-wrap">
<div id="footer">
- [% IF ctx.get_org_setting(
- ctx.physical_loc || ctx.aou_tree.id, 'opac.allow_pending_user') %]
- <a href="[% mkurl(ctx.opac_root _ '/register') %]">
- <img alt="[% l('Request Library Card') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
- [% l('Request Library Card') %]</a>
- [% END %]
- <!-- <a href="/eg/kpac/home">[% l("Kids Catalog") %]</a> | -->
- <!-- <a href="http://pines.georgialibraries.org/pinesLocator/locator.html">[% l('Library Locations') %]</a> | -->
- <!-- <a href="http://pines.georgialibraries.org/catalog-help">[% l('Help') %]</a> | -->
+
+<div id="footer-menu-wrapper">
<a href="http://pines.georgialibraries.org/about">
- <img alt="[% l('About PINES') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" /> [%- l('Learn More About PINES') %]</a> <!-- | -->
-
+ <img alt="[% l('About PINES') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" /> [%- l('Learn More About PINES') %]</a>
+
<a href="http://pines.georgialibraries.org/catalog-help">
<img alt="[% l('Help') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
[% l('Help') %]</a>
-
-
- <!-- | -->
- <!-- <a href="/">[% l('Dynamic catalog') -%]</a> | -->
- <!-- [% gurl = mkurl(ctx.opac_root _ '/galileo', {}, 1) %]
- <a href="[% gurl %]"> [% l('GALILEO') %] </a> -->
</div>
<div id="footer-menu-slim">
<a href="/eg/opac/home">[% l("Home") %]</a><br/>
- <a href="http://www.georgialibraries.org/lib/directories/pineslibdir.html">
- [% l('Library Locations') %]</a><br/>
+ <a href="http://pines.georgialibraries.org/pinesLocator/locator.html">
+ [% l('Library Locations') %]</a><br/>
<a href="/eg/kpac/home">
- [% l("Kids' Catalog") %]</a><br/>
+ [% l("Kids' Catalog") %]</a><br/>
[% 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><br/>
+ <a href="[% mkurl(ctx.opac_root _ '/register') %]">
+ [% l('Request Library Card') %]</a><br/>
[% END %]
[% IF ctx.physical_loc;
# patron is at the branch, no redirect needed
# send patron to galileo auth redirector
gurl = mkurl(ctx.opac_root _ '/galileo', {}, 1);
END %]
- <a href="[% gurl %]">
- [% l('GALILEO Research Databases') %]</a><br/>
- <a href="http://pines.georgialibraries.org/about">
- [%- l('Learn More About PINES') %]</a><br/>
+ <a href="[% gurl %]">
+ [% l('GALILEO Research Databases') %]</a><br/>
+ <a href="http://pines.georgialibraries.org/about">
+ [%- l('Learn More About PINES') %]</a><br/>
+ <a href="https://play.google.com/store/apps/details?id=net.kenstir.apps.pines">
+ PINES Android App</a><br/>
<a href="http://pines.georgialibraries.org/catalog-help">
- [% l('Help') %]</a>
-
- <hr style="margin-top:20px;">
-</div>
-
+ [% l('Help') %]</a>
+
+ <hr style="margin-top:20px;">
+</div>
+
[% IF ctx.timing %]
<div id="timing">
[% FOR event IN ctx.timing %]
alt="[% l('Evergreen') %]"
/>
</a>
+
<br/> <br/>
<div style="float:left; margin:10px 10px 0px 0px;"><a href="http://www.imls.gov">
<img src="[% ctx.media_prefix %]/images/IMLS-logo.jpg" style="border:none; width: 150px;" alt="IMLS"></a>
</div>
- <p style="font-weight:normal;font-style:italic;">
- [% l('Funding Note') %]</p>
+ <p style="font-weight:normal;font-style:italic;">
+ [% l('Funding Note') %]</p>
+
</div>
</div>
</div>
<div id="gold-links-holder">
<div id="gold-links">
- <div id="header-links">
- <a href="/eg/opac/home">
- <img alt="[% l('PINES Home Page') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
- [% l("Home") %]</a>
- <!--
- [% IF ctx.get_org_setting(
- ctx.physical_loc || ctx.aou_tree.id, 'opac.allow_pending_user') %]
- <a href="[% mkurl(ctx.opac_root _ '/register') %]">[%
- l('Apply for a PINES Library Card') %]</a>
- [% END %]
- -->
- <a href="http://pines.georgialibraries.org/pinesLocator/locator.html">
- <img alt="[% l('Library Locations') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
- [% l('Library Locations') %]</a>
- <a href="/eg/kpac/home">
- <img alt="[% l('Kids Catalog') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
- [% l("Kids Catalog") %]</a>
- <a href="https://pines.georgialibraries.org/app">
- <img alt="[% l('PINES App') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
- [% l("Download the PINES App") %]</a>
- [% IF ctx.physical_loc;
- # patron is at the branch, no redirect needed
- gurl = 'http://www.galileo.usg.edu/express';
- ELSE;
- # send patron to galileo auth redirector
- gurl = mkurl(ctx.opac_root _ '/galileo', {}, 1);
- END %]
- <a href="[% gurl %]">
- <img alt="[% l('GALILEO') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
- [% l('GALILEO Virtual Library') %]</a>
- </div>
+ <div id="header-links">
+ <a href="/eg/opac/home">
+ <img alt="[% l('PINES Home Page') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
+ [% l("Home") %]</a>
+ <a href="http://pines.georgialibraries.org/pinesLocator/locator.html">
+ <img alt="[% l('Library Locations') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
+ [% l('Library Locations') %]</a>
+ <a href="/eg/kpac/home">
+ <img alt="[% l('Kids Catalog') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
+ [% l("Kids Catalog") %]</a>
+ <a href="https://pines.georgialibraries.org/app">
+ <img alt="[% l('PINES App') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
+ [% l("Download the PINES App") %]</a>
+ [% IF ctx.physical_loc;
+ # patron is at the branch, no redirect needed
+ gurl = 'http://www.galileo.usg.edu/express';
+ ELSE;
+ # send patron to galileo auth redirector
+ gurl = mkurl(ctx.opac_root _ '/galileo', {}, 1);
+ END %]
+ <a href="[% gurl %]">
+ <img alt="[% l('GALILEO') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
+ [% l('GALILEO Virtual Library') %]</a>
+ [% IF ctx.get_org_setting(
+ ctx.physical_loc || ctx.aou_tree.id, 'opac.allow_pending_user') %]
+ <a href="[% mkurl(ctx.opac_root _ '/register') %]">
+ <img alt="[% l('Apply for a PINES Library Card') %]" src="[% ctx.media_prefix %]/images/pines-dot.png" />
+ [% l('Apply for a PINES Library Card') %]</a>
+ [% END %]
+
+ </div>
</div>
</div>
+
+<div style="clear:both;" />
WRAPPER "opac/parts/base.tt2";
INCLUDE "opac/parts/topnav.tt2";
ctx.page_title = l("Request Library Card");
-
-# for privacy, reload the page after (default) 5 minutes
-refresh_time = ctx.register.settings.refresh_timeout || 300;
-ctx.refresh = refresh_time _ '; ' _ ctx.opac_root _ '/home';
-
-# 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 => 'stgu', name = 'pref_first_given_name', label => l('Preferred First Name')},
- {class => 'stgu', name = 'pref_second_given_name', label => l('Preferred Middle Name')},
- {class => 'stgu', name = 'pref_family_name', label => l('Preferred 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')}
-];
-
-# The dojo date widget in the patron edit UI only accepts default
-# values in ISO8601 format. It will not accept locale-shaped dates.
-IF !ctx.register.settings.stgu.dob.example;
- ctx.register.settings.stgu.dob.example = l('YYYY-MM-DD or YYYY/MM/DD');
-END;
-
%]
<h2 class="sr-only">[% l('Account Registration') %]</h2>
<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 %]
+ <div id="eCARDFiles">
- <br/>
- <p>
- <a href="[% ctx.opac_root %]/home"
- class="opac-button">[% l('Return to the Catalog') %]</a>
- </p>
+ <link href="https://ecard.quipugroup.net/css/eCARD.css" rel="stylesheet">
+
+ <link href="https://ecard.quipugroup.net/js/jqueryUI/css/ui-lightness/jquery-ui-1.10.4.custom.min.css" rel="stylesheet">
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jquery-1.11.1.min.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jqueryUI/js/jquery-ui-1.10.4.custom.min.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jquery.xdomainrequest.min.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jquery.PrintArea.js"></script>
+
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/jQuery-Mask-Plugin-master/dist/jquery.mask.min.js"></script>
- [% ELSIF ctx.register.error %]
- <h3>[% l('A registration error has occurred') %]</h3>
- <h4>[% l('Please see library staff to complete your registration.') %]</h4>
+ <script type="text/javascript" src="https://ecard.quipugroup.net/js/eCARDMain2.js"></script>
- <br/>
- <p>
- <a href="[% ctx.opac_root %]/home"
- class="opac-button">[% l('Return to the Catalog') %]</a>
+ <script type="text/javascript" src="https://ecard.quipugroup.net/Libraries/48/eCARDLibrary.js"></script>
+
+ </div>
+
+ <div id="pines_intro_inhouse" style="display:none;">
+ <h1>Welcome to PINES!</h1>
+ <p>Please fill out the form below to register for a PINES e-card. An e-card provides immediate access to your library's online resources.</p>
+
+ <p>If you'd like to upgrade to a full PINES card in order to check out library books and other materials, please bring your e-card account number and your photo ID to the library's circulation desk to complete the registration process.</p>
+ </div>
+
+ <div id="pines_intro_remote" style="display:none;">
+ <h1>Welcome to PINES!</h1>
+ <p>Please fill out the form below to register for a PINES e-card. An e-card provides immediate access to your library's online resources.</p>
+
+ <p>If you'd like to upgrade to a full PINES card in order to check out library books and other materials, please bring your e-card account number and your photo ID to any PINES library to complete the registration process. If you have any questions, please call your local library for assistance.</p>
+ </div>
+
+ <div id="eCARD" data-language="en" data-branchid="">
+
+
+ <!-- eCARD requires JavaScript in order to display the registration form -->
+ <!-- The following will detect if JavaScript is enabled on the patron's browser -->
+ <noscript>
+ <h2 style="color:red;">Warning - JavaScript Required</h2>
+ <p>For full functionality of this web page it is necessary to enable JavaScript in your browser. For more information on most browsers, try <a href="http://www.enable-javascript.com/" target="_blank">How to enable JavaScript</a> OR <a href="http://activatejavascript.org/en/instructions" target="_blank">activatejavascript.org</a><
</p>
+ </noscript>
+ </div>
+
+ <div class="common-full-pad"></div>
- [% 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 %]
-
- [%
- #prepopulate org selector on refresh or error
- cgi_org = CGI.param('stgu.home_ou') | html;
- IF cgi_org && ctx.register.valid_orgs.grep("^$cgi_org$").size;
- prepopulate_org = cgi_org;
- END
- %]
-
- <form method='POST' onSubmit="return dobValidate(document.getElementById('stgu.dob'))">
- <table>
- <tr>
- <td>
- <label for='stgu.home_ou'>[% l('Home Library') %]</label>
- </td>
- <td>[% INCLUDE build_org_selector
- name='stgu.home_ou'
- value=value || prepopulate_org || ctx_org
- can_have_users_only=1
- valid_org_list=ctx.register.valid_orgs
- %]<br/>
- <div style="font-size:small;font-style:italic;">
- [% l("PINES Location Tip") %]
- </div>
- </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;
- orig_name = fname;
-
- field_path = fclass _ "." _ fname;
-
- IF fname.match('^pref_');
- # Preferred name fields adopt most visibility, etc.
- # settings from the primary name counterparts.
- fname = fname.remove('^pref_');
- END;
-
- 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;
-
- IF orig_name.match('^pref_');
- show = show || require;
- require = 0; # pref name values never required
- END;
-
- NEXT UNLESS require OR show;
-%]
-<tr>
- <td>
- <label for='[% field_path %]'>[% field_def.label | html %]</label>
- </td>
- <td>
- <input
- type='text'
- id='[% field_path %]'
- 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 -->
- [% IF ctx.register.opt_in_settings.size > 0 %]
- [% FOR optin IN ctx.register.opt_in_settings %]
- <tr>
- <td><label for="stgs.[% optin.name | uri %]'">[% optin.label | html %]</label></td>
- <td>
- <input type='checkbox'
- name='stgs.[% optin.name | uri %]'
- id='stgs.[% optin.name | uri %]'
- title="[% optin.label | html %]"
- ></input>
- </td>
- <td><!-- display errors and example text --></td>
- </tr>
- [% END %]
- [% END %]
- <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>
+</div>
[%- END %]