Revision 7647d46a
Von Moritz Bunkus vor fast 12 Jahren hinzugefügt
SL/Controller/Base.pm | ||
---|---|---|
60 | 60 |
my $template = shift; |
61 | 61 |
my ($options, %locals) = (@_ && ref($_[0])) ? @_ : ({ }, @_); |
62 | 62 |
|
63 |
$options->{type} = lc($options->{type} || 'html'); |
|
64 |
$options->{no_layout} = 1 if $options->{type} eq 'js'; |
|
63 |
# Set defaults for all available options. |
|
64 |
my %defaults = ( |
|
65 |
type => 'html', |
|
66 |
output => 1, |
|
67 |
header => 1, |
|
68 |
layout => 1, |
|
69 |
process => 1, |
|
70 |
); |
|
71 |
$options->{$_} //= $defaults{$_} for keys %defaults; |
|
72 |
$options->{type} = lc $options->{type}; |
|
73 |
|
|
74 |
# Check supplied options for validity. |
|
75 |
foreach (keys %{ $options }) { |
|
76 |
croak "Unsupported option: $_" unless $defaults{$_}; |
|
77 |
} |
|
78 |
|
|
79 |
# Only certain types are supported. |
|
80 |
croak "Unsupported type: " . $options->{type} unless $options->{type} =~ m/^(?:html|js|json)$/; |
|
81 |
|
|
82 |
# The "template" argument must be a string or a reference to one. |
|
83 |
croak "Unsupported 'template' reference type: " . ref($template) if ref($template) && (ref($template) !~ m/^(?:SCALAR|SL::Presenter::EscapedText)$/); |
|
84 |
|
|
85 |
# If all output is turned off then don't output the header either. |
|
86 |
if (!$options->{output}) { |
|
87 |
$options->{header} = 0; |
|
88 |
$options->{layout} = 0; |
|
89 |
|
|
90 |
} else { |
|
91 |
# Layout only makes sense if we're outputting HTML. |
|
92 |
$options->{layout} = 0 if $options->{type} ne 'html'; |
|
93 |
} |
|
94 |
|
|
95 |
if ($options->{header}) { |
|
96 |
# Output the HTTP response and the layout in case of HTML output. |
|
65 | 97 |
|
66 |
if (!$options->{partial} && !$options->{inline} && !$::form->{header}) { |
|
67 |
if ($options->{no_layout}) { |
|
98 |
if ($options->{layout}) { |
|
99 |
$::form->{title} = $locals{title} if $locals{title}; |
|
100 |
$::form->header; |
|
101 |
|
|
102 |
} else { |
|
103 |
# No layout: just the standard HTTP response. Also notify |
|
104 |
# $::form that the header has already been output so that |
|
105 |
# $::form->header() won't output it again. |
|
68 | 106 |
$::form->{header} = 1; |
69 |
my $content_type = $options->{type} eq 'js' ? 'text/javascript' : 'text/html'; |
|
107 |
my $content_type = $options->{type} eq 'html' ? 'text/html' |
|
108 |
: $options->{type} eq 'js' ? 'text/javascript' |
|
109 |
: 'application/json'; |
|
70 | 110 |
|
71 | 111 |
print $::form->create_http_response(content_type => $content_type, |
72 | 112 |
charset => $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET()); |
73 |
|
|
74 |
} else { |
|
75 |
$::form->{title} = $locals{title} if $locals{title}; |
|
76 |
$::form->header(no_menu => $options->{no_menu}); |
|
77 | 113 |
} |
78 | 114 |
} |
79 | 115 |
|
80 |
my $output; |
|
81 |
if ($options->{raw}) { |
|
82 |
$output = $$template; |
|
83 |
} else { |
|
84 |
$output = $self->presenter->render( |
|
85 |
$template, $options, |
|
86 |
%locals, |
|
87 |
SELF => $self, |
|
88 |
); |
|
89 |
} |
|
116 |
# Let the presenter do the rest of the work. |
|
117 |
my $output = $self->presenter->render( |
|
118 |
$template, |
|
119 |
{ type => $options->{type}, process => $options->{process} }, |
|
120 |
%locals, |
|
121 |
SELF => $self, |
|
122 |
); |
|
90 | 123 |
|
91 |
print $output unless $options->{inline} || $options->{no_output}; |
|
124 |
# Print the output if wanted. |
|
125 |
print $output if $options->{output}; |
|
92 | 126 |
|
93 | 127 |
return $output; |
94 | 128 |
} |
... | ... | |
334 | 368 |
C<$options>, if present, must be a hash reference. All remaining |
335 | 369 |
parameters are slurped into C<%locals>. |
336 | 370 |
|
337 |
What is rendered and how C<$template> is interpreted is determined by
|
|
338 |
the options I<type>, I<inline>, I<partial> and I<no_layout>. The
|
|
371 |
What is rendered and how C<$template> is interpreted is determined |
|
372 |
both by C<$template>'s reference type and by the supplied options. The
|
|
339 | 373 |
actual rendering is handled by L<SL::Presenter/render>. |
340 | 374 |
|
341 |
If C<< $options->{inline} >> is trueish then C<$template> is a string |
|
342 |
containing the template code to interprete. Additionally the output |
|
343 |
will not be sent to the browser. Instead it is only returned to the |
|
344 |
caller. |
|
375 |
If C<$template> is a normal scalar (not a reference) then it is meant |
|
376 |
to be a template file name relative to the C<templates/webpages> |
|
377 |
directory. The file name to use is determined by the C<type> option. |
|
378 |
|
|
379 |
If C<$template> is a reference to a scalar then the referenced |
|
380 |
scalar's content is used as the content to process. The C<type> option |
|
381 |
is not considered in this case. |
|
382 |
|
|
383 |
Other reference types, unknown options and unknown arguments to the |
|
384 |
C<type> option cause the function to L<croak>. |
|
385 |
|
|
386 |
The following options are available (defaults: C<type> = 'html', |
|
387 |
C<process> = 1, C<output> = 1, C<header> = 1, C<layout> = 1): |
|
388 |
|
|
389 |
=over 2 |
|
390 |
|
|
391 |
=item C<type> |
|
345 | 392 |
|
346 |
If C<< $options->{raw} >> is trueish, the function will treat the |
|
347 |
input as already parsed, and will not filter the input through |
|
348 |
Template. This also means that L<SL::Presenter/render> is not |
|
349 |
called either. Unlike C<inline>, the input is taken as a reference. |
|
393 |
The template type. Can be C<html> (the default), C<js> for JavaScript |
|
394 |
or C<json> for JSON content. Affects the extension that's added to the |
|
395 |
file name given with a non-reference C<$template> argument, the |
|
396 |
content type HTTP header that is output and whether or not the layout |
|
397 |
will be output as well (see description of C<layout> below). |
|
350 | 398 |
|
351 |
If C<< $options->{inline} >> is falsish then C<$template> is |
|
352 |
interpreted as the name of a template file. It is prefixed with |
|
353 |
"templates/webpages/" and postfixed with a file extension based on |
|
354 |
C<< $options->{type} >>. C<< $options->{type} >> can be either C<html> |
|
355 |
or C<js> and defaults to C<html>. An exception will be thrown if that |
|
356 |
file does not exist. |
|
399 |
=item C<process> |
|
357 | 400 |
|
358 |
If C<< $options->{partial} >> or C<< $options->{inline} >> is trueish
|
|
359 |
then neither the HTTP response header nor the standard HTML header is
|
|
360 |
generated.
|
|
401 |
If trueish (which is also the default) it causes the template/content
|
|
402 |
to be processed by the Template toolkit. Otherwise the
|
|
403 |
template/content is output as-is.
|
|
361 | 404 |
|
362 |
Otherwise at least the HTTP response header will be generated based on |
|
363 |
the template type (C<< $options->{type} >>). |
|
405 |
=item C<output> |
|
364 | 406 |
|
365 |
If the template type is C<html> then the standard HTML header will be |
|
366 |
output via C<< $::form->header >> with C<< $::form->{title} >> set to |
|
367 |
C<$locals{title}> (the latter only if C<$locals{title}> is |
|
368 |
trueish). Setting C<< $options->{no_layout} >> to trueish will prevent |
|
369 |
this. |
|
407 |
If trueish (the default) then the generated output will be sent to the |
|
408 |
browser in addition to being returned. If falsish then the options |
|
409 |
C<header> and C<layout> are set to 0 as well. |
|
410 |
|
|
411 |
=item C<header> |
|
412 |
|
|
413 |
Determines whether or not to output the HTTP response |
|
414 |
headers. Defaults to the same value that C<output> is set to. If set |
|
415 |
to falsish then the layout is not output either. |
|
416 |
|
|
417 |
=item C<layout> |
|
418 |
|
|
419 |
Determines whether or not the basic HTML layout structure should be |
|
420 |
output (HTML header, common JavaScript and stylesheet inclusions, menu |
|
421 |
etc.). Defaults to 0 if C<type> is not C<html> and to the same value |
|
422 |
C<header> is set to otherwise. |
|
423 |
|
|
424 |
=back |
|
370 | 425 |
|
371 | 426 |
The template itself has access to several variables. These are listed |
372 | 427 |
in the documentation to L<SL::Presenter/render>. |
373 | 428 |
|
374 |
Unless C<< $options->{inline} >> is trueish the function will send the |
|
375 |
output to the browser. |
|
376 |
|
|
377 | 429 |
The function will always return the output. |
378 | 430 |
|
379 | 431 |
Example: Render a HTML template with a certain title and a few locals |
... | ... | |
383 | 435 |
TODO_ITEMS => SL::DB::Manager::Todo->get_all_sorted); |
384 | 436 |
|
385 | 437 |
Example: Render a string and return its content for further processing |
386 |
by the calling function. No header is generated due to C<inline>.
|
|
438 |
by the calling function. No header is generated due to C<output>.
|
|
387 | 439 |
|
388 |
my $content = $self->render('[% USE JavaScript %][% JavaScript.replace_with("#someid", "js/something") %]', |
|
389 |
{ type => 'js', inline => 1 });
|
|
440 |
my $content = $self->render(\'[% USE JavaScript %][% JavaScript.replace_with("#someid", "js/something") %]',
|
|
441 |
{ output => 0 });
|
|
390 | 442 |
|
391 |
Example: Render a JavaScript template and send it to the |
|
443 |
Example: Render a JavaScript template |
|
444 |
"templates/webpages/todo/single_item.js" and send it to the |
|
392 | 445 |
browser. Typical use for actions called via AJAX: |
393 | 446 |
|
394 | 447 |
$self->render('todo/single_item', { type => 'js' }, |
SL/Controller/CustomVariableConfig.pm | ||
---|---|---|
17 | 17 |
|
18 | 18 |
SL::DB::CustomVariableConfig->reorder_list(@{ $::form->{cvarcfg_id} || [] }); |
19 | 19 |
|
20 |
$self->render('1;', { type => 'js', inline => 1 });
|
|
20 |
$self->render(\'', { type => 'json' });
|
|
21 | 21 |
} |
22 | 22 |
|
23 | 23 |
# |
SL/Controller/Customer.pm | ||
---|---|---|
22 | 22 |
$self->{customers} = SL::DB::Manager::Customer->get_all(query => [ @filter ], limit => $limit); |
23 | 23 |
$self->{value} = $::form->{column} || 'name'; |
24 | 24 |
|
25 |
$self->render('ct/ajax_autocomplete2', { no_layout => 1 });
|
|
25 |
$self->render('ct/ajax_autocomplete2', { layout => 0, type => 'json' });
|
|
26 | 26 |
} |
27 |
|
SL/Controller/DeliveryPlan.pm | ||
---|---|---|
177 | 177 |
controller_class => 'DeliveryPlan', |
178 | 178 |
output_format => 'HTML', |
179 | 179 |
top_info_text => $::locale->text('Delivery Plan for currently outstanding sales orders'), |
180 |
raw_top_info_text => $self->render('delivery_plan/report_top', { no_output => 1, partial => 1 }),
|
|
181 |
raw_bottom_info_text => $self->render('delivery_plan/report_bottom', { no_output => 1, partial => 1 }),
|
|
180 |
raw_top_info_text => $self->render('delivery_plan/report_top', { output => 0 }),
|
|
181 |
raw_bottom_info_text => $self->render('delivery_plan/report_bottom', { output => 0 }),
|
|
182 | 182 |
title => $::locale->text('Delivery Plan'), |
183 | 183 |
allow_pdf_export => 1, |
184 | 184 |
allow_csv_export => 1, |
SL/Controller/Layout.pm | ||
---|---|---|
3 | 3 |
use strict; |
4 | 4 |
use parent qw(SL::Controller::Base); |
5 | 5 |
|
6 |
use JSON (); |
|
6 |
use SL::JSON ();
|
|
7 | 7 |
|
8 | 8 |
sub action_empty { |
9 | 9 |
my ($self) = @_; |
... | ... | |
20 | 20 |
stylesheets_inline => [ $::request->{layout}->stylesheets_inline ], |
21 | 21 |
}; |
22 | 22 |
|
23 |
$self->render(\ JSON::to_json($layout), { type => 'js', raw => 1 });
|
|
23 |
$self->render(\ SL::JSON::to_json($layout), { type => 'json', process => 0 });
|
|
24 | 24 |
} |
25 | 25 |
} |
26 | 26 |
|
SL/Controller/LoginScreen.pm | ||
---|---|---|
20 | 20 |
return if $self->_redirect_to_main_script_if_already_logged_in; |
21 | 21 |
|
22 | 22 |
# Otherwise show the login form. |
23 |
$self->render('login_screen/user_login', { no_menu => 1 }, error => error_state($::form->{error}));
|
|
23 |
$self->render('login_screen/user_login', error => error_state($::form->{error})); |
|
24 | 24 |
} |
25 | 25 |
|
26 | 26 |
sub action_logout { |
... | ... | |
28 | 28 |
|
29 | 29 |
$::auth->destroy_session; |
30 | 30 |
$::auth->create_or_refresh_session; |
31 |
$self->render('login_screen/user_login', { no_menu => 1 }, error => $::locale->text('You are logged out!'));
|
|
31 |
$self->render('login_screen/user_login', error => $::locale->text('You are logged out!')); |
|
32 | 32 |
} |
33 | 33 |
|
34 | 34 |
sub action_login { |
... | ... | |
57 | 57 |
# Other login errors. |
58 | 58 |
if (0 > $result) { |
59 | 59 |
$::auth->punish_wrong_login; |
60 |
return $self->render('login_screen/user_login', { no_menu => 1 }, error => $::locale->text('Incorrect username or password!'));
|
|
60 |
return $self->render('login_screen/user_login', error => $::locale->text('Incorrect username or password!')); |
|
61 | 61 |
} |
62 | 62 |
|
63 | 63 |
# Everything is fine. |
SL/Controller/Part.pm | ||
---|---|---|
23 | 23 |
$self->{parts} = SL::DB::Manager::Part->get_all(query => [ @filter ], limit => $limit); |
24 | 24 |
$self->{value} = $::form->{column} || 'description'; |
25 | 25 |
|
26 |
$self->render('part/ajax_autocomplete', { no_layout => 1 });
|
|
26 |
$self->render('part/ajax_autocomplete', { layout => 0, type => 'json' });
|
|
27 | 27 |
} |
28 | 28 |
|
29 | 29 |
|
SL/Controller/PaymentTerm.pm | ||
---|---|---|
70 | 70 |
|
71 | 71 |
SL::DB::PaymentTerm->reorder_list(@{ $::form->{payment_term_id} || [] }); |
72 | 72 |
|
73 |
$self->render('1;', { type => 'js', inline => 1 });
|
|
73 |
$self->render(\'', { type => 'json' });
|
|
74 | 74 |
} |
75 | 75 |
|
76 | 76 |
# |
SL/Controller/PriceFactor.pm | ||
---|---|---|
17 | 17 |
|
18 | 18 |
SL::DB::PriceFactor->reorder_list(@{ $::form->{price_factor_id} || [] }); |
19 | 19 |
|
20 |
$self->render('1;', { type => 'js', inline => 1 });
|
|
20 |
$self->render(\'', { type => 'json' });
|
|
21 | 21 |
} |
22 | 22 |
|
23 | 23 |
# |
SL/Controller/Project.pm | ||
---|---|---|
262 | 262 |
controller_class => 'Project', |
263 | 263 |
output_format => 'HTML', |
264 | 264 |
top_info_text => $::locale->text('Projects'), |
265 |
raw_bottom_info_text => $self->render('project/report_bottom', { no_output => 1, partial => 1 }),
|
|
265 |
raw_bottom_info_text => $self->render('project/report_bottom', { output => 0 }),
|
|
266 | 266 |
title => $::locale->text('Projects'), |
267 | 267 |
allow_pdf_export => 1, |
268 | 268 |
allow_csv_export => 1, |
SL/Controller/Unit.pm | ||
---|---|---|
17 | 17 |
|
18 | 18 |
SL::DB::Unit->reorder_list(@{ $::form->{unit_id} || [] }); |
19 | 19 |
|
20 |
$self->render('1;', { type => 'js', inline => 1 });
|
|
20 |
$self->render(\'', { type => 'json' });
|
|
21 | 21 |
} |
22 | 22 |
|
23 | 23 |
# |
SL/Controller/Warehouse.pm | ||
---|---|---|
17 | 17 |
|
18 | 18 |
SL::DB::Warehouse->reorder_list(@{ $::form->{warehouse_id} || [] }); |
19 | 19 |
|
20 |
$self->render('1;', { type => 'js', inline => 1 });
|
|
20 |
$self->render(\'', { type => 'json' });
|
|
21 | 21 |
} |
22 | 22 |
|
23 | 23 |
# |
SL/Dispatcher.pm | ||
---|---|---|
82 | 82 |
$::form->{error} = $::locale->text('The session is invalid or has expired.') if ($error_type eq 'session'); |
83 | 83 |
$::form->{error} = $::locale->text('Incorrect password!') if ($error_type eq 'password'); |
84 | 84 |
|
85 |
$::form->header(no_menu => 1);
|
|
85 |
$::form->header; |
|
86 | 86 |
print $::form->parse_html_template($template, \%params); |
87 | 87 |
$::lxdebug->leave_sub; |
88 | 88 |
|
SL/JSON.pm | ||
---|---|---|
1 |
package SL::JSON; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use JSON (); |
|
6 |
|
|
7 |
use parent qw(Exporter); |
|
8 |
our @EXPORT = qw(encode_json decode_json to_json from_json); |
|
9 |
|
|
10 |
sub new { |
|
11 |
shift; |
|
12 |
return JSON->new(@_)->convert_blessed(1); |
|
13 |
} |
|
14 |
|
|
15 |
sub encode_json { |
|
16 |
return JSON->new->convert_blessed(1)->encode(@_); |
|
17 |
} |
|
18 |
|
|
19 |
sub decode_json { |
|
20 |
goto &JSON::decode_json; |
|
21 |
} |
|
22 |
|
|
23 |
sub to_json { |
|
24 |
my ($object, $options) = @_; |
|
25 |
$options ||= {}; |
|
26 |
$options->{convert_blessed} = 1; |
|
27 |
return JSON::to_json($object, $options); |
|
28 |
} |
|
29 |
|
|
30 |
sub from_json { |
|
31 |
goto &JSON::decode_json; |
|
32 |
} |
|
33 |
|
|
34 |
1; |
|
35 |
__END__ |
|
36 |
|
|
37 |
=pod |
|
38 |
|
|
39 |
=encoding utf8 |
|
40 |
|
|
41 |
=head1 NAME |
|
42 |
|
|
43 |
SL::JSON - Thin wrapper around the JSON module that provides default options |
|
44 |
|
|
45 |
=head1 SYNOPSIS |
|
46 |
|
|
47 |
use SL::JSON; |
|
48 |
|
|
49 |
my $escaped_text_object = SL::Presenter->get->render('some/template'); |
|
50 |
my $json = encode_json($escaped_text_object); |
|
51 |
|
|
52 |
=head1 OVERVIEW |
|
53 |
|
|
54 |
JSON by default does not dump or stringify blessed |
|
55 |
objects. kivitendo's rendering infrastructure always returns thin |
|
56 |
proxy objects as instances of L<SL::Presenter::EscapedText>. This |
|
57 |
module provides the same functions that L<JSON> does but changes their |
|
58 |
default regarding converting blessed arguments. |
|
59 |
|
|
60 |
=head1 FUNCTIONS |
|
61 |
|
|
62 |
=over 4 |
|
63 |
|
|
64 |
=item C<decode_json $json> |
|
65 |
|
|
66 |
Same as L<JSON/decode_json>. |
|
67 |
|
|
68 |
=item C<encode_json $object> |
|
69 |
|
|
70 |
Same as L<JSON/encode_json> but sets C<convert_blessed> first. |
|
71 |
|
|
72 |
=item C<from_json $object [, $options]> |
|
73 |
|
|
74 |
Same as L<JSON/from_json>. |
|
75 |
|
|
76 |
=item C<to_json $object [, $options ]> |
|
77 |
|
|
78 |
Same as L<JSON/to_json> but sets C<convert_blessed> first. |
|
79 |
|
|
80 |
=back |
|
81 |
|
|
82 |
=head1 BUGS |
|
83 |
|
|
84 |
Nothing here yet. |
|
85 |
|
|
86 |
=head1 AUTHOR |
|
87 |
|
|
88 |
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt> |
|
89 |
|
|
90 |
=cut |
SL/Layout/Javascript.pm | ||
---|---|---|
47 | 47 |
$callback = URI->new($callback)->rel($callback) if $callback; |
48 | 48 |
$callback = "login.pl?action=company_logo" if $callback =~ /^(\.\/)?$/; |
49 | 49 |
|
50 |
$self->render("menu/menunew", { partial => 1, no_output => 1 },
|
|
50 |
$self->render("menu/menunew", { output => 0 },
|
|
51 | 51 |
force_ul_width => 1, |
52 | 52 |
date => $self->clock_line, |
53 | 53 |
menu_items => $self->acc_menu, |
SL/Layout/MenuLeft.pm | ||
---|---|---|
14 | 14 |
sub javascripts_inline { |
15 | 15 |
my $self = shift; |
16 | 16 |
my $sections = [ section_menu($self->menu) ]; |
17 |
$self->render('menu/menu', { partial => 1, no_output => 1 },
|
|
17 |
$self->render('menu/menu', { output => 0 },
|
|
18 | 18 |
sections => $sections, |
19 | 19 |
) |
20 | 20 |
} |
SL/Layout/Top.pm | ||
---|---|---|
6 | 6 |
sub pre_content { |
7 | 7 |
my ($self) = @_; |
8 | 8 |
|
9 |
$self->SUPER::render('menu/header', { partial => 1, no_output => 1 },
|
|
9 |
$self->SUPER::render('menu/header', { output => 0 },
|
|
10 | 10 |
now => DateTime->now_local, |
11 | 11 |
is_fastcgi => scalar($::dispatcher->interface_type =~ /fastcgi/i), |
12 | 12 |
is_links => scalar($ENV{HTTP_USER_AGENT} =~ /links/i)); |
SL/Layout/V3.pm | ||
---|---|---|
164 | 164 |
$callback = URI->new($callback)->rel($callback) if $callback; |
165 | 165 |
$callback = "login.pl?action=company_logo" if $callback =~ /^(\.\/)?$/; |
166 | 166 |
|
167 |
$self->SUPER::render('menu/menuv3', { no_menu => 1, no_output => 1 },
|
|
167 |
$self->SUPER::render('menu/menuv3', { output => 0 },
|
|
168 | 168 |
force_ul_width => 1, |
169 | 169 |
date => $self->clock_line, |
170 | 170 |
menu => $self->print_menu, |
SL/Presenter.pm | ||
---|---|---|
25 | 25 |
my $template = shift; |
26 | 26 |
my ($options, %locals) = (@_ && ref($_[0])) ? @_ : ({ }, @_); |
27 | 27 |
|
28 |
$options->{type} = lc($options->{type} || 'html'); |
|
28 |
# Set defaults for all available options. |
|
29 |
my %defaults = ( |
|
30 |
type => 'html', |
|
31 |
process => 1, |
|
32 |
); |
|
33 |
$options->{$_} //= $defaults{$_} for keys %defaults; |
|
34 |
$options->{type} = lc $options->{type}; |
|
29 | 35 |
|
30 |
my $source; |
|
31 |
if ($options->{inline}) { |
|
32 |
$source = \$template; |
|
36 |
# Check supplied options for validity. |
|
37 |
foreach (keys %{ $options }) { |
|
38 |
croak "Unsupported option: $_" unless $defaults{$_}; |
|
39 |
} |
|
33 | 40 |
|
34 |
} else { |
|
41 |
# Only certain types are supported. |
|
42 |
croak "Unsupported type: " . $options->{type} unless $options->{type} =~ m/^(?:html|js|json)$/; |
|
43 |
|
|
44 |
# The "template" argument must be a string or a reference to one. |
|
45 |
croak "Unsupported 'template' reference type: " . ref($template) if ref($template) && (ref($template) !~ m/^(?:SCALAR|SL::Presenter::EscapedText)$/); |
|
46 |
|
|
47 |
# Look for the file given by $template if $template is not a reference. |
|
48 |
my $source; |
|
49 |
if (!ref $template) { |
|
35 | 50 |
$source = "templates/webpages/${template}." . $options->{type}; |
36 | 51 |
croak "Template file ${source} not found" unless -f $source; |
52 |
|
|
53 |
} elsif (ref($template) eq 'SCALAR') { |
|
54 |
# Normal scalar reference: hand over to Template |
|
55 |
$source = $template; |
|
56 |
|
|
57 |
} else { |
|
58 |
# Instance of SL::Presenter::EscapedText. Get reference to its content. |
|
59 |
$source = \$template->{text}; |
|
60 |
} |
|
61 |
|
|
62 |
# If no processing is requested then return the content. |
|
63 |
if (!$options->{process}) { |
|
64 |
# If $template is a reference then don't try to read a file. |
|
65 |
return SL::Presenter::EscapedText->new(text => ${ $template }, is_escaped => 1) if ref $template; |
|
66 |
|
|
67 |
# Otherwise return the file's content. |
|
68 |
my $file = IO::File->new($source, "r") || croak("Template file ${source} could not be read"); |
|
69 |
my $content = do { local $/ = ''; <$file> }; |
|
70 |
$file->close; |
|
71 |
|
|
72 |
return SL::Presenter::EscapedText->new(text => $content, is_escaped => 1); |
|
37 | 73 |
} |
38 | 74 |
|
75 |
# Processing was requested. Set up all variables. |
|
39 | 76 |
my %params = ( %locals, |
40 | 77 |
AUTH => $::auth, |
41 | 78 |
FLASH => $::form->{FLASH}, |
... | ... | |
147 | 184 |
all of the parameters for controlling the output that the controller's |
148 | 185 |
function does. |
149 | 186 |
|
150 |
What is rendered and how C<$template> is interpreted is determined by |
|
151 |
the options I<type> and I<inline>. |
|
187 |
What is rendered and how C<$template> is interpreted is determined |
|
188 |
both by C<$template>'s reference type and by the supplied options. |
|
189 |
|
|
190 |
If C<$template> is a normal scalar (not a reference) then it is meant |
|
191 |
to be a template file name relative to the C<templates/webpages> |
|
192 |
directory. The file name to use is determined by the C<type> option. |
|
152 | 193 |
|
153 |
If C<< $options->{inline} >> is trueish then C<$template> is a string |
|
154 |
containing the template code to interprete. |
|
194 |
If C<$template> is a reference to a scalar then the referenced |
|
195 |
scalar's content is used as the content to process. The C<type> option |
|
196 |
is not considered in this case. |
|
155 | 197 |
|
156 |
If C<< $options->{inline} >> is falsish then C<$template> is |
|
157 |
interpreted as the name of a template file. It is prefixed with |
|
158 |
"templates/webpages/" and postfixed with a file extension based on |
|
159 |
C<< $options->{type} >>. C<< $options->{type} >> can be either C<html> |
|
160 |
or C<js> and defaults to C<html>. An exception will be thrown if that |
|
161 |
file does not exist. |
|
198 |
Other reference types, unknown options and unknown arguments to the |
|
199 |
C<type> option cause the function to L<croak>. |
|
162 | 200 |
|
163 |
The template itself has access to the following variables: |
|
201 |
The following options are available: |
|
202 |
|
|
203 |
=over 2 |
|
204 |
|
|
205 |
=item C<type> |
|
206 |
|
|
207 |
The template type. Can be C<html> (the default), C<js> for JavaScript |
|
208 |
or C<json> for JSON content. Affects only the extension that's added |
|
209 |
to the file name given with a non-reference C<$template> argument. |
|
210 |
|
|
211 |
=item C<process> |
|
212 |
|
|
213 |
If trueish (which is also the default) it causes the template/content |
|
214 |
to be processed by the Template toolkit. Otherwise the |
|
215 |
template/content is returned as-is. |
|
216 |
|
|
217 |
=back |
|
218 |
|
|
219 |
If template processing is requested then the template has access to |
|
220 |
the following variables: |
|
164 | 221 |
|
165 | 222 |
=over 2 |
166 | 223 |
|
... | ... | |
197 | 254 |
Example: Render a string and return its content for further processing |
198 | 255 |
by the calling function. |
199 | 256 |
|
200 |
my $content = $presenter->render( |
|
201 |
'[% USE JavaScript %][% JavaScript.replace_with("#someid", "js/something") %]', |
|
202 |
{ type => 'js', inline => 1 } |
|
257 |
my $content = $presenter->render(\'[% USE JavaScript %][% JavaScript.replace_with("#someid", "js/something") %]'); |
|
258 |
|
|
259 |
Example: Return the content of a JSON template file without processing |
|
260 |
it at all: |
|
261 |
|
|
262 |
my $template_content = $presenter->render( |
|
263 |
'customer/contact', |
|
264 |
{ type => 'json', process => 0 } |
|
203 | 265 |
); |
204 | 266 |
|
205 | 267 |
=item C<escape $text> |
SL/Presenter/EscapedText.pm | ||
---|---|---|
2 | 2 |
|
3 | 3 |
use strict; |
4 | 4 |
|
5 |
use JSON (); |
|
6 |
|
|
5 | 7 |
use overload '""' => \&escaped; |
6 | 8 |
|
7 | 9 |
sub new { |
... | ... | |
20 | 22 |
return $self->{text}; |
21 | 23 |
} |
22 | 24 |
|
25 |
sub TO_JSON { |
|
26 |
goto &escaped; |
|
27 |
} |
|
28 |
|
|
23 | 29 |
1; |
24 | 30 |
__END__ |
25 | 31 |
|
SL/Template/Plugin/JSON.pm | ||
---|---|---|
21 | 21 |
my ($self, %params) = @_; |
22 | 22 |
|
23 | 23 |
if (!$self->{json}) { |
24 |
$self->{json} = JSON->new->allow_nonref(1); |
|
24 |
$self->{json} = JSON->new->allow_nonref(1)->convert_blessed(1);
|
|
25 | 25 |
|
26 | 26 |
my $args = $self->{json_args}; |
27 | 27 |
|
t/controllers/base/render.t | ||
---|---|---|
1 |
use strict; |
|
2 |
use Test::Exception; |
|
3 |
use Test::More; |
|
4 |
use Test::Output; |
|
5 |
|
|
6 |
use lib 't'; |
|
7 |
use Support::TestSetup; |
|
8 |
|
|
9 |
use SL::Presenter; |
|
10 |
|
|
11 |
no warnings 'uninitialized'; |
|
12 |
|
|
13 |
Support::TestSetup::login(); |
|
14 |
|
|
15 |
sub reset_test_env { |
|
16 |
$ENV{HTTP_USER_AGENT} = 'Perl Tests'; |
|
17 |
|
|
18 |
$::request = { |
|
19 |
cgi => CGI->new({}), |
|
20 |
layout => SL::Layout::Javascript->new, |
|
21 |
}; |
|
22 |
|
|
23 |
$::myconfig{stylesheet} = 'javascript'; |
|
24 |
|
|
25 |
delete @{ $::form }{qw(header footer)}; |
|
26 |
} |
|
27 |
|
|
28 |
my $ctrl = SL::Controller::Base->new; |
|
29 |
|
|
30 |
# Passing invalid parameters: |
|
31 |
throws_ok { $ctrl->render(\'dummy', { unknown => 1 }) } qr/unsupported option/i, 'string ref, unknown parameter'; |
|
32 |
throws_ok { $ctrl->render(\'dummy', { type => "excel" }) } qr/unsupported type/i, 'string ref, unsupported "type"'; |
|
33 |
throws_ok { $ctrl->render({}) } qr/unsupported.*template.*reference.*type/i, 'string ref, unsupported template argument reference type'; |
|
34 |
throws_ok { $ctrl->render('does/not/exist') } qr/template.*file.*not.*found/i, 'non-existing template file name'; |
|
35 |
|
|
36 |
# No output: |
|
37 |
stdout_is { $ctrl->render(\'Hallo', { output => 0 }) } '', 'no output'; |
|
38 |
|
|
39 |
# Type of return value: |
|
40 |
is(ref($ctrl->render(\'Hallo', { output => 0 })), 'SL::Presenter::EscapedText', 'render returns SL::Presenter::EscapedText'); |
|
41 |
|
|
42 |
# Actual return value for string ref parameters (enforce stringification from SL::Presenter::EscapedText before comparison): |
|
43 |
is("" . $ctrl->render(\'Hallo [% world %]', { output => 0 }, world => 'Welt'), 'Hallo Welt', 'render string ref, no output'); |
|
44 |
is("" . $ctrl->render(\'Hallo [% world %]', { output => 0, process => 0 }, world => 'Welt'), 'Hallo [% world %]', 'render string ref, no output, no processing'); |
|
45 |
is("" . $ctrl->render(\'Hallo [% world %]', { output => 0, type => 'js' }, world => 'Welt'), 'Hallo Welt', 'render string ref, no output, different type'); |
|
46 |
|
|
47 |
# Actual return value for template file name parameters (enforce stringification from SL::Presenter::EscapedText before comparison): |
|
48 |
is("" . $ctrl->render('t/render', { output => 0 }, world => 'Welt'), "Hallo Welt\n", 'render template file, no args'); |
|
49 |
is("" . $ctrl->render('t/render', { output => 0, process => 0 }, world => 'Welt'), "[\% USE HTML \%]Hallo [\% HTML.escape(world) \%]\n", 'render template file, no processing'); |
|
50 |
is("" . $ctrl->render('t/render', { output => 0, type => 'js' }, thingy => 'jungle'), "Welcome to the jungle\n", 'render template file, different type'); |
|
51 |
|
|
52 |
# No HTTP header in screen output: |
|
53 |
reset_test_env(); |
|
54 |
stdout_unlike { $ctrl->render(\'Hallo [% world %]', { header => 0 }, world => 'Welt') } qr/content-type/i, 'no HTTP header with header=0'; |
|
55 |
|
|
56 |
reset_test_env(); |
|
57 |
stdout_unlike { $ctrl->render(\'Hallo [% world %]', { header => 0 }, world => 'Welt') } qr/<html>/i, 'no HTML header with header=0'; |
|
58 |
|
|
59 |
# With HTTP header in screen output: |
|
60 |
reset_test_env(); |
|
61 |
stdout_like { $ctrl->render(\'Hallo [% world %]', world => 'Welt') } qr/content-type/i, 'HTTP header with header=1'; |
|
62 |
|
|
63 |
reset_test_env(); |
|
64 |
stdout_like { $ctrl->render(\'Hallo [% world %]', world => 'Welt') } qr/<html>/i, 'HTML header with header=1'; |
|
65 |
|
|
66 |
# Menu yes/no: |
|
67 |
reset_test_env(); |
|
68 |
stdout_like { $ctrl->render(\'Hallo [% world %]', world => 'Welt') } qr/<table.*class=.*menunew/i, 'HTML header & menu with header=1'; |
|
69 |
|
|
70 |
reset_test_env(); |
|
71 |
stdout_unlike { $ctrl->render(\'Hallo [% world %]', { header => 0 }, world => 'Welt') } qr/<table.*class=.*menunew/i, 'HTML header & menu with header=0'; |
|
72 |
|
|
73 |
reset_test_env(); |
|
74 |
stdout_unlike { $ctrl->render(\'Hallo [% world %]', { layout => 0 }, world => 'Welt') } qr/<table.*class=.*menunew/i, 'HTML header & menu with layout=0'; |
|
75 |
|
|
76 |
done_testing; |
|
77 |
|
|
78 |
1; |
t/presenter/base/render.t | ||
---|---|---|
1 |
use strict; |
|
2 |
use Test::Exception; |
|
3 |
use Test::More; |
|
4 |
|
|
5 |
use lib 't'; |
|
6 |
use Support::TestSetup; |
|
7 |
|
|
8 |
use SL::Presenter; |
|
9 |
|
|
10 |
Support::TestSetup::login(); |
|
11 |
|
|
12 |
my $pr = SL::Presenter->get; |
|
13 |
|
|
14 |
# Passing invalid parameters: |
|
15 |
throws_ok { $pr->render(\'dummy', { unknown => 1 }) } qr/unsupported option/i, 'string ref, unknown parameter'; |
|
16 |
throws_ok { $pr->render(\'dummy', { type => "excel" }) } qr/unsupported type/i, 'string ref, unsupported "type"'; |
|
17 |
throws_ok { $pr->render({}) } qr/unsupported.*template.*reference.*type/i, 'string ref, unsupported template argument reference type'; |
|
18 |
throws_ok { $pr->render('does/not/exist') } qr/template.*file.*not.*found/i, 'non-existing template file name'; |
|
19 |
|
|
20 |
# Type of return value: |
|
21 |
is(ref($pr->render(\'Hallo')), 'SL::Presenter::EscapedText', 'render returns SL::Presenter::EscapedText'); |
|
22 |
|
|
23 |
# Actual return value for string ref parameters (enforce stringification from SL::Presenter::EscapedText before comparison): |
|
24 |
is("" . $pr->render(\'Hallo [% world %]', world => 'Welt'), 'Hallo Welt', 'render string ref, no args'); |
|
25 |
is("" . $pr->render(\'Hallo [% world %]', { process => 0 }, world => 'Welt'), 'Hallo [% world %]', 'render string ref, no processing'); |
|
26 |
is("" . $pr->render(\'Hallo [% world %]', { type => 'js' }, world => 'Welt'), 'Hallo Welt', 'render string ref, different type'); |
|
27 |
|
|
28 |
# Actual return value for template file name parameters (enforce stringification from SL::Presenter::EscapedText before comparison): |
|
29 |
is("" . $pr->render('t/render', world => 'Welt'), "Hallo Welt\n", 'render template file, no args'); |
|
30 |
is("" . $pr->render('t/render', { process => 0 }, world => 'Welt'), "[\% USE HTML \%]Hallo [\% HTML.escape(world) \%]\n", 'render template file, no processing'); |
|
31 |
is("" . $pr->render('t/render', { type => 'js' }, thingy => 'jungle'), "Welcome to the jungle\n", 'render template file, different type'); |
|
32 |
|
|
33 |
done_testing; |
|
34 |
|
|
35 |
1; |
t/test.sh | ||
---|---|---|
6 | 6 |
else |
7 | 7 |
echo -- "$@" |
8 | 8 |
fi |
9 |
} | HARNESS_OPTIONS=j:c xargs perl -Imodules/override -MTest::Harness -e 'BEGIN { push @INC, "modules/fallback" } runtests(@ARGV)' |
|
9 |
} | HARNESS_OPTIONS=j:c xargs perl -X -Imodules/override -MTest::Harness -e 'BEGIN { push @INC, "modules/fallback" } runtests(@ARGV)' |
templates/webpages/ct/ajax_autocomplete2.html | ||
---|---|---|
1 |
[%- USE HTML %][% USE JSON %][ |
|
2 |
[%- FOREACH customer = SELF.customers %] |
|
3 |
{ |
|
4 |
"value": [% customer.${SELF.value}.json %], |
|
5 |
"label": [% customer.displayable_name.json %], |
|
6 |
"id": [% customer.id.json %], |
|
7 |
"customernumber": [% customer.customernumber.json %], |
|
8 |
"name": [% customer.name.json %] |
|
9 |
}[% ',' UNLESS loop.last %] |
|
10 |
[%- END %] |
|
11 |
] |
templates/webpages/ct/ajax_autocomplete2.json | ||
---|---|---|
1 |
[%- USE HTML %][% USE JSON %][ |
|
2 |
[%- FOREACH customer = SELF.customers %] |
|
3 |
{ |
|
4 |
"value": [% customer.${SELF.value}.json %], |
|
5 |
"label": [% customer.displayable_name.json %], |
|
6 |
"id": [% customer.id.json %], |
|
7 |
"customernumber": [% customer.customernumber.json %], |
|
8 |
"name": [% customer.name.json %] |
|
9 |
}[% ',' UNLESS loop.last %] |
|
10 |
[%- END %] |
|
11 |
] |
templates/webpages/part/ajax_autocomplete.html | ||
---|---|---|
1 |
[%- USE HTML %][% USE JSON %][ |
|
2 |
[%- FOREACH part = SELF.parts %] |
|
3 |
[%- ajax_autocomplete__label = part.partnumber _ " " _ part.description %] |
|
4 |
{ |
|
5 |
"value": [% part.${SELF.value}.json %], |
|
6 |
"label": [% ajax_autocomplete__label.json %], |
|
7 |
"id": [% part.id.json %], |
|
8 |
"partnumber": [% part.partnumber.json %], |
|
9 |
"description": [% part.description.json %], |
|
10 |
"type": [% part.type.json %] |
|
11 |
}[% ',' UNLESS loop.last %] |
|
12 |
[%- END %] |
|
13 |
] |
templates/webpages/part/ajax_autocomplete.json | ||
---|---|---|
1 |
[%- USE HTML %][% USE JSON %][ |
|
2 |
[%- FOREACH part = SELF.parts %] |
|
3 |
[%- ajax_autocomplete__label = part.partnumber _ " " _ part.description %] |
|
4 |
{ |
|
5 |
"value": [% part.${SELF.value}.json %], |
|
6 |
"label": [% ajax_autocomplete__label.json %], |
|
7 |
"id": [% part.id.json %], |
|
8 |
"partnumber": [% part.partnumber.json %], |
|
9 |
"description": [% part.description.json %], |
|
10 |
"type": [% part.type.json %] |
|
11 |
}[% ',' UNLESS loop.last %] |
|
12 |
[%- END %] |
|
13 |
] |
templates/webpages/t/render.html | ||
---|---|---|
1 |
[% USE HTML %]Hallo [% HTML.escape(world) %] |
templates/webpages/t/render.js | ||
---|---|---|
1 |
Welcome to the [% thingy %] |
Auch abrufbar als: Unified diff
Refactoring: Parameterredesign SL::Controller::Base::render und SL::Presenter::render
Conflicts:
SL/Controller/FinancialControllingReport.pm
SL/Controller/ProjectType.pm