use parent qw(Rose::Object);
use Carp;
+use IO::File;
use List::Util qw(first);
+use SL::Request qw(flatten);
+use SL::MoreCommon qw(uri_encode);
+
+use Rose::Object::MakeMethods::Generic
+(
+ scalar => [ qw(action_name) ],
+);
#
# public/helper functions
return $_[0] if (scalar(@_) == 1) && !ref($_[0]);
my %params = ref($_[0]) eq 'HASH' ? %{ $_[0] } : @_;
- my $controller = delete($params{controller}) || $self->_controller_name;
- my $action = delete($params{action}) || 'dispatch';
- $params{action} = "${controller}/${action}";
- my $query = join('&', map { $::form->escape($_) . '=' . $::form->escape($params{$_}) } keys %params);
+ my $controller = delete($params{controller}) || $self->controller_name;
+ my $action = $params{action} || 'dispatch';
+
+ my $script;
+ if ($controller =~ m/\.pl$/) {
+ # Old-style controller
+ $script = $controller;
+ } else {
+ $params{action} = "${controller}/${action}";
+ $script = "controller.pl";
+ }
- return "controller.pl?${query}";
+ my $query = join '&', map { uri_encode($_->[0]) . '=' . uri_encode($_->[1]) } @{ flatten(\%params) };
+
+ return "${script}?${query}";
}
sub redirect_to {
my $self = shift;
my $url = $self->url_for(@_);
- print $::cgi->redirect($url);
+ if ($self->delay_flash_on_redirect) {
+ require SL::Helper::Flash;
+ SL::Helper::Flash::delay_flash();
+ }
+
+ print $::request->{cgi}->redirect($url);
}
sub render {
if ($options->{inline}) {
$source = \$template;
+ } elsif($options->{raw}) {
+ $source = $template;
+
} else {
$source = "templates/webpages/${template}." . $options->{type};
croak "Template file ${source} not found" unless -f $source;
} else {
$::form->{title} = $locals{title} if $locals{title};
- $::form->header;
+ $::form->header(no_menu => $options->{no_menu});
}
}
my %params = ( %locals,
- AUTH => $::auth,
- FLASH => $::form->{FLASH},
- FORM => $::form,
- LOCALE => $::locale,
- LXCONFIG => \%::lx_office_conf,
- LXDEBUG => $::lxdebug,
- MYCONFIG => \%::myconfig,
- SELF => $self,
+ AUTH => $::auth,
+ FLASH => $::form->{FLASH},
+ FORM => $::form,
+ INSTANCE_CONF => $::instance_conf,
+ LOCALE => $::locale,
+ LXCONFIG => \%::lx_office_conf,
+ LXDEBUG => $::lxdebug,
+ MYCONFIG => \%::myconfig,
+ SELF => $self,
);
my $output;
- my $parser = $self->_template_obj;
- $parser->process($source, \%params, \$output) || croak $parser->error;
+ if (!$options->{raw}) {
+ my $parser = $self->_template_obj;
+ $parser->process($source, \%params, \$output) || croak $parser->error;
+ } else {
+ $output = $$source;
+ }
print $output unless $options->{inline} || $options->{no_output};
return $output;
}
+sub send_file {
+ my ($self, $file_name, %params) = @_;
+
+ my $file = IO::File->new($file_name, 'r') || croak("Cannot open file '${file_name}'");
+ my $content_type = $params{type} || 'application/octet_stream';
+ my $attachment_name = $params{name} || $file_name;
+ $attachment_name =~ s:.*//::g;
+
+ print $::form->create_http_response(content_type => $content_type,
+ content_disposition => 'attachment; filename="' . $attachment_name . '"',
+ content_length => -s $file);
+
+ $::locale->with_raw_io(\*STDOUT, sub { print while <$file> });
+ $file->close;
+}
+
+sub controller_name {
+ my $class = ref($_[0]) || $_[0];
+ $class =~ s/^SL::Controller:://;
+ return $class;
+}
+
#
# Before/after run hooks
#
|| ($hook->{except} && $hook->{except}->{$action});
if (ref($hook->{code}) eq 'CODE') {
- $hook->{code}->($self);
+ $hook->{code}->($self, $action);
} else {
my $sub = $hook->{code};
- $self->$sub;
+ $self->$sub($action);
}
}
}
+#
+# behaviour. override these
+#
+
+sub delay_flash_on_redirect {
+ 0;
+}
+
+sub get_auth_level {
+ # Ignore the 'action' parameter.
+ return 'user';
+}
+
+sub keep_auth_vars_in_form {
+ return 0;
+}
+
#
# private functions -- for use in Base only
#
$::form->error("Invalid action '${action}' for controller " . ref($self)) if !$self->can($sub);
+ $self->action_name($action);
$self->_run_hooks('before', $action);
$self->$sub(@_);
$self->_run_hooks('after', $action);
}
-sub _controller_name {
- return (split(/::/, ref($_[0])))[-1];
-}
-
sub _dispatch {
my $self = shift;
my $action = first { $::form->{"action_${_}"} } @actions;
my $sub = "action_${action}";
- $self->_run_hooks('before', $action);
- $self->$sub(@_);
- $self->_run_hooks('after', $action);
+ if ($self->can($sub)) {
+ $self->action_name($action);
+ $self->_run_hooks('before', $action);
+ $self->$sub(@_);
+ $self->_run_hooks('after', $action);
+ } else {
+ $::form->error($::locale->text('Oops. No valid action found to dispatch. Please report this case to the kivitendo team.'));
+ }
}
sub _template_obj {
INCLUDE_PATH => '.:templates/webpages',
COMPILE_EXT => '.tcc',
COMPILE_DIR => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
+ ERROR => 'templates/webpages/generic/exception.html',
}) || croak;
return $self->{__basepriv_template_obj};
Hooks are run in the order they're added.
+The hooks receive a single parameter: the name of the action that is
+about to be called (for C<before> hooks) / was called (for C<after>
+hooks).
+
The return value of the hooks is discarded.
Hooks can be defined to run for all actions, for only specific actions
will not be sent to the browser. Instead it is only returned to the
caller.
+If C<< $options->{raw} >> is trueish, the function will treat the input as
+already parsed, and will not filter the input through Template. Unlike
+C<inline>, the input is taked as a reference.
+
If C<< $options->{inline} >> is falsish then C<$template> is
interpreted as the name of a template file. It is prefixed with
"templates/webpages/" and postfixed with a file extension based on
=item * C<LOCALE> -- C<$::locale>
-=item * C<LXCONFIG> -- all parameters from C<config/lx_office.conf>
+=item * C<LXCONFIG> -- all parameters from C<config/kivitendo.conf>
with the same name they appear in the file (first level is the
section, second the actual variable, e.g. C<system.dbcharset>,
C<features.webdav> etc)
$self->render('todo/single_item', { type => 'js' },
item => $employee->most_important_todo_item);
+=item C<send_file $file_name, [%params]>
+
+Sends the file C<$file_name> to the browser including appropriate HTTP
+headers for a download. C<%params> can include the following:
+
+=over 2
+
+=item * C<type> -- the file's content type; defaults to
+'application/octet_stream'
+
+=item * C<name> -- the name presented to the browser; defaults to
+C<$file_name>
+
+=back
+
=item C<url_for $url>
=item C<url_for $params>
The controller to call is given by C<$params{controller}>. It defaults
to the current controller as returned by
-L</_controller_name>.
+L</controller_name>.
The action to call is given by C<$params{action}>. It defaults to
C<dispatch>.
The hook's return values are discarded.
+=item C<delay_flash_on_redirect>
+
+May be overridden by a controller. If this method returns true, redirect_to
+will delay all flash messages for the current request. Defaults to false for
+compatibility reasons.
+
+=item C<get_auth_level $action>
+
+May be overridden by a controller. Determines what kind of
+authentication is required for a particular action. Must return either
+C<admin> (which means that authentication as an admin is required),
+C<user> (authentication as a normal user suffices) with a possible
+future value C<none> (which would require no authentication but is not
+yet implemented).
+
+=item C<keep_auth_vars_in_form>
+
+May be overridden by a controller. If falsish (the default) all form
+variables whose name starts with C<{AUTH}> are removed before the
+request is routed. Only controllers that handle login requests
+themselves should return trueish for this function.
+
+=item C<controller_name>
+
+Returns the name of the curernt controller package without the
+C<SL::Controller::> prefix. This method can be called both as a class
+method and an instance method.
+
+=item C<action_name>
+
+Returns the name of the currently executing action. If the dispatcher
+mechanism was used then this is not C<dispatch> but the actual method
+name the dispatching resolved to.
+
=back
=head2 PRIVATE FUNCTIONS
=over 4
-=item C<_controller_name>
-
-Returns the name of the curernt controller package without the
-C<SL::Controller::> prefix.
-
=item C<_dispatch>
Implements the method lookup for indirect dispatching mentioned in the