1 package SL::Controller::Base;
5 use parent qw(Rose::Object);
9 use List::Util qw(first);
11 use SL::Request qw(flatten);
12 use SL::MoreCommon qw(uri_encode);
15 use Rose::Object::MakeMethods::Generic
17 scalar => [ qw(action_name) ],
18 'scalar --get_set_init' => [ qw(js p) ],
22 # public/helper functions
28 return $_[0] if (scalar(@_) == 1) && !ref($_[0]);
30 my %params = ref($_[0]) eq 'HASH' ? %{ $_[0] } : @_;
31 my $controller = delete($params{controller}) || $self->controller_name;
32 my $action = $params{action} || 'dispatch';
33 my $fragment = delete $params{fragment};
36 if ($controller =~ m/\.pl$/) {
37 # Old-style controller
38 $script = $controller;
40 $params{action} = "${controller}/${action}";
41 $script = "controller.pl";
44 my $query = join '&', map { uri_encode($_->[0]) . '=' . uri_encode($_->[1]) } @{ flatten(\%params) };
46 return "${script}?${query}" . (defined $fragment ? "#$fragment" : '');
51 my $url = $self->url_for(@_);
53 if ($self->delay_flash_on_redirect) {
54 require SL::Helper::Flash;
55 SL::Helper::Flash::delay_flash();
58 return $self->render(SL::ClientJS->new->redirect_to($url)) if $::request->is_ajax;
60 print $::request->{cgi}->redirect($url);
66 my ($options, %locals) = (@_ && ref($_[0])) ? @_ : ({ }, @_);
68 # Special handling/shortcut for an instance of SL::ClientJS:
69 return $self->render(\$template->to_json, { type => 'json' }) if ref($template) eq 'SL::ClientJS';
71 # Set defaults for all available options.
80 $options->{$_} //= $defaults{$_} for keys %defaults;
81 $options->{type} = lc $options->{type};
83 # Check supplied options for validity.
84 foreach (keys %{ $options }) {
85 croak "Unsupported option: $_" unless $defaults{$_};
88 # Only certain types are supported.
89 croak "Unsupported type: " . $options->{type} unless $options->{type} =~ m/^(?:html|js|json|text)$/;
91 # The "template" argument must be a string or a reference to one.
92 $template = ${ $template } if ((ref($template) || '') eq 'REF') && (ref(${ $template }) eq 'SL::Presenter::EscapedText');
93 croak "Unsupported 'template' reference type: " . ref($template) if ref($template) && (ref($template) !~ m/^(?:SCALAR|SL::Presenter::EscapedText)$/);
95 # If all output is turned off then don't output the header either.
96 if (!$options->{output}) {
97 $options->{header} = 0;
98 $options->{layout} = 0;
101 # Layout only makes sense if we're outputting HTML.
102 $options->{layout} = 0 if $options->{type} ne 'html';
105 # Let the presenter do the rest of the work.
108 local $::form->{title} = $locals{title} if $locals{title};
109 $output = $self->presenter->render(
111 { type => $options->{type}, process => $options->{process} },
117 if ($options->{header}) {
118 # Output the HTTP response and the layout in case of HTML output.
120 if ($options->{layout}) {
121 $::form->{title} = $locals{title} if $locals{title};
125 # No layout: just the standard HTTP response. Also notify
126 # $::form that the header has already been output so that
127 # $::form->header() won't output it again.
128 $::form->{header} = 1;
129 my $content_type = $options->{type} eq 'html' ? 'text/html'
130 : $options->{type} eq 'js' ? 'text/javascript'
131 : $options->{type} eq 'text' ? 'text/plain'
132 : 'application/json';
134 print $::form->create_http_response(content_type => $content_type,
136 (status => $options->{status}) x !!$options->{status});
140 # Print the output if wanted.
141 print $output if $options->{output};
147 my ($self, $file_name_or_content, %params) = @_;
151 if (!ref $file_name_or_content) {
152 $file = IO::File->new($file_name_or_content, 'r') || croak("Cannot open file '${file_name_or_content}'");
153 $size = -s $file_name_or_content;
155 $size = length $$file_name_or_content;
158 my $content_type = $params{type} || 'application/octet_stream';
159 my $content_disposition = $params{content_disposition} || 'attachment';
160 my $attachment_name = $params{name} || (!ref($file_name_or_content) ? $file_name_or_content : '');
161 $attachment_name =~ s:.*//::g;
163 if ($::request->is_ajax || $params{ajax}) {
164 my $octets = ref $file_name_or_content ? $file_name_or_content : \ do { local $/ = undef; <$file> };
165 $self->js->save_file(MIME::Base64::encode_base64($$octets), $content_type, $size, $attachment_name);
166 $self->js->render unless $params{js_no_render};
168 print $::form->create_http_response(content_type => $content_type,
169 content_disposition => $content_disposition . '; filename="' . $attachment_name . '"',
170 content_length => $size);
172 if (!ref $file_name_or_content) {
173 $::locale->with_raw_io(\*STDOUT, sub { print while <$file> });
175 unlink $file_name_or_content if $params{unlink};
177 $::locale->with_raw_io(\*STDOUT, sub { print $$file_name_or_content });
185 return SL::Presenter->get;
189 return SL::Presenter->get;
192 sub controller_name {
193 my $class = ref($_[0]) || $_[0];
194 $class =~ s/^SL::Controller:://;
199 SL::ClientJS->new(controller => $_[0])
203 # Before/after run hooks
207 _add_hook('before', @_);
211 _add_hook('after', @_);
217 my ($when, $class, $sub, %params) = @_;
219 foreach my $key (qw(only except)) {
220 $params{$key} = { map { ( $_ => 1 ) } @{ $params{$key} } } if $params{$key};
223 my $idx = "${when}/${class}";
224 $hooks{$idx} ||= [ ];
225 push @{ $hooks{$idx} }, { %params, code => $sub };
229 my ($self, $when, $action) = @_;
231 my $idx = "${when}/" . ref($self);
233 foreach my $hook (@{ $hooks{$idx} || [] }) {
234 next if ($hook->{only } && !$hook->{only }->{$action})
235 || ($hook->{except} && $hook->{except}->{$action});
237 if (ref($hook->{code}) eq 'CODE') {
238 $hook->{code}->($self, $action);
240 my $sub = $hook->{code};
241 $self->$sub($action);
247 # behaviour. override these
250 sub delay_flash_on_redirect {
255 # Ignore the 'action' parameter.
259 sub keep_auth_vars_in_form {
264 # private functions -- for use in Base only
270 my $sub = "action_${action}";
272 return $self->_dispatch(@_) if $action eq 'dispatch';
274 $::form->error("Invalid action '${action}' for controller " . ref($self)) if !$self->can($sub);
276 $self->action_name($action);
277 $self->_run_hooks('before', $action);
279 $self->_run_hooks('after', $action);
286 my @actions = map { s/^action_//; $_ } grep { m/^action_/ } keys %{ ref($self) . "::" };
287 my $action = first { $::form->{"action_${_}"} } @actions;
288 my $sub = "action_${action}";
290 if ($self->can($sub)) {
291 $self->action_name($action);
292 $self->_run_hooks('before', $action);
294 $self->_run_hooks('after', $action);
296 $::form->error($::locale->text('Oops. No valid action found to dispatch. Please report this case to the kivitendo team.'));
306 SL::Controller::Base - base class for all action controllers
312 This is a base class for all action controllers. Action controllers
313 provide subs that are callable by special URLs.
315 For each request made to the web server an instance of the controller
316 will be created. After the request has been served that instance will
317 handed over to garbage collection.
319 This base class is derived from L<Rose::Object>.
323 The URLs have the following properties:
329 The script part of the URL must be C<controller.pl>.
333 There must be a GET or POST parameter named C<action> containing the
334 name of the controller and the sub to call separated by C</>,
335 e.g. C<Message/list>.
339 The controller name is the package's name without the
340 C<SL::Controller::> prefix. At the moment only packages in the
341 C<SL::Controller> namespace are valid; sub-namespaces are not
342 allowed. The package name must start with an upper-case letter.
346 The sub part of the C<action> parameter is the name of the sub to
347 call. However, the sub's name is automatically prefixed with
348 C<action_>. Therefore for the example C<Message/list> the sub
349 C<SL::DB::Message::action_list> would be called. This in turn means
350 that subs whose name does not start with C<action_> cannot be invoked
351 directly via the URL.
355 =head2 INDIRECT DISPATCHING
357 In the case that there are several submit buttons on a page it is
358 often impractical to have a single C<action> parameter match up
359 properly. For such a case a special dispatcher method is available. In
360 that case the C<action> parameter of the URL must be
361 C<Controller/dispatch>.
363 The C<SL::Controller::Base::_dispatch> method will iterate over all
364 subs in the controller package whose names start with C<action_>. The
365 first one for which there's a GET or POST parameter with the same name
366 and that's trueish is called.
368 Usage from a template usually looks like this:
370 <form method="POST" action="controller.pl">
372 <input type="hidden" name="action" value="Message/dispatch">
373 <input type="submit" name="action_mark_as_read" value="Mark messages as read">
374 <input type="submit" name="action_delete" value="Delete messages">
377 The dispatching is handled by the function L</_dispatch>.
381 Hooks are functions that are called before or after the controller's
382 action is called. The controller package defines the hooks, and those
383 hooks themselves are run as instance methods.
385 Hooks are run in the order they're added.
387 The hooks receive a single parameter: the name of the action that is
388 about to be called (for C<before> hooks) / was called (for C<after>
391 The return value of the hooks is discarded.
393 Hooks can be defined to run for all actions, for only specific actions
394 or for all actions except a list of actions. Each entry is the action
395 name, not the sub's name. Therefore in order to run a hook before one
396 of the subs C<action_edit> or C<action_save> is called the following
399 __PACKAGE__->run_before('things_to_do_before_edit_and_save', only => [ 'edit', 'save' ]);
403 =head2 PUBLIC HELPER FUNCTIONS
405 These functions are supposed to be called by sub-classed controllers.
409 =item C<render $template, [ $options, ] %locals>
411 Renders the template C<$template>. Provides other variables than
412 C<Form::parse_html_template> does.
414 C<$options>, if present, must be a hash reference. All remaining
415 parameters are slurped into C<%locals>.
417 What is rendered and how C<$template> is interpreted is determined
418 both by C<$template>'s reference type and by the supplied options. The
419 actual rendering is handled by L<SL::Presenter/render>.
421 If C<$template> is a normal scalar (not a reference) then it is meant
422 to be a template file name relative to the C<templates/webpages>
423 directory. The file name to use is determined by the C<type> option.
425 If C<$template> is a reference to a scalar then the referenced
426 scalar's content is used as the content to process. The C<type> option
427 is not considered in this case.
429 C<$template> can also be an instance of L<SL::Presenter::EscapedText>
430 or a reference to such an instance. Both of these cases are handled
431 the same way as if C<$template> were a reference to a scalar: its
432 content is processed, and C<type> is not considered.
434 Other reference types, unknown options and unknown arguments to the
435 C<type> option cause the function to L<croak>.
437 The following options are available (defaults: C<type> = 'html',
438 C<process> = 1, C<output> = 1, C<header> = 1, C<layout> = 1):
444 The template type. Can be C<html> (the default), C<js> for JavaScript,
445 C<json> for JSON and C<text> for plain text content. Affects the
446 extension that's added to the file name given with a non-reference
447 C<$template> argument, the content type HTTP header that is output and
448 whether or not the layout will be output as well (see description of
453 If trueish (which is also the default) it causes the template/content
454 to be processed by the Template toolkit. Otherwise the
455 template/content is output as-is.
459 If trueish (the default) then the generated output will be sent to the
460 browser in addition to being returned. If falsish then the options
461 C<header> and C<layout> are set to 0 as well.
465 Determines whether or not to output the HTTP response
466 headers. Defaults to the same value that C<output> is set to. If set
467 to falsish then the layout is not output either.
471 Determines whether or not the basic HTML layout structure should be
472 output (HTML header, common JavaScript and stylesheet inclusions, menu
473 etc.). Defaults to 0 if C<type> is not C<html> and to the same value
474 C<header> is set to otherwise.
478 The template itself has access to several variables. These are listed
479 in the documentation to L<SL::Presenter/render>.
481 The function will always return the output.
483 Example: Render a HTML template with a certain title and a few locals
485 $self->render('todo/list',
486 title => 'List TODO items',
487 TODO_ITEMS => SL::DB::Manager::Todo->get_all_sorted);
489 Example: Render a string and return its content for further processing
490 by the calling function. No header is generated due to C<output>.
492 my $content = $self->render(\'[% USE JavaScript %][% JavaScript.replace_with("#someid", "js/something") %]',
495 Example: Render a JavaScript template
496 "templates/webpages/todo/single_item.js" and send it to the
497 browser. Typical use for actions called via AJAX:
499 $self->render('todo/single_item', { type => 'js' },
500 item => $employee->most_important_todo_item);
502 =item C<send_file $file_name_or_content, [%params]>
504 Sends the file C<$file_name_or_content> to the browser including
505 appropriate HTTP headers for a download. If C<$file_name_or_content>
506 is a scalar then it is interpreted as a file name which is opened and
507 whose content is sent. Otherwise (C<$file_name_or_content> being a
508 reference) the referenced scalar's data itself is sent.
510 C<%params> can include the following:
514 =item * C<type> -- the file's content type; defaults to
515 'application/octet_stream'
517 =item * C<name> -- the name presented to the browser; defaults to
518 C<$file_name>; mandatory if C<$file_name_or_content> is a reference
520 =item * C<unlink> -- if trueish and C<$file_name_or_content> refers to
521 a file name then unlink the file after it has been sent to the browser
522 (e.g. for temporary files)
526 =item C<url_for $url>
528 =item C<url_for $params>
530 =item C<url_for %params>
532 Creates an URL for the given parameters suitable for calling an action
533 controller. If there's only one scalar parameter then it is returned
536 Otherwise the parameters are given either as a single hash ref
537 parameter or as a normal hash.
539 The controller to call is given by C<$params{controller}>. It defaults
540 to the current controller as returned by
543 The action to call is given by C<$params{action}>. It defaults to
546 If C<$params{fragment}> is present, it's used as the fragment of the resulting
549 All other key/value pairs in C<%params> are appended as GET parameters
552 Usage from a template might look like this:
554 <a href="[% SELF.url_for(controller => 'Message', action => 'new', recipient_id => 42) %]">create new message</a>
556 =item C<redirect_to %url_params>
558 Redirects the browser to a new URL. The URL is generated by calling
559 L</url_for> with C<%url_params>.
561 This function implements the redirection depending on whether or not
562 the current request is an AJAX request as determined by
563 L<SL::Request/is_ajax>. If it is a normal request then it outputs a
564 standard HTTP redirect header (HTTP code 302). If it is an AJAX
565 request then it outputs an AJAX response suitable for the
566 C<kivi.eval_json_result> function from the L<SL::ClientJS> module.
568 =item C<run_before $sub, %params>
570 =item C<run_after $sub, %params>
572 Adds a hook to run before or after certain actions are run for the
573 current package. The code to run is C<$sub> which is either the name
574 of an instance method or a code reference. If it's the latter then the
575 first parameter will be C<$self>.
577 C<%params> can contain two possible values that restrict the code to
578 be run only for certain actions:
582 =item C<< only => \@list >>
584 Only run the code for actions given in C<@list>. The entries are the
585 action names, not the names of the sub (so it's C<list> instead of
588 =item C<< except => \@list >>
590 Run the code for all actions but for those given in C<@list>. The
591 entries are the action names, not the names of the sub (so it's
592 C<list> instead of C<action_list>).
596 If neither restriction is used then the code will be run for any
599 The hook's return values are discarded.
601 =item C<delay_flash_on_redirect>
603 May be overridden by a controller. If this method returns true, redirect_to
604 will delay all flash messages for the current request. Defaults to false for
605 compatibility reasons.
607 =item C<get_auth_level $action>
609 May be overridden by a controller. Determines what kind of
610 authentication is required for a particular action. Must return either
611 C<admin> (which means that authentication as an admin is required),
612 C<user> (authentication as a normal user suffices) with a possible
613 future value C<none> (which would require no authentication but is not
616 =item C<keep_auth_vars_in_form %params>
618 May be overridden by a controller. If falsish (the default) all form
619 variables whose name starts with C<{AUTH}> are removed before the
620 request is routed. Only controllers that handle login requests
621 themselves should return trueish for this function.
623 C<$params{action}> contains the action name that the request will be
626 =item C<controller_name>
628 Returns the name of the curernt controller package without the
629 C<SL::Controller::> prefix. This method can be called both as a class
630 method and an instance method.
634 Returns the name of the currently executing action. If the dispatcher
635 mechanism was used then this is not C<dispatch> but the actual method
636 name the dispatching resolved to.
640 Returns the global presenter object by calling
641 L<SL::Presenter/get>.
645 Returns an L<SL::ClientJS> instance for this controller.
649 =head2 PRIVATE FUNCTIONS
651 These functions are supposed to be used from this base class only.
657 Implements the method lookup for indirect dispatching mentioned in the
658 section L</INDIRECT DISPATCHING>.
660 =item C<_run_action $action>
662 Executes a sub based on the value of C<$action>. C<$action> is the sub
663 name part of the C<action> GET or POST parameter as described in
666 If C<$action> equals C<dispatch> then the sub L</_dispatch> in this
667 base class is called for L</INDIRECT DISPATCHING>. Otherwise
668 C<$action> is prefixed with C<action_>, and that sub is called on the
669 current controller instance.
675 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>