1 package SL::Controller::Helper::GetModels::Sorted;
4 use parent 'SL::Controller::Helper::GetModels::Base';
7 use List::MoreUtils qw(uniq);
9 use Rose::Object::MakeMethods::Generic (
10 scalar => [ qw(by dir specs form_data) ],
11 'scalar --get_set_init' => [ qw(form_params) ],
15 my ($self, %specs) = @_;
17 $self->set_get_models(delete $specs{get_models});
18 my %model_sort_spec = $self->get_models->manager->_sort_spec;
20 if (my $default = delete $specs{_default}) {
21 $self->by ($default->{by});
22 $self->dir($default->{dir});
24 $self->by ($model_sort_spec{default}[0]);
25 $self->dir($model_sort_spec{default}[1]);
28 while (my ($column, $spec) = each %specs) {
29 next if $column =~ m/^[A-Z_]+$/;
31 $spec = $specs{$column} = { title => $spec } if (ref($spec) || '') ne 'HASH';
33 $spec->{model} ||= $self->get_models->model;
34 $spec->{model_column} ||= $column;
36 $self->specs(\%specs);
38 $self->get_models->register_handlers(
39 callback => sub { shift; $self->_callback_handler_for_sorted(@_) },
42 # $::lxdebug->dump(0, "CONSPEC", \%specs);
46 my ($self, %params) = @_;
48 return %{ $self->form_data } if $self->form_data;
51 my ($by, $dir) = @{ $self->form_params };
52 my $source = $self->get_models->source;
54 if ($source->{ $by }) {
56 sort_by => $source->{$by},
57 sort_dir => defined($source->{$dir}) ? $source->{$dir} * 1 : undef,
59 } elsif (!$self->by) {
60 %sort_params = %params;
64 sort_dir => $self->dir,
68 $self->form_data(\%sort_params);
74 my ($self, %params) = @_;
76 my %sort_params = $self->read_params;
77 my $sort_spec = $self->specs->{ $sort_params{sort_by} };
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});
84 sub set_report_generator_sort_options {
85 my ($self, %params) = @_;
87 $params{$_} or croak("Missing parameter '$_'") for qw(report sortable_columns);
89 my %current_sort_params = $self->read_params;
91 foreach my $col (@{ $params{sortable_columns} }) {
92 $params{report}->{columns}->{$col}->{link} = $self->get_models->get_callback(
94 sort_dir => ($current_sort_params{sort_by} eq $col ? 1 - $current_sort_params{sort_dir} : $current_sort_params{sort_dir}),
98 $params{report}->set_sort_indicator($current_sort_params{sort_by}, 1 - $current_sort_params{sort_dir});
100 if ($params{report}->{export}) {
101 $params{report}->{export}->{variable_list} = [ uniq(
102 @{ $params{report}->{export}->{variable_list} },
103 @{ $self->form_params }
112 sub _callback_handler_for_sorted {
113 my ($self, %params) = @_;
114 my %spec = $self->read_params;
116 if ($spec{sort_by}) {
117 $params{ $self->form_params->[0] } = $spec{sort_by};
118 $params{ $self->form_params->[1] } = $spec{sort_dir};
121 # $::lxdebug->dump(0, "CB handler for sorted; params nach modif:", \%params);
126 sub init_form_params {
127 [ qw(sort_by sort_dir) ]
139 SL::Controller::Helper::Sorted - A helper for semi-automatic handling
140 of sorting lists of database models in a controller
146 use SL::Controller::Helper::GetModels;
147 use SL::Controller::Helper::Sorted;
149 __PACKAGE__->make_sorted( # update this
150 DEFAULT_BY => 'run_at',
152 MODEL => 'BackgroundJobHistory',
153 ONLY => [ qw(list) ],
155 error => $::locale->text('Error'),
156 package_name => $::locale->text('Package name'),
157 run_at => $::locale->text('Run at'),
163 my $sorted_models = $self->get_models;
164 $self->render('controller/list', ENTRIES => $sorted_models);
173 <th>[% L.sortable_table_header('package_name') %]</th> # models
174 <th>[% L.sortable_table_header('run_at') %]</th>
175 <th>[% L.sortable_table_header('error') %]</th>
178 [% FOREACH entry = ENTRIES %]
180 <td>[% HTML.escape(entry.package_name) %]</td>
181 <td>[% HTML.escape(entry.run_at) %]</td>
182 <td>[% HTML.escape(entry.error) %]</td>
189 This specialized helper module enables controllers to display a
190 sortable list of database models with as few lines as possible.
192 For this to work the controller has to provide the information which
193 indexes are eligible for sorting etc. by a call to L<make_sorted> at #not compiletime
196 The underlying functionality that enables the use of more than just
197 the sort helper is provided by the controller helper C<GetModels>. It
198 provides mechanisms for helpers like this one to hook into certain
199 calls made by the controller (C<get_callback> and C<get_models>) so
200 that the specialized helpers can inject their parameters into the
201 calls to e.g. C<SL::DB::Manager::SomeModel::get_all>.
203 A template on the other hand can use the method
204 C<sortable_table_header> from the layout helper module C<L>.
206 This module requires that the Rose model managers use their C<Sorted>
209 The C<Sorted> helper hooks into the controller call to the action via
210 a C<run_before> hook. This is done so that it can remember the sort
211 parameters that were used in the current view.
213 =head1 PACKAGE FUNCTIONS
217 =item C<make_sorted %sort_spec> # meh complete rewrite
219 This function must be called by a controller at compile time. It is
220 uesd to set the various parameters required for this helper to do its
223 There are two sorts of keys in the hash C<%sort_spec>. The first kind
224 is written in all upper-case. Those parameters are control
225 parameters. The second kind are all lower-case and represent indexes
226 that can be used for sorting (similar to database column names). The
227 second kind are also the indexes you use in a template when calling
228 C<[% L.sorted_table_header(...) %]>.
230 Control parameters include the following:
236 Optional. A string: the name of the Rose database model that is used
237 as a default in certain cases. If this parameter is missing then it is
238 derived from the controller's package (e.g. for the controller
239 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
240 C<BackgroundJobHistory>).
242 =item * C<DEFAULT_BY>
244 Optional. A string: the index to sort by if the user hasn't clicked on
245 any column yet (meaning: if the C<$::form> parameters for sorting do
246 not contain a valid index).
248 Defaults to the underlying database model's default sort column name.
250 =item * C<DEFAULT_DIR>
252 Optional. Default sort direction (ascending for trueish values,
253 descrending for falsish values).
255 Defaults to the underlying database model's default sort direction.
257 =item * C<FORM_PARAMS>
259 Optional. An array reference with exactly two strings that name the
260 indexes in C<$::form> in which the sort index (the first element in
261 the array) and sort direction (the second element in the array) are
264 Defaults to the values C<sort_by> and C<sort_dir> if missing.
268 Optional. An array reference containing a list of action names for
269 which the sort parameters should be saved. If missing or empty then
270 all actions invoked on the controller are monitored.
274 All keys that are written in all lower-case name indexes that can be
275 used for sorting. Each value to such a key can be either a string or a
276 hash reference containing certain elements. If the value is only a
277 string then such a hash reference is constructed, and the string is
278 used as the value for the C<title> key.
280 These possible elements are:
286 Required. A user-displayable title to be used by functions like the
287 layout helper's C<sortable_table_header>. Does not have a default
290 Note that this string must be the untranslated English version of the
291 string. The titles will be translated whenever they're requested.
295 Optional. The name of a Rose database model this sort index refers
296 to. If missing then the value of C<$sort_spec{MODEL}> is used.
298 =item * C<model_column>
300 Optional. The name of the Rose database model column this sort index
301 refers to. It must be one of the columns named by the model's
302 C<Sorted> helper (not to be confused with the controller's C<Sorted>
305 If missing it defaults to the key in C<%sort_spec> for which this hash
306 reference is the value.
312 =head1 INSTANCE FUNCTIONS
314 These functions are called on a controller instance.
318 =item C<get_sort_spec>
320 Returns a hash containing the currently active sort parameters.
322 The key C<by> contains the active sort index referring to the
323 C<%sort_spec> given to L<make_sorted>.
325 The key C<dir> is either C<1> or C<0>.
327 =item C<get_current_sort_params>
329 Returns a hash reference to the sort spec structure given in the call
330 to L<make_sorted> after normalization (hash reference construction,
331 applying default parameters etc).
333 =item C<set_report_generator_sort_options %params>
335 This function does three things with an instance of
336 L<SL::ReportGenerator>:
340 =item 1. it sets the sort indicator,
342 =item 2. it sets the the links for those column headers that are
345 =item 3. it adds the C<FORM_PARAMS> fields to the list of variables in
346 the report generator's export options.
350 The report generator instance must be passed as the parameter
351 C<report>. The parameter C<sortable_columns> must be an array
352 reference of column names that are sortable.
354 The report generator instance must already have its columns and export
355 options set via calls to its L<SL::ReportGenerator::set_columns> and
356 L<SL::ReportGenerator::set_export_options> functions.
366 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>