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