Verknüpfte Dokumente: Auch SEPA-Überweisungen/-Einzüge anzeigen
[kivitendo-erp.git] / SL / Presenter.pm
1 package SL::Presenter;
2
3 use strict;
4
5 use parent qw(Rose::Object);
6
7 use Carp;
8 use Template;
9
10 use SL::Presenter::CustomerVendor;
11 use SL::Presenter::DeliveryOrder;
12 use SL::Presenter::EscapedText;
13 use SL::Presenter::Invoice;
14 use SL::Presenter::Order;
15 use SL::Presenter::Project;
16 use SL::Presenter::Record;
17 use SL::Presenter::SepaExport;
18 use SL::Presenter::Text;
19 use SL::Presenter::Tag;
20
21 sub get {
22   return $::request->presenter;
23 }
24
25 sub render {
26   my $self               = shift;
27   my $template           = shift;
28   my ($options, %locals) = (@_ && ref($_[0])) ? @_ : ({ }, @_);
29
30   # Set defaults for all available options.
31   my %defaults = (
32     type       => 'html',
33     process    => 1,
34   );
35   $options->{$_} //= $defaults{$_} for keys %defaults;
36   $options->{type} = lc $options->{type};
37
38   # Check supplied options for validity.
39   foreach (keys %{ $options }) {
40     croak "Unsupported option: $_" unless $defaults{$_};
41   }
42
43   # Only certain types are supported.
44   croak "Unsupported type: " . $options->{type} unless $options->{type} =~ m/^(?:html|js|json|text)$/;
45
46   # The "template" argument must be a string or a reference to one.
47   $template = ${ $template }                                       if ((ref($template) || '') eq 'REF') && (ref(${ $template }) eq 'SL::Presenter::EscapedText');
48   croak "Unsupported 'template' reference type: " . ref($template) if ref($template) && (ref($template) !~ m/^(?:SCALAR|SL::Presenter::EscapedText)$/);
49
50   # Look for the file given by $template if $template is not a reference.
51   my $source;
52   if (!ref $template) {
53     my $ext = $options->{type} eq 'text' ? 'txt' : $options->{type};
54     $source = "templates/webpages/${template}.${ext}";
55     croak "Template file ${source} not found" unless -f $source;
56
57   } elsif (ref($template) eq 'SCALAR') {
58     # Normal scalar reference: hand over to Template
59     $source = $template;
60
61   } else {
62     # Instance of SL::Presenter::EscapedText. Get reference to its content.
63     $source = \$template->{text};
64   }
65
66   # If no processing is requested then return the content.
67   if (!$options->{process}) {
68     # If $template is a reference then don't try to read a file.
69     my $ref = ref $template;
70     return $template                                                                if $ref eq 'SL::Presenter::EscapedText';
71     return SL::Presenter::EscapedText->new(text => ${ $template }, is_escaped => 1) if $ref eq 'SCALAR';
72
73     # Otherwise return the file's content.
74     my $file    = IO::File->new($source, "r") || croak("Template file ${source} could not be read");
75     my $content = do { local $/ = ''; <$file> };
76     $file->close;
77
78     return SL::Presenter::EscapedText->new(text => $content, is_escaped => 1);
79   }
80
81   # Processing was requested. Set up all variables.
82   my %params = ( %locals,
83                  AUTH          => $::auth,
84                  FLASH         => $::form->{FLASH},
85                  FORM          => $::form,
86                  INSTANCE_CONF => $::instance_conf,
87                  LOCALE        => $::locale,
88                  LXCONFIG      => \%::lx_office_conf,
89                  LXDEBUG       => $::lxdebug,
90                  MYCONFIG      => \%::myconfig,
91                  PRESENTER     => $self,
92                );
93
94   my $output;
95   my $parser = $self->get_template;
96   $parser->process($source, \%params, \$output) || croak $parser->error;
97
98   return SL::Presenter::EscapedText->new(text => $output, is_escaped => 1);
99 }
100
101 sub get_template {
102   my ($self) = @_;
103
104   $self->{template} ||=
105     Template->new({ INTERPOLATE  => 0,
106                     EVAL_PERL    => 0,
107                     ABSOLUTE     => 1,
108                     CACHE_SIZE   => 0,
109                     PLUGIN_BASE  => 'SL::Template::Plugin',
110                     INCLUDE_PATH => '.:templates/webpages',
111                     COMPILE_EXT  => '.tcc',
112                     COMPILE_DIR  => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
113                     ERROR        => 'templates/webpages/generic/exception.html',
114                   }) || croak;
115
116   return $self->{template};
117 }
118
119 sub escape {
120   my ($self, $text) = @_;
121
122   return SL::Presenter::EscapedText->new(text => $text);
123 }
124
125 sub escaped_text {
126   my ($self, $text) = @_;
127
128   return SL::Presenter::EscapedText->new(text => $text, is_escaped => 1);
129 }
130
131 sub escape_js {
132   my ($self, $text) = @_;
133
134   $text =~ s|\\|\\\\|g;
135   $text =~ s|\"|\\\"|g;
136   $text =~ s|\n|\\n|g;
137
138   return SL::Presenter::EscapedText->new(text => $text, is_escaped => 1);
139 }
140
141 1;
142
143 __END__
144
145 =head1 NAME
146
147 SL::Presenter - presentation layer class
148
149 =head1 SYNOPSIS
150
151   use SL::Presenter;
152   my $presenter = SL::Presenter->get;
153
154   # Lower-level template parsing:
155   my $html = $presenter->render(
156     'presenter/dir/template.html',
157     var1 => 'value',
158   );
159
160   # Higher-level rendering of certain objects:
161   use SL::DB::Customer;
162
163   my $linked_customer_name = $presenter->customer($customer, display => 'table-cell');
164
165   # Render a list of links to sales/purchase records:
166   use SL::DB::Order;
167
168   my $quotation = SL::DB::Manager::Order->get_first(where => { quotation => 1 });
169   my $records   = $quotation->linked_records(direction => 'to');
170   my $html      = $presenter->grouped_record_list($records);
171
172 =head1 CLASS FUNCTIONS
173
174 =over 4
175
176 =item C<get>
177
178 Returns the global presenter object and creates it if it doesn't exist
179 already.
180
181 =back
182
183 =head1 INSTANCE FUNCTIONS
184
185 =over 4
186
187 =item C<render $template, [ $options, ] %locals>
188
189 Renders the template C<$template>. Provides other variables than
190 C<Form::parse_html_template> does.
191
192 C<$options>, if present, must be a hash reference. All remaining
193 parameters are slurped into C<%locals>.
194
195 This is the backend function that L<SL::Controller::Base/render>
196 calls. The big difference is that the presenter's L<render> function
197 always returns the input and never sends anything to the browser while
198 the controller's function usually sends the result to the
199 controller. Therefore the presenter's L<render> function does not use
200 all of the parameters for controlling the output that the controller's
201 function does.
202
203 What is rendered and how C<$template> is interpreted is determined
204 both by C<$template>'s reference type and by the supplied options.
205
206 If C<$template> is a normal scalar (not a reference) then it is meant
207 to be a template file name relative to the C<templates/webpages>
208 directory. The file name to use is determined by the C<type> option.
209
210 If C<$template> is a reference to a scalar then the referenced
211 scalar's content is used as the content to process. The C<type> option
212 is not considered in this case.
213
214 C<$template> can also be an instance of L<SL::Presenter::EscapedText>
215 or a reference to such an instance. Both of these cases are handled
216 the same way as if C<$template> were a reference to a scalar: its
217 content is processed, and C<type> is not considered.
218
219 Other reference types, unknown options and unknown arguments to the
220 C<type> option cause the function to L<croak>.
221
222 The following options are available:
223
224 =over 2
225
226 =item C<type>
227
228 The template type. Can be C<html> (the default), C<js> for JavaScript,
229 C<json> for JSON and C<text> for plain text content. Affects only the
230 extension that's added to the file name given with a non-reference
231 C<$template> argument.
232
233 =item C<process>
234
235 If trueish (which is also the default) it causes the template/content
236 to be processed by the Template toolkit. Otherwise the
237 template/content is returned as-is.
238
239 =back
240
241 If template processing is requested then the template has access to
242 the following variables:
243
244 =over 2
245
246 =item * C<AUTH> -- C<$::auth>
247
248 =item * C<FORM> -- C<$::form>
249
250 =item * C<LOCALE> -- C<$::locale>
251
252 =item * C<LXCONFIG> -- all parameters from C<config/kivitendo.conf>
253 with the same name they appear in the file (first level is the
254 section, second the actual variable, e.g. C<system.dbcharset>,
255 C<features.webdav> etc)
256
257 =item * C<LXDEBUG> -- C<$::lxdebug>
258
259 =item * C<MYCONFIG> -- C<%::myconfig>
260
261 =item * C<SELF> -- the controller instance
262
263 =item * All items from C<%locals>
264
265 =back
266
267 The function will always return the output and never send anything to
268 the browser.
269
270 Example: Render a HTML template with a certain title and a few locals
271
272   $presenter->render('todo/list',
273                      title      => 'List TODO items',
274                      TODO_ITEMS => SL::DB::Manager::Todo->get_all_sorted);
275
276 Example: Render a string and return its content for further processing
277 by the calling function.
278
279   my $content = $presenter->render(\'[% USE JavaScript %][% JavaScript.replace_with("#someid", "js/something") %]');
280
281 Example: Return the content of a JSON template file without processing
282 it at all:
283
284   my $template_content = $presenter->render(
285     'customer/contact',
286     { type => 'json', process => 0 }
287   );
288
289 =item C<escape $text>
290
291 Returns an HTML-escaped version of C<$text>. Instead of a string an
292 instance of the thin proxy-object L<SL::Presenter::EscapedText> is
293 returned.
294
295 It is safe to call C<escape> on an instance of
296 L<SL::Presenter::EscapedText>. This is a no-op (the same instance will
297 be returned).
298
299 =item C<escaped_text $text>
300
301 Returns an instance of L<SL::Presenter::EscapedText>. C<$text> is
302 assumed to be a string that has already been HTML-escaped.
303
304 It is safe to call C<escaped_text> on an instance of
305 L<SL::Presenter::EscapedText>. This is a no-op (the same instance will
306 be returned).
307
308 =item C<escape_js $text>
309
310 Returns a JavaScript-escaped version of C<$text>. Instead of a string
311 an instance of the thin proxy-object L<SL::Presenter::EscapedText> is
312 returned.
313
314 It is safe to call C<escape> on an instance of
315 L<SL::Presenter::EscapedText>. This is a no-op (the same instance will
316 be returned).
317
318 =item C<get_template>
319
320 Returns the global instance of L<Template> and creates it if it
321 doesn't exist already.
322
323 =back
324
325 =head1 AUTHOR
326
327 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
328
329 =cut