From ef32afedff7750a5ef937bad68d7fde5672d5d8a Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Mon, 3 Sep 2012 16:36:28 +0200 Subject: [PATCH] Controller-Helfer zum Paginaten von Listen --- SL/Controller/Helper/Paginated.pm | 285 ++++++++++++++++++ SL/Template/Plugin/L.pm | 34 +++ templates/webpages/common/paginate.html | 16 +- templates/webpages/delivery_plan/_list.html | 2 +- .../webpages/delivery_plan/report_bottom.html | 2 +- 5 files changed, 333 insertions(+), 6 deletions(-) create mode 100644 SL/Controller/Helper/Paginated.pm diff --git a/SL/Controller/Helper/Paginated.pm b/SL/Controller/Helper/Paginated.pm new file mode 100644 index 000000000..f0d617d31 --- /dev/null +++ b/SL/Controller/Helper/Paginated.pm @@ -0,0 +1,285 @@ +package SL::Controller::Helper::Paginated; + +use strict; + +use Exporter qw(import); +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); + +my ($controller_paginate_spec, $current_page, $current_per_page); + +sub make_paginated { + my ($class, %specs) = @_; + + $specs{MODEL} ||= $class->_controller_name; + $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}; + + $controller_paginate_spec = \%specs; + + 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) = @_; + + return $controller_paginate_spec; +} + +sub get_current_paginate_params { + my ($self, %params) = @_; + + my $spec = $self->get_paginate_spec; + + $params{page} = $current_page unless defined $params{page}; + $params{per_page} = $current_per_page unless defined $params{per_page}; + + my %paginate_params = ( + page => ($params{page} * 1) || 1, + per_page => ($params{per_page} * 1) || $spec->{PER_PAGE}, + ); + + my $calculated_params = "SL::DB::Manager::$spec->{MODEL}"->paginate(%paginate_params, args => {}); + %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; +} + +# +# private functions +# + +sub _save_current_paginate_params { + my ($self) = @_; + + my $paginate_spec = $self->get_paginate_spec; + $current_page = $::form->{ $paginate_spec->{FORM_PARAMS}->[0] } || 1; + $current_per_page = $::form->{ $paginate_spec->{FORM_PARAMS}->[1] } * 1; + + # $::lxdebug->message(0, "saving current paginate params to $current_page / $current_per_page"); +} + +sub _callback_handler_for_paginated { + my ($self, %params) = @_; + + if ($current_page) { + my $paginate_spec = $self->get_paginate_spec; + $params{ $paginate_spec->{FORM_PARAMS}->[0] } = $current_page; + $params{ $paginate_spec->{FORM_PARAMS}->[1] } = $current_per_page if $current_per_page; + } + + # $::lxdebug->dump(0, "CB handler for paginated; params nach modif:", \%params); + + return %params; +} + +sub _get_models_handler_for_paginated { + my ($self, %params) = @_; + $params{model} ||= $self->get_paginate_spec->{MODEL}; + + "SL::DB::Manager::$params{model}"->paginate($self->get_current_paginate_params, args => \%params); + + # $::lxdebug->dump(0, "GM handler for paginated; params nach modif:", \%params); + + return %params; +} + +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 %] + + + + + ... + + + + + [% FOREACH entry = ENTRIES %] + + ... + + [% END %] + +
+ + [% 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 +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 at compile time. + +The underlying functionality that enables the use of more than just +the paginate helper is provided by the controller helper +C. See the documentation for L for +more information on it. + +A template can use the method C from the layout +helper module C which renders the links for navigation between the +pages. + +This module requires that the Rose model managers use their C +helper. + +The C helper hooks into the controller call to the action via +a C 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 + +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 + +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 the C would default to +C). + +=item * C + +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 + +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 and C if missing. + +=item * C + +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 + +Returns a hash containing the currently active paginate +parameters. The following keys are returned: + +=over 4 + +=item * C + +The currently active page number (numbering starts at 1). + +=item * C + +Number of models per page (at least 1). + +=item * C + +Number of pages to display (at least 1). + +=item * C + +An array reference with one hash reference for each possible +page. Each hash ref contains the keys C (C<1> if that page is +the currently active page), C (the page number this hash +reference describes) and C (whether or not it should be +displayed). + +=back + +=item C + +Returns a hash reference to the paginate spec structure given in the call +to L after normalization (hash reference construction, +applying default parameters etc). + +=back + +=head1 BUGS + +Nothing here yet. + +=head1 AUTHOR + +Moritz Bunkus Em.bunkus@linet-services.deE + +=cut diff --git a/SL/Template/Plugin/L.pm b/SL/Template/Plugin/L.pm index fdb22b31a..b3eaac9e7 100644 --- a/SL/Template/Plugin/L.pm +++ b/SL/Template/Plugin/L.pm @@ -605,6 +605,33 @@ sub sortable_table_header { return '' . _H($title) . $image . ''; } +sub paginate_controls { + my ($self) = @_; + + my $controller = $self->{CONTEXT}->stash->get('SELF'); + my $paginate_spec = $controller->get_paginate_spec; + my %paginate_params = $controller->get_current_paginate_params; + + my %template_params = ( + pages => { + cur => $paginate_params{page}, + max => $paginate_params{num_pages}, + common => $paginate_params{common_pages}, + }, + url_maker => sub { + my %url_params = _hashify(@_); + $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page}; + $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page}; + + return $controller->get_callback(%url_params); + }, + ); + + my $output; + $controller->_template_obj->process('templates/webpages/common/paginate.html', \%template_params, \$output); + return $output; +} + 1; __END__ @@ -903,6 +930,13 @@ underlying call to L. See the documentation of L for an overview and further usage instructions. +=item C + +Create a set of links used to paginate a list view. + +See the documentation of L for an +overview and further usage instructions. + =back =head2 CONVERSION FUNCTIONS diff --git a/templates/webpages/common/paginate.html b/templates/webpages/common/paginate.html index 8878193f2..750561efb 100644 --- a/templates/webpages/common/paginate.html +++ b/templates/webpages/common/paginate.html @@ -1,10 +1,18 @@ [%- USE T8 %] - +[%- MACRO build_url BLOCK %] + [%- IF base_url %] + [%- base_url %]&page=[% page %] + [%- ELSE %] + [% url_maker('page' => page) %] + [%- END %] +[%- END %] [%- IF pages.max > 1 %] -[%- IF pages.cur > 1 %]« [% 'prev' | $T8 %] [% ELSE %]« [% END %] + +[%- IF pages.cur > 1 %]« [% 'prev' | $T8 %] [% ELSE %]« [% END %] [%- FOR p = pages.common %] [%- NEXT UNLESS p.visible %] - [%- IF p.active %][% p.page %] [% ELSE %][% p.page %] [%- END %] + [%- IF p.active %][% p.page %] [% ELSE %][% p.page %] [%- END %] [%- END %] -[%- IF pages.cur < pages.max %][% 'next' | $T8 %] »[% ELSE %]»[%- END %] +[%- IF pages.cur < pages.max %][% 'next' | $T8 %] »[% ELSE %]»[%- END %] + [%- END %] diff --git a/templates/webpages/delivery_plan/_list.html b/templates/webpages/delivery_plan/_list.html index f0bb2772b..d0d2159d1 100644 --- a/templates/webpages/delivery_plan/_list.html +++ b/templates/webpages/delivery_plan/_list.html @@ -38,7 +38,7 @@ [%- END %] -

[% PROCESS 'common/paginate.html' pages=SELF.pages, base_url=SELF.url_for(action='list', sort_dir=SELF.sort_dir, sort_by=SELF.sort_by) %]

+ [% PROCESS 'common/paginate.html' pages=SELF.pages, base_url=SELF.url_for(action='list', sort_dir=SELF.sort_dir, sort_by=SELF.sort_by) %] [%- END %] diff --git a/templates/webpages/delivery_plan/report_bottom.html b/templates/webpages/delivery_plan/report_bottom.html index 3193961d9..1843c054c 100644 --- a/templates/webpages/delivery_plan/report_bottom.html +++ b/templates/webpages/delivery_plan/report_bottom.html @@ -1,4 +1,4 @@ [% SET report_bottom_url_args = {} %] [% report_bottom_url_args.import(SELF.flat_filter) %] [% report_bottom_url_args.import({action='list', sort_dir=SELF.sort_dir, sort_by=SELF.sort_by}) %] -

[% PROCESS 'common/paginate.html' pages=SELF.pages, base_url=SELF.url_for(report_bottom_url_args) %]

+[% PROCESS 'common/paginate.html' pages=SELF.pages, base_url=SELF.url_for(report_bottom_url_args) %] -- 2.20.1