1 package SL::Controller::Helper::Sorted;
5 use Exporter qw(import);
6 our @EXPORT = qw(make_sorted get_sort_spec get_current_sort_params _save_current_sort_params _get_models_handler_for_sorted _callback_handler_for_sorted);
8 use constant PRIV => '__sortedhelperpriv';
10 my $controller_sort_spec;
13 my ($class, %specs) = @_;
15 $specs{MODEL} ||= $class->_controller_name;
16 $specs{MODEL} =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
18 while (my ($column, $spec) = each %specs) {
19 next if $column =~ m/^[A-Z_]+$/;
21 $spec = $specs{$column} = { title => $spec } if !ref $spec;
23 $spec->{model} ||= $specs{MODEL};
24 $spec->{model_column} ||= $column;
27 my %model_sort_spec = "SL::DB::Manager::$specs{MODEL}"->_sort_spec;
28 $specs{DEFAULT_DIR} = $specs{DEFAULT_DIR} ? 1 : defined($specs{DEFAULT_DIR}) ? $specs{DEFAULT_DIR} * 1 : $model_sort_spec{default}->[1];
29 $specs{DEFAULT_BY} ||= $model_sort_spec{default}->[0];
30 $specs{FORM_PARAMS} ||= [ qw(sort_by sort_dir) ];
32 $specs{ONLY} = [ $specs{ONLY} ] if !ref $specs{ONLY};
34 $controller_sort_spec = \%specs;
36 my %hook_params = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
37 $class->run_before('_save_current_sort_params', %hook_params);
39 SL::Controller::Helper::GetModels::register_get_models_handlers(
41 callback => '_callback_handler_for_sorted',
42 get_models => '_get_models_handler_for_sorted',
46 # $::lxdebug->dump(0, "CONSPEC", \%specs);
50 my ($class_or_self) = @_;
52 return $controller_sort_spec;
55 sub get_current_sort_params {
56 my ($self, %params) = @_;
58 my $sort_spec = $self->get_sort_spec;
60 if (!$params{sort_by}) {
61 my $priv = $self->{PRIV()} || {};
62 $params{sort_by} = $priv->{by};
63 $params{sort_dir} = $priv->{dir};
66 my $by = $params{sort_by} || $sort_spec->{DEFAULT_BY};
68 dir => defined($params{sort_dir}) ? $params{sort_dir} * 1 : $sort_spec->{DEFAULT_DIR},
69 by => $sort_spec->{$by} ? $by : $sort_spec->{DEFAULT_BY},
79 sub _save_current_sort_params {
82 my $sort_spec = $self->get_sort_spec;
84 by => $::form->{ $sort_spec->{FORM_PARAMS}->[0] },
85 dir => !!$::form->{ $sort_spec->{FORM_PARAMS}->[1] } * 1,
88 # $::lxdebug->message(0, "saving current sort params to " . $self->{PRIV()}->{by} . ' / ' . $self->{PRIV()}->{dir});
91 sub _callback_handler_for_sorted {
92 my ($self, %params) = @_;
94 my $priv = $self->{PRIV()} || {};
96 my $sort_spec = $self->get_sort_spec;
97 $params{ $sort_spec->{FORM_PARAMS}->[0] } = $priv->{by};
98 $params{ $sort_spec->{FORM_PARAMS}->[1] } = $priv->{dir};
101 # $::lxdebug->dump(0, "CB handler for sorted; params nach modif:", \%params);
106 sub _get_models_handler_for_sorted {
107 my ($self, %params) = @_;
109 my %sort_params = $self->get_current_sort_params;
110 my $sort_spec = $self->get_sort_spec->{ $sort_params{by} };
112 $params{model} = $sort_spec->{model};
113 $params{sort_by} = "SL::DB::Manager::$params{model}"->make_sort_string(sort_by => $sort_spec->{model_column}, sort_dir => $sort_params{dir});
115 # $::lxdebug->dump(0, "GM handler for sorted; params nach modif:", \%params);
129 SL::Controller::Helper::Sorted - A helper for semi-automatic handling
130 of sorting lists of database models in a controller
136 use SL::Controller::Helper::GetModels;
137 use SL::Controller::Helper::Sorted;
139 __PACKAGE__->make_sorted(
140 DEFAULT_BY => 'run_at',
142 MODEL => 'BackgroundJobHistory',
143 ONLY => [ qw(list) ],
145 error => $::locale->text('Error'),
146 package_name => $::locale->text('Package name'),
147 run_at => $::locale->text('Run at'),
153 my $sorted_models = $self->get_models;
154 $self->render('controller/list', ENTRIES => $sorted_models);
163 <th>[% L.sortable_table_header('package_name') %]</th>
164 <th>[% L.sortable_table_header('run_at') %]</th>
165 <th>[% L.sortable_table_header('error') %]</th>
168 [% FOREACH entry = ENTRIES %]
170 <td>[% HTML.escape(entry.package_name) %]</td>
171 <td>[% HTML.escape(entry.run_at) %]</td>
172 <td>[% HTML.escape(entry.error) %]</td>
179 This specialized helper module enables controllers to display a
180 sortable list of database models with as few lines as possible.
182 For this to work the controller has to provide the information which
183 indexes are eligible for sorting etc. by a call to L<make_sorted> at
186 The underlying functionality that enables the use of more than just
187 the sort helper is provided by the controller helper C<GetModels>. It
188 provides mechanisms for helpers like this one to hook into certain
189 calls made by the controller (C<get_callback> and C<get_models>) so
190 that the specialized helpers can inject their parameters into the
191 calls to e.g. C<SL::DB::Manager::SomeModel::get_all>.
193 A template on the other hand can use the method
194 C<sortable_table_header> from the layout helper module C<L>.
196 This module requires that the Rose model managers use their C<Sorted>
199 The C<Sorted> helper hooks into the controller call to the action via
200 a C<run_before> hook. This is done so that it can remember the sort
201 parameters that were used in the current view.
203 =head1 PACKAGE FUNCTIONS
207 =item C<make_sorted %sort_spec>
209 This function must be called by a controller at compile time. It is
210 uesd to set the various parameters required for this helper to do its
213 There are two sorts of keys in the hash C<%sort_spec>. The first kind
214 is written in all upper-case. Those parameters are control
215 parameters. The second kind are all lower-case and represent indexes
216 that can be used for sorting (similar to database column names). The
217 second kind are also the indexes you use in a template when calling
218 C<[% L.sorted_table_header(...) %]>.
220 Control parameters include the following:
226 Optional. A string: the name of the Rose database model that is used
227 as a default in certain cases. If this parameter is missing then it is
228 derived from the controller's package (e.g. for the controller
229 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
230 C<BackgroundJobHistory>).
232 =item * C<DEFAULT_BY>
234 Optional. A string: the index to sort by if the user hasn't clicked on
235 any column yet (meaning: if the C<$::form> parameters for sorting do
236 not contain a valid index).
238 Defaults to the underlying database model's default sort column name.
240 =item * C<DEFAULT_DIR>
242 Optional. Default sort direction (ascending for trueish values,
243 descrending for falsish values).
245 Defaults to the underlying database model's default sort direction.
247 =item * C<FORM_PARAMS>
249 Optional. An array reference with exactly two strings that name the
250 indexes in C<$::form> in which the sort index (the first element in
251 the array) and sort direction (the second element in the array) are
254 Defaults to the values C<sort_by> and C<sort_dir> if missing.
258 Optional. An array reference containing a list of action names for
259 which the sort parameters should be saved. If missing or empty then
260 all actions invoked on the controller are monitored.
264 All keys that are written in all lower-case name indexes that can be
265 used for sorting. Each value to such a key can be either a string or a
266 hash reference containing certain elements. If the value is only a
267 string then such a hash reference is constructed, and the string is
268 used as the value for the C<title> key.
270 These possible elements are:
276 Required. A user-displayable title to be used by functions like the
277 layout helper's C<sortable_table_header>. Does not have a default
282 Optional. The name of a Rose database model this sort index refers
283 to. If missing then the value of C<$sort_spec{MODEL}> is used.
285 =item * C<model_column>
287 Optional. The name of the Rose database model column this sort index
288 refers to. It must be one of the columns named by the model's
289 C<Sorted> helper (not to be confused with the controller's C<Sorted>
292 If missing it defaults to the key in C<%sort_spec> for which this hash
293 reference is the value.
299 =head1 INSTANCE FUNCTIONS
301 These functions are called on a controller instance.
305 =item C<get_sort_spec>
307 Returns a hash containing the currently active sort parameters.
309 The key C<by> contains the active sort index referring to the
310 C<%sort_spec> given to L<make_sorted>.
312 The key C<dir> is either C<1> or C<0>.
314 =item C<get_current_sort_params>
316 Returns a hash reference to the sort spec structure given in the call
317 to L<make_sorted> after normalization (hash reference construction,
318 applying default parameters etc).
328 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>