Kosmetik: Alignment, Einrückung
[kivitendo-erp.git] / SL / Controller / Helper / Paginated.pm
index f4a319d..0db285f 100644 (file)
@@ -3,25 +3,29 @@ 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);
+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);
 
 use constant PRIV => '__paginatedhelper_priv';
 
-my $controller_paginate_spec;
+use List::Util qw(min);
+
+my %controller_paginate_spec;
 
 sub make_paginated {
-  my ($class, %specs)       = @_;
+  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};
+  $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{PAGINATE_ARGS}           ||= '__FILTER__';
+  $specs{ONLY}                    ||= [];
+  $specs{ONLY}                      = [ $specs{ONLY} ] if !ref $specs{ONLY};
+  $specs{ONLY_MAP}                  = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 };
 
-  $controller_paginate_spec = \%specs;
+  $controller_paginate_spec{$class} = \%specs;
 
-  my %hook_params           = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
+  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(
@@ -37,7 +41,7 @@ sub make_paginated {
 sub get_paginate_spec {
   my ($class_or_self) = @_;
 
-  return $controller_paginate_spec;
+  return $controller_paginate_spec{ref($class_or_self) || $class_or_self};
 }
 
 sub get_current_paginate_params {
@@ -45,7 +49,7 @@ sub get_current_paginate_params {
 
   my $spec              = $self->get_paginate_spec;
 
-  my $priv              = $self->{PRIV()} || {};
+  my $priv              = _priv($self);
   $params{page}         = $priv->{page}     unless defined $params{page};
   $params{per_page}     = $priv->{per_page} unless defined $params{per_page};
 
@@ -54,16 +58,23 @@ sub get_current_paginate_params {
     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},
-  );
+  # try to use Filtered if available and nothing else is configured, but don't
+  # blow up if the controller does not use Filtered
+  my %paginate_args     = ref($spec->{PAGINATE_ARGS}) eq 'CODE'       ? %{ $spec->{PAGINATE_ARGS}->($self) }
+                        :     $spec->{PAGINATE_ARGS}  eq '__FILTER__'
+                           && $self->can('get_current_filter_params') ? $self->get_current_filter_params
+                        :     $spec->{PAGINATE_ARGS}  ne '__FILTER__' ? do { my $sub = $spec->{PAGINATE_ARGS}; %{ $self->$sub() } }
+                        :                                               ();
+  my $calculated_params = "SL::DB::Manager::$spec->{MODEL}"->paginate(%paginate_params, args => \%paginate_args);
 
-  # $::lxdebug->dump(0, "get_current_paginate_params: ", \%paginate_params);
+  # $::lxdebug->dump(0, "get_current_paginate_params: ", $calculated_params);
+
+  return %{ $calculated_params };
+}
 
-  return %paginate_params;
+sub disable_pagination {
+  my ($self)               = @_;
+  _priv($self)->{disabled} = 1;
 }
 
 #
@@ -73,6 +84,8 @@ sub get_current_paginate_params {
 sub _save_current_paginate_params {
   my ($self)        = @_;
 
+  return if !_is_enabled($self);
+
   my $paginate_spec = $self->get_paginate_spec;
   $self->{PRIV()}   = {
     page            => $::form->{ $paginate_spec->{FORM_PARAMS}->[0] } || 1,
@@ -84,9 +97,9 @@ sub _save_current_paginate_params {
 
 sub _callback_handler_for_paginated {
   my ($self, %params) = @_;
-  my $priv            = $self->{PRIV()} || {};
+  my $priv            = _priv($self);
 
-  if ($priv->{page}) {
+  if (_is_enabled($self) && $priv->{page}) {
     my $paginate_spec                             = $self->get_paginate_spec;
     $params{ $paginate_spec->{FORM_PARAMS}->[0] } = $priv->{page};
     $params{ $paginate_spec->{FORM_PARAMS}->[1] } = $priv->{per_page} if $priv->{per_page};
@@ -99,15 +112,27 @@ sub _callback_handler_for_paginated {
 
 sub _get_models_handler_for_paginated {
   my ($self, %params)    = @_;
-  $params{model}       ||= $self->get_paginate_spec->{MODEL};
+  my $spec               = $self->get_paginate_spec;
+  $params{model}       ||= $spec->{MODEL};
 
-  "SL::DB::Manager::$params{model}"->paginate($self->get_current_paginate_params, args => \%params);
+  "SL::DB::Manager::$params{model}"->paginate($self->get_current_paginate_params, args => \%params) if _is_enabled($self);
 
-  # $::lxdebug->dump(0, "GM handler for paginated; params nach modif:", \%params);
+  # $::lxdebug->dump(0, "GM handler for paginated; params nach modif (is_enabled? " . _is_enabled($self) . ")", \%params);
 
   return %params;
 }
 
+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__'});
+}
+
 1;
 __END__
 
@@ -211,6 +236,19 @@ derived from the controller's package (e.g. for the controller
 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
 C<BackgroundJobHistory>).
 
+=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.
+
 =item * C<PER_PAGE>
 
 Optional. An integer: the number of models to return per page.
@@ -278,6 +316,12 @@ 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).
 
+=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.
+
 =back
 
 =head1 BUGS