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