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