kivitendo/SL/Controller/Helper/Paginated.pm @ 705d8c59
ef32afed | Moritz Bunkus | package SL::Controller::Helper::Paginated;
|
||
use strict;
|
||||
use Exporter qw(import);
|
||||
5e45f456 | Moritz Bunkus | our @EXPORT = qw(make_paginated get_paginate_spec get_current_paginate_params _save_current_paginate_params _get_models_handler_for_paginated _callback_handler_for_paginated disable_pagination);
|
||
ef32afed | Moritz Bunkus | |||
93f51d62 | Moritz Bunkus | use constant PRIV => '__paginatedhelper_priv';
|
||
ccacef74 | Moritz Bunkus | my %controller_paginate_spec;
|
||
ef32afed | Moritz Bunkus | |||
sub make_paginated {
|
||||
my ($class, %specs) = @_;
|
||||
29637d3f | Moritz Bunkus | $specs{MODEL} ||= $class->controller_name;
|
||
ef32afed | Moritz Bunkus | $specs{MODEL} =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
|
||
$specs{PER_PAGE} ||= "SL::DB::Manager::$specs{MODEL}"->default_objects_per_page;
|
||||
$specs{FORM_PARAMS} ||= [ qw(page per_page) ];
|
||||
$specs{ONLY} ||= [];
|
||||
$specs{ONLY} = [ $specs{ONLY} ] if !ref $specs{ONLY};
|
||||
5e45f456 | Moritz Bunkus | $specs{ONLY_MAP} = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 };
|
||
ef32afed | Moritz Bunkus | |||
ccacef74 | Moritz Bunkus | $controller_paginate_spec{$class} = \%specs;
|
||
ef32afed | Moritz Bunkus | |||
my %hook_params = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
|
||||
$class->run_before('_save_current_paginate_params', %hook_params);
|
||||
SL::Controller::Helper::GetModels::register_get_models_handlers(
|
||||
$class,
|
||||
callback => '_callback_handler_for_paginated',
|
||||
get_models => '_get_models_handler_for_paginated',
|
||||
ONLY => $specs{ONLY},
|
||||
);
|
||||
# $::lxdebug->dump(0, "CONSPEC", \%specs);
|
||||
}
|
||||
sub get_paginate_spec {
|
||||
my ($class_or_self) = @_;
|
||||
ccacef74 | Moritz Bunkus | return $controller_paginate_spec{ref($class_or_self) || $class_or_self};
|
||
ef32afed | Moritz Bunkus | }
|
||
sub get_current_paginate_params {
|
||||
my ($self, %params) = @_;
|
||||
my $spec = $self->get_paginate_spec;
|
||||
5e45f456 | Moritz Bunkus | my $priv = _priv($self);
|
||
93f51d62 | Moritz Bunkus | $params{page} = $priv->{page} unless defined $params{page};
|
||
$params{per_page} = $priv->{per_page} unless defined $params{per_page};
|
||||
ef32afed | Moritz Bunkus | |||
my %paginate_params = (
|
||||
page => ($params{page} * 1) || 1,
|
||||
per_page => ($params{per_page} * 1) || $spec->{PER_PAGE},
|
||||
);
|
||||
5e45f456 | Moritz Bunkus | my %paginate_args = ref($spec->{PAGINATE_ARGS}) eq 'CODE' ? %{ $spec->{PAGINATE_ARGS}->($self) }
|
||
: $spec->{PAGINATE_ARGS} ? do { my $sub = $spec->{PAGINATE_ARGS}; %{ $self->$sub() } }
|
||||
: ();
|
||||
my $calculated_params = "SL::DB::Manager::$spec->{MODEL}"->paginate(%paginate_params, args => \%paginate_args);
|
||||
ef32afed | Moritz Bunkus | %paginate_params = (
|
||
%paginate_params,
|
||||
num_pages => $calculated_params->{max},
|
||||
common_pages => $calculated_params->{common},
|
||||
);
|
||||
# $::lxdebug->dump(0, "get_current_paginate_params: ", \%paginate_params);
|
||||
return %paginate_params;
|
||||
}
|
||||
5e45f456 | Moritz Bunkus | sub disable_pagination {
|
||
my ($self) = @_;
|
||||
_priv($self)->{disabled} = 1;
|
||||
}
|
||||
ef32afed | Moritz Bunkus | #
|
||
# private functions
|
||||
#
|
||||
sub _save_current_paginate_params {
|
||||
my ($self) = @_;
|
||||
5e45f456 | Moritz Bunkus | return if !_is_enabled($self);
|
||
ef32afed | Moritz Bunkus | my $paginate_spec = $self->get_paginate_spec;
|
||
93f51d62 | Moritz Bunkus | $self->{PRIV()} = {
|
||
page => $::form->{ $paginate_spec->{FORM_PARAMS}->[0] } || 1,
|
||||
per_page => $::form->{ $paginate_spec->{FORM_PARAMS}->[1] } * 1,
|
||||
};
|
||||
ef32afed | Moritz Bunkus | |||
93f51d62 | Moritz Bunkus | # $::lxdebug->message(0, "saving current paginate params to " . $self->{PRIV()}->{page} . ' / ' . $self->{PRIV()}->{per_page});
|
||
ef32afed | Moritz Bunkus | }
|
||
sub _callback_handler_for_paginated {
|
||||
my ($self, %params) = @_;
|
||||
5e45f456 | Moritz Bunkus | my $priv = _priv($self);
|
||
ef32afed | Moritz Bunkus | |||
5e45f456 | Moritz Bunkus | if (_is_enabled($self) && $priv->{page}) {
|
||
ef32afed | Moritz Bunkus | my $paginate_spec = $self->get_paginate_spec;
|
||
93f51d62 | Moritz Bunkus | $params{ $paginate_spec->{FORM_PARAMS}->[0] } = $priv->{page};
|
||
$params{ $paginate_spec->{FORM_PARAMS}->[1] } = $priv->{per_page} if $priv->{per_page};
|
||||
ef32afed | Moritz Bunkus | }
|
||
# $::lxdebug->dump(0, "CB handler for paginated; params nach modif:", \%params);
|
||||
return %params;
|
||||
}
|
||||
sub _get_models_handler_for_paginated {
|
||||
my ($self, %params) = @_;
|
||||
5e45f456 | Moritz Bunkus | my $spec = $self->get_paginate_spec;
|
||
$params{model} ||= $spec->{MODEL};
|
||||
ef32afed | Moritz Bunkus | |||
5e45f456 | Moritz Bunkus | "SL::DB::Manager::$params{model}"->paginate($self->get_current_paginate_params, args => \%params) if _is_enabled($self);
|
||
ef32afed | Moritz Bunkus | |||
5e45f456 | Moritz Bunkus | # $::lxdebug->dump(0, "GM handler for paginated; params nach modif (is_enabled? " . _is_enabled($self) . ")", \%params);
|
||
ef32afed | Moritz Bunkus | |||
return %params;
|
||||
}
|
||||
5e45f456 | Moritz Bunkus | sub _priv {
|
||
my ($self) = @_;
|
||||
$self->{PRIV()} ||= {};
|
||||
return $self->{PRIV()};
|
||||
}
|
||||
sub _is_enabled {
|
||||
my ($self) = @_;
|
||||
return !_priv($self)->{disabled} && ($self->get_paginate_spec->{ONLY_MAP}->{$self->action_name} || $self->get_paginate_spec->{ONLY_MAP}->{'__ALL__'});
|
||||
}
|
||||
ef32afed | Moritz Bunkus | 1;
|
||
__END__
|
||||
=pod
|
||||
=encoding utf8
|
||||
=head1 NAME
|
||||
SL::Controller::Helper::Paginated - A helper for semi-automatic handling
|
||||
of paginating lists of database models in a controller
|
||||
=head1 SYNOPSIS
|
||||
In a controller:
|
||||
use SL::Controller::Helper::GetModels;
|
||||
use SL::Controller::Helper::Paginated;
|
||||
__PACKAGE__->make_paginated(
|
||||
MODEL => 'BackgroundJobHistory',
|
||||
ONLY => [ qw(list) ],
|
||||
FORM_PARAMS => [ qw(page per_page) ],
|
||||
);
|
||||
sub action_list {
|
||||
my ($self) = @_;
|
||||
my $paginated_models = $self->get_models;
|
||||
$self->render('controller/list', ENTRIES => $paginated_models);
|
||||
}
|
||||
In said template:
|
||||
[% USE L %]
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
...
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
[% FOREACH entry = ENTRIES %]
|
||||
<tr>
|
||||
...
|
||||
</tr>
|
||||
[% END %]
|
||||
</tbody>
|
||||
</table>
|
||||
[% L.paginate_controls %]
|
||||
=head1 OVERVIEW
|
||||
This specialized helper module enables controllers to display a
|
||||
paginatable list of database models with as few lines as possible. It
|
||||
can also be combined trivially with the L<SL::Controller::Sorted>
|
||||
helper for sortable lists.
|
||||
For this to work the controller has to provide the information which
|
||||
indexes are eligible for paginateing etc. by a call to
|
||||
L<make_paginated> at compile time.
|
||||
The underlying functionality that enables the use of more than just
|
||||
the paginate helper is provided by the controller helper
|
||||
C<GetModels>. See the documentation for L<SL::Controller::Sorted> for
|
||||
more information on it.
|
||||
A template can use the method C<paginate_controls> from the layout
|
||||
helper module C<L> which renders the links for navigation between the
|
||||
pages.
|
||||
This module requires that the Rose model managers use their C<Paginated>
|
||||
helper.
|
||||
The C<Paginated> helper hooks into the controller call to the action via
|
||||
a C<run_before> hook. This is done so that it can remember the paginate
|
||||
parameters that were used in the current view.
|
||||
=head1 PACKAGE FUNCTIONS
|
||||
=over 4
|
||||
=item C<make_paginated %paginate_spec>
|
||||
This function must be called by a controller at compile time. It is
|
||||
uesd to set the various parameters required for this helper to do its
|
||||
magic.
|
||||
The hash C<%paginate_spec> can include the following parameters:
|
||||
=over 4
|
||||
=item * C<MODEL>
|
||||
Optional. A string: the name of the Rose database model that is used
|
||||
as a default in certain cases. If this parameter is missing then it is
|
||||
derived from the controller's package (e.g. for the controller
|
||||
C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
|
||||
C<BackgroundJobHistory>).
|
||||
a99a31d6 | Moritz Bunkus | =item * C<PAGINATE_ARGS>
|
||
Optional. Either a code reference or the name of function to be called
|
||||
on the controller importing this helper.
|
||||
If this funciton is given then the paginate helper calls it whenever
|
||||
it has to count the total number of models for calculating the number
|
||||
of pages to display. The function must return a hash reference with
|
||||
elements suitable for passing to a Rose model manager's C<get_all>
|
||||
function.
|
||||
This can be used e.g. when filtering is used.
|
||||
ef32afed | Moritz Bunkus | =item * C<PER_PAGE>
|
||
Optional. An integer: the number of models to return per page.
|
||||
Defaults to the underlying database model's default number of models
|
||||
per page.
|
||||
=item * C<FORM_PARAMS>
|
||||
Optional. An array reference with exactly two strings that name the
|
||||
indexes in C<$::form> in which the current page's number (the first
|
||||
element in the array) and the number of models per page (the second
|
||||
element in the array) are stored.
|
||||
Defaults to the values C<page> and C<per_page> if missing.
|
||||
=item * C<ONLY>
|
||||
Optional. An array reference containing a list of action names for
|
||||
which the paginate parameters should be saved. If missing or empty then
|
||||
all actions invoked on the controller are monitored.
|
||||
=back
|
||||
=back
|
||||
=head1 INSTANCE FUNCTIONS
|
||||
These functions are called on a controller instance.
|
||||
=over 4
|
||||
=item C<get_paginate_spec>
|
||||
Returns a hash containing the currently active paginate
|
||||
parameters. The following keys are returned:
|
||||
=over 4
|
||||
=item * C<page>
|
||||
The currently active page number (numbering starts at 1).
|
||||
=item * C<per_page>
|
||||
Number of models per page (at least 1).
|
||||
=item * C<num_pages>
|
||||
Number of pages to display (at least 1).
|
||||
=item * C<common_pages>
|
||||
An array reference with one hash reference for each possible
|
||||
page. Each hash ref contains the keys C<active> (C<1> if that page is
|
||||
the currently active page), C<page> (the page number this hash
|
||||
reference describes) and C<visible> (whether or not it should be
|
||||
displayed).
|
||||
=back
|
||||
=item C<get_current_paginate_params>
|
||||
Returns a hash reference to the paginate spec structure given in the call
|
||||
to L<make_paginated> after normalization (hash reference construction,
|
||||
applying default parameters etc).
|
||||
5e45f456 | Moritz Bunkus | =item C<disable_pagination>
|
||
Disable pagination for the duration of the current action. Can be used
|
||||
when using the attribute C<ONLY> to L<make_paginated> does not
|
||||
cover all cases.
|
||||
ef32afed | Moritz Bunkus | =back
|
||
=head1 BUGS
|
||||
Nothing here yet.
|
||||
=head1 AUTHOR
|
||||
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
|
||||
=cut
|