get_models callback entefrnt und in den finalize prozess integriert
[kivitendo-erp.git] / SL / Controller / Helper / GetModels / Sorted.pm
1 package SL::Controller::Helper::GetModels::Sorted;
2
3 use strict;
4 use parent 'SL::Controller::Helper::GetModels::Base';
5
6 use Carp;
7 use List::MoreUtils qw(uniq);
8
9 use Rose::Object::MakeMethods::Generic (
10   scalar => [ qw(by dir specs form_data) ],
11   'scalar --get_set_init' => [ qw(form_params) ],
12 );
13
14 sub init {
15   my ($self, %specs) = @_;
16
17   $self->set_get_models(delete $specs{get_models});
18   my %model_sort_spec   = $self->get_models->manager->_sort_spec;
19
20   if (my $default = delete $specs{_default}) {
21     $self->by ($default->{by});
22     $self->dir($default->{dir});
23   } else {
24     $self->by ($model_sort_spec{default}[0]);
25     $self->dir($model_sort_spec{default}[1]);
26   }
27
28   while (my ($column, $spec) = each %specs) {
29     next if $column =~ m/^[A-Z_]+$/;
30
31     $spec = $specs{$column} = { title => $spec } if (ref($spec) || '') ne 'HASH';
32
33     $spec->{model}        ||= $self->get_models->model;
34     $spec->{model_column} ||= $column;
35   }
36   $self->specs(\%specs);
37
38   $self->get_models->register_handlers(
39     callback   => sub { shift; $self->_callback_handler_for_sorted(@_) },
40     get_models => sub { shift; $self->_get_models_handler_for_sorted(@_) },
41   );
42
43 #   $::lxdebug->dump(0, "CONSPEC", \%specs);
44 }
45
46 sub read_params {
47   my ($self, %params) = @_;
48
49   return %{ $self->form_data } if $self->form_data;
50
51   my %sort_params;
52   my ($by, $dir) = @{ $self->form_params };
53   my $source = $self->get_models->source;
54
55   if ($source->{ $by }) {
56     %sort_params = (
57       sort_by  => $source->{$by},
58       sort_dir => defined($source->{$dir}) ? $source->{$dir} * 1 : undef,
59     );
60   } elsif (!$self->by) {
61     %sort_params = %params;
62   } else {
63     %sort_params = (
64       sort_by  => $self->by,
65       sort_dir => $self->dir,
66     );
67   }
68
69   $self->form_data(\%sort_params);
70
71   return %sort_params;
72 }
73
74 sub finalize {
75   my ($self, %params) = @_;
76
77   my %sort_params     = $self->read_params;
78   my $sort_spec       = $self->specs->{ $sort_params{sort_by} };
79
80   $params{sort_by}    = "SL::DB::Manager::$sort_spec->{model}"->make_sort_string(sort_by => $sort_spec->{model_column}, sort_dir => $sort_params{sort_dir});
81
82   %params;
83 }
84
85 sub set_report_generator_sort_options {
86   my ($self, %params) = @_;
87
88   $params{$_} or croak("Missing parameter '$_'") for qw(report sortable_columns);
89
90   my %current_sort_params = $self->read_params;
91
92   foreach my $col (@{ $params{sortable_columns} }) {
93     $params{report}->{columns}->{$col}->{link} = $self->get_models->get_callback(
94       sort_by  => $col,
95       sort_dir => ($current_sort_params{sort_by} eq $col ? 1 - $current_sort_params{sort_dir} : $current_sort_params{sort_dir}),
96     );
97   }
98
99   $params{report}->set_sort_indicator($current_sort_params{sort_by}, 1 - $current_sort_params{sort_dir});
100
101   if ($params{report}->{export}) {
102     $params{report}->{export}->{variable_list} = [ uniq(
103       @{ $params{report}->{export}->{variable_list} },
104       @{ $self->form_params }
105     )];
106   }
107 }
108
109 #
110 # private functions
111 #
112
113 sub _callback_handler_for_sorted {
114   my ($self, %params) = @_;
115   my %spec = $self->read_params;
116
117   if ($spec{sort_by}) {
118     $params{ $self->form_params->[0] } = $spec{sort_by};
119     $params{ $self->form_params->[1] } = $spec{sort_dir};
120   }
121
122   # $::lxdebug->dump(0, "CB handler for sorted; params nach modif:", \%params);
123
124   return %params;
125 }
126
127 #sub _get_models_handler_for_sorted {
128 #  my ($self, %params) = @_;
129 #
130 #
131 #
132 #  # $::lxdebug->dump(0, "GM handler for sorted; params nach modif:", \%params);
133 #
134 #  return %params;
135 #}
136
137
138 sub init_form_params {
139   [ qw(sort_by sort_dir) ]
140 }
141
142 1;
143 __END__
144
145 =pod
146
147 =encoding utf8
148
149 =head1 NAME
150
151 SL::Controller::Helper::Sorted - A helper for semi-automatic handling
152 of sorting lists of database models in a controller
153
154 =head1 SYNOPSIS
155
156 In a controller:
157
158   use SL::Controller::Helper::GetModels;
159   use SL::Controller::Helper::Sorted;
160
161   __PACKAGE__->make_sorted(
162     DEFAULT_BY   => 'run_at',
163     DEFAULT_DIR  => 1,
164     MODEL        => 'BackgroundJobHistory',
165     ONLY         => [ qw(list) ],
166
167     error        => $::locale->text('Error'),
168     package_name => $::locale->text('Package name'),
169     run_at       => $::locale->text('Run at'),
170   );
171
172   sub action_list {
173     my ($self) = @_;
174
175     my $sorted_models = $self->get_models;
176     $self->render('controller/list', ENTRIES => $sorted_models);
177   }
178
179 In said template:
180
181   [% USE L %]
182
183   <table>
184    <tr>
185     <th>[% L.sortable_table_header('package_name') %]</th>
186     <th>[% L.sortable_table_header('run_at') %]</th>
187     <th>[% L.sortable_table_header('error') %]</th>
188    </tr>
189
190    [% FOREACH entry = ENTRIES %]
191     <tr>
192      <td>[% HTML.escape(entry.package_name) %]</td>
193      <td>[% HTML.escape(entry.run_at) %]</td>
194      <td>[% HTML.escape(entry.error) %]</td>
195     </tr>
196    [% END %]
197   </table>
198
199 =head1 OVERVIEW
200
201 This specialized helper module enables controllers to display a
202 sortable list of database models with as few lines as possible.
203
204 For this to work the controller has to provide the information which
205 indexes are eligible for sorting etc. by a call to L<make_sorted> at
206 compile time.
207
208 The underlying functionality that enables the use of more than just
209 the sort helper is provided by the controller helper C<GetModels>. It
210 provides mechanisms for helpers like this one to hook into certain
211 calls made by the controller (C<get_callback> and C<get_models>) so
212 that the specialized helpers can inject their parameters into the
213 calls to e.g. C<SL::DB::Manager::SomeModel::get_all>.
214
215 A template on the other hand can use the method
216 C<sortable_table_header> from the layout helper module C<L>.
217
218 This module requires that the Rose model managers use their C<Sorted>
219 helper.
220
221 The C<Sorted> helper hooks into the controller call to the action via
222 a C<run_before> hook. This is done so that it can remember the sort
223 parameters that were used in the current view.
224
225 =head1 PACKAGE FUNCTIONS
226
227 =over 4
228
229 =item C<make_sorted %sort_spec>
230
231 This function must be called by a controller at compile time. It is
232 uesd to set the various parameters required for this helper to do its
233 magic.
234
235 There are two sorts of keys in the hash C<%sort_spec>. The first kind
236 is written in all upper-case. Those parameters are control
237 parameters. The second kind are all lower-case and represent indexes
238 that can be used for sorting (similar to database column names). The
239 second kind are also the indexes you use in a template when calling
240 C<[% L.sorted_table_header(...) %]>.
241
242 Control parameters include the following:
243
244 =over 4
245
246 =item * C<MODEL>
247
248 Optional. A string: the name of the Rose database model that is used
249 as a default in certain cases. If this parameter is missing then it is
250 derived from the controller's package (e.g. for the controller
251 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
252 C<BackgroundJobHistory>).
253
254 =item * C<DEFAULT_BY>
255
256 Optional. A string: the index to sort by if the user hasn't clicked on
257 any column yet (meaning: if the C<$::form> parameters for sorting do
258 not contain a valid index).
259
260 Defaults to the underlying database model's default sort column name.
261
262 =item * C<DEFAULT_DIR>
263
264 Optional. Default sort direction (ascending for trueish values,
265 descrending for falsish values).
266
267 Defaults to the underlying database model's default sort direction.
268
269 =item * C<FORM_PARAMS>
270
271 Optional. An array reference with exactly two strings that name the
272 indexes in C<$::form> in which the sort index (the first element in
273 the array) and sort direction (the second element in the array) are
274 stored.
275
276 Defaults to the values C<sort_by> and C<sort_dir> if missing.
277
278 =item * C<ONLY>
279
280 Optional. An array reference containing a list of action names for
281 which the sort parameters should be saved. If missing or empty then
282 all actions invoked on the controller are monitored.
283
284 =back
285
286 All keys that are written in all lower-case name indexes that can be
287 used for sorting. Each value to such a key can be either a string or a
288 hash reference containing certain elements. If the value is only a
289 string then such a hash reference is constructed, and the string is
290 used as the value for the C<title> key.
291
292 These possible elements are:
293
294 =over 4
295
296 =item * C<title>
297
298 Required. A user-displayable title to be used by functions like the
299 layout helper's C<sortable_table_header>. Does not have a default
300 value.
301
302 Note that this string must be the untranslated English version of the
303 string. The titles will be translated whenever they're requested.
304
305 =item * C<model>
306
307 Optional. The name of a Rose database model this sort index refers
308 to. If missing then the value of C<$sort_spec{MODEL}> is used.
309
310 =item * C<model_column>
311
312 Optional. The name of the Rose database model column this sort index
313 refers to. It must be one of the columns named by the model's
314 C<Sorted> helper (not to be confused with the controller's C<Sorted>
315 helper!).
316
317 If missing it defaults to the key in C<%sort_spec> for which this hash
318 reference is the value.
319
320 =back
321
322 =back
323
324 =head1 INSTANCE FUNCTIONS
325
326 These functions are called on a controller instance.
327
328 =over 4
329
330 =item C<get_sort_spec>
331
332 Returns a hash containing the currently active sort parameters.
333
334 The key C<by> contains the active sort index referring to the
335 C<%sort_spec> given to L<make_sorted>.
336
337 The key C<dir> is either C<1> or C<0>.
338
339 =item C<get_current_sort_params>
340
341 Returns a hash reference to the sort spec structure given in the call
342 to L<make_sorted> after normalization (hash reference construction,
343 applying default parameters etc).
344
345 =item C<set_report_generator_sort_options %params>
346
347 This function does three things with an instance of
348 L<SL::ReportGenerator>:
349
350 =over 4
351
352 =item 1. it sets the sort indicator,
353
354 =item 2. it sets the the links for those column headers that are
355 sortable and
356
357 =item 3. it adds the C<FORM_PARAMS> fields to the list of variables in
358 the report generator's export options.
359
360 =back
361
362 The report generator instance must be passed as the parameter
363 C<report>. The parameter C<sortable_columns> must be an array
364 reference of column names that are sortable.
365
366 The report generator instance must already have its columns and export
367 options set via calls to its L<SL::ReportGenerator::set_columns> and
368 L<SL::ReportGenerator::set_export_options> functions.
369
370 =back
371
372 =head1 BUGS
373
374 Nothing here yet.
375
376 =head1 AUTHOR
377
378 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
379
380 =cut