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