From 230631f2c3693660368d02a967891ac18e656b51 Mon Sep 17 00:00:00 2001 From: Mike Rylander Date: Thu, 1 Nov 2018 17:56:09 -0400 Subject: [PATCH] LP#1749475: OPAC email/print record improvements This commit provides new functionality for controlling and delivering print and email bibliographic record export options. Staff will be able to adjust existing and create new print and email formats using the Action/Trigger infrastructure and the new Event Definition Group capability. Patrons will be able to choose from one of several formats, as configured by staff, in which to receive one or more bibliographic records. Print and email preview is made available for individual records via the existing links on the record detail page, and for lists of records via new entry points on both the Basket (anonymous list) and My Lists interfaces. Signed-off-by: Mike Rylander Signed-off-by: Dan Wells Signed-off-by: Galen Charlton Signed-off-by: Michele Morgan --- Open-ILS/examples/fm_IDL.xml | 56 ++++++ .../staff/admin/basic-admin-page.component.ts | 6 +- .../server/admin-server-splash.component.html | 4 + .../lib/OpenILS/Application/Search/Biblio.pm | 120 ++++++++++++- .../OpenILS/Application/Trigger/Reactor.pm | 95 ++++++++++- .../Application/Trigger/Reactor/SendEmail.pm | 4 + .../perlmods/lib/OpenILS/WWW/EGCatLoader.pm | 34 +++- .../lib/OpenILS/WWW/EGCatLoader/Account.pm | 36 +++- .../lib/OpenILS/WWW/EGCatLoader/Container.pm | 22 ++- .../lib/OpenILS/WWW/EGCatLoader/Record.pm | 154 +++++++++++++---- .../src/sql/Pg/400.schema.action_trigger.sql | 27 +++ Open-ILS/src/sql/Pg/950.data.seed-values.sql | 143 +++++++++++----- .../Pg/upgrade/XXXX.schema.AT-def-groups.sql | 160 ++++++++++++++++++ .../src/templates/opac/parts/anon_list.tt2 | 1 + Open-ILS/src/templates/opac/parts/cart.tt2 | 4 +- .../templates/opac/parts/record/summary.tt2 | 4 +- Open-ILS/src/templates/opac/record/email.tt2 | 7 +- .../templates/opac/record/email_preview.tt2 | 111 ++++++++++++ Open-ILS/src/templates/opac/record/print.tt2 | 23 +-- .../templates/opac/record/print_preview.tt2 | 82 +++++++++ .../templates/staff/admin/server/t_splash.tt2 | 2 + 21 files changed, 984 insertions(+), 111 deletions(-) create mode 100644 Open-ILS/src/sql/Pg/upgrade/XXXX.schema.AT-def-groups.sql create mode 100644 Open-ILS/src/templates/opac/record/email_preview.tt2 create mode 100644 Open-ILS/src/templates/opac/record/print_preview.tt2 diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index e5c945d1d4..23b951f54c 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -1476,6 +1476,62 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts index 1c9b4c80a3..6a43a628a1 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/basic-admin-page.component.ts @@ -56,10 +56,10 @@ export class BasicAdminPageComponent implements OnInit { // the prefix because that will cause it to double-up. // e.g. eg.grid.acq.acq.cancel_reason this.persistKeyPfx = this.route.snapshot.parent.url[0].path; - const selfPrefixers = ['acq', 'booking']; + const selfPrefixers = ['acq', 'action_trigger', 'booking']; if (selfPrefixers.indexOf(this.persistKeyPfx) > -1) { - // ACQ is a special case, because unlike 'server', 'local', - // 'workstation', the schema ('acq') is the root of the path. + // selfPrefixers, unlike 'server', 'local', and + // 'workstation', are the root of the path. this.persistKeyPfx = ''; } else { this.configLinkBasePath += '/' + this.persistKeyPfx; diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html index de399d6517..849c2eacf5 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html @@ -102,5 +102,9 @@ routerLink="/staff/admin/server/config/z3950_index_field_map"> + + diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm index b07440e65a..f2d7d1ac60 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm @@ -10,6 +10,8 @@ use OpenSRF::Utils::SettingsClient; use OpenILS::Utils::CStoreEditor q/:funcs/; use OpenSRF::Utils::Cache; use Encode; +use Email::Send; +use Email::Simple; use OpenSRF::Utils::Logger qw/:logger/; @@ -1832,6 +1834,68 @@ sub biblio_record_to_marc_html { return $html->documentElement->toString(); } +__PACKAGE__->register_method( + method => "send_event_email_output", + api_name => "open-ils.search.biblio.record.email.send_output", +); +sub send_event_email_output { + my($self, $client, $auth, $event_id, $capkey, $capanswer) = @_; + return undef unless $event_id; + + my $captcha_pass = 0; + my $real_answer; + if ($capkey) { + $real_answer = $cache->get_cache(md5_hex($capkey)); + $captcha_pass++ if ($real_answer eq $capanswer); + } + + my $e = new_editor(authtoken => $auth); + return $e->die_event unless $captcha_pass || $e->checkauth; + + my $event = $e->retrieve_action_trigger_event([$event_id,{flesh => 1, flesh_fields => { atev => ['template_output']}}]); + return undef unless ($event and $event->template_output); + + my $smtp = OpenSRF::Utils::SettingsClient + ->new + ->config_value('email_notify', 'smtp_server'); + + my $sender = Email::Send->new({mailer => 'SMTP'}); + $sender->mailer_args([Host => $smtp]); + + my $stat; + my $err; + + my $email = Email::Simple->new($event->template_output->data); + + for my $hfield (qw/From To Subject Bcc Cc Reply-To Sender/) { + my @headers = $email->header($hfield); + $email->header_set($hfield => map { encode("MIME-Header", $_) } @headers) if ($headers[0]); + } + + $email->header_set('MIME-Version' => '1.0'); + $email->header_set('Content-Type' => "text/plain; charset=UTF-8"); + $email->header_set('Content-Transfer-Encoding' => '8bit'); + + try { + $stat = $sender->send($email); + } catch Error with { + $err = $stat = shift; + $logger->error("resend_at_email: Email failed with error: $err"); + }; + + return undef; +} + +__PACKAGE__->register_method( + method => "format_biblio_record_entry", + api_name => "open-ils.search.biblio.record.print.preview", +); + +__PACKAGE__->register_method( + method => "format_biblio_record_entry", + api_name => "open-ils.search.biblio.record.email.preview", +); + __PACKAGE__->register_method( method => "format_biblio_record_entry", api_name => "open-ils.search.biblio.record.print", @@ -1839,6 +1903,9 @@ __PACKAGE__->register_method( desc => 'Returns a printable version of the specified bib record', params => [ { desc => 'Biblio record entry ID or array of IDs', type => 'number' }, + { desc => 'Context library for holdings, if applicable' => 'number' }, + { desc => 'Sort order, if applicable' => 'string' }, + { desc => 'Definition Group Member id' => 'number' }, ], return => { desc => q/An action_trigger.event object or error event./, @@ -1854,6 +1921,13 @@ __PACKAGE__->register_method( params => [ { desc => 'Authentication token', type => 'string'}, { desc => 'Biblio record entry ID or array of IDs', type => 'number' }, + { desc => 'Context library for holdings, if applicable' => 'number' }, + { desc => 'Sort order, if applicable' => 'string' }, + { desc => 'Sort direction, if applicable' => 'string' }, + { desc => 'Definition Group Member id' => 'number' }, + { desc => 'Whether to bypass auth due to captcha' => 'bool' }, + { desc => 'Email address, if none for the user' => 'string' }, + { desc => 'Subject, if customized' => 'string' }, ], return => { desc => q/Undefined on success, otherwise an error event./, @@ -1863,25 +1937,44 @@ __PACKAGE__->register_method( ); sub format_biblio_record_entry { - my($self, $conn, $arg1, $arg2) = @_; + my($self, $conn, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $captcha_pass, $email, $subject) = @_; my $for_print = ($self->api_name =~ /print/); my $for_email = ($self->api_name =~ /email/); + my $preview = ($self->api_name =~ /preview/); - my $e; my $auth; my $bib_id; my $context_org; + my $e; my $auth; my $bib_id; my $context_org; my $holdings_context; my $bib_sort; my $group_member; my $type = 'brief'; my $sort_dir; if ($for_print) { $bib_id = $arg1; $context_org = $arg2 || $U->get_org_tree->id; + $holdings_context = $context_org; + $bib_sort = $arg3 || 'author'; + $sort_dir = $arg4 || 'ascending'; + $group_member = $arg5; $e = new_editor(xact => 1); } elsif ($for_email) { $auth = $arg1; $bib_id = $arg2; + $bib_sort = $arg4 || 'author'; + $sort_dir = $arg5 || 'ascending'; + $group_member = $arg6; $e = new_editor(authtoken => $auth, xact => 1); - return $e->die_event unless $e->checkauth; - $context_org = $e->requestor->home_ou; + return $e->die_event unless $captcha_pass || $e->checkauth; + $holdings_context = $arg3 || $U->get_org_tree->id; + $context_org = $e->requestor ? $e->requestor->home_ou : $arg3; + $email ||= $e->requestor ? $e->requestor->email : ''; + } + + if ($group_member) { + $group_member = $e->retrieve_action_trigger_event_def_group_member($group_member); + if ($group_member and $U->is_true($group_member->holdings)) { + $type = 'full'; + } } + $holdings_context = $e->retrieve_actor_org_unit($holdings_context); + my $bib_ids; if (ref $bib_id ne 'ARRAY') { $bib_ids = [ $bib_id ]; @@ -1893,7 +1986,7 @@ sub format_biblio_record_entry { $bucket->btype('temp'); $bucket->name('format_biblio_record_entry ' . $U->create_uuid_string); if ($for_email) { - $bucket->owner($e->requestor) + $bucket->owner($e->requestor || 1) } else { $bucket->owner(1); } @@ -1911,13 +2004,26 @@ sub format_biblio_record_entry { $e->commit; + my $usr_data = { + type => $type, + email => $email, + subject => $subject, + context_org => $holdings_context->shortname, + sort_by => $bib_sort, + sort_dir => $sort_dir, + preview => $preview + }; + if ($for_print) { - return $U->fire_object_event(undef, 'biblio.format.record_entry.print', [ $bucket ], $context_org); + return $U->fire_object_event(undef, 'biblio.format.record_entry.print', [ $bucket ], $context_org, undef, [ $usr_data ]); } elsif ($for_email) { - $U->create_events_for_hook('biblio.format.record_entry.email', $bucket, $context_org, undef, undef, 1); + return $U->fire_object_event(undef, 'biblio.format.record_entry.email', [ $bucket ], $context_org, undef, [ $usr_data ]) + if ($preview); + + $U->create_events_for_hook('biblio.format.record_entry.email', $bucket, $context_org, undef, $usr_data, 1); } return undef; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm index bf6b67eb2f..f011417cfc 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor.pm @@ -418,7 +418,7 @@ $_TT_helpers = { $unapi_args->{flesh}, $unapi_args->{site}, $unapi_args->{depth}, - $unapi_args->{flesh_depth}, + $unapi_args->{flesh_limit}, ] }; @@ -427,6 +427,99 @@ $_TT_helpers = { return $_TT_helpers->{xml_doc}->($unapi->[0]->{'unapi.bre'}); }, + # input: list of bib bucket items; output: sorted list of unapi_bre objects + sort_bucket_unapi_bre => sub { + my ($list, $unapi_args, $sortby, $sortdir) = @_; + #$logger->info("sort_bucket_unapi_bre unapi_bre params: " . join(', ', map { "$_: $$unapi_args{$_}" } keys(%$unapi_args))); + my @sorted_list; + for my $i (@$list) { + my $xml = $_TT_helpers->{unapi_bre}->($i->target_biblio_record_entry, $unapi_args); + if ($xml) { + my $bib = { xml => $xml, id => $i->target_biblio_record_entry }; + + $$bib{title} = ''; + for my $part ($xml->findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]')) { + $$bib{title} = $$bib{title} . $part->textContent; + } + $$bib{titlesort} = lc(substr($$bib{title}, $xml->findnodes('//*[@tag="245"]')->get_node(1)->getAttribute('ind2'))) + if ($$bib{title}); + + $$bib{authorsort} = $$bib{author} = $xml->findnodes('//*[@tag="100"]/*[@code="a"]')->to_literal_delimited(' '); + $$bib{authorsort} = lc($$bib{authorsort}); + $$bib{item_type} = $xml->findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]')->get_node(1)->getAttribute('coded-value'); + my $p = $xml->findnodes('//*[@tag="260" or @tag="264"]/*[@code="b"]')->get_node(1); + $$bib{publisher} = $p ? $p->textContent : ''; + $$bib{pubdatesort} = $$bib{pubdate} = $xml->findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="date1"]')->get_node(1)->textContent; + $$bib{pubdatesort} = lc($$bib{pubdatesort}); + $$bib{isbn} = $xml->findnodes('//*[@tag="020"]/*[@code="a"]')->to_literal_delimited(', '); + $$bib{issn} = $xml->findnodes('//*[@tag="022"]/*[@code="a"]')->to_literal_delimited(', '); + $$bib{upc} = $xml->findnodes('//*[@tag="024"]/*[@code="a"]')->to_literal_delimited(', '); + + $$bib{holdings} = []; + + for my $vol ($xml->findnodes('//*[local-name()="volume" and @deleted="false" and @opac_visible="true"]')) { + my $vol_data = {}; + $$vol_data{prefix_sort} = $vol->findnodes('.//*[local-name()="call_number_prefix"]')->get_node(1)->getAttribute('label_sortkey'); + $$vol_data{prefix} = $vol->findnodes('.//*[local-name()="call_number_prefix"]')->get_node(1)->getAttribute('label'); + $$vol_data{callnumber} = $vol->getAttribute('label'); + $$vol_data{callnumber_sort} = $vol->getAttribute('label_sortkey'); + $$vol_data{suffix_sort} = $vol->findnodes('.//*[local-name()="call_number_suffix"]')->get_node(1)->getAttribute('label_sortkey'); + $$vol_data{suffix} = $vol->findnodes('.//*[local-name()="call_number_suffix"]')->get_node(1)->getAttribute('label'); + #$logger->info("sort_bucket_unapi_bre found volume: " . join(', ', map { "$_: $$vol_data{$_}" } keys(%$vol_data))); + + my @copies; + for my $cp ($vol->findnodes('.//*[local-name()="copy" and @deleted="false"]')) { + my $cp_data = {%$vol_data}; + my $l = $cp->findnodes('.//*[local-name()="location" and @opac_visible="true"]')->get_node(1); + next unless ($l); + $$cp_data{location} = $l->textContent; + + my $s = $cp->findnodes('.//*[local-name()="status" and @opac_visible="true"]')->get_node(1); + next unless ($s); + $$cp_data{status_label} = $s->textContent; + $$cp_data{status_id} = $s->getAttribute('ident'); + + my $c = $cp->findnodes('.//*[local-name()="circ_lib" and @opac_visible="true"]')->get_node(1); + next unless ($c); + $$cp_data{circ_lib} = $c->getAttribute('name'); + + $$cp_data{barcode} = $cp->getAttribute('barcode'); + + $$cp_data{parts} = ''; + for my $mp ($cp->findnodes('.//*[local-name()="monograph_part"]')) { + $$cp_data{parts} .= ', ' if $$cp_data{parts}; + $$cp_data{parts} .= $mp->textContent; + } + push @copies, $cp_data; + #$logger->info("sort_bucket_unapi_bre found copy: " . join(', ', map { "$_: $$cp_data{$_}" } keys(%$cp_data))); + } + if (@copies) { + push @{$$bib{holdings}}, @copies; + } + } + + # sort 'em! + $$bib{holdings} = [ sort { + $$a{circ_lib} cmp $$b{circ_lib} || + $$a{location} cmp $$b{location} || + $$a{prefix_sort} cmp $$b{prefix_sort} || + $$a{callnumber_sort} cmp $$b{callnumber_sort} || + $$a{suffix_sort} cmp $$b{suffix_sort} || + ($$a{status_id} == 0 ? -1 : 0) || + ($$a{status_id} == 7 ? -1 : 0) || + $$a{status_label} cmp $$b{status_label}; + } @{$$bib{holdings}} ]; + + push @sorted_list, $bib; + } + } + + if ($sortdir =~ /^d/) { + return [ sort { $$b{$sortby.'sort'} cmp $$a{$sortby.'sort'} } @sorted_list ]; + } + return [ sort { $$a{$sortby.'sort'} cmp $$b{$sortby.'sort'} } @sorted_list ]; + }, + # escapes quotes in csv string values escape_csv => sub { my $string = shift; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor/SendEmail.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor/SendEmail.pm index 8f7a3219ca..880e95bd48 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor/SendEmail.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Reactor/SendEmail.pm @@ -49,6 +49,10 @@ sub handler { my $text = encode_utf8($self->run_TT($env)); return 0 if (!$text); + if ($$env{user_data} && ref($$env{user_data}) =~ /HASH/ && $$env{user_data}{preview}) { + $logger->info("SendEmail Reactor: success in preview mode, not sending email"); + return 1; + } my $sender = Email::Send->new({mailer => 'SMTP'}); $sender->mailer_args([Host => $smtp]); diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm index c458848523..c214c1ad18 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm @@ -12,6 +12,7 @@ use OpenSRF::Utils::Logger qw/$logger/; use OpenILS::Application::AppUtils; use OpenILS::Utils::CStoreEditor qw/:funcs/; use OpenILS::Utils::Fieldmapper; +use OpenSRF::Utils::Cache; use DateTime::Format::ISO8601; use CGI qw(:all -utf8); use Time::HiRes; @@ -133,6 +134,9 @@ sub load { $path =~ /opac\/my(opac\/lists|list)/ || $path =~ m!opac/api/mylist!; + my $org_unit = $self->ctx->{physical_loc} || $self->cgi->param('context_org') || $self->_get_search_lib; + $self->ctx->{selected_print_email_loc} = $org_unit; + return $self->load_api_mylist_retrieve if $path =~ m|opac/api/mylist/retrieve|; return $self->load_api_mylist_add if $path =~ m|opac/api/mylist/add|; return $self->load_api_mylist_delete if $path =~ m|opac/api/mylist/delete|; @@ -145,6 +149,7 @@ sub load { return $self->load_library if $path =~ m|opac/library|; return $self->load_rresults if $path =~ m|opac/results|; + return $self->load_print_or_email_preview('print') if $path =~ m|opac/record/print_preview|; return $self->load_print_record if $path =~ m|opac/record/print|; return $self->load_record if $path =~ m|opac/record/\d|; return $self->load_cnbrowse if $path =~ m|opac/cnbrowse|; @@ -184,11 +189,36 @@ sub load { } if ($path =~ m|opac/sms_cn| and !$self->editor->requestor) { - my $org_unit = $self->ctx->{physical_loc} || $self->cgi->param('loc') || $self->ctx->{aou_tree}->()->id; my $skip_sms_auth = $self->ctx->{get_org_setting}->($org_unit, 'sms.disable_authentication_requirement.callnumbers'); return $self->load_sms_cn if $skip_sms_auth; } + if (!$self->editor->requestor && $path =~ m|opac/record/email|) { + if ($self->ctx->{get_org_setting}->($org_unit, 'opac.email_record.allow_without_login')) { + my $cache = OpenSRF::Utils::Cache->new('global'); + + if ($path !~ m|preview|) { # the real thing! + $logger->info("not preview"); + my $cap_key = $self->ctx->{cap}->{key} = $self->cgi->param('capkey'); + $logger->info("got cap_key $cap_key"); + if ($cap_key) { + my $cap_answer = $self->ctx->{cap_answer} = $self->cgi->param('capanswer'); + my $real_answer = $self->ctx->{real_answer} = $cache->get_cache(md5_hex($cap_key)); + $logger->info("got answers $cap_answer $real_answer"); + return $self->load_email_record(1) if ( $cap_answer eq $real_answer ); + } + } + + my $captcha = {}; + $$captcha{key} = time() . $$ . rand(); + $$captcha{left} = int(rand(10)); + $$captcha{right} = int(rand(10)); + $cache->put_cache(md5_hex($$captcha{key}), $$captcha{left} + $$captcha{right}); + $self->ctx->{captcha} = $captcha; + return $self->load_print_or_email_preview('email', 1) if $path =~ m|opac/record/email_preview|; + } + } + # ---------------------------------------------------------------- # Everything below here requires authentication # ---------------------------------------------------------------- @@ -202,7 +232,9 @@ sub load { (undef, $self->ctx->{mylist}) = $self->fetch_mylist; } $self->load_simple("mylist/email") if $path =~ m|opac/mylist/email|; + return $self->load_print_or_email_preview('email') if $path =~ m|opac/mylist/doemail_preview|; return $self->load_mylist_email if $path =~ m|opac/mylist/doemail|; + return $self->load_print_or_email_preview('email') if $path =~ m|opac/record/email_preview|; return $self->load_email_record if $path =~ m|opac/record/email|; return $self->load_place_hold if $path =~ m|opac/place_hold|; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm index 4065e24b77..3c78ea23a8 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm @@ -2907,10 +2907,42 @@ sub load_myopac_bookbag_update { return $self->generic_redirect($url); } elsif ($action eq 'print') { - my $temp_cache_key = $self->_stash_record_list_in_anon_cache(@selected_item); + my ($incoming_sort,$sort_dir) = $self->_get_bookbag_sort_params('sort'); + $sort_dir = $self->cgi->param('sort_dir') if $self->cgi->param('sort_dir'); + if (!$incoming_sort) { + ($incoming_sort,$sort_dir) = $self->_get_bookbag_sort_params('anonsort'); + } + if (!$incoming_sort) { + $incoming_sort = 'author'; + } + + $incoming_sort =~ s/sort.*$//; + + $self->ctx->{sort} = $incoming_sort; + $self->ctx->{sort_dir} = $sort_dir; + + my $items = $self->editor->search_container_biblio_record_entry_bucket_item({id=>\@selected_item}); + my @bib_ids = map { $_->target_biblio_record_entry } @$items; + my $temp_cache_key = $self->_stash_record_list_in_anon_cache(@bib_ids); return $self->load_mylist_print($temp_cache_key); } elsif ($action eq 'email') { - my $temp_cache_key = $self->_stash_record_list_in_anon_cache(@selected_item); + my ($incoming_sort,$sort_dir) = $self->_get_bookbag_sort_params('sort'); + $sort_dir = $self->cgi->param('sort_dir') if $self->cgi->param('sort_dir'); + if (!$incoming_sort) { + ($incoming_sort,$sort_dir) = $self->_get_bookbag_sort_params('anonsort'); + } + if (!$incoming_sort) { + $incoming_sort = 'author'; + } + + $incoming_sort =~ s/sort.*$//; + + $self->ctx->{sort} = $incoming_sort; + $self->ctx->{sort_dir} = $sort_dir; + + my $items = $self->editor->search_container_biblio_record_entry_bucket_item({id=>\@selected_item}); + my @bib_ids = map { $_->target_biblio_record_entry } @$items; + my $temp_cache_key = $self->_stash_record_list_in_anon_cache(@bib_ids); return $self->load_mylist_email($temp_cache_key); } else { diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Container.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Container.pm index 6b076720ee..89ccdd08c4 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Container.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Container.pm @@ -205,7 +205,7 @@ sub load_mylist_print { } my $url = sprintf( - "%s://%s%s/record/print/%s", + "%s://%s%s/record/print_preview/%s", $self->ctx->{proto}, $self->ctx->{hostname}, $self->ctx->{opac_root}, @@ -215,8 +215,12 @@ sub load_mylist_print { my $redirect = $self->cgi->param('redirect_to'); $url .= '?redirect_to=' . uri_escape_utf8($redirect); my $clear_cart = $self->cgi->param('clear_cart'); - $url .= '&is_list=1'; $url .= '&clear_cart=1' if $clear_cart; + my $sort = $self->cgi->param('sort') || $self->cgi->param('anonsort'); + my $sort_dir = $self->cgi->param('sort_dir'); + $url .= '&sort='.$sort if $sort; + $url .= '&sort_dir='.$sort_dir if $sort_dir; + $url .= '&is_list=1'; return $self->generic_redirect($url); } @@ -231,7 +235,7 @@ sub load_mylist_email { } my $url = sprintf( - "%s://%s%s/record/email/%s", + "%s://%s%s/record/email_preview/%s", $self->ctx->{proto}, $self->ctx->{hostname}, $self->ctx->{opac_root}, @@ -241,8 +245,12 @@ sub load_mylist_email { my $redirect = $self->cgi->param('redirect_to'); $url .= '?redirect_to=' . uri_escape_utf8($redirect); my $clear_cart = $self->cgi->param('clear_cart'); - $url .= '&is_list=1'; $url .= '&clear_cart=1' if $clear_cart; + my $sort = $self->cgi->param('sort') || $self->cgi->param('anonsort'); + my $sort_dir = $self->cgi->param('sort_dir'); + $url .= '&sort='.$sort if $sort; + $url .= '&sort_dir='.$sort_dir if $sort_dir; + $url .= '&is_list=1'; return $self->generic_redirect($url); } @@ -279,10 +287,16 @@ sub load_mylist_move { return $self->load_myopac_bookbag_update('place_hold', undef, @rec_ids); } if ($action eq 'print') { + if ($self->cgi->param('entire_list')) { + @rec_ids = @$list; + } my $temp_cache_key = $self->_stash_record_list_in_anon_cache(@rec_ids); return $self->load_mylist_print($temp_cache_key); } if ($action eq 'email') { + if ($self->cgi->param('entire_list')) { + @rec_ids = @$list; + } my $temp_cache_key = $self->_stash_record_list_in_anon_cache(@rec_ids); return $self->load_mylist_email($temp_cache_key); } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm index bc3972bac8..5032716395 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm @@ -507,13 +507,30 @@ sub get_hold_copy_summary { $search->kill_me; } -sub load_print_record { +sub load_print_or_email_preview { my $self = shift; + my $type = shift; + my $captcha_pass = shift; + + my $ctx = $self->ctx; + my $e = new_editor(xact => 1); + my $old_event = $self->cgi->param('old_event'); + if ($old_event) { + $old_event = $e->retrieve_action_trigger_event([ + $old_event, + {flesh => 1, flesh_fields => { atev => ['template_output'] }} + ]); + $e->delete_action_trigger_event($old_event) if ($old_event); + $e->delete_action_trigger_event_output($old_event->template_output) if ($old_event && $old_event->template_output); + $e->commit; + } - my $rec_or_list_id = $self->ctx->{page_args}->[0] + my $rec_or_list_id = $ctx->{page_args}->[0] or return Apache2::Const::HTTP_BAD_REQUEST; - my $is_list = $self->cgi->param('is_list'); + $ctx->{bre_id} = $rec_or_list_id; + + my $is_list = $ctx->{is_list} = $self->cgi->param('is_list'); my $list; if ($is_list) { @@ -533,12 +550,88 @@ sub load_print_record { }; } else { $list = $rec_or_list_id; - $self->{ctx}->{bre_id} = $rec_or_list_id; + $ctx->{bre_id} = $rec_or_list_id; } - $self->{ctx}->{printable_record} = $U->simplereq( - 'open-ils.search', - 'open-ils.search.biblio.record.print', $list); + $ctx->{sortable} = (ref($list) && @$list > 1); + + my $group = $type eq 'print' ? 1 : 2; + + $ctx->{formats} = $self->editor->search_action_trigger_event_def_group_member([{grp => $group},{order_by => { atevdefgm => 'name'}}]); + $ctx->{format} = $self->cgi->param('format') || $ctx->{formats}[0]->id; + if ($type eq 'email') { + $ctx->{email} = $self->cgi->param('email') || ($ctx->{user} ? $ctx->{user}->email : ''); + $ctx->{subject} = $self->cgi->param('subject'); + } + + my $context_org = $self->cgi->param('context_org'); + if ($context_org) { + $context_org = $self->ctx->{get_aou}->($context_org); + } + + if (!$context_org) { + $context_org = $self->ctx->{get_aou}->($self->_get_search_lib()) || + $self->ctx->{aou_tree}->(); + } + + $ctx->{context_org} = $context_org->id; + + my ($incoming_sort,$sort_dir) = $self->_get_bookbag_sort_params('sort'); + $sort_dir = $self->cgi->param('sort_dir') if $self->cgi->param('sort_dir'); + if (!$incoming_sort) { + ($incoming_sort,$sort_dir) = $self->_get_bookbag_sort_params('anonsort'); + } + if (!$incoming_sort) { + $incoming_sort = 'author'; + } + + $incoming_sort =~ s/sort.*$//; + + $incoming_sort = 'author' + unless (grep {$_ eq $incoming_sort} qw/title author pubdate/); + + $ctx->{sort} = $incoming_sort; + $ctx->{sort_dir} = $sort_dir; + + my $method = "open-ils.search.biblio.record.$type.preview"; + my @args = ( + $list, + $ctx->{context_org}, + $ctx->{sort}, + $ctx->{sort_dir}, + $ctx->{format}, + $captcha_pass, + $ctx->{email}, + $ctx->{subject} + ); + + unshift(@args, $ctx->{authtoken}) if ($type eq 'email'); + + $ctx->{preview_record} = $U->simplereq( + 'open-ils.search', $method, @args); + + $ctx->{'redirect_to'} = $self->cgi->param('redirect_to') || $self->cgi->referer; + + return Apache2::Const::OK; +} + +sub load_print_record { + my $self = shift; + + my $event_id = $self->ctx->{page_args}->[0] + or return Apache2::Const::HTTP_BAD_REQUEST; + + my $event = $self->editor->retrieve_action_trigger_event([ + $event_id, + {flesh => 1, flesh_fields => { atev => ['template_output'] }} + ]); + + return Apache2::Const::HTTP_BAD_REQUEST + unless ($event and $event->template_output and $event->template_output->data); + + $self->ctx->{bre_id} = $self->cgi->param('bre_id'); + $self->ctx->{is_list} = $self->cgi->param('is_list'); + $self->ctx->{print_data} = $event->template_output->data; if ($self->cgi->param('clear_cart')) { $self->clear_anon_cache; @@ -550,37 +643,40 @@ sub load_print_record { sub load_email_record { my $self = shift; + my $captcha_pass = shift; - my $rec_or_list_id = $self->ctx->{page_args}->[0] + my $event_id = $self->ctx->{page_args}->[0] or return Apache2::Const::HTTP_BAD_REQUEST; - my $is_list = $self->cgi->param('is_list'); - my $list; - if ($is_list) { + my $e = new_editor(xact => 1, authtoken => $self->ctx->{authtoken}); + return Apache2::Const::HTTP_BAD_REQUEST + unless $captcha_pass || $e->checkauth; - $list = $U->simplereq( - 'open-ils.actor', - 'open-ils.actor.anon_cache.get_value', - $rec_or_list_id, (ref $self)->CART_CACHE_MYLIST); + my $event = $e->retrieve_action_trigger_event([ + $event_id, + {flesh => 1, flesh_fields => { atev => ['template_output'] }} + ]); - if(!$list) { - $list = []; - } + return Apache2::Const::HTTP_BAD_REQUEST + unless ($event and $event->template_output and $event->template_output->data); - { # sanitize - no warnings qw/numeric/; - $list = [map { int $_ } @$list]; - $list = [grep { $_ > 0} @$list]; - }; - } else { - $list = $rec_or_list_id; - $self->{ctx}->{bre_id} = $rec_or_list_id; - } + $self->ctx->{email} = $self->cgi->param('email'); + $self->ctx->{subject} = $self->cgi->param('subject'); + $self->ctx->{bre_id} = $self->cgi->param('bre_id'); + $self->ctx->{is_list} = $self->cgi->param('is_list'); + $self->ctx->{print_data} = $event->template_output->data; $U->simplereq( 'open-ils.search', - 'open-ils.search.biblio.record.email', - $self->ctx->{authtoken}, $list); + 'open-ils.search.biblio.record.email.send_output', + $self->ctx->{authtoken}, $event_id, + $self->ctx->{cap}->{key}, $self->ctx->{cap_answer}); + + # Move the output to async so it can't be used in a resend attack + $event->async_output($event->template_output->id); + $event->clear_template_output; + $e->update_action_trigger_event($event); + $e->commit; if ($self->cgi->param('clear_cart')) { $self->clear_anon_cache; diff --git a/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql b/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql index 9f990a3f67..8a0c2138f1 100644 --- a/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql +++ b/Open-ILS/src/sql/Pg/400.schema.action_trigger.sql @@ -291,6 +291,33 @@ CREATE TABLE action_trigger.event_params ( CONSTRAINT event_params_event_def_param_once UNIQUE (event_def,param) ); +CREATE TABLE action_trigger.event_def_group ( + id SERIAL PRIMARY KEY, + owner INT NOT NULL REFERENCES actor.org_unit (id) + ON DELETE RESTRICT ON UPDATE CASCADE + DEFERRABLE INITIALLY DEFERRED, + hook TEXT NOT NULL REFERENCES action_trigger.hook (key) + ON DELETE RESTRICT ON UPDATE CASCADE + DEFERRABLE INITIALLY DEFERRED, + active BOOL NOT NULL DEFAULT TRUE, + name TEXT NOT NULL +); +SELECT SETVAL('action_trigger.event_def_group_id_seq'::TEXT, 100, TRUE); + +CREATE TABLE action_trigger.event_def_group_member ( + id SERIAL PRIMARY KEY, + grp INT NOT NULL REFERENCES action_trigger.event_def_group (id) + ON DELETE CASCADE ON UPDATE CASCADE + DEFERRABLE INITIALLY DEFERRED, + event_def INT NOT NULL REFERENCES action_trigger.event_definition (id) + ON DELETE RESTRICT ON UPDATE CASCADE + DEFERRABLE INITIALLY DEFERRED, + sortable BOOL NOT NULL DEFAULT TRUE, + holdings BOOL NOT NULL DEFAULT FALSE, + external BOOL NOT NULL DEFAULT FALSE, + name TEXT NOT NULL +); + CREATE OR REPLACE FUNCTION action_trigger.purge_events() RETURNS VOID AS $_$ /** * Deleting expired events without simultaneously deleting their outputs 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 e203f288d4..3eab0bdedc 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -12023,39 +12023,47 @@ INSERT INTO action_trigger.event_definition ( $$ [%- USE date -%] [%- SET user = target.0.owner -%] -To: [%- params.recipient_email || user.email %] +To: [%- params.recipient_email || user_data.0.email || user.email %] From: [%- params.sender_email || default_sender %] Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %] -Subject: Bibliographic Records +Subject: [%- user_data.0.subject || 'Bibliographic Records' %] Auto-Submitted: auto-generated -[% FOR cbreb IN target %] -[% FOR item IN cbreb.items; - bre_id = item.target_biblio_record_entry; +[% FOR cbreb IN target; - bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'}); - title = ''; - FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]'); - title = title _ part.textContent; + flesh_list = '{mra'; + IF user_data.0.type == 'full'; + flesh_list = flesh_list _ ',holdings_xml,acp'; + IF params.holdings_limit; + flimit = 'acn=>' _ params.holdings_limit _ ',acp=>' _ params.holdings_limit; + END; END; + flesh_list = flesh_list _ '}'; + + item_list = helpers.sort_bucket_unapi_bre(cbreb.items,{flesh => flesh_list, site => user_data.0.context_org, flesh_limit => flimit}, user_data.0.sort_by, user_data.0.sort_dir); + +FOR item IN item_list %] + +[% loop.count %]/[% loop.size %]. Bib ID# [% item.id %] +[% IF item.isbn %]ISBN: [% item.isbn _ "\n" %][% END -%] +[% IF item.issn %]ISSN: [% item.issn _ "\n" %][% END -%] +[% IF item.upc %]UPC: [% item.upc _ "\n" %][% END -%] +Title: [% item.title %] +[% IF item.author %]Author: [% item.author _ "\n" %][% END -%] +Publication Info: [% item.publisher %] [% item.pubdate %] +Item Type: [% item.item_type %] +[% IF user_data.0.type == 'full' && item.holdings.size == 0 %] + * No items for this record at the selected location +[% END %] +[% FOR cp IN item.holdings -%] + * Library: [% cp.circ_lib %] + Location: [% cp.location %] + Call Number: [% cp.prefix _ ' ' _ cp.callnumber _ ' ' _ cp.suffix %] +[% IF cp.parts %] Parts: [% cp.parts _ "\n" %][% END -%] + Status: [% cp.status_label %] + Barcode: [% cp.barcode %] - author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent; - item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value'); - publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent; - pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent; - isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent; - issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent; - upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent; -%] - -[% loop.count %]/[% loop.size %]. Bib ID# [% bre_id %] -[% IF isbn %]ISBN: [% isbn _ "\n" %][% END -%] -[% IF issn %]ISSN: [% issn _ "\n" %][% END -%] -[% IF upc %]UPC: [% upc _ "\n" %] [% END -%] -Title: [% title %] -Author: [% author %] -Publication Info: [% publisher %] [% pubdate %] -Item Type: [% item_type %] +[% END %] [% END %] [% END %] @@ -12078,29 +12086,43 @@ $$
    - [% FOR cbreb IN target %] - [% FOR item IN cbreb.items; - bre_id = item.target_biblio_record_entry; - - bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'}); - title = ''; - FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]'); - title = title _ part.textContent; - END; + [% FOR cbreb IN target; - author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent; - item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value'); - publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent; - pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent; - isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent; - %] + flesh_list = '{mra'; + IF user_data.0.type == 'full'; + flesh_list = flesh_list _ ',holdings_xml,acp'; + IF params.holdings_limit; + flimit = 'acn=>' _ params.holdings_limit _ ',acp=>' _ params.holdings_limit; + END; + END; + flesh_list = flesh_list _ '}'; + item_list = helpers.sort_bucket_unapi_bre(cbreb.items,{flesh => flesh_list, site => user_data.0.context_org, flesh_limit => flimit}, user_data.0.sort_by, user_data.0.sort_dir); + FOR item IN item_list %]
  1. - Bib ID# [% bre_id %] ISBN: [% isbn %]
    - Title: [% title %]
    - Author: [% author %]
    - Publication Info: [% publisher %] [% pubdate %]
    - Item Type: [% item_type %] + Bib ID# [% item.id %]
    + [% IF item.isbn %]ISBN: [% item.isbn %]
    [% END %] + [% IF item.issn %]ISSN: [% item.issn %]
    [% END %] + [% IF item.upc %]UPC: [% item.upc %]
    [% END %] + Title: [% item.title %]
    +[% IF item.author %] Author: [% item.author %]
    [% END -%] + Publication Info: [% item.publisher %] [% item.pubdate %]
    + Item Type: [% item.item_type %] +
      + [% IF user_data.0.type == 'full' && item.holdings.size == 0 %] +
    • No items for this record at the selected location
    • + [% END %] + [% FOR cp IN item.holdings -%] +
    • + Library: [% cp.circ_lib %]
      + Location: [% cp.location %]
      + Call Number: [% cp.prefix _ ' ' _ cp.callnumber _ ' ' _ cp.suffix %]
      + [% IF cp.parts %]Parts: [% cp.parts %]
      [% END %] + Status: [% cp.status_label %]
      + Barcode: [% cp.barcode %] +
    • + [% END %] +
  2. [% END %] [% END %] @@ -19813,6 +19835,35 @@ VALUES ( ) ); +INSERT into config.org_unit_setting_type +( name, grp, label, description, datatype, fm_class ) VALUES +( 'opac.email_record.allow_without_login', 'opac', + oils_i18n_gettext('opac.email_record.allow_without_login', + 'Allow record emailing without login', + 'coust', 'label'), + oils_i18n_gettext('opac.email_record.allow_without_login', + 'Instead of forcing a patron to log in in order to email the details of a record, just challenge them with a simple catpcha.', + 'coust', 'description'), + 'bool', null) +; + +INSERT INTO action_trigger.event_def_group (id, owner, hook, name) + VALUES (1, 1, 'biblio.format.record_entry.print','Print Record(s)'); + +INSERT INTO action_trigger.event_def_group_member (grp, name, event_def) + SELECT 1, 'Brief', id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.print'; + +INSERT INTO action_trigger.event_def_group_member (grp, name, holdings, event_def) + SELECT 1, 'Full', TRUE, id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.print'; + +INSERT INTO action_trigger.event_def_group (id, owner, hook, name) + VALUES (2,1,'biblio.format.record_entry.email','Email Record(s)'); + +INSERT INTO action_trigger.event_def_group_member (grp, name, event_def) + SELECT 2, 'Brief', id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.email'; + +INSERT INTO action_trigger.event_def_group_member (grp, name, holdings, event_def) + SELECT 2, 'Full', TRUE, id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.email'; INSERT into config.org_unit_setting_type (name, label, description, datatype) VALUES ( diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.AT-def-groups.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.AT-def-groups.sql new file mode 100644 index 0000000000..573aabb243 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.schema.AT-def-groups.sql @@ -0,0 +1,160 @@ +BEGIN; + +SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version); + +INSERT into config.org_unit_setting_type +( name, grp, label, description, datatype, fm_class ) VALUES +( 'opac.email_record.allow_without_login', 'opac', + oils_i18n_gettext('opac.email_record.allow_without_login', + 'Allow record emailing without login', + 'coust', 'label'), + oils_i18n_gettext('opac.email_record.allow_without_login', + 'Instead of forcing a patron to log in in order to email the details of a record, just challenge them with a simple catpcha.', + 'coust', 'description'), + 'bool', null) +; + +CREATE TABLE action_trigger.event_def_group ( + id SERIAL PRIMARY KEY, + owner INT NOT NULL REFERENCES actor.org_unit (id) + ON DELETE RESTRICT ON UPDATE CASCADE + DEFERRABLE INITIALLY DEFERRED, + hook TEXT NOT NULL REFERENCES action_trigger.hook (key) + ON DELETE RESTRICT ON UPDATE CASCADE + DEFERRABLE INITIALLY DEFERRED, + active BOOL NOT NULL DEFAULT TRUE, + name TEXT NOT NULL +); +SELECT SETVAL('action_trigger.event_def_group_id_seq'::TEXT, 100, TRUE); + +CREATE TABLE action_trigger.event_def_group_member ( + id SERIAL PRIMARY KEY, + grp INT NOT NULL REFERENCES action_trigger.event_def_group (id) + ON DELETE CASCADE ON UPDATE CASCADE + DEFERRABLE INITIALLY DEFERRED, + event_def INT NOT NULL REFERENCES action_trigger.event_definition (id) + ON DELETE RESTRICT ON UPDATE CASCADE + DEFERRABLE INITIALLY DEFERRED, + sortable BOOL NOT NULL DEFAULT TRUE, + holdings BOOL NOT NULL DEFAULT FALSE, + external BOOL NOT NULL DEFAULT FALSE, + name TEXT NOT NULL +); + +INSERT INTO action_trigger.event_def_group (id, owner, hook, name) + VALUES (1, 1, 'biblio.format.record_entry.print','Print Record(s)'); + +INSERT INTO action_trigger.event_def_group_member (grp, name, event_def) + SELECT 1, 'Brief', id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.print'; + +INSERT INTO action_trigger.event_def_group_member (grp, name, holdings, event_def) + SELECT 1, 'Full', TRUE, id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.print'; + +INSERT INTO action_trigger.event_def_group (id, owner, hook, name) + VALUES (2,1,'biblio.format.record_entry.email','Email Record(s)'); + +INSERT INTO action_trigger.event_def_group_member (grp, name, event_def) + SELECT 2, 'Brief', id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.email'; + +INSERT INTO action_trigger.event_def_group_member (grp, name, holdings, event_def) + SELECT 2, 'Full', TRUE, id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.email'; + +UPDATE action_trigger.event_definition SET template = $$ +[%- USE date -%] +[%- SET user = target.0.owner -%] +To: [%- params.recipient_email || user_data.0.email || user.email %] +From: [%- params.sender_email || default_sender %] +Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %] +Subject: [%- user_data.0.subject || 'Bibliographic Records' %] +Auto-Submitted: auto-generated + +[% FOR cbreb IN target; + + flesh_list = '{mra'; + IF user_data.0.type == 'full'; + flesh_list = flesh_list _ ',holdings_xml,acp'; + IF params.holdings_limit; + flimit = 'acn=>' _ params.holdings_limit _ ',acp=>' _ params.holdings_limit; + END; + END; + flesh_list = flesh_list _ '}'; + + item_list = helpers.sort_bucket_unapi_bre(cbreb.items,{flesh => flesh_list, site => user_data.0.context_org, flesh_limit => flimit}, user_data.0.sort_by, user_data.0.sort_dir); + +FOR item IN item_list %] + +[% loop.count %]/[% loop.size %]. Bib ID# [% item.id %] +[% IF item.isbn %]ISBN: [% item.isbn _ "\n" %][% END -%] +[% IF item.issn %]ISSN: [% item.issn _ "\n" %][% END -%] +[% IF item.upc %]UPC: [% item.upc _ "\n" %][% END -%] +Title: [% item.title %] +[% IF item.author %]Author: [% item.author _ "\n" %][% END -%] +Publication Info: [% item.publisher %] [% item.pubdate %] +Item Type: [% item.item_type %] +[% IF user_data.0.type == 'full' && item.holdings.size == 0 %] + * No items for this record at the selected location +[% END %] +[% FOR cp IN item.holdings -%] + * Library: [% cp.circ_lib %] + Location: [% cp.location %] + Call Number: [% cp.prefix _ ' ' _ cp.callnumber _ ' ' _ cp.suffix %] +[% IF cp.parts %] Parts: [% cp.parts _ "\n" %][% END -%] + Status: [% cp.status_label %] + Barcode: [% cp.barcode %] + +[% END %] + +[% END %] +[% END %] +$$ WHERE hook = 'biblio.format.record_entry.email'; + +UPDATE action_trigger.event_definition SET template = $$ +
    + +
      + [% FOR cbreb IN target; + + flesh_list = '{mra'; + IF user_data.0.type == 'full'; + flesh_list = flesh_list _ ',holdings_xml,acp'; + IF params.holdings_limit; + flimit = 'acn=>' _ params.holdings_limit _ ',acp=>' _ params.holdings_limit; + END; + END; + flesh_list = flesh_list _ '}'; + + item_list = helpers.sort_bucket_unapi_bre(cbreb.items,{flesh => flesh_list, site => user_data.0.context_org, flesh_limit => flimit}, user_data.0.sort_by, user_data.0.sort_dir); + FOR item IN item_list %] +
    1. + Bib ID# [% item.id %]
      + [% IF item.isbn %]ISBN: [% item.isbn %]
      [% END %] + [% IF item.issn %]ISSN: [% item.issn %]
      [% END %] + [% IF item.upc %]UPC: [% item.upc %]
      [% END %] + Title: [% item.title %]
      +[% IF item.author %] Author: [% item.author %]
      [% END -%] + Publication Info: [% item.publisher %] [% item.pubdate %]
      + Item Type: [% item.item_type %] +
        + [% IF user_data.0.type == 'full' && item.holdings.size == 0 %] +
      • No items for this record at the selected location
      • + [% END %] + [% FOR cp IN item.holdings -%] +
      • + Library: [% cp.circ_lib %]
        + Location: [% cp.location %]
        + Call Number: [% cp.prefix _ ' ' _ cp.callnumber _ ' ' _ cp.suffix %]
        + [% IF cp.parts %]Parts: [% cp.parts %]
        [% END %] + Status: [% cp.status_label %]
        + Barcode: [% cp.barcode %] +
      • + [% END %] +
      +
    2. + [% END %] + [% END %] +
    +
    +$$ WHERE hook = 'biblio.format.record_entry.print'; + +COMMIT; + diff --git a/Open-ILS/src/templates/opac/parts/anon_list.tt2 b/Open-ILS/src/templates/opac/parts/anon_list.tt2 index ed0b54624a..afe60fa499 100644 --- a/Open-ILS/src/templates/opac/parts/anon_list.tt2 +++ b/Open-ILS/src/templates/opac/parts/anon_list.tt2 @@ -13,6 +13,7 @@
+
diff --git a/Open-ILS/src/templates/opac/parts/cart.tt2 b/Open-ILS/src/templates/opac/parts/cart.tt2 index 84514133f5..9a97f36fa6 100644 --- a/Open-ILS/src/templates/opac/parts/cart.tt2 +++ b/Open-ILS/src/templates/opac/parts/cart.tt2 @@ -4,8 +4,8 @@ - - + + [% IF !ctx.is_browser_staff %] [% END %] diff --git a/Open-ILS/src/templates/opac/parts/record/summary.tt2 b/Open-ILS/src/templates/opac/parts/record/summary.tt2 index fb6f84a1e8..be248abe38 100644 --- a/Open-ILS/src/templates/opac/parts/record/summary.tt2 +++ b/Open-ILS/src/templates/opac/parts/record/summary.tt2 @@ -139,8 +139,8 @@
[%- IF ctx.refworks.enabled == 'true' %] [%- INCLUDE 'opac/parts/record/refworks.tt2' %] diff --git a/Open-ILS/src/templates/opac/record/email.tt2 b/Open-ILS/src/templates/opac/record/email.tt2 index 88cf88cd76..f086b993fb 100644 --- a/Open-ILS/src/templates/opac/record/email.tt2 +++ b/Open-ILS/src/templates/opac/record/email.tt2 @@ -8,11 +8,10 @@

- [% IF ctx.user.email %] -

[% l('Your email has been queued for delivery to [_1]', ctx.user.email ) %]

- [% ELSE %] +

[% l('Your email has been queued for delivery to [_1]', ctx.email ) %]

+ [% IF ctx.user && !ctx.user.email %]

- [% here_link_text = ""; + [% here_link_text = ""; here_link_text = here_link_text _ l("here"); here_link_text = here_link_text _ ""; l('Your account does not currently have an email address set. Set your email address [_1]', here_link_text) %] diff --git a/Open-ILS/src/templates/opac/record/email_preview.tt2 b/Open-ILS/src/templates/opac/record/email_preview.tt2 new file mode 100644 index 0000000000..969809117f --- /dev/null +++ b/Open-ILS/src/templates/opac/record/email_preview.tt2 @@ -0,0 +1,111 @@ +[%- PROCESS "opac/parts/header.tt2"; + WRAPPER "opac/parts/base.tt2"; + INCLUDE "opac/parts/topnav.tt2"; + ctx.page_title = l("Email Record Preview"); + PROCESS "opac/parts/org_selector.tt2"; +-%] +

[% l('Email Record Preview') %]

+ [% INCLUDE "opac/parts/searchbar.tt2" %] +
+
+ + + + + + + + + + + + + + + [% IF ctx.is_list == '1' %] + + [% END %] + + [% IF ctx.format_obj.holdings == 't' %] + + [% END %] + +
+ +
+ + +
+ +
+ + + + + + + + + [% IF ctx.preview_record.template_output %] + [% IF ctx.captcha.key %] + + [% END %] + | + [% l("Return") %] +
+
[% ctx.preview_record.template_output.data %]
+ [% ELSE %] + +
+ + [% END %] +
+
+
+[%- END %] diff --git a/Open-ILS/src/templates/opac/record/print.tt2 b/Open-ILS/src/templates/opac/record/print.tt2 index 26c35432a8..c1b55a4a3f 100644 --- a/Open-ILS/src/templates/opac/record/print.tt2 +++ b/Open-ILS/src/templates/opac/record/print.tt2 @@ -6,18 +6,21 @@ - [% IF ctx.printable_record.template_output %] - - [% ctx.printable_record.template_output.data %] - +
+ [% IF ctx.redirect_to %] +

[ [% l("Return") %] ]

+ [% ELSE %] +

[ [% l("Back to Record") %] ]

+ [% END %] +
+
+ [% IF ctx.print_data %] +
+ [% ctx.print_data %] +
[% ELSE %] [% END %]
diff --git a/Open-ILS/src/templates/opac/record/print_preview.tt2 b/Open-ILS/src/templates/opac/record/print_preview.tt2 new file mode 100644 index 0000000000..8fc58390bb --- /dev/null +++ b/Open-ILS/src/templates/opac/record/print_preview.tt2 @@ -0,0 +1,82 @@ +[%- PROCESS "opac/parts/header.tt2"; + WRAPPER "opac/parts/base.tt2"; + INCLUDE "opac/parts/topnav.tt2"; + ctx.page_title = l("Print Record Preview"); + PROCESS "opac/parts/org_selector.tt2"; +-%] +

[% l('Print Record Preview') %]

+ [% INCLUDE "opac/parts/searchbar.tt2" %] +
+
+ +
+ + + + + + + + + [% IF ctx.is_list == '1' %] + + [% END %] + + [% IF ctx.format_obj.holdings == 't' %] + + [% END %] + +
+ + +
+
+ + [% IF ctx.preview_record.template_output %] + [% l("Print Now") %] | + [% l("Return") %] +
+
[% ctx.preview_record.template_output.data %]
+ [% ELSE %] + +
+ + [% END %] +
+
+
+[%- END %] diff --git a/Open-ILS/src/templates/staff/admin/server/t_splash.tt2 b/Open-ILS/src/templates/staff/admin/server/t_splash.tt2 index 1377308693..c85b06b487 100644 --- a/Open-ILS/src/templates/staff/admin/server/t_splash.tt2 +++ b/Open-ILS/src/templates/staff/admin/server/t_splash.tt2 @@ -27,6 +27,8 @@ ,[ l('Circulation Modifiers'), "./admin/server/config/circ_modifier" ] ,[ l('Circulation Recurring Fine Rules'), "./admin/server/config/rule_recurring_fine" ] ,[ l('Custom Org Unit Trees'), "./admin/server/actor/org_unit_custom_tree" ] + ,[ l('Event Definition Groups'), "/eg2/staff/admin/server/action_trigger/event_def_group" ] + ,[ l('Event Definition Group Members'), "/eg2/staff/admin/server/action_trigger/event_def_group_member" ] ,[ l('Floating Groups'), "./admin/server/config/floating_groups" ] ,[ l('Global Flags'), "./admin/server/config/global_flag" ] ,[ l('Hard Due Date Changes'), "./admin/server/config/hard_due_date" ] -- 2.43.2