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