1 package SL::Controller::Base;
 
   3 use parent qw(Rose::Object);
 
   5 use List::Util qw(first);
 
   8 # public/helper functions
 
  11 sub parse_html_template {
 
  14   my $locals = shift || {};
 
  16   return $::form->parse_html_template($name, { %{ $locals }, SELF => $self });
 
  22   return $_[0] if (scalar(@_) == 1) && !ref($_[0]);
 
  24   my %params      = ref($_[0]) eq 'HASH' ? %{ $_[0] } : @_;
 
  25   my $controller  = delete($params{controller}) || $self->_controller_name;
 
  26   my $action      = delete($params{action})     || 'dispatch';
 
  27   $params{action} = "${controller}/${action}";
 
  28   my $query       = join('&', map { $::form->escape($_) . '=' . $::form->escape($params{$_}) } keys %params);
 
  30   return "controller.pl?${query}";
 
  35   my $url  = $self->url_for(@_);
 
  37   print $::cgi->redirect($url);
 
  44   $template  = shift if scalar(@_) % 2;
 
  48     $::form->{title} = delete $params{title};
 
  52   print $self->parse_html_template($template, $params{locals});
 
  56 # private functions -- for use in Base only
 
  61   my $action = "action_" . shift;
 
  63   return $self->_dispatch(@_) if $action eq 'action_dispatch';
 
  65   $::form->error("Invalid action ${action} for controller " . ref($self)) if !$self->can($action);
 
  69 sub _controller_name {
 
  70   return (split(/::/, ref($_[0])))[-1];
 
  76   my @actions = grep { m/^action_/ } keys %{ ref($self) . "::" };
 
  77   my $action  = first { $::form->{$_} } @actions;
 
  88 SL::Controller::Base - base class for all action controllers
 
  94 This is a base class for all action controllers. Action controllers
 
  95 provide subs that are callable by special URLs.
 
  97 For each request made to the web server an instance of the controller
 
  98 will be created. After the request has been served that instance will
 
  99 handed over to garbage collection.
 
 101 This base class is derived from L<Rose::Object>.
 
 105 The URLs have the following properties:
 
 111 The script part of the URL must be C<controller.pl>.
 
 115 There must be a GET or POST parameter named C<action> containing the
 
 116 name of the controller and the sub to call separated by C</>,
 
 117 e.g. C<Message/list>.
 
 121 The controller name is the package's name without the
 
 122 C<SL::Controller::> prefix. At the moment only packages in the
 
 123 C<SL::Controller> namespace are valid; sub-namespaces are not
 
 124 allowed. The package name must start with an upper-case letter.
 
 128 The sub part of the C<action> parameter is the name of the sub to
 
 129 call. However, the sub's name is automatically prefixed with
 
 130 C<action_>. Therefore for the example C<Message/list> the sub
 
 131 C<SL::DB::Message::action_list> would be called. This in turn means
 
 132 that subs whose name does not start with C<action_> cannot be invoked
 
 133 directly via the URL.
 
 137 =head2 INDIRECT DISPATCHING
 
 139 In the case that there are several submit buttons on a page it is
 
 140 often impractical to have a single C<action> parameter match up
 
 141 properly. For such a case a special dispatcher method is available. In
 
 142 that case the C<action> parameter of the URL must be
 
 143 C<Controller/dispatch>.
 
 145 The C<SL::Controller::Base::_dispatch> method will iterate over all
 
 146 subs in the controller package whose names start with C<action_>. The
 
 147 first one for which there's a GET or POST parameter with the same name
 
 148 and that's trueish is called.
 
 150 Usage from a template usually looks like this:
 
 152   <form method="POST" action="controller.pl">
 
 154     <input type="hidden" name="action" value="Message/dispatch">
 
 155     <input type="submit" name="action_mark_as_read" value="Mark messages as read">
 
 156     <input type="submit" name="action_delete" value="Delete messages">
 
 159 The dispatching is handled by the function L</_dispatch>.
 
 163 =head2 PUBLIC HELPER FUNCTIONS
 
 165 These functions are supposed to be called by sub-classed controllers.
 
 169 =item C<parse_html_template $file_name, $local_variables>
 
 171 Outputs an HTML template. It is a thin wrapper around
 
 172 C<Form::parse_html_template> which also adds the current object as the
 
 173 template variable C<SELF>.
 
 175 =item C<render $template, %params>
 
 177 Renders the template C<$template> by calling
 
 178 L</parse_html_template>. C<$params{locals}> will be used as the second
 
 179 parameter to L</parse_html_template>.
 
 181 If C<$params{title}> is trueish then the function also sets
 
 182 C<< $::form->{header} >> to that value and calls C<< $::form->header >>.
 
 184 =item C<url_for $url>
 
 186 =item C<url_for $params>
 
 188 =item C<url_for %params>
 
 190 Creates an URL for the given parameters suitable for calling an action
 
 191 controller. If there's only one scalar parameter then it is returned
 
 194 Otherwise the parameters are given either as a single hash ref
 
 195 parameter or as a normal hash.
 
 197 The controller to call is given by C<$params{controller}>. It defaults
 
 198 to the current controller as returned by
 
 199 L</_controller_name>.
 
 201 The action to call is given by C<$params{action}>. It defaults to
 
 204 All other key/value pairs in C<%params> are appended as GET parameters
 
 207 Usage from a template might look like this:
 
 209   <a href="[% SELF.url_for(controller => 'Message', action => 'new', recipient_id => 42) %]">create new message</a>
 
 211 =item redirect_to %url_params
 
 213 Redirects the browser to a new URL by outputting a HTTP redirect
 
 214 header. The URL is generated by calling L</url_for> with
 
 219 =head2 PRIVATE FUNCTIONS
 
 221 These functions are supposed to be used from this base class only.
 
 225 =item C<_controller_name>
 
 227 Returns the name of the curernt controller package without the
 
 228 C<SL::Controller::> prefix.
 
 232 Implements the method lookup for indirect dispatching mentioned in the
 
 233 section L</INDIRECT DISPATCHING>.
 
 235 =item C<_run_action $action>
 
 237 Executes a sub based on the value of C<$action>. C<$action> is the sub
 
 238 name part of the C<action> GET or POST parameter as described in
 
 241 If C<$action> equals C<dispatch> then the sub L</_dispatch> in this
 
 242 base class is called for L</INDIRECT DISPATCHING>. Otherwise
 
 243 C<$action> is prefixed with C<action_>, and that sub is called on the
 
 244 current controller instance.
 
 250 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>