f0d617d3174160ac5452349c090fd3d88cebf160
[kivitendo-erp.git] / SL / Controller / Helper / Paginated.pm
1 package SL::Controller::Helper::Paginated;
2
3 use strict;
4
5 use Exporter qw(import);
6 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);
7
8 my ($controller_paginate_spec, $current_page, $current_per_page);
9
10 sub make_paginated {
11   my ($class, %specs)       = @_;
12
13   $specs{MODEL}           ||=  $class->_controller_name;
14   $specs{MODEL}             =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
15   $specs{PER_PAGE}        ||= "SL::DB::Manager::$specs{MODEL}"->default_objects_per_page;
16   $specs{FORM_PARAMS}     ||= [ qw(page per_page) ];
17   $specs{ONLY}            ||= [];
18   $specs{ONLY}              = [ $specs{ONLY} ] if !ref $specs{ONLY};
19
20   $controller_paginate_spec = \%specs;
21
22   my %hook_params           = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
23   $class->run_before('_save_current_paginate_params', %hook_params);
24
25   SL::Controller::Helper::GetModels::register_get_models_handlers(
26     $class,
27     callback   => '_callback_handler_for_paginated',
28     get_models => '_get_models_handler_for_paginated',
29     ONLY       => $specs{ONLY},
30   );
31
32   # $::lxdebug->dump(0, "CONSPEC", \%specs);
33 }
34
35 sub get_paginate_spec {
36   my ($class_or_self) = @_;
37
38   return $controller_paginate_spec;
39 }
40
41 sub get_current_paginate_params {
42   my ($self, %params)   = @_;
43
44   my $spec              = $self->get_paginate_spec;
45
46   $params{page}         = $current_page     unless defined $params{page};
47   $params{per_page}     = $current_per_page unless defined $params{per_page};
48
49   my %paginate_params   =  (
50     page                => ($params{page}     * 1) || 1,
51     per_page            => ($params{per_page} * 1) || $spec->{PER_PAGE},
52   );
53
54   my $calculated_params = "SL::DB::Manager::$spec->{MODEL}"->paginate(%paginate_params, args => {});
55   %paginate_params      = (
56     %paginate_params,
57     num_pages    => $calculated_params->{max},
58     common_pages => $calculated_params->{common},
59   );
60
61   # $::lxdebug->dump(0, "get_current_paginate_params: ", \%paginate_params);
62
63   return %paginate_params;
64 }
65
66 #
67 # private functions
68 #
69
70 sub _save_current_paginate_params {
71   my ($self)        = @_;
72
73   my $paginate_spec = $self->get_paginate_spec;
74   $current_page     = $::form->{ $paginate_spec->{FORM_PARAMS}->[0] } || 1;
75   $current_per_page = $::form->{ $paginate_spec->{FORM_PARAMS}->[1] } * 1;
76
77   # $::lxdebug->message(0, "saving current paginate params to $current_page / $current_per_page");
78 }
79
80 sub _callback_handler_for_paginated {
81   my ($self, %params) = @_;
82
83   if ($current_page) {
84     my $paginate_spec                             = $self->get_paginate_spec;
85     $params{ $paginate_spec->{FORM_PARAMS}->[0] } = $current_page;
86     $params{ $paginate_spec->{FORM_PARAMS}->[1] } = $current_per_page if $current_per_page;
87   }
88
89   # $::lxdebug->dump(0, "CB handler for paginated; params nach modif:", \%params);
90
91   return %params;
92 }
93
94 sub _get_models_handler_for_paginated {
95   my ($self, %params)    = @_;
96   $params{model}       ||= $self->get_paginate_spec->{MODEL};
97
98   "SL::DB::Manager::$params{model}"->paginate($self->get_current_paginate_params, args => \%params);
99
100   # $::lxdebug->dump(0, "GM handler for paginated; params nach modif:", \%params);
101
102   return %params;
103 }
104
105 1;
106 __END__
107
108 =pod
109
110 =encoding utf8
111
112 =head1 NAME
113
114 SL::Controller::Helper::Paginated - A helper for semi-automatic handling
115 of paginating lists of database models in a controller
116
117 =head1 SYNOPSIS
118
119 In a controller:
120
121   use SL::Controller::Helper::GetModels;
122   use SL::Controller::Helper::Paginated;
123
124   __PACKAGE__->make_paginated(
125     MODEL       => 'BackgroundJobHistory',
126     ONLY        => [ qw(list) ],
127     FORM_PARAMS => [ qw(page per_page) ],
128   );
129
130   sub action_list {
131     my ($self) = @_;
132
133     my $paginated_models = $self->get_models;
134     $self->render('controller/list', ENTRIES => $paginated_models);
135   }
136
137 In said template:
138
139   [% USE L %]
140
141   <table>
142    <thead>
143     <tr>
144      ...
145     </tr>
146    </thead>
147
148    <tbody>
149     [% FOREACH entry = ENTRIES %]
150      <tr>
151       ...
152      </tr>
153     [% END %]
154    </tbody>
155   </table>
156
157   [% L.paginate_controls %]
158
159 =head1 OVERVIEW
160
161 This specialized helper module enables controllers to display a
162 paginatable list of database models with as few lines as possible. It
163 can also be combined trivially with the L<SL::Controller::Sorted>
164 helper for sortable lists.
165
166 For this to work the controller has to provide the information which
167 indexes are eligible for paginateing etc. by a call to
168 L<make_paginated> at compile time.
169
170 The underlying functionality that enables the use of more than just
171 the paginate helper is provided by the controller helper
172 C<GetModels>. See the documentation for L<SL::Controller::Sorted> for
173 more information on it.
174
175 A template can use the method C<paginate_controls> from the layout
176 helper module C<L> which renders the links for navigation between the
177 pages.
178
179 This module requires that the Rose model managers use their C<Paginated>
180 helper.
181
182 The C<Paginated> helper hooks into the controller call to the action via
183 a C<run_before> hook. This is done so that it can remember the paginate
184 parameters that were used in the current view.
185
186 =head1 PACKAGE FUNCTIONS
187
188 =over 4
189
190 =item C<make_paginated %paginate_spec>
191
192 This function must be called by a controller at compile time. It is
193 uesd to set the various parameters required for this helper to do its
194 magic.
195
196 The hash C<%paginate_spec> can include the following parameters:
197
198 =over 4
199
200 =item * C<MODEL>
201
202 Optional. A string: the name of the Rose database model that is used
203 as a default in certain cases. If this parameter is missing then it is
204 derived from the controller's package (e.g. for the controller
205 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
206 C<BackgroundJobHistory>).
207
208 =item * C<PER_PAGE>
209
210 Optional. An integer: the number of models to return per page.
211
212 Defaults to the underlying database model's default number of models
213 per page.
214
215 =item * C<FORM_PARAMS>
216
217 Optional. An array reference with exactly two strings that name the
218 indexes in C<$::form> in which the current page's number (the first
219 element in the array) and the number of models per page (the second
220 element in the array) are stored.
221
222 Defaults to the values C<page> and C<per_page> if missing.
223
224 =item * C<ONLY>
225
226 Optional. An array reference containing a list of action names for
227 which the paginate parameters should be saved. If missing or empty then
228 all actions invoked on the controller are monitored.
229
230 =back
231
232 =back
233
234 =head1 INSTANCE FUNCTIONS
235
236 These functions are called on a controller instance.
237
238 =over 4
239
240 =item C<get_paginate_spec>
241
242 Returns a hash containing the currently active paginate
243 parameters. The following keys are returned:
244
245 =over 4
246
247 =item * C<page>
248
249 The currently active page number (numbering starts at 1).
250
251 =item * C<per_page>
252
253 Number of models per page (at least 1).
254
255 =item * C<num_pages>
256
257 Number of pages to display (at least 1).
258
259 =item * C<common_pages>
260
261 An array reference with one hash reference for each possible
262 page. Each hash ref contains the keys C<active> (C<1> if that page is
263 the currently active page), C<page> (the page number this hash
264 reference describes) and C<visible> (whether or not it should be
265 displayed).
266
267 =back
268
269 =item C<get_current_paginate_params>
270
271 Returns a hash reference to the paginate spec structure given in the call
272 to L<make_paginated> after normalization (hash reference construction,
273 applying default parameters etc).
274
275 =back
276
277 =head1 BUGS
278
279 Nothing here yet.
280
281 =head1 AUTHOR
282
283 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
284
285 =cut