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