1 package SL::Controller::Base;
 
   5 use parent qw(Rose::Object);
 
   9 use List::Util qw(first);
 
  12 # public/helper functions
 
  18   return $_[0] if (scalar(@_) == 1) && !ref($_[0]);
 
  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);
 
  26   return "controller.pl?${query}";
 
  31   my $url  = $self->url_for(@_);
 
  33   print $::cgi->redirect($url);
 
  39   my ($options, %locals) = (@_ && ref($_[0])) ? @_ : ({ }, @_);
 
  41   $options->{type}       = lc($options->{type} || 'html');
 
  42   $options->{no_layout}  = 1 if $options->{type} eq 'js';
 
  45   if ($options->{inline}) {
 
  49     $source = "templates/webpages/${template}." . $options->{type};
 
  50     croak "Template file ${source} not found" unless -f $source;
 
  53   if (!$options->{partial} && !$options->{inline} && !$::form->{header}) {
 
  54     if ($options->{no_layout}) {
 
  55       $::form->{header} = 1;
 
  56       my $content_type  = $options->{type} eq 'js' ? 'text/javascript' : 'text/html';
 
  58       print $::form->create_http_response(content_type => $content_type,
 
  59                                           charset      => $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET());
 
  62       $::form->{title} = $locals{title} if $locals{title};
 
  67   my %params = ( %locals,
 
  69                  FLASH         => $::form->{FLASH},
 
  71                  INSTANCE_CONF => $::instance_conf,
 
  73                  LXCONFIG      => \%::lx_office_conf,
 
  74                  LXDEBUG       => $::lxdebug,
 
  75                  MYCONFIG      => \%::myconfig,
 
  80   my $parser = $self->_template_obj;
 
  81   $parser->process($source, \%params, \$output) || croak $parser->error;
 
  83   print $output unless $options->{inline} || $options->{no_output};
 
  89   my ($self, $file_name, %params) = @_;
 
  91   my $file            = IO::File->new($file_name, 'r') || croak("Cannot open file '${file_name}'");
 
  92   my $content_type    =  $params{type} || 'application/octet_stream';
 
  93   my $attachment_name =  $params{name} || $file_name;
 
  94   $attachment_name    =~ s:.*//::g;
 
  96   print $::form->create_http_response(content_type        => $content_type,
 
  97                                       content_disposition => 'attachment; filename="' . $attachment_name . '"',
 
  98                                       content_length      => -s $file);
 
 100   $::locale->with_raw_io(\*STDOUT, sub { print while <$file> });
 
 105 # Before/after run hooks
 
 109   _add_hook('before', @_);
 
 113   _add_hook('after', @_);
 
 119   my ($when, $class, $sub, %params) = @_;
 
 121   foreach my $key (qw(only except)) {
 
 122     $params{$key} = { map { ( $_ => 1 ) } @{ $params{$key} } } if $params{$key};
 
 125   my $idx = "${when}/${class}";
 
 126   $hooks{$idx} ||= [ ];
 
 127   push @{ $hooks{$idx} }, { %params, code => $sub };
 
 131   my ($self, $when, $action) = @_;
 
 133   my $idx = "${when}/" . ref($self);
 
 135   foreach my $hook (@{ $hooks{$idx} || [] }) {
 
 136     next if ($hook->{only  } && !$hook->{only  }->{$action})
 
 137          || ($hook->{except} &&  $hook->{except}->{$action});
 
 139     if (ref($hook->{code}) eq 'CODE') {
 
 140       $hook->{code}->($self);
 
 142       my $sub = $hook->{code};
 
 149 # private functions -- for use in Base only
 
 155   my $sub    = "action_${action}";
 
 157   return $self->_dispatch(@_) if $action eq 'dispatch';
 
 159   $::form->error("Invalid action '${action}' for controller " . ref($self)) if !$self->can($sub);
 
 161   $self->_run_hooks('before', $action);
 
 163   $self->_run_hooks('after', $action);
 
 166 sub _controller_name {
 
 167   return (split(/::/, ref($_[0])))[-1];
 
 174   my @actions = map { s/^action_//; $_ } grep { m/^action_/ } keys %{ ref($self) . "::" };
 
 175   my $action  = first { $::form->{"action_${_}"} } @actions;
 
 176   my $sub     = "action_${action}";
 
 178   if ($self->can($sub)) {
 
 179     $self->_run_hooks('before', $action);
 
 181     $self->_run_hooks('after', $action);
 
 183     $::form->error($::locale->text('Oops. No valid action found to dispatch. Please report this case to the Lx-Office team.'));
 
 190   $self->{__basepriv_template_obj} ||=
 
 191     Template->new({ INTERPOLATE  => 0,
 
 195                     PLUGIN_BASE  => 'SL::Template::Plugin',
 
 196                     INCLUDE_PATH => '.:templates/webpages',
 
 197                     COMPILE_EXT  => '.tcc',
 
 198                     COMPILE_DIR  => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
 
 201   return $self->{__basepriv_template_obj};
 
 210 SL::Controller::Base - base class for all action controllers
 
 216 This is a base class for all action controllers. Action controllers
 
 217 provide subs that are callable by special URLs.
 
 219 For each request made to the web server an instance of the controller
 
 220 will be created. After the request has been served that instance will
 
 221 handed over to garbage collection.
 
 223 This base class is derived from L<Rose::Object>.
 
 227 The URLs have the following properties:
 
 233 The script part of the URL must be C<controller.pl>.
 
 237 There must be a GET or POST parameter named C<action> containing the
 
 238 name of the controller and the sub to call separated by C</>,
 
 239 e.g. C<Message/list>.
 
 243 The controller name is the package's name without the
 
 244 C<SL::Controller::> prefix. At the moment only packages in the
 
 245 C<SL::Controller> namespace are valid; sub-namespaces are not
 
 246 allowed. The package name must start with an upper-case letter.
 
 250 The sub part of the C<action> parameter is the name of the sub to
 
 251 call. However, the sub's name is automatically prefixed with
 
 252 C<action_>. Therefore for the example C<Message/list> the sub
 
 253 C<SL::DB::Message::action_list> would be called. This in turn means
 
 254 that subs whose name does not start with C<action_> cannot be invoked
 
 255 directly via the URL.
 
 259 =head2 INDIRECT DISPATCHING
 
 261 In the case that there are several submit buttons on a page it is
 
 262 often impractical to have a single C<action> parameter match up
 
 263 properly. For such a case a special dispatcher method is available. In
 
 264 that case the C<action> parameter of the URL must be
 
 265 C<Controller/dispatch>.
 
 267 The C<SL::Controller::Base::_dispatch> method will iterate over all
 
 268 subs in the controller package whose names start with C<action_>. The
 
 269 first one for which there's a GET or POST parameter with the same name
 
 270 and that's trueish is called.
 
 272 Usage from a template usually looks like this:
 
 274   <form method="POST" action="controller.pl">
 
 276     <input type="hidden" name="action" value="Message/dispatch">
 
 277     <input type="submit" name="action_mark_as_read" value="Mark messages as read">
 
 278     <input type="submit" name="action_delete" value="Delete messages">
 
 281 The dispatching is handled by the function L</_dispatch>.
 
 285 Hooks are functions that are called before or after the controller's
 
 286 action is called. The controller package defines the hooks, and those
 
 287 hooks themselves are run as instance methods.
 
 289 Hooks are run in the order they're added.
 
 291 The return value of the hooks is discarded.
 
 293 Hooks can be defined to run for all actions, for only specific actions
 
 294 or for all actions except a list of actions. Each entry is the action
 
 295 name, not the sub's name. Therefore in order to run a hook before one
 
 296 of the subs C<action_edit> or C<action_save> is called the following
 
 299   __PACKAGE__->run_before('things_to_do_before_edit_and_save', only => [ 'edit', 'save' ]);
 
 303 =head2 PUBLIC HELPER FUNCTIONS
 
 305 These functions are supposed to be called by sub-classed controllers.
 
 309 =item C<render $template, [ $options, ] %locals>
 
 311 Renders the template C<$template>. Provides other variables than
 
 312 C<Form::parse_html_template> does.
 
 314 C<$options>, if present, must be a hash reference. All remaining
 
 315 parameters are slurped into C<%locals>.
 
 317 What is rendered and how C<$template> is interpreted is determined by
 
 318 the options I<type>, I<inline>, I<partial> and I<no_layout>.
 
 320 If C<< $options->{inline} >> is trueish then C<$template> is a string
 
 321 containing the template code to interprete. Additionally the output
 
 322 will not be sent to the browser. Instead it is only returned to the
 
 325 If C<< $options->{inline} >> is falsish then C<$template> is
 
 326 interpreted as the name of a template file. It is prefixed with
 
 327 "templates/webpages/" and postfixed with a file extension based on
 
 328 C<< $options->{type} >>. C<< $options->{type} >> can be either C<html>
 
 329 or C<js> and defaults to C<html>. An exception will be thrown if that
 
 332 If C<< $options->{partial} >> or C<< $options->{inline} >> is trueish
 
 333 then neither the HTTP response header nor the standard HTML header is
 
 336 Otherwise at least the HTTP response header will be generated based on
 
 337 the template type (C<< $options->{type} >>).
 
 339 If the template type is C<html> then the standard HTML header will be
 
 340 output via C<< $::form->header >> with C<< $::form->{title} >> set to
 
 341 C<$locals{title}> (the latter only if C<$locals{title}> is
 
 342 trueish). Setting C<< $options->{no_layout} >> to trueish will prevent
 
 345 The template itself has access to the following variables:
 
 349 =item * C<AUTH> -- C<$::auth>
 
 351 =item * C<FORM> -- C<$::form>
 
 353 =item * C<LOCALE> -- C<$::locale>
 
 355 =item * C<LXCONFIG> -- all parameters from C<config/lx_office.conf>
 
 356 with the same name they appear in the file (first level is the
 
 357 section, second the actual variable, e.g. C<system.dbcharset>,
 
 358 C<features.webdav> etc)
 
 360 =item * C<LXDEBUG> -- C<$::lxdebug>
 
 362 =item * C<MYCONFIG> -- C<%::myconfig>
 
 364 =item * C<SELF> -- the controller instance
 
 366 =item * All items from C<%locals>
 
 370 Unless C<< $options->{inline} >> is trueish the function will send the
 
 371 output to the browser.
 
 373 The function will always return the output.
 
 375 Example: Render a HTML template with a certain title and a few locals
 
 377   $self->render('todo/list',
 
 378                 title      => 'List TODO items',
 
 379                 TODO_ITEMS => SL::DB::Manager::Todo->get_all_sorted);
 
 381 Example: Render a string and return its content for further processing
 
 382 by the calling function. No header is generated due to C<inline>.
 
 384   my $content = $self->render('[% USE JavaScript %][% JavaScript.replace_with("#someid", "js/something") %]',
 
 385                               { type => 'js', inline => 1 });
 
 387 Example: Render a JavaScript template and send it to the
 
 388 browser. Typical use for actions called via AJAX:
 
 390   $self->render('todo/single_item', { type => 'js' },
 
 391                 item => $employee->most_important_todo_item);
 
 393 =item C<send_file $file_name, [%params]>
 
 395 Sends the file C<$file_name> to the browser including appropriate HTTP
 
 396 headers for a download. C<%params> can include the following:
 
 400 =item * C<type> -- the file's content type; defaults to
 
 401 'application/octet_stream'
 
 403 =item * C<name> -- the name presented to the browser; defaults to
 
 408 =item C<url_for $url>
 
 410 =item C<url_for $params>
 
 412 =item C<url_for %params>
 
 414 Creates an URL for the given parameters suitable for calling an action
 
 415 controller. If there's only one scalar parameter then it is returned
 
 418 Otherwise the parameters are given either as a single hash ref
 
 419 parameter or as a normal hash.
 
 421 The controller to call is given by C<$params{controller}>. It defaults
 
 422 to the current controller as returned by
 
 423 L</_controller_name>.
 
 425 The action to call is given by C<$params{action}>. It defaults to
 
 428 All other key/value pairs in C<%params> are appended as GET parameters
 
 431 Usage from a template might look like this:
 
 433   <a href="[% SELF.url_for(controller => 'Message', action => 'new', recipient_id => 42) %]">create new message</a>
 
 435 =item C<redirect_to %url_params>
 
 437 Redirects the browser to a new URL by outputting a HTTP redirect
 
 438 header. The URL is generated by calling L</url_for> with
 
 441 =item C<run_before $sub, %params>
 
 443 =item C<run_after $sub, %params>
 
 445 Adds a hook to run before or after certain actions are run for the
 
 446 current package. The code to run is C<$sub> which is either the name
 
 447 of an instance method or a code reference. If it's the latter then the
 
 448 first parameter will be C<$self>.
 
 450 C<%params> can contain two possible values that restrict the code to
 
 451 be run only for certain actions:
 
 455 =item C<< only => \@list >>
 
 457 Only run the code for actions given in C<@list>. The entries are the
 
 458 action names, not the names of the sub (so it's C<list> instead of
 
 461 =item C<< except => \@list >>
 
 463 Run the code for all actions but for those given in C<@list>. The
 
 464 entries are the action names, not the names of the sub (so it's
 
 465 C<list> instead of C<action_list>).
 
 469 If neither restriction is used then the code will be run for any
 
 472 The hook's return values are discarded.
 
 476 =head2 PRIVATE FUNCTIONS
 
 478 These functions are supposed to be used from this base class only.
 
 482 =item C<_controller_name>
 
 484 Returns the name of the curernt controller package without the
 
 485 C<SL::Controller::> prefix.
 
 489 Implements the method lookup for indirect dispatching mentioned in the
 
 490 section L</INDIRECT DISPATCHING>.
 
 492 =item C<_run_action $action>
 
 494 Executes a sub based on the value of C<$action>. C<$action> is the sub
 
 495 name part of the C<action> GET or POST parameter as described in
 
 498 If C<$action> equals C<dispatch> then the sub L</_dispatch> in this
 
 499 base class is called for L</INDIRECT DISPATCHING>. Otherwise
 
 500 C<$action> is prefixed with C<action_>, and that sub is called on the
 
 501 current controller instance.
 
 507 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>