Doku cleanup
[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 init_form_params {
127   [ qw(sort_by sort_dir) ]
128 }
129
130 1;
131 __END__
132
133 =pod
134
135 =encoding utf8
136
137 =head1 NAME
138
139 SL::Controller::Helper::Sorted - A helper for semi-automatic handling
140 of sorting lists of database models in a controller
141
142 =head1 SYNOPSIS
143
144 In a controller:
145
146   SL::Controller::Helper::GetModels->new(
147     ...
148     sorted => {
149       _default => {
150         by  => 'run_at',
151         dir => 1,
152       },
153       error        => $::locale->text('Error'),
154       package_name => $::locale->text('Package name'),
155       run_at       => $::locale->text('Run at'),
156     },
157   );
158
159 In template:
160
161   [% USE L %]
162
163   <table>
164    <tr>
165     <th>[% L.sortable_table_header('package_name') %]</th>
166     <th>[% L.sortable_table_header('run_at') %]</th>
167     <th>[% L.sortable_table_header('error') %]</th>
168    </tr>
169
170    [% FOREACH entry = ENTRIES %]
171     <tr>
172      <td>[% HTML.escape(entry.package_name) %]</td>
173      <td>[% HTML.escape(entry.run_at) %]</td>
174      <td>[% HTML.escape(entry.error) %]</td>
175     </tr>
176    [% END %]
177   </table>
178
179 =head1 OVERVIEW
180
181 This C<GetModels> plugin enables controllers to display a
182 sortable list of database models with as few lines as possible.
183
184 For this to work the controller has to provide the information which
185 indexes are eligible for sorting etc. through it's configuration of
186 C<GetModels>.
187
188 A template can then use the method C<sortable_table_header> from the layout
189 helper module C<L>.
190
191 This module requires that the Rose model managers use their
192 C<SL::DB::Helper::Sorted> helper.
193
194 =head1 OPTIONS
195
196 =over 4
197
198 =item * C<_default HASHREF>
199
200 Optional. If it exists, it is expected to contain the keys C<by> and C<dir> and
201 will be used to set the default sorting if nothing is found in C<source>.
202
203 Defaults to the underlying database model's default.
204
205 =item * C<form_params>
206
207 Optional. An array reference with exactly two strings that name the
208 indexes in C<source> in which the sort index (the first element in
209 the array) and sort direction (the second element in the array) are
210 stored.
211
212 Defaults to the values C<sort_by> and C<sort_dir> if missing.
213
214 =back
215
216 All other keys can be used for sorting. Each value to such a key can be either
217 a string or a hash reference containing certain elements. If the value is only
218 a string then such a hash reference is constructed, and the string is
219 used as the value for the C<title> key.
220
221 These possible elements are:
222
223 =over 4
224
225 =item * C<title>
226
227 Required. A user-displayable title to be used by functions like the
228 layout helper's C<sortable_table_header>. Does not have a default
229 value.
230
231 Note that this string must be the untranslated English version of the
232 string. The titles will be translated whenever they're requested.
233
234 =item * C<model>
235
236 Optional. The name of a Rose database model this sort index refers
237 to. If missing then the value of C<$sort_spec{MODEL}> is used.
238
239 =item * C<model_column>
240
241 Optional. The name of the Rose database model column this sort index
242 refers to. It must be one of the columns named by the model's
243 C<Sorted> helper (not to be confused with the controller's C<Sorted>
244 helper!).
245
246 If missing it defaults to the key in C<%sort_spec> for which this hash
247 reference is the value.
248
249 =back
250
251 =back
252
253 =head1 INSTANCE FUNCTIONS
254
255 These functions are called on a C<GetModels> instance and delegating to this plugin.
256
257 =over 4
258
259 =item C<get_sort_spec>
260
261 Returns a hash containing the currently active sort parameters.
262
263 The key C<by> contains the active sort index referring to the
264 C<%sort_spec> given by the configuration.
265
266 The key C<dir> is either C<1> or C<0>.
267
268 =item C<get_current_sort_params>
269
270 Returns a hash reference to the sort spec structure given in the configuration
271 after normalization (hash reference construction, applying default parameters
272 etc).
273
274 =item C<set_report_generator_sort_options %params>
275
276 This function does three things with an instance of
277 L<SL::ReportGenerator>:
278
279 =over 4
280
281 =item 1. it sets the sort indicator,
282
283 =item 2. it sets the the links for those column headers that are
284 sortable and
285
286 =item 3. it adds the C<form_params> fields to the list of variables in
287 the report generator's export options.
288
289 =back
290
291 The report generator instance must be passed as the parameter
292 C<report>. The parameter C<sortable_columns> must be an array
293 reference of column names that are sortable.
294
295 The report generator instance must already have its columns and export
296 options set via calls to its L<SL::ReportGenerator::set_columns> and
297 L<SL::ReportGenerator::set_export_options> functions.
298
299 =back
300
301 =head1 BUGS
302
303 Nothing here yet.
304
305 =head1 AUTHOR
306
307 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
308
309 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
310
311 =cut