198f16e0f53c3dc7544df05474b4195fa94d9849
[kivitendo-erp.git] / SL / Controller / Base.pm
1 package SL::Controller::Base;
2
3 use strict;
4
5 use parent qw(Rose::Object);
6
7 use Carp;
8 use IO::File;
9 use List::Util qw(first);
10
11 #
12 # public/helper functions
13 #
14
15 sub url_for {
16   my $self = shift;
17
18   return $_[0] if (scalar(@_) == 1) && !ref($_[0]);
19
20   my %params      = ref($_[0]) eq 'HASH' ? %{ $_[0] } : @_;
21   my $controller  = delete($params{controller}) || $self->_controller_name;
22   my $action      = delete($params{action})     || 'dispatch';
23   $params{action} = "${controller}/${action}";
24   my $query       = join('&', map { $::form->escape($_) . '=' . $::form->escape($params{$_}) } keys %params);
25
26   return "controller.pl?${query}";
27 }
28
29 sub redirect_to {
30   my $self = shift;
31   my $url  = $self->url_for(@_);
32
33   print $::cgi->redirect($url);
34 }
35
36 sub render {
37   my $self               = shift;
38   my $template           = shift;
39   my ($options, %locals) = (@_ && ref($_[0])) ? @_ : ({ }, @_);
40
41   $options->{type}       = lc($options->{type} || 'html');
42   $options->{no_layout}  = 1 if $options->{type} eq 'js';
43
44   my $source;
45   if ($options->{inline}) {
46     $source = \$template;
47
48   } elsif($options->{raw}) {
49     $source =  $template;
50
51   } else {
52     $source = "templates/webpages/${template}." . $options->{type};
53     croak "Template file ${source} not found" unless -f $source;
54   }
55
56   if (!$options->{partial} && !$options->{inline} && !$::form->{header}) {
57     if ($options->{no_layout}) {
58       $::form->{header} = 1;
59       my $content_type  = $options->{type} eq 'js' ? 'text/javascript' : 'text/html';
60
61       print $::form->create_http_response(content_type => $content_type,
62                                           charset      => $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET());
63
64     } else {
65       $::form->{title} = $locals{title} if $locals{title};
66       $::form->header;
67     }
68   }
69
70   my %params = ( %locals,
71                  AUTH          => $::auth,
72                  FLASH         => $::form->{FLASH},
73                  FORM          => $::form,
74                  INSTANCE_CONF => $::instance_conf,
75                  LOCALE        => $::locale,
76                  LXCONFIG      => \%::lx_office_conf,
77                  LXDEBUG       => $::lxdebug,
78                  MYCONFIG      => \%::myconfig,
79                  SELF          => $self,
80                );
81
82   my $output;
83   if (!$options->{raw}) {
84     my $parser = $self->_template_obj;
85     $parser->process($source, \%params, \$output) || croak $parser->error;
86   } else {
87     $output = $$source;
88   }
89
90   print $output unless $options->{inline} || $options->{no_output};
91
92   return $output;
93 }
94
95 sub send_file {
96   my ($self, $file_name, %params) = @_;
97
98   my $file            = IO::File->new($file_name, 'r') || croak("Cannot open file '${file_name}'");
99   my $content_type    =  $params{type} || 'application/octet_stream';
100   my $attachment_name =  $params{name} || $file_name;
101   $attachment_name    =~ s:.*//::g;
102
103   print $::form->create_http_response(content_type        => $content_type,
104                                       content_disposition => 'attachment; filename="' . $attachment_name . '"',
105                                       content_length      => -s $file);
106
107   $::locale->with_raw_io(\*STDOUT, sub { print while <$file> });
108   $file->close;
109 }
110
111 #
112 # Before/after run hooks
113 #
114
115 sub run_before {
116   _add_hook('before', @_);
117 }
118
119 sub run_after {
120   _add_hook('after', @_);
121 }
122
123 my %hooks;
124
125 sub _add_hook {
126   my ($when, $class, $sub, %params) = @_;
127
128   foreach my $key (qw(only except)) {
129     $params{$key} = { map { ( $_ => 1 ) } @{ $params{$key} } } if $params{$key};
130   }
131
132   my $idx = "${when}/${class}";
133   $hooks{$idx} ||= [ ];
134   push @{ $hooks{$idx} }, { %params, code => $sub };
135 }
136
137 sub _run_hooks {
138   my ($self, $when, $action) = @_;
139
140   my $idx = "${when}/" . ref($self);
141
142   foreach my $hook (@{ $hooks{$idx} || [] }) {
143     next if ($hook->{only  } && !$hook->{only  }->{$action})
144          || ($hook->{except} &&  $hook->{except}->{$action});
145
146     if (ref($hook->{code}) eq 'CODE') {
147       $hook->{code}->($self);
148     } else {
149       my $sub = $hook->{code};
150       $self->$sub;
151     }
152   }
153 }
154
155 #
156 # private functions -- for use in Base only
157 #
158
159 sub _run_action {
160   my $self   = shift;
161   my $action = shift;
162   my $sub    = "action_${action}";
163
164   return $self->_dispatch(@_) if $action eq 'dispatch';
165
166   $::form->error("Invalid action '${action}' for controller " . ref($self)) if !$self->can($sub);
167
168   $self->_run_hooks('before', $action);
169   $self->$sub(@_);
170   $self->_run_hooks('after', $action);
171 }
172
173 sub _controller_name {
174   return (split(/::/, ref($_[0])))[-1];
175 }
176
177 sub _dispatch {
178   my $self    = shift;
179
180   no strict 'refs';
181   my @actions = map { s/^action_//; $_ } grep { m/^action_/ } keys %{ ref($self) . "::" };
182   my $action  = first { $::form->{"action_${_}"} } @actions;
183   my $sub     = "action_${action}";
184
185   if ($self->can($sub)) {
186     $self->_run_hooks('before', $action);
187     $self->$sub(@_);
188     $self->_run_hooks('after', $action);
189   } else {
190     $::form->error($::locale->text('Oops. No valid action found to dispatch. Please report this case to the Lx-Office team.'));
191   }
192 }
193
194 sub _template_obj {
195   my ($self) = @_;
196
197   $self->{__basepriv_template_obj} ||=
198     Template->new({ INTERPOLATE  => 0,
199                     EVAL_PERL    => 0,
200                     ABSOLUTE     => 1,
201                     CACHE_SIZE   => 0,
202                     PLUGIN_BASE  => 'SL::Template::Plugin',
203                     INCLUDE_PATH => '.:templates/webpages',
204                     COMPILE_EXT  => '.tcc',
205                     COMPILE_DIR  => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
206                   }) || croak;
207
208   return $self->{__basepriv_template_obj};
209 }
210
211 1;
212
213 __END__
214
215 =head1 NAME
216
217 SL::Controller::Base - base class for all action controllers
218
219 =head1 SYNOPSIS
220
221 =head2 OVERVIEW
222
223 This is a base class for all action controllers. Action controllers
224 provide subs that are callable by special URLs.
225
226 For each request made to the web server an instance of the controller
227 will be created. After the request has been served that instance will
228 handed over to garbage collection.
229
230 This base class is derived from L<Rose::Object>.
231
232 =head2 CONVENTIONS
233
234 The URLs have the following properties:
235
236 =over 2
237
238 =item *
239
240 The script part of the URL must be C<controller.pl>.
241
242 =item *
243
244 There must be a GET or POST parameter named C<action> containing the
245 name of the controller and the sub to call separated by C</>,
246 e.g. C<Message/list>.
247
248 =item *
249
250 The controller name is the package's name without the
251 C<SL::Controller::> prefix. At the moment only packages in the
252 C<SL::Controller> namespace are valid; sub-namespaces are not
253 allowed. The package name must start with an upper-case letter.
254
255 =item *
256
257 The sub part of the C<action> parameter is the name of the sub to
258 call. However, the sub's name is automatically prefixed with
259 C<action_>. Therefore for the example C<Message/list> the sub
260 C<SL::DB::Message::action_list> would be called. This in turn means
261 that subs whose name does not start with C<action_> cannot be invoked
262 directly via the URL.
263
264 =back
265
266 =head2 INDIRECT DISPATCHING
267
268 In the case that there are several submit buttons on a page it is
269 often impractical to have a single C<action> parameter match up
270 properly. For such a case a special dispatcher method is available. In
271 that case the C<action> parameter of the URL must be
272 C<Controller/dispatch>.
273
274 The C<SL::Controller::Base::_dispatch> method will iterate over all
275 subs in the controller package whose names start with C<action_>. The
276 first one for which there's a GET or POST parameter with the same name
277 and that's trueish is called.
278
279 Usage from a template usually looks like this:
280
281   <form method="POST" action="controller.pl">
282     ...
283     <input type="hidden" name="action" value="Message/dispatch">
284     <input type="submit" name="action_mark_as_read" value="Mark messages as read">
285     <input type="submit" name="action_delete" value="Delete messages">
286   </form>
287
288 The dispatching is handled by the function L</_dispatch>.
289
290 =head2 HOOKS
291
292 Hooks are functions that are called before or after the controller's
293 action is called. The controller package defines the hooks, and those
294 hooks themselves are run as instance methods.
295
296 Hooks are run in the order they're added.
297
298 The return value of the hooks is discarded.
299
300 Hooks can be defined to run for all actions, for only specific actions
301 or for all actions except a list of actions. Each entry is the action
302 name, not the sub's name. Therefore in order to run a hook before one
303 of the subs C<action_edit> or C<action_save> is called the following
304 code can be used:
305
306   __PACKAGE__->run_before('things_to_do_before_edit_and_save', only => [ 'edit', 'save' ]);
307
308 =head1 FUNCTIONS
309
310 =head2 PUBLIC HELPER FUNCTIONS
311
312 These functions are supposed to be called by sub-classed controllers.
313
314 =over 4
315
316 =item C<render $template, [ $options, ] %locals>
317
318 Renders the template C<$template>. Provides other variables than
319 C<Form::parse_html_template> does.
320
321 C<$options>, if present, must be a hash reference. All remaining
322 parameters are slurped into C<%locals>.
323
324 What is rendered and how C<$template> is interpreted is determined by
325 the options I<type>, I<inline>, I<partial> and I<no_layout>.
326
327 If C<< $options->{inline} >> is trueish then C<$template> is a string
328 containing the template code to interprete. Additionally the output
329 will not be sent to the browser. Instead it is only returned to the
330 caller.
331
332 If C<< $options->{raw} >> is trueish, the function will treat the input as
333 already parsed, and will not filter the input through Template. Unlike
334 C<inline>, the input is taked as a reference.
335
336 If C<< $options->{inline} >> is falsish then C<$template> is
337 interpreted as the name of a template file. It is prefixed with
338 "templates/webpages/" and postfixed with a file extension based on
339 C<< $options->{type} >>. C<< $options->{type} >> can be either C<html>
340 or C<js> and defaults to C<html>. An exception will be thrown if that
341 file does not exist.
342
343 If C<< $options->{partial} >> or C<< $options->{inline} >> is trueish
344 then neither the HTTP response header nor the standard HTML header is
345 generated.
346
347 Otherwise at least the HTTP response header will be generated based on
348 the template type (C<< $options->{type} >>).
349
350 If the template type is C<html> then the standard HTML header will be
351 output via C<< $::form->header >> with C<< $::form->{title} >> set to
352 C<$locals{title}> (the latter only if C<$locals{title}> is
353 trueish). Setting C<< $options->{no_layout} >> to trueish will prevent
354 this.
355
356 The template itself has access to the following variables:
357
358 =over 2
359
360 =item * C<AUTH> -- C<$::auth>
361
362 =item * C<FORM> -- C<$::form>
363
364 =item * C<LOCALE> -- C<$::locale>
365
366 =item * C<LXCONFIG> -- all parameters from C<config/lx_office.conf>
367 with the same name they appear in the file (first level is the
368 section, second the actual variable, e.g. C<system.dbcharset>,
369 C<features.webdav> etc)
370
371 =item * C<LXDEBUG> -- C<$::lxdebug>
372
373 =item * C<MYCONFIG> -- C<%::myconfig>
374
375 =item * C<SELF> -- the controller instance
376
377 =item * All items from C<%locals>
378
379 =back
380
381 Unless C<< $options->{inline} >> is trueish the function will send the
382 output to the browser.
383
384 The function will always return the output.
385
386 Example: Render a HTML template with a certain title and a few locals
387
388   $self->render('todo/list',
389                 title      => 'List TODO items',
390                 TODO_ITEMS => SL::DB::Manager::Todo->get_all_sorted);
391
392 Example: Render a string and return its content for further processing
393 by the calling function. No header is generated due to C<inline>.
394
395   my $content = $self->render('[% USE JavaScript %][% JavaScript.replace_with("#someid", "js/something") %]',
396                               { type => 'js', inline => 1 });
397
398 Example: Render a JavaScript template and send it to the
399 browser. Typical use for actions called via AJAX:
400
401   $self->render('todo/single_item', { type => 'js' },
402                 item => $employee->most_important_todo_item);
403
404 =item C<send_file $file_name, [%params]>
405
406 Sends the file C<$file_name> to the browser including appropriate HTTP
407 headers for a download. C<%params> can include the following:
408
409 =over 2
410
411 =item * C<type> -- the file's content type; defaults to
412 'application/octet_stream'
413
414 =item * C<name> -- the name presented to the browser; defaults to
415 C<$file_name>
416
417 =back
418
419 =item C<url_for $url>
420
421 =item C<url_for $params>
422
423 =item C<url_for %params>
424
425 Creates an URL for the given parameters suitable for calling an action
426 controller. If there's only one scalar parameter then it is returned
427 verbatim.
428
429 Otherwise the parameters are given either as a single hash ref
430 parameter or as a normal hash.
431
432 The controller to call is given by C<$params{controller}>. It defaults
433 to the current controller as returned by
434 L</_controller_name>.
435
436 The action to call is given by C<$params{action}>. It defaults to
437 C<dispatch>.
438
439 All other key/value pairs in C<%params> are appended as GET parameters
440 to the URL.
441
442 Usage from a template might look like this:
443
444   <a href="[% SELF.url_for(controller => 'Message', action => 'new', recipient_id => 42) %]">create new message</a>
445
446 =item C<redirect_to %url_params>
447
448 Redirects the browser to a new URL by outputting a HTTP redirect
449 header. The URL is generated by calling L</url_for> with
450 C<%url_params>.
451
452 =item C<run_before $sub, %params>
453
454 =item C<run_after $sub, %params>
455
456 Adds a hook to run before or after certain actions are run for the
457 current package. The code to run is C<$sub> which is either the name
458 of an instance method or a code reference. If it's the latter then the
459 first parameter will be C<$self>.
460
461 C<%params> can contain two possible values that restrict the code to
462 be run only for certain actions:
463
464 =over 2
465
466 =item C<< only => \@list >>
467
468 Only run the code for actions given in C<@list>. The entries are the
469 action names, not the names of the sub (so it's C<list> instead of
470 C<action_list>).
471
472 =item C<< except => \@list >>
473
474 Run the code for all actions but for those given in C<@list>. The
475 entries are the action names, not the names of the sub (so it's
476 C<list> instead of C<action_list>).
477
478 =back
479
480 If neither restriction is used then the code will be run for any
481 action.
482
483 The hook's return values are discarded.
484
485 =back
486
487 =head2 PRIVATE FUNCTIONS
488
489 These functions are supposed to be used from this base class only.
490
491 =over 4
492
493 =item C<_controller_name>
494
495 Returns the name of the curernt controller package without the
496 C<SL::Controller::> prefix.
497
498 =item C<_dispatch>
499
500 Implements the method lookup for indirect dispatching mentioned in the
501 section L</INDIRECT DISPATCHING>.
502
503 =item C<_run_action $action>
504
505 Executes a sub based on the value of C<$action>. C<$action> is the sub
506 name part of the C<action> GET or POST parameter as described in
507 L</CONVENTIONS>.
508
509 If C<$action> equals C<dispatch> then the sub L</_dispatch> in this
510 base class is called for L</INDIRECT DISPATCHING>. Otherwise
511 C<$action> is prefixed with C<action_>, and that sub is called on the
512 current controller instance.
513
514 =back
515
516 =head1 AUTHOR
517
518 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
519
520 =cut