1 package OpenILS::WWW::PrintTemplate;
2 use strict; use warnings;
3 use Apache2::Const -compile =>
4 qw(OK FORBIDDEN NOT_FOUND HTTP_INTERNAL_SERVER_ERROR HTTP_BAD_REQUEST);
5 use Apache2::RequestRec;
9 use DateTime::Format::ISO8601;
10 use Unicode::Normalize;
11 use OpenSRF::Utils::JSON;
13 use OpenSRF::Utils::SettingsClient;
14 use OpenILS::Utils::CStoreEditor q/:funcs/;
15 use OpenSRF::Utils::Logger q/$logger/;
16 use OpenILS::Application::AppUtils;
17 use OpenILS::Utils::DateTime qw/:datetime/;
19 my $U = 'OpenILS::Application::AppUtils';
23 my $enable_cache; # Enable process-level template caching
26 $enable_cache = shift;
29 my $init_complete = 0;
33 OpenSRF::System->bootstrap_client(config_file => $bs_config);
34 OpenILS::Utils::CStoreEditor->init;
35 return Apache2::Const::OK;
39 # https://metacpan.org/pod/HTML::Defang
40 my $defang = HTML::Defang->new;
46 child_init() unless $init_complete;
48 my $auth = $cgi->param('ses') ||
49 $cgi->cookie('eg.auth.token') || $cgi->cookie('ses');
51 my $e = new_editor(authtoken => $auth);
53 # Requires staff login
54 return Apache2::Const::FORBIDDEN
55 unless $e->checkauth && $e->requestor->wsid;
57 # Let pcrud handle the authz
58 $e->personality('open-ils.pcrud');
60 my $tmpl_owner = $cgi->param('template_owner') || $e->requestor->ws_ou;
61 my $tmpl_locale = $cgi->param('template_locale') || 'en-US';
62 my $tmpl_id = $cgi->param('template_id');
63 my $tmpl_name = $cgi->param('template_name');
64 my $tmpl_data = $cgi->param('template_data');
65 my $client_timezone = $cgi->param('client_timezone');
67 return Apache2::Const::HTTP_BAD_REQUEST unless $tmpl_name || $tmpl_id;
70 find_template($e, $tmpl_id, $tmpl_name, $tmpl_locale, $tmpl_owner)
71 or return Apache2::Const::NOT_FOUND;
74 eval { $data = OpenSRF::Utils::JSON->JSON2perl($tmpl_data); };
76 $logger->error("Invalid JSON in template compilation: $tmpl_data");
77 return Apache2::Const::HTTP_BAD_REQUEST;
80 my ($staff_org) = $U->fetch_org_unit($e->requestor->ws_ou);
83 my $tt = Template->new;
84 my $tmpl = $template->template;
87 template_locale => $tmpl_locale,
88 client_timezone => $client_timezone,
89 staff => $e->requestor,
90 staff_org => $staff_org,
91 staff_org_timezone => get_org_timezone($e, $staff_org->id),
93 template_data => $data
96 my $stat = $tt->process(\$tmpl, $context, \$output);
99 my $ctype = $template->content_type;
100 if ($ctype eq 'text/html') {
101 $output = $defang->defang($output); # Scrub the HTML
104 # client current expects content type to only contain type.
105 # $r->content_type("$ctype; encoding=utf8");
106 $r->content_type($ctype);
108 return Apache2::Const::OK;
112 (my $error = $tt->error) =~ s/\n/ /og;
113 $logger->error("Error processing print template: $error");
114 return Apache2::Const::HTTP_INTERNAL_SERVER_ERROR;
118 my %org_timezone_cache;
119 sub get_org_timezone {
120 my ($e, $org_id) = @_;
122 if (!$org_timezone_cache{$org_id}) {
124 # open-ils.auth call required since our $e is in pcrud mode.
125 my $value = $U->simplereq(
127 'open-ils.actor.ou_setting.ancestor_default',
128 $org_id, 'lib.timezone');
130 $org_timezone_cache{$org_id} = $value ? $value->{value} :
131 DateTime->now(time_zone => 'local')->time_zone->name;
134 return $org_timezone_cache{$org_id};
138 # Find the template closest to the specific org unit owner.
141 my ($e, $template_id, $name, $locale, $owner) = @_;
144 # Requesting by ID, generally used for testing,
145 # always pulls the latest value and ignores the active flag
146 return $e->retrieve_config_print_template($template_id);
149 return $template_cache{$owner}{$name}{$locale}
151 $template_cache{$owner} &&
152 $template_cache{$owner}{$name} &&
153 $template_cache{$owner}{$name}{$locale};
156 my ($org) = $U->fetch_org_unit($owner); # cached in AppUtils
158 my $template = $e->search_config_print_template({
168 $template_cache{$owner} = {} unless $template_cache{$owner};
169 $template_cache{$owner}{$name} = {}
170 unless $template_cache{$owner}{$name};
171 $template_cache{$owner}{$name}{$locale} = $template;
177 $owner = $org->parent_ou;
183 # Utility / helper functions passed into every template
187 # turns a date w/ optional timezone modifier into something
193 $date = DateTime::Format::ISO8601->new->parse_datetime(clean_ISO8601($date));
194 $date->set_time_zone($tz) if $tz;
197 "%0.2d:%0.2d:%0.2d %0.2d-%0.2d-%0.4d",
207 current_date => sub {
208 my $tz = shift || 'local';
209 my $date = DateTime->now(time_zone => $tz);
210 return $helpers->{format_date}->($date);