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 my ($controller_sort_spec, $current_sort_by, $current_sort_dir);
 
  11   my ($class, %specs) = @_;
 
  13   $specs{MODEL} ||=  $class->_controller_name;
 
  14   $specs{MODEL}   =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
 
  16   while (my ($column, $spec) = each %specs) {
 
  17     next if $column =~ m/^[A-Z_]+$/;
 
  19     $spec = $specs{$column} = { title => $spec } if !ref $spec;
 
  21     $spec->{model}        ||= $specs{MODEL};
 
  22     $spec->{model_column} ||= $column;
 
  25   $specs{DEFAULT_DIR}   = $specs{DEFAULT_DIR} || !defined($specs{DEFAULT_DIR}) ? 1 : 0;
 
  26   $specs{DEFAULT_BY}  ||= { "SL::DB::Manager::$specs{MODEL}"->_sort_spec }->{default}->[0];
 
  27   $specs{FORM_PARAMS} ||= [ qw(sort_by sort_dir) ];
 
  29   $specs{ONLY}          = [ $specs{ONLY} ] if !ref $specs{ONLY};
 
  31   $controller_sort_spec = \%specs;
 
  33   my %hook_params = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
 
  34   $class->run_before('_save_current_sort_params', %hook_params);
 
  36   SL::Controller::Helper::GetModels::register_get_models_handlers(
 
  38     callback   => '_callback_handler_for_sorted',
 
  39     get_models => '_get_models_handler_for_sorted',
 
  43   # $::lxdebug->dump(0, "CONSPEC", \%specs);
 
  47   my ($class_or_self) = @_;
 
  49   return $controller_sort_spec;
 
  52 sub get_current_sort_params {
 
  53   my ($self, %params) = @_;
 
  55   my $sort_spec = $self->get_sort_spec;
 
  57   if (!$params{sort_by}) {
 
  58     $params{sort_by}  = $current_sort_by;
 
  59     $params{sort_dir} = $current_sort_dir;
 
  62   my $by          = $params{sort_by} || $sort_spec->{DEFAULT_BY};
 
  64     dir => defined($params{sort_dir}) ? $params{sort_dir} * 1 : $sort_spec->{DEFAULT_DIR},
 
  65     by  => $sort_spec->{$by} ? $by : $sort_spec->{DEFAULT_BY},
 
  75 sub _save_current_sort_params {
 
  78   my $sort_spec     = $self->get_sort_spec;
 
  79   $current_sort_by  =   $::form->{ $sort_spec->{FORM_PARAMS}->[0] };
 
  80   $current_sort_dir = !!$::form->{ $sort_spec->{FORM_PARAMS}->[1] } * 1;
 
  82   # $::lxdebug->message(0, "saving current sort params to $current_sort_by / $current_sort_dir");
 
  85 sub _callback_handler_for_sorted {
 
  86   my ($self, %params) = @_;
 
  88   if ($current_sort_by) {
 
  89     my $sort_spec                             = $self->get_sort_spec;
 
  90     $params{ $sort_spec->{FORM_PARAMS}->[0] } = $current_sort_by;
 
  91     $params{ $sort_spec->{FORM_PARAMS}->[1] } = $current_sort_dir;
 
  94   # $::lxdebug->dump(0, "CB handler for sorted; params nach modif:", \%params);
 
  99 sub _get_models_handler_for_sorted {
 
 100   my ($self, %params) = @_;
 
 102   my %sort_params     = $self->get_current_sort_params;
 
 103   my $sort_spec       = $self->get_sort_spec->{ $sort_params{by} };
 
 105   $params{model}      = $sort_spec->{model};
 
 106   $params{sort_by}    = "SL::DB::Manager::$params{model}"->make_sort_string(sort_by => $sort_spec->{model_column}, sort_dir => $sort_params{dir});
 
 108   # $::lxdebug->dump(0, "GM handler for sorted; params nach modif:", \%params);
 
 122 SL::Controller::Helper::Sorted - A helper for semi-automatic handling
 
 123 of sorting lists of database models in a controller
 
 129   use SL::Controller::Helper::GetModels;
 
 130   use SL::Controller::Helper::Sorted;
 
 132   __PACKAGE__->make_sorted(
 
 133     DEFAULT_BY   => 'run_at',
 
 135     MODEL        => 'BackgroundJobHistory',
 
 136     ONLY         => [ qw(list) ],
 
 138     error        => $::locale->text('Error'),
 
 139     package_name => $::locale->text('Package name'),
 
 140     run_at       => $::locale->text('Run at'),
 
 146     my $sorted_models = $self->get_sorted;
 
 147     $self->render('controller/list', ENTRIES => $sorted_models);
 
 156     <th>[% L.sortable_table_header('package_name') %]</th>
 
 157     <th>[% L.sortable_table_header('run_at') %]</th>
 
 158     <th>[% L.sortable_table_header('error') %]</th>
 
 161    [% FOREACH entry = ENTRIES %]
 
 163      <td>[% HTML.escape(entry.package_name) %]</td>
 
 164      <td>[% HTML.escape(entry.run_at) %]</td>
 
 165      <td>[% HTML.escape(entry.error) %]</td>
 
 172 This specialized helper module enables controllers to display a
 
 173 sortable list of database models with as few lines as possible.
 
 175 For this to work the controller has to provide the information which
 
 176 indexes are eligible for sorting etc. by a call to L<make_sorted> at
 
 179 The underlying functionality that enables the use of more than just
 
 180 the sort helper is provided by the controller helper C<GetModels>. It
 
 181 provides mechanisms for helpers like this one to hook into certain
 
 182 calls made by the controller (C<get_callback> and C<get_models>) so
 
 183 that the specialized helpers can inject their parameters into the
 
 184 calls to e.g. C<SL::DB::Manager::SomeModel::get_all>.
 
 186 A template on the other hand can use the method
 
 187 C<sortable_table_header> from the layout helper module C<L>.
 
 189 The C<Sorted> helper hooks into the controller call to the action via
 
 190 a C<run_before> hook. This is done so that it can remember the sort
 
 191 parameters that were used in the current view.
 
 193 =head1 PACKAGE FUNCTIONS
 
 197 =item C<make_sorted %sort_spec>
 
 199 This function must be called by a controller at compile time. It is
 
 200 uesd to set the various parameters required for this helper to do its
 
 203 There are two sorts of keys in the hash C<%sort_spec>. The first kind
 
 204 is written in all upper-case. Those parameters are control
 
 205 parameters. The second kind are all lower-case and represent indexes
 
 206 that can be used for sorting (similar to database column names). The
 
 207 second kind are also the indexes you use in a template when calling
 
 208 C<[% L.sorted_table_header(...) %]>.
 
 210 Control parameters include the following (all required parameters
 
 215 =item * C<DEFAULT_BY>
 
 217 Required. A string: the index to sort by if the user hasn't clicked on
 
 218 any column yet (meaning: if the C<$::form> parameters for sorting do
 
 219 not contain a valid index).
 
 221 =item * C<DEFAULT_DIR>
 
 223 Optional. Default sort direction (ascending for trueish values,
 
 224 descrending for falsish values).
 
 226 Defaults to C<1> if missing.
 
 230 Optional. A string: the name of the Rose database model that is used
 
 231 as a default in certain cases. If this parameter is missing then it is
 
 232 derived from the controller's package (e.g. for the controller
 
 233 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
 
 234 C<BackgroundJobHistory>).
 
 236 =item * C<FORM_PARAMS>
 
 238 Optional. An array reference with exactly two strings that name the
 
 239 indexes in C<$::form> in which the sort index (the first element in
 
 240 the array) and sort direction (the second element in the array) are
 
 243 Defaults to the values C<sort_by> and C<sort_dir> if missing.
 
 247 Optional. An array reference containing a list of action names for
 
 248 which the sort parameters should be saved. If missing or empty then
 
 249 all actions invoked on the controller are monitored.
 
 253 All keys that are written in all lower-case name indexes that can be
 
 254 used for sorting. Each value to such a key can be either a string or a
 
 255 hash reference containing certain elements. If the value is only a
 
 256 string then such a hash reference is constructed, and the string is
 
 257 used as the value for the C<title> key.
 
 259 These possible elements are:
 
 265 Required. A user-displayable title to be used by functions like the
 
 266 layout helper's C<sortable_table_header>. Does not have a default
 
 271 Optional. The name of a Rose database model this sort index refers
 
 272 to. If missing then the value of C<$sort_spec{MODEL}> is used.
 
 274 =item * C<model_column>
 
 276 Optional. The name of the Rose database model column this sort index
 
 277 refers to. It must be one of the columns named by the model's
 
 278 C<Sorted> helper (not to be confused with the controller's C<Sorted>
 
 281 If missing it defaults to the key in C<%sort_spec> for which this hash
 
 282 reference is the value.
 
 288 =head1 INSTANCE FUNCTIONS
 
 290 These functions are called on a controller instance.
 
 294 =item C<get_sort_spec>
 
 296 Returns a hash containing the currently active sort parameters.
 
 298 The key C<by> contains the active sort index referring to the
 
 299 C<%sort_spec> given to L<make_sorted>.
 
 301 The key C<dir> is either C<1> or C<0>.
 
 303 =item C<get_current_sort_params>
 
 305 Returns a hash reference to the sort spec structure given in the call
 
 306 to L<make_sorted> after normalization (hash reference construction,
 
 307 applying default parameters etc).
 
 317 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>