Revision ec3a4636
Von Sven Schöling vor etwa 11 Jahren hinzugefügt
SL/Controller/DeliveryPlan.pm | ||
---|---|---|
6 | 6 |
use Clone qw(clone); |
7 | 7 |
use SL::DB::OrderItem; |
8 | 8 |
use SL::Controller::Helper::GetModels; |
9 |
use SL::Controller::Helper::Paginated; |
|
10 |
use SL::Controller::Helper::Sorted; |
|
11 |
use SL::Controller::Helper::Filtered; |
|
12 | 9 |
use SL::Controller::Helper::ReportGenerator; |
13 | 10 |
use SL::Locale::String; |
14 | 11 |
|
15 | 12 |
use Rose::Object::MakeMethods::Generic ( |
16 | 13 |
scalar => [ qw(db_args flat_filter) ], |
14 |
'scalar --get_set_init' => [ qw(models) ], |
|
17 | 15 |
); |
18 | 16 |
|
19 | 17 |
__PACKAGE__->run_before(sub { $::auth->assert('sales_order_edit'); }); |
20 | 18 |
|
21 |
__PACKAGE__->make_filtered( |
|
22 |
MODEL => 'OrderItem', |
|
23 |
LAUNDER_TO => 'filter' |
|
24 |
); |
|
25 |
__PACKAGE__->make_paginated( |
|
26 |
MODEL => 'OrderItem', |
|
27 |
ONLY => [ qw(list) ], |
|
28 |
); |
|
29 |
|
|
30 |
__PACKAGE__->make_sorted( |
|
31 |
MODEL => 'OrderItem', |
|
32 |
ONLY => [ qw(list) ], |
|
33 |
|
|
34 |
DEFAULT_BY => 'reqdate', |
|
35 |
DEFAULT_DIR => 1, |
|
36 |
|
|
19 |
#__PACKAGE__->make_filtered( |
|
20 |
# MODEL => 'OrderItem', |
|
21 |
# LAUNDER_TO => 'filter' |
|
22 |
#); |
|
23 |
#__PACKAGE__->make_paginated( |
|
24 |
# MODEL => 'OrderItem', |
|
25 |
# ONLY => [ qw(list) ], |
|
26 |
#); |
|
27 |
# |
|
28 |
#__PACKAGE__->make_sorted( |
|
29 |
# MODEL => 'OrderItem', |
|
30 |
# ONLY => [ qw(list) ], |
|
31 |
# |
|
32 |
# DEFAULT_BY => 'reqdate', |
|
33 |
# DEFAULT_DIR => 1, |
|
34 |
# |
|
35 |
# reqdate => t8('Reqdate'), |
|
36 |
# description => t8('Description'), |
|
37 |
# partnumber => t8('Part Number'), |
|
38 |
# qty => t8('Qty'), |
|
39 |
# shipped_qty => t8('shipped'), |
|
40 |
# not_shipped_qty => t8('not shipped'), |
|
41 |
# ordnumber => t8('Order'), |
|
42 |
# customer => t8('Customer'), |
|
43 |
#); |
|
44 |
|
|
45 |
my %sort_columns = ( |
|
37 | 46 |
reqdate => t8('Reqdate'), |
38 | 47 |
description => t8('Description'), |
39 | 48 |
partnumber => t8('Part Number'), |
... | ... | |
121 | 130 |
|
122 | 131 |
$self->make_filter_summary; |
123 | 132 |
|
124 |
my $orderitems = $self->get_models(query => $delivery_plan_query, with_objects => [ 'order', 'order.customer', 'part' ]);
|
|
133 |
my $orderitems = $self->models->get;
|
|
125 | 134 |
|
126 | 135 |
$self->prepare_report; |
127 | 136 |
$self->report_generator_list_objects(report => $self->{report}, objects => $orderitems); |
... | ... | |
149 | 158 |
not_shipped_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]->qty - $_[0]->shipped_qty, 2) . ' ' . $_[0]->unit } }, |
150 | 159 |
ordnumber => { sub => sub { $_[0]->order->ordnumber }, |
151 | 160 |
obj_link => sub { $self->link_to($_[0]->order) } }, |
152 |
customer => { sub => sub { $_[0]->order->customer->name }, |
|
161 |
customer => { sub => sub { return ''; $_[0]->order->customer->name },
|
|
153 | 162 |
obj_link => sub { $self->link_to($_[0]->order->customer) } }, |
154 | 163 |
); |
155 | 164 |
|
156 |
map { $column_defs{$_}->{text} = $::locale->text( $self->get_sort_spec->{$_}->{title} ) } keys %column_defs;
|
|
165 |
$column_defs{$_}->{text} = $sort_columns{$_} for keys %column_defs;
|
|
157 | 166 |
|
158 | 167 |
$report->set_options( |
159 | 168 |
std_column_visibility => 1, |
... | ... | |
161 | 170 |
output_format => 'HTML', |
162 | 171 |
top_info_text => $::locale->text('Delivery Plan for currently outstanding sales orders'), |
163 | 172 |
raw_top_info_text => $self->render('delivery_plan/report_top', { output => 0 }), |
164 |
raw_bottom_info_text => $self->render('delivery_plan/report_bottom', { output => 0 }), |
|
173 |
raw_bottom_info_text => $self->render('delivery_plan/report_bottom', { output => 0 }, models => $self->models),
|
|
165 | 174 |
title => $::locale->text('Delivery Plan'), |
166 | 175 |
allow_pdf_export => 1, |
167 | 176 |
allow_csv_export => 1, |
... | ... | |
170 | 179 |
$report->set_column_order(@columns); |
171 | 180 |
$report->set_export_options(qw(list filter)); |
172 | 181 |
$report->set_options_from_form; |
173 |
$self->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable); |
|
182 |
$self->models->sorted->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable);
|
|
174 | 183 |
|
175 |
$self->disable_pagination if $report->{options}{output_format} =~ /^(pdf|csv)$/i; |
|
184 |
$self->models->paginated->disable_pagination if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
|
|
176 | 185 |
} |
177 | 186 |
|
178 | 187 |
sub make_filter_summary { |
... | ... | |
209 | 218 |
$self->{filter_summary} = join ', ', @filter_strings; |
210 | 219 |
} |
211 | 220 |
|
221 |
sub init_models { |
|
222 |
my ($self) = @_; |
|
223 |
|
|
224 |
SL::Controller::Helper::GetModels->new( |
|
225 |
controller => $self, |
|
226 |
model => 'OrderItem', # defaults to controller |
|
227 |
filtered => { |
|
228 |
launder_to => 'filter', |
|
229 |
}, |
|
230 |
sorted => { |
|
231 |
_default => { |
|
232 |
by => 'reqdate', |
|
233 |
dir => 1, |
|
234 |
}, |
|
235 |
%sort_columns, |
|
236 |
}, |
|
237 |
query => $delivery_plan_query, |
|
238 |
with_objects => [ 'order', 'order.customer', 'part' ], |
|
239 |
); |
|
240 |
} |
|
241 |
|
|
212 | 242 |
sub link_to { |
213 | 243 |
my ($self, $object, %params) = @_; |
214 | 244 |
|
SL/Controller/Helper/Filtered.pm | ||
---|---|---|
1 |
package SL::Controller::Helper::Filtered; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use Exporter qw(import); |
|
6 |
use SL::Controller::Helper::ParseFilter (); |
|
7 |
use List::MoreUtils qw(uniq); |
|
8 |
our @EXPORT = qw(make_filtered get_filter_spec get_current_filter_params disable_filtering _save_current_filter_params _callback_handler_for_filtered _get_models_handler_for_filtered); |
|
9 |
|
|
10 |
use constant PRIV => '__filteredhelper_priv'; |
|
11 |
|
|
12 |
my %controller_filter_spec; |
|
13 |
|
|
14 |
sub make_filtered { |
|
15 |
my ($class, %specs) = @_; |
|
16 |
|
|
17 |
$specs{MODEL} //= $class->controller_name; |
|
18 |
$specs{MODEL} =~ s{ ^ SL::DB:: (?: .* :: )? }{}x; |
|
19 |
$specs{FORM_PARAMS} //= 'filter'; |
|
20 |
$specs{LAUNDER_TO} = '__INPLACE__' unless exists $specs{LAUNDER_TO}; |
|
21 |
$specs{ONLY} //= []; |
|
22 |
$specs{ONLY} = [ $specs{ONLY} ] if !ref $specs{ONLY}; |
|
23 |
$specs{ONLY_MAP} = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 }; |
|
24 |
|
|
25 |
$controller_filter_spec{$class} = \%specs; |
|
26 |
|
|
27 |
my %hook_params = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : (); |
|
28 |
$class->run_before('_save_current_filter_params', %hook_params); |
|
29 |
|
|
30 |
SL::Controller::Helper::GetModels::register_get_models_handlers( |
|
31 |
$class, |
|
32 |
callback => '_callback_handler_for_filtered', |
|
33 |
get_models => '_get_models_handler_for_filtered', |
|
34 |
ONLY => $specs{ONLY}, |
|
35 |
); |
|
36 |
|
|
37 |
# $::lxdebug->dump(0, "CONSPEC", \%specs); |
|
38 |
} |
|
39 |
|
|
40 |
sub get_filter_spec { |
|
41 |
my ($class_or_self) = @_; |
|
42 |
|
|
43 |
return $controller_filter_spec{ref($class_or_self) || $class_or_self}; |
|
44 |
} |
|
45 |
|
|
46 |
sub get_current_filter_params { |
|
47 |
my ($self) = @_; |
|
48 |
|
|
49 |
return %{ _priv($self)->{filter_params} } if _priv($self)->{filter_params}; |
|
50 |
|
|
51 |
require Carp; |
|
52 |
Carp::confess('It seems a GetModels plugin tries to access filter params before they got calculated. Make sure your make_filtered call comes first.'); |
|
53 |
} |
|
54 |
|
|
55 |
sub _make_current_filter_params { |
|
56 |
my ($self, %params) = @_; |
|
57 |
|
|
58 |
my $spec = $self->get_filter_spec; |
|
59 |
my $filter = $params{filter} // _priv($self)->{filter} // {}, |
|
60 |
my %filter_args = _get_filter_args($self, $spec); |
|
61 |
my %parse_filter_args = ( |
|
62 |
class => "SL::DB::Manager::$spec->{MODEL}", |
|
63 |
with_objects => $params{with_objects}, |
|
64 |
); |
|
65 |
my $laundered; |
|
66 |
if ($spec->{LAUNDER_TO} eq '__INPLACE__') { |
|
67 |
|
|
68 |
} elsif ($spec->{LAUNDER_TO}) { |
|
69 |
$laundered = {}; |
|
70 |
$parse_filter_args{launder_to} = $laundered; |
|
71 |
} else { |
|
72 |
$parse_filter_args{no_launder} = 1; |
|
73 |
} |
|
74 |
|
|
75 |
my %calculated_params = SL::Controller::Helper::ParseFilter::parse_filter($filter, %parse_filter_args); |
|
76 |
|
|
77 |
$calculated_params{query} = [ |
|
78 |
@{ $calculated_params{query} || [] }, |
|
79 |
@{ $filter_args{ query} || [] }, |
|
80 |
@{ $params{ query} || [] }, |
|
81 |
]; |
|
82 |
|
|
83 |
$calculated_params{with_objects} = [ |
|
84 |
uniq |
|
85 |
@{ $calculated_params{with_objects} || [] }, |
|
86 |
@{ $filter_args{ with_objects} || [] }, |
|
87 |
@{ $params{ with_objects} || [] }, |
|
88 |
]; |
|
89 |
|
|
90 |
if ($laundered) { |
|
91 |
if ($self->can($spec->{LAUNDER_TO})) { |
|
92 |
$self->${\ $spec->{LAUNDER_TO} }($laundered); |
|
93 |
} else { |
|
94 |
$self->{$spec->{LAUNDER_TO}} = $laundered; |
|
95 |
} |
|
96 |
} |
|
97 |
|
|
98 |
# $::lxdebug->dump(0, "get_current_filter_params: ", \%calculated_params); |
|
99 |
|
|
100 |
_priv($self)->{filter_params} = \%calculated_params; |
|
101 |
|
|
102 |
return %calculated_params; |
|
103 |
} |
|
104 |
|
|
105 |
sub disable_filtering { |
|
106 |
my ($self) = @_; |
|
107 |
_priv($self)->{disabled} = 1; |
|
108 |
} |
|
109 |
|
|
110 |
# |
|
111 |
# private functions |
|
112 |
# |
|
113 |
|
|
114 |
sub _get_filter_args { |
|
115 |
my ($self, $spec) = @_; |
|
116 |
|
|
117 |
$spec ||= $self->get_filter_spec; |
|
118 |
|
|
119 |
my %filter_args = ref($spec->{FILTER_ARGS}) eq 'CODE' ? %{ $spec->{FILTER_ARGS}->($self) } |
|
120 |
: $spec->{FILTER_ARGS} ? do { my $sub = $spec->{FILTER_ARGS}; %{ $self->$sub() } } |
|
121 |
: (); |
|
122 |
} |
|
123 |
|
|
124 |
sub _save_current_filter_params { |
|
125 |
my ($self) = @_; |
|
126 |
|
|
127 |
return if !_is_enabled($self); |
|
128 |
|
|
129 |
my $filter_spec = $self->get_filter_spec; |
|
130 |
$self->{PRIV()}{filter} = $::form->{ $filter_spec->{FORM_PARAMS} }; |
|
131 |
|
|
132 |
# $::lxdebug->message(0, "saving current filter params to " . $self->{PRIV()}->{page} . ' / ' . $self->{PRIV()}->{per_page}); |
|
133 |
} |
|
134 |
|
|
135 |
sub _callback_handler_for_filtered { |
|
136 |
my ($self, %params) = @_; |
|
137 |
my $priv = _priv($self); |
|
138 |
|
|
139 |
if (_is_enabled($self) && $priv->{filter}) { |
|
140 |
my $filter_spec = $self->get_filter_spec; |
|
141 |
my ($flattened) = SL::Controller::Helper::ParseFilter::flatten($priv->{filter}, $filter_spec->{FORM_PARAMS}); |
|
142 |
%params = (%params, @$flattened); |
|
143 |
} |
|
144 |
|
|
145 |
# $::lxdebug->dump(0, "CB handler for filtered; params after flatten:", \%params); |
|
146 |
|
|
147 |
return %params; |
|
148 |
} |
|
149 |
|
|
150 |
sub _get_models_handler_for_filtered { |
|
151 |
my ($self, %params) = @_; |
|
152 |
my $spec = $self->get_filter_spec; |
|
153 |
|
|
154 |
# $::lxdebug->dump(0, "params in get_models_for_filtered", \%params); |
|
155 |
|
|
156 |
my %filter_params; |
|
157 |
%filter_params = _make_current_filter_params($self, %params) if _is_enabled($self); |
|
158 |
|
|
159 |
# $::lxdebug->dump(0, "GM handler for filtered; params nach modif (is_enabled? " . _is_enabled($self) . ")", \%params); |
|
160 |
|
|
161 |
return (%params, %filter_params); |
|
162 |
} |
|
163 |
|
|
164 |
sub _priv { |
|
165 |
my ($self) = @_; |
|
166 |
$self->{PRIV()} ||= {}; |
|
167 |
return $self->{PRIV()}; |
|
168 |
} |
|
169 |
|
|
170 |
sub _is_enabled { |
|
171 |
my ($self) = @_; |
|
172 |
return !_priv($self)->{disabled} && ($self->get_filter_spec->{ONLY_MAP}->{$self->action_name} || $self->get_filter_spec->{ONLY_MAP}->{'__ALL__'}); |
|
173 |
} |
|
174 |
|
|
175 |
1; |
|
176 |
|
|
177 |
__END__ |
|
178 |
|
|
179 |
=pod |
|
180 |
|
|
181 |
=encoding utf8 |
|
182 |
|
|
183 |
=head1 NAME |
|
184 |
|
|
185 |
SL::Controller::Helper::Filtered - A helper for semi-automatic handling |
|
186 |
of filtered lists of database models in a controller |
|
187 |
|
|
188 |
=head1 SYNOPSIS |
|
189 |
|
|
190 |
In a controller: |
|
191 |
|
|
192 |
use SL::Controller::Helper::GetModels; |
|
193 |
use SL::Controller::Helper::Filtered; |
|
194 |
|
|
195 |
__PACKAGE__->make_filter( |
|
196 |
MODEL => 'Part', |
|
197 |
ONLY => [ qw(list) ], |
|
198 |
FORM_PARAMS => [ qw(filter) ], |
|
199 |
); |
|
200 |
|
|
201 |
sub action_list { |
|
202 |
my ($self) = @_; |
|
203 |
|
|
204 |
my $filtered_models = $self->get_models(%addition_filters); |
|
205 |
$self->render('controller/list', ENTRIES => $filtered_models); |
|
206 |
} |
|
207 |
|
|
208 |
|
|
209 |
=head1 OVERVIEW |
|
210 |
|
|
211 |
This helper module enables use of the L<SL::Controller::Helper::ParseFilter> |
|
212 |
methods in conjunction with the L<SL::Controller::Helper::GetModels> style of |
|
213 |
plugins. Additional filters can be defined in the database models and filtering |
|
214 |
can be reduced to a minimum of work. |
|
215 |
|
|
216 |
This plugin can be combined with L<SL::Controller::Sorted> and |
|
217 |
L<SL::Controller::Paginated> for filtered, sorted and paginated lists. |
|
218 |
|
|
219 |
The controller has to provive information where to look for filter information |
|
220 |
at compile time. This call is L<make_filtered>. |
|
221 |
|
|
222 |
The underlying functionality that enables the use of more than just |
|
223 |
the paginate helper is provided by the controller helper |
|
224 |
C<GetModels>. See the documentation for L<SL::Controller::Sorted> for |
|
225 |
more information on it. |
|
226 |
|
|
227 |
=head1 PACKAGE FUNCTIONS |
|
228 |
|
|
229 |
=over 4 |
|
230 |
|
|
231 |
=item C<make_filtered %filter_spec> |
|
232 |
|
|
233 |
This function must be called by a controller at compile time. It is |
|
234 |
uesd to set the various parameters required for this helper to do its |
|
235 |
magic. |
|
236 |
|
|
237 |
Careful: If you want to use this in conjunction with |
|
238 |
L<SL:Controller::Helper::Paginated>, you need to call C<make_filtered> first, |
|
239 |
or the paginating will not get all the relevant information to estimate the |
|
240 |
number of pages correctly. To ensure this does not happen, this module will |
|
241 |
croak when it detects such a scenario. |
|
242 |
|
|
243 |
The hash C<%filter_spec> can include the following parameters: |
|
244 |
|
|
245 |
=over 4 |
|
246 |
|
|
247 |
=item * C<MODEL> |
|
248 |
|
|
249 |
Optional. A string: the name of the Rose database model that is used |
|
250 |
as a default in certain cases. If this parameter is missing then it is |
|
251 |
derived from the controller's package (e.g. for the controller |
|
252 |
C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to |
|
253 |
C<BackgroundJobHistory>). |
|
254 |
|
|
255 |
=item * C<FORM_PARAMS> |
|
256 |
|
|
257 |
Optional. Indicates a key in C<$::form> to be used as filter. |
|
258 |
|
|
259 |
Defaults to the values C<filter> if missing. |
|
260 |
|
|
261 |
=item * C<LAUNDER_TO> |
|
262 |
|
|
263 |
Option. Indicates a target for laundered filter arguments in the controller. |
|
264 |
Can be set to C<undef> to disable laundering, and can be set to method named or |
|
265 |
hash keys of the controller. In the latter case the laundered structure will be |
|
266 |
put there. |
|
267 |
|
|
268 |
Defaults to inplace laundering which is not normally settable. |
|
269 |
|
|
270 |
=item * C<ONLY> |
|
271 |
|
|
272 |
Optional. An array reference containing a list of action names for |
|
273 |
which the paginate parameters should be saved. If missing or empty then |
|
274 |
all actions invoked on the controller are monitored. |
|
275 |
|
|
276 |
=back |
|
277 |
|
|
278 |
=back |
|
279 |
|
|
280 |
=head1 INSTANCE FUNCTIONS |
|
281 |
|
|
282 |
These functions are called on a controller instance. |
|
283 |
|
|
284 |
=over 4 |
|
285 |
|
|
286 |
=item C<get_current_filter_params> |
|
287 |
|
|
288 |
Returns a hash to be used in manager C<get_all> calls or to be passed on to |
|
289 |
GetModels. Will only work if the get_models chain has been called at least |
|
290 |
once, because only then the full parameters can get parsed and stored. Will |
|
291 |
croak otherwise. |
|
292 |
|
|
293 |
=item C<disable_filtering> |
|
294 |
|
|
295 |
Disable filtering for the duration of the current action. Can be used |
|
296 |
when using the attribute C<ONLY> to L<make_filtered> does not |
|
297 |
cover all cases. |
|
298 |
|
|
299 |
=back |
|
300 |
|
|
301 |
=head1 BUGS |
|
302 |
|
|
303 |
Nothing here yet. |
|
304 |
|
|
305 |
=head1 AUTHOR |
|
306 |
|
|
307 |
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt> |
|
308 |
|
|
309 |
=cut |
SL/Controller/Helper/GetModels.pm | ||
---|---|---|
2 | 2 |
|
3 | 3 |
use strict; |
4 | 4 |
|
5 |
use Exporter qw(import); |
|
6 |
our @EXPORT = qw(get_models_url_params get_callback get_models); |
|
5 |
use parent 'Rose::Object'; |
|
6 |
use SL::Controller::Helper::GetModels::Filtered; |
|
7 |
use SL::Controller::Helper::GetModels::Sorted; |
|
8 |
use SL::Controller::Helper::GetModels::Paginated; |
|
9 |
|
|
10 |
use Rose::Object::MakeMethods::Generic ( |
|
11 |
scalar => [ qw(controller model query with_objects filtered sorted paginated) ], |
|
12 |
'scalar --get_set_init' => [ qw(handlers) ], |
|
13 |
); |
|
7 | 14 |
|
8 | 15 |
use constant PRIV => '__getmodelshelperpriv'; |
9 | 16 |
|
10 |
my $registered_handlers = {}; |
|
17 |
#my $registered_handlers = {}; |
|
18 |
|
|
19 |
sub init { |
|
20 |
my ($self, %params) = @_; |
|
21 |
|
|
22 |
# for my $plugin (qw(filtered sorted paginated)) { |
|
23 |
# next unless $params{$plugin}; |
|
24 |
# $self->${ \"make_$plugin" }(%{ delete $params{$plugin} || {} }); |
|
25 |
# } |
|
26 |
# |
|
27 |
# TODO: default model |
|
28 |
$self->model(delete $params{model}); |
|
29 |
|
|
30 |
for my $plugin (qw(filtered sorted paginated)) { |
|
31 |
next unless my $spec = delete $params{$plugin} // {}; |
|
32 |
my $plugin_class = "SL::Controller::Helper::GetModels::" . ucfirst $plugin; |
|
33 |
$self->$plugin($plugin_class->new(%$spec, get_models => $self)); |
|
34 |
} |
|
35 |
|
|
36 |
$self->SUPER::init(%params); |
|
37 |
} |
|
11 | 38 |
|
12 |
sub register_get_models_handlers {
|
|
13 |
my ($class, %additional_handlers) = @_;
|
|
39 |
sub register_handlers { |
|
40 |
my ($self, %additional_handlers) = @_;
|
|
14 | 41 |
|
15 |
my $only = delete($additional_handlers{ONLY}) || []; |
|
16 |
$only = [ $only ] if !ref $only; |
|
17 |
my %hook_params = @{ $only } ? ( only => $only ) : (); |
|
42 |
# my $only = delete($additional_handlers{ONLY}) || [];
|
|
43 |
# $only = [ $only ] if !ref $only;
|
|
44 |
# my %hook_params = @{ $only } ? ( only => $only ) : ();
|
|
18 | 45 |
|
19 |
my $handlers = _registered_handlers($class);
|
|
46 |
my $handlers = $self->handlers;
|
|
20 | 47 |
map { push @{ $handlers->{$_} }, $additional_handlers{$_} if $additional_handlers{$_} } keys %$handlers; |
21 | 48 |
} |
22 | 49 |
|
... | ... | |
39 | 66 |
sub get_callback { |
40 | 67 |
my ($self, %override_params) = @_; |
41 | 68 |
|
42 |
my %default_params = _run_handlers($self, 'callback', action => $self->action_name);
|
|
69 |
my %default_params = $self->_run_handlers('callback', action => $self->controller->action_name);
|
|
43 | 70 |
|
44 |
return $self->url_for(%default_params, %override_params); |
|
71 |
return $self->controller->url_for(%default_params, %override_params);
|
|
45 | 72 |
} |
46 | 73 |
|
47 |
sub get_models { |
|
48 |
my ($self, %override_params) = @_; |
|
74 |
sub get { |
|
75 |
my ($self, %params) = @_; |
|
76 |
|
|
77 |
push @{ $params{query} ||= [] }, @{ $self->query || [] }; |
|
78 |
push @{ $params{with_objects} ||= [] }, @{ $self->with_objects || [] }; |
|
79 |
|
|
80 |
%params = $self->_run_handlers('get_models', %params); |
|
81 |
|
|
82 |
return $self->manager->get_all(%params); |
|
83 |
} |
|
84 |
|
|
85 |
sub get_paginate_args { |
|
86 |
my ($self, %params) = @_; |
|
49 | 87 |
|
50 |
my %params = _run_handlers($self, 'get_models', %override_params); |
|
88 |
push @{ $params{query} ||= [] }, @{ $self->query || [] }; |
|
89 |
push @{ $params{with_objects} ||= [] }, @{ $self->with_objects || [] }; |
|
51 | 90 |
|
52 |
my $model = delete($params{model}) || die "No 'model' to work on"; |
|
91 |
$self->paginated->get_current_paginate_params(%params); |
|
92 |
} |
|
53 | 93 |
|
54 |
return "SL::DB::Manager::${model}"->get_all(%params); |
|
94 |
sub manager { |
|
95 |
die "No 'model' to work on" unless $_[0]->model; |
|
96 |
"SL::DB::Manager::" . $_[0]->model; |
|
55 | 97 |
} |
56 | 98 |
|
57 | 99 |
# |
... | ... | |
61 | 103 |
sub _run_handlers { |
62 | 104 |
my ($self, $handler_type, %params) = @_; |
63 | 105 |
|
64 |
foreach my $sub (@{ _registered_handlers(ref $self)->{$handler_type} }) {
|
|
106 |
foreach my $sub (@{ $self->handlers->{$handler_type} }) {
|
|
65 | 107 |
if (ref $sub eq 'CODE') { |
66 | 108 |
%params = $sub->($self, %params); |
67 | 109 |
} elsif ($self->can($sub)) { |
... | ... | |
74 | 116 |
return %params; |
75 | 117 |
} |
76 | 118 |
|
77 |
sub _registered_handlers { |
|
78 |
$registered_handlers->{$_[0]} //= { callback => [], get_models => [] } |
|
119 |
sub init_handlers { |
|
120 |
{ |
|
121 |
callback => [], |
|
122 |
get_models => [], |
|
123 |
} |
|
79 | 124 |
} |
80 | 125 |
|
81 | 126 |
1; |
SL/Controller/Helper/GetModels/Base.pm | ||
---|---|---|
1 |
package SL::Controller::Helper::GetModels::Base; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent 'Rose::Object'; |
|
5 |
use Scalar::Util qw(weaken); |
|
6 |
|
|
7 |
|
|
8 |
use Rose::Object::MakeMethods::Generic ( |
|
9 |
scalar => [ qw(get_models) ], |
|
10 |
); |
|
11 |
|
|
12 |
sub set_get_models { |
|
13 |
$_[0]->get_models($_[1]); |
|
14 |
|
|
15 |
weaken($_[1]); |
|
16 |
} |
|
17 |
|
|
18 |
sub merge_args { |
|
19 |
my ($self, @args) = @_; |
|
20 |
my $final_args = { }; |
|
21 |
|
|
22 |
for my $field (qw(query with_objects)) { |
|
23 |
$final_args->{$field} = [ map { @{ $_->{$field} || [] } } @args ]; |
|
24 |
} |
|
25 |
|
|
26 |
return %$final_args; |
|
27 |
} |
|
28 |
|
|
29 |
1; |
SL/Controller/Helper/GetModels/Filtered.pm | ||
---|---|---|
1 |
package SL::Controller::Helper::GetModels::Filtered; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent 'SL::Controller::Helper::GetModels::Base'; |
|
5 |
|
|
6 |
use Exporter qw(import); |
|
7 |
use SL::Controller::Helper::ParseFilter (); |
|
8 |
use List::MoreUtils qw(uniq); |
|
9 |
|
|
10 |
use Rose::Object::MakeMethods::Generic ( |
|
11 |
scalar => [ qw(disabled filter_args filter_params) ], |
|
12 |
'scalar --get_set_init' => [ qw(form_params launder_to) ], |
|
13 |
); |
|
14 |
|
|
15 |
sub init { |
|
16 |
my ($self, %specs) = @_; |
|
17 |
|
|
18 |
$self->set_get_models(delete $specs{get_models}); |
|
19 |
$self->SUPER::init(%specs); |
|
20 |
|
|
21 |
$self->get_models->register_handlers( |
|
22 |
callback => sub { shift; $self->_callback_handler_for_filtered(@_) }, |
|
23 |
get_models => sub { shift; $self->_get_models_handler_for_filtered(@_) }, |
|
24 |
); |
|
25 |
|
|
26 |
# $::lxdebug->dump(0, "CONSPEC", \%specs); |
|
27 |
} |
|
28 |
|
|
29 |
sub get_current_filter_params { |
|
30 |
my ($self) = @_; |
|
31 |
|
|
32 |
return $self->filter_params if $self->filter_params; |
|
33 |
|
|
34 |
require Carp; |
|
35 |
Carp::confess('It seems a GetModels plugin tries to access filter params before they got calculated. Make sure your make_filtered call comes first.'); |
|
36 |
} |
|
37 |
|
|
38 |
sub _make_current_filter_params { |
|
39 |
my ($self, %params) = @_; |
|
40 |
|
|
41 |
# my $spec = $self->get_filter_spec; |
|
42 |
my $filter = $params{filter} // $::form->{ $self->form_params } // {}, |
|
43 |
my %filter_args = $self->_get_filter_args; |
|
44 |
my %parse_filter_args = ( |
|
45 |
class => $self->get_models->manager, |
|
46 |
with_objects => $params{with_objects}, |
|
47 |
); |
|
48 |
my $laundered; |
|
49 |
if ($self->launder_to eq '__INPLACE__') { |
|
50 |
# nothing to do |
|
51 |
} elsif ($self->launder_to) { |
|
52 |
$laundered = {}; |
|
53 |
$parse_filter_args{launder_to} = $laundered; |
|
54 |
} else { |
|
55 |
$parse_filter_args{no_launder} = 1; |
|
56 |
} |
|
57 |
|
|
58 |
my %calculated_params = SL::Controller::Helper::ParseFilter::parse_filter($filter, %parse_filter_args); |
|
59 |
%calculated_params = $self->merge_args(\%calculated_params, \%filter_args, \%params); |
|
60 |
|
|
61 |
# $calculated_params{query} = [ |
|
62 |
# @{ $calculated_params{query} || [] }, |
|
63 |
# @{ $filter_args{ query} || [] }, |
|
64 |
# @{ $params{ query} || [] }, |
|
65 |
# ]; |
|
66 |
# |
|
67 |
# $calculated_params{with_objects} = [ |
|
68 |
# uniq |
|
69 |
# @{ $calculated_params{with_objects} || [] }, |
|
70 |
# @{ $filter_args{ with_objects} || [] }, |
|
71 |
# @{ $params{ with_objects} || [] }, |
|
72 |
# ]; |
|
73 |
|
|
74 |
if ($laundered) { |
|
75 |
if ($self->get_models->controller->can($self->launder_to)) { |
|
76 |
$self->get_models->controller->${\ $self->launder_to }($laundered); |
|
77 |
} else { |
|
78 |
$self->get_models->controller->{$self->launder_to} = $laundered; |
|
79 |
} |
|
80 |
} |
|
81 |
|
|
82 |
# $::lxdebug->dump(0, "get_current_filter_params: ", \%calculated_params); |
|
83 |
|
|
84 |
$self->filter_params(\%calculated_params); |
|
85 |
|
|
86 |
return %calculated_params; |
|
87 |
} |
|
88 |
|
|
89 |
sub disable_filtering { |
|
90 |
my ($self) = @_; |
|
91 |
$self->disabled(1); |
|
92 |
} |
|
93 |
|
|
94 |
# |
|
95 |
# private functions |
|
96 |
# |
|
97 |
|
|
98 |
sub _get_filter_args { |
|
99 |
my ($self, $spec) = @_; |
|
100 |
|
|
101 |
my %filter_args = ref($self->filter_args) eq 'CODE' ? %{ $self->filter_args->($self) } |
|
102 |
: $self->filter_args ? do { my $sub = $self->filter_args; %{ $self->get_models->controller->$sub() } } |
|
103 |
: (); |
|
104 |
} |
|
105 |
|
|
106 |
sub _callback_handler_for_filtered { |
|
107 |
my ($self, %params) = @_; |
|
108 |
|
|
109 |
if ($self->is_enabled) { |
|
110 |
my ($flattened) = SL::Controller::Helper::ParseFilter::flatten($::form->{ $self->form_params }, $self->form_params); |
|
111 |
%params = (%params, @{ $flattened || [] }); |
|
112 |
} |
|
113 |
|
|
114 |
# $::lxdebug->dump(0, "CB handler for filtered; params after flatten:", \%params); |
|
115 |
|
|
116 |
return %params; |
|
117 |
} |
|
118 |
|
|
119 |
sub _get_models_handler_for_filtered { |
|
120 |
my ($self, %params) = @_; |
|
121 |
|
|
122 |
# $::lxdebug->dump(0, "params in get_models_for_filtered", \%params); |
|
123 |
|
|
124 |
my %filter_params; |
|
125 |
%filter_params = $self->_make_current_filter_params(%params) if $self->is_enabled; |
|
126 |
|
|
127 |
# $::lxdebug->dump(0, "GM handler for filtered; params nach modif (is_enabled? " . $self->is_enabled . ")", \%params); |
|
128 |
|
|
129 |
return (%params, %filter_params); |
|
130 |
} |
|
131 |
|
|
132 |
sub is_enabled { |
|
133 |
!$_[0]->disabled; |
|
134 |
} |
|
135 |
|
|
136 |
sub init_form_params { |
|
137 |
'filter' |
|
138 |
} |
|
139 |
|
|
140 |
sub init_launder_to { |
|
141 |
'filter' |
|
142 |
} |
|
143 |
|
|
144 |
|
|
145 |
1; |
|
146 |
|
|
147 |
__END__ |
|
148 |
|
|
149 |
=pod |
|
150 |
|
|
151 |
=encoding utf8 |
|
152 |
|
|
153 |
=head1 NAME |
|
154 |
|
|
155 |
SL::Controller::Helper::Filtered - A helper for semi-automatic handling |
|
156 |
of filtered lists of database models in a controller |
|
157 |
|
|
158 |
=head1 SYNOPSIS |
|
159 |
|
|
160 |
In a controller: |
|
161 |
|
|
162 |
use SL::Controller::Helper::GetModels; |
|
163 |
use SL::Controller::Helper::Filtered; |
|
164 |
|
|
165 |
__PACKAGE__->make_filter( |
|
166 |
MODEL => 'Part', |
|
167 |
ONLY => [ qw(list) ], |
|
168 |
FORM_PARAMS => [ qw(filter) ], |
|
169 |
); |
|
170 |
|
|
171 |
sub action_list { |
|
172 |
my ($self) = @_; |
|
173 |
|
|
174 |
my $filtered_models = $self->get_models(%addition_filters); |
|
175 |
$self->render('controller/list', ENTRIES => $filtered_models); |
|
176 |
} |
|
177 |
|
|
178 |
|
|
179 |
=head1 OVERVIEW |
|
180 |
|
|
181 |
This helper module enables use of the L<SL::Controller::Helper::ParseFilter> |
|
182 |
methods in conjunction with the L<SL::Controller::Helper::GetModels> style of |
|
183 |
plugins. Additional filters can be defined in the database models and filtering |
|
184 |
can be reduced to a minimum of work. |
|
185 |
|
|
186 |
This plugin can be combined with L<SL::Controller::Sorted> and |
|
187 |
L<SL::Controller::Paginated> for filtered, sorted and paginated lists. |
|
188 |
|
|
189 |
The controller has to provive information where to look for filter information |
|
190 |
at compile time. This call is L<make_filtered>. |
|
191 |
|
|
192 |
The underlying functionality that enables the use of more than just |
|
193 |
the paginate helper is provided by the controller helper |
|
194 |
C<GetModels>. See the documentation for L<SL::Controller::Sorted> for |
|
195 |
more information on it. |
|
196 |
|
|
197 |
=head1 PACKAGE FUNCTIONS |
|
198 |
|
|
199 |
=over 4 |
|
200 |
|
|
201 |
=item C<make_filtered %filter_spec> |
|
202 |
|
|
203 |
This function must be called by a controller at compile time. It is |
|
204 |
uesd to set the various parameters required for this helper to do its |
|
205 |
magic. |
|
206 |
|
|
207 |
Careful: If you want to use this in conjunction with |
|
208 |
L<SL:Controller::Helper::Paginated>, you need to call C<make_filtered> first, |
|
209 |
or the paginating will not get all the relevant information to estimate the |
|
210 |
number of pages correctly. To ensure this does not happen, this module will |
|
211 |
croak when it detects such a scenario. |
|
212 |
|
|
213 |
The hash C<%filter_spec> can include the following parameters: |
|
214 |
|
|
215 |
=over 4 |
|
216 |
|
|
217 |
=item * C<MODEL> |
|
218 |
|
|
219 |
Optional. A string: the name of the Rose database model that is used |
|
220 |
as a default in certain cases. If this parameter is missing then it is |
|
221 |
derived from the controller's package (e.g. for the controller |
|
222 |
C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to |
|
223 |
C<BackgroundJobHistory>). |
|
224 |
|
|
225 |
=item * C<FORM_PARAMS> |
|
226 |
|
|
227 |
Optional. Indicates a key in C<$::form> to be used as filter. |
|
228 |
|
|
229 |
Defaults to the values C<filter> if missing. |
|
230 |
|
|
231 |
=item * C<LAUNDER_TO> |
|
232 |
|
|
233 |
Option. Indicates a target for laundered filter arguments in the controller. |
|
234 |
Can be set to C<undef> to disable laundering, and can be set to method named or |
|
235 |
hash keys of the controller. In the latter case the laundered structure will be |
|
236 |
put there. |
|
237 |
|
|
238 |
Defaults to inplace laundering which is not normally settable. |
|
239 |
|
|
240 |
=item * C<ONLY> |
|
241 |
|
|
242 |
Optional. An array reference containing a list of action names for |
|
243 |
which the paginate parameters should be saved. If missing or empty then |
|
244 |
all actions invoked on the controller are monitored. |
|
245 |
|
|
246 |
=back |
|
247 |
|
|
248 |
=back |
|
249 |
|
|
250 |
=head1 INSTANCE FUNCTIONS |
|
251 |
|
|
252 |
These functions are called on a controller instance. |
|
253 |
|
|
254 |
=over 4 |
|
255 |
|
|
256 |
=item C<get_current_filter_params> |
|
257 |
|
|
258 |
Returns a hash to be used in manager C<get_all> calls or to be passed on to |
|
259 |
GetModels. Will only work if the get_models chain has been called at least |
|
260 |
once, because only then the full parameters can get parsed and stored. Will |
|
261 |
croak otherwise. |
|
262 |
|
|
263 |
=item C<disable_filtering> |
|
264 |
|
|
265 |
Disable filtering for the duration of the current action. Can be used |
|
266 |
when using the attribute C<ONLY> to L<make_filtered> does not |
|
267 |
cover all cases. |
|
268 |
|
|
269 |
=back |
|
270 |
|
|
271 |
=head1 BUGS |
|
272 |
|
|
273 |
Nothing here yet. |
|
274 |
|
|
275 |
=head1 AUTHOR |
|
276 |
|
|
277 |
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt> |
|
278 |
|
|
279 |
=cut |
SL/Controller/Helper/GetModels/Paginated.pm | ||
---|---|---|
1 |
package SL::Controller::Helper::GetModels::Paginated; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent 'SL::Controller::Helper::GetModels::Base'; |
|
5 |
|
|
6 |
use List::Util qw(min); |
|
7 |
|
|
8 |
use Rose::Object::MakeMethods::Generic ( |
|
9 |
scalar => [ qw(disabled per_page) ], |
|
10 |
'scalar --get_set_init' => [ qw(form_params paginate_args) ], |
|
11 |
); |
|
12 |
|
|
13 |
sub init { |
|
14 |
my ($self, %specs) = @_; |
|
15 |
|
|
16 |
$self->set_get_models(delete $specs{get_models}); |
|
17 |
$self->SUPER::init(%specs); |
|
18 |
|
|
19 |
$self->per_page($self->get_models->manager->default_objects_per_page) unless $self->per_page; |
|
20 |
|
|
21 |
$self->get_models->register_handlers( |
|
22 |
callback => sub { shift; $self->_callback_handler_for_paginated(@_) }, |
|
23 |
get_models => sub { shift; $self->_get_models_handler_for_paginated(@_) }, |
|
24 |
); |
|
25 |
|
|
26 |
# $::lxdebug->dump(0, "CONSPEC", \%specs); |
|
27 |
} |
|
28 |
|
|
29 |
sub get_current_paginate_params { |
|
30 |
my ($self, %args) = @_; |
|
31 |
return () unless $self->is_enabled; |
|
32 |
|
|
33 |
my %paginate_params = $self->final_params(%args); |
|
34 |
|
|
35 |
# try to use Filtered if available and nothing else is configured, but don't |
|
36 |
# blow up if the controller does not use Filtered |
|
37 |
my %paginate_args = ref($self->paginate_args) eq 'CODE' ? %{ $self->paginate_args->($self) } |
|
38 |
: $self->paginate_args eq '__FILTER__' |
|
39 |
&& $self->get_models->filtered ? %{ $self->get_models->filtered->get_current_filter_params } |
|
40 |
: $self->paginate_args ne '__FILTER__' ? do { my $sub = $self->paginate_args; %{ $self->get_models->controller->$sub() } } |
|
41 |
: (); |
|
42 |
|
|
43 |
%args = $self->merge_args(\%args, \%paginate_args); |
|
44 |
|
|
45 |
my $calculated_params = $self->get_models->manager->paginate(%paginate_params, args => \%args); |
|
46 |
|
|
47 |
# $::lxdebug->dump(0, "get_current_paginate_params: ", $calculated_params); |
|
48 |
|
|
49 |
return %{ $calculated_params }; |
|
50 |
} |
|
51 |
|
|
52 |
sub disable_pagination { |
|
53 |
my ($self) = @_; |
|
54 |
$self->disabled(1); |
|
55 |
} |
|
56 |
|
|
57 |
sub final_params { |
|
58 |
my ($self, %params) = @_; |
|
59 |
|
|
60 |
my $from_form = { |
|
61 |
page => $::form->{ $self->form_params->[0] } || 1, |
|
62 |
per_page => $::form->{ $self->form_params->[1] } * 1, |
|
63 |
}; |
|
64 |
|
|
65 |
# my $priv = _priv($self); |
|
66 |
$params{page} = $from_form->{page} unless defined $params{page}; |
|
67 |
$params{per_page} = $from_form->{per_page} unless defined $params{per_page}; |
|
68 |
|
|
69 |
$params{page} = ($params{page} * 1) || 1; |
|
70 |
$params{per_page} = ($params{per_page} * 1) || $self->per_page; |
|
71 |
|
|
72 |
%params; |
|
73 |
} |
|
74 |
|
|
75 |
# |
|
76 |
# private functions |
|
77 |
# |
|
78 |
|
|
79 |
sub init_form_params { |
|
80 |
[ qw(page per_page) ] |
|
81 |
} |
|
82 |
|
|
83 |
sub init_paginate_args { |
|
84 |
'__FILTER__' |
|
85 |
} |
|
86 |
|
|
87 |
sub _callback_handler_for_paginated { |
|
88 |
my ($self, %params) = @_; |
|
89 |
my %form_params = $self->final_params; |
|
90 |
# my $priv = _priv($self); |
|
91 |
|
|
92 |
if ($self->is_enabled && $form_params{page}) { |
|
93 |
$params{ $self->form_params->[0] } = $form_params{page}; |
|
94 |
$params{ $self->form_params->[1] } = $form_params{per_page} if $form_params{per_page}; |
|
95 |
} |
|
96 |
|
|
97 |
# $::lxdebug->dump(0, "CB handler for paginated; params nach modif:", \%params); |
|
98 |
|
|
99 |
return %params; |
|
100 |
} |
|
101 |
|
|
102 |
sub _get_models_handler_for_paginated { |
|
103 |
my ($self, %params) = @_; |
|
104 |
|
|
105 |
$self->get_models->manager->paginate($self->final_params, args => \%params) if $self->is_enabled; |
|
106 |
|
|
107 |
# $::lxdebug->dump(0, "GM handler for paginated; params nach modif (is_enabled? " . _is_enabled($self) . ")", \%params); |
|
108 |
|
|
109 |
return %params; |
|
110 |
} |
|
111 |
|
|
112 |
sub is_enabled { |
|
113 |
my ($self) = @_; |
|
114 |
return !$self->disabled; |
|
115 |
} |
|
116 |
|
|
117 |
1; |
|
118 |
__END__ |
|
119 |
|
|
120 |
=pod |
|
121 |
|
|
122 |
=encoding utf8 |
|
123 |
|
|
124 |
=head1 NAME |
|
125 |
|
|
126 |
SL::Controller::Helper::Paginated - A helper for semi-automatic handling |
|
127 |
of paginating lists of database models in a controller |
|
128 |
|
|
129 |
=head1 SYNOPSIS |
|
130 |
|
|
131 |
In a controller: |
|
132 |
|
|
133 |
use SL::Controller::Helper::GetModels; |
|
134 |
use SL::Controller::Helper::Paginated; |
|
135 |
|
|
136 |
__PACKAGE__->make_paginated( |
|
137 |
MODEL => 'BackgroundJobHistory', |
|
138 |
ONLY => [ qw(list) ], |
|
139 |
FORM_PARAMS => [ qw(page per_page) ], |
|
140 |
); |
|
141 |
|
|
142 |
sub action_list { |
|
143 |
my ($self) = @_; |
|
144 |
|
|
145 |
my $paginated_models = $self->get_models; |
|
146 |
$self->render('controller/list', ENTRIES => $paginated_models); |
|
147 |
} |
|
148 |
|
|
149 |
In said template: |
|
150 |
|
|
151 |
[% USE L %] |
|
152 |
|
|
153 |
<table> |
|
154 |
<thead> |
|
155 |
<tr> |
|
156 |
... |
|
157 |
</tr> |
|
158 |
</thead> |
|
159 |
|
|
160 |
<tbody> |
|
161 |
[% FOREACH entry = ENTRIES %] |
|
162 |
<tr> |
|
163 |
... |
|
164 |
</tr> |
|
165 |
[% END %] |
|
166 |
</tbody> |
|
167 |
</table> |
|
168 |
|
|
169 |
[% L.paginate_controls %] |
|
170 |
|
|
171 |
=head1 OVERVIEW |
|
172 |
|
|
173 |
This specialized helper module enables controllers to display a |
|
174 |
paginatable list of database models with as few lines as possible. It |
|
175 |
can also be combined trivially with the L<SL::Controller::Sorted> |
|
176 |
helper for sortable lists. |
|
177 |
|
|
178 |
For this to work the controller has to provide the information which |
|
179 |
indexes are eligible for paginateing etc. by a call to |
|
180 |
L<make_paginated> at compile time. |
|
181 |
|
|
182 |
The underlying functionality that enables the use of more than just |
|
183 |
the paginate helper is provided by the controller helper |
|
184 |
C<GetModels>. See the documentation for L<SL::Controller::Sorted> for |
|
185 |
more information on it. |
|
186 |
|
|
187 |
A template can use the method C<paginate_controls> from the layout |
|
188 |
helper module C<L> which renders the links for navigation between the |
|
189 |
pages. |
|
190 |
|
|
191 |
This module requires that the Rose model managers use their C<Paginated> |
|
192 |
helper. |
|
193 |
|
|
194 |
The C<Paginated> helper hooks into the controller call to the action via |
|
195 |
a C<run_before> hook. This is done so that it can remember the paginate |
|
196 |
parameters that were used in the current view. |
|
197 |
|
|
198 |
=head1 PACKAGE FUNCTIONS |
|
199 |
|
|
200 |
=over 4 |
|
201 |
|
|
202 |
=item C<make_paginated %paginate_spec> |
|
203 |
|
|
204 |
This function must be called by a controller at compile time. It is |
|
205 |
uesd to set the various parameters required for this helper to do its |
|
206 |
magic. |
|
207 |
|
|
208 |
The hash C<%paginate_spec> can include the following parameters: |
|
209 |
|
|
210 |
=over 4 |
|
211 |
|
|
212 |
=item * C<MODEL> |
|
213 |
|
|
214 |
Optional. A string: the name of the Rose database model that is used |
|
215 |
as a default in certain cases. If this parameter is missing then it is |
|
216 |
derived from the controller's package (e.g. for the controller |
|
217 |
C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to |
|
218 |
C<BackgroundJobHistory>). |
|
219 |
|
|
220 |
=item * C<PAGINATE_ARGS> |
|
221 |
|
|
222 |
Optional. Either a code reference or the name of function to be called |
|
223 |
on the controller importing this helper. |
|
224 |
|
|
225 |
If this funciton is given then the paginate helper calls it whenever |
|
226 |
it has to count the total number of models for calculating the number |
|
227 |
of pages to display. The function must return a hash reference with |
|
228 |
elements suitable for passing to a Rose model manager's C<get_all> |
|
229 |
function. |
|
230 |
|
|
231 |
This can be used e.g. when filtering is used. |
|
232 |
|
|
233 |
=item * C<PER_PAGE> |
|
234 |
|
|
235 |
Optional. An integer: the number of models to return per page. |
|
236 |
|
|
237 |
Defaults to the underlying database model's default number of models |
|
238 |
per page. |
|
239 |
|
|
240 |
=item * C<FORM_PARAMS> |
|
241 |
|
|
242 |
Optional. An array reference with exactly two strings that name the |
|
243 |
indexes in C<$::form> in which the current page's number (the first |
|
244 |
element in the array) and the number of models per page (the second |
|
245 |
element in the array) are stored. |
|
246 |
|
|
247 |
Defaults to the values C<page> and C<per_page> if missing. |
|
248 |
|
|
249 |
=item * C<ONLY> |
|
250 |
|
|
251 |
Optional. An array reference containing a list of action names for |
|
252 |
which the paginate parameters should be saved. If missing or empty then |
|
253 |
all actions invoked on the controller are monitored. |
|
254 |
|
|
255 |
=back |
|
256 |
|
|
257 |
=back |
|
258 |
|
|
259 |
=head1 INSTANCE FUNCTIONS |
|
260 |
|
|
261 |
These functions are called on a controller instance. |
|
262 |
|
|
263 |
=over 4 |
|
264 |
|
|
265 |
=item C<get_paginate_spec> |
|
266 |
|
|
267 |
Returns a hash containing the currently active paginate |
|
268 |
parameters. The following keys are returned: |
|
269 |
|
|
270 |
=over 4 |
|
271 |
|
|
272 |
=item * C<page> |
|
273 |
|
|
274 |
The currently active page number (numbering starts at 1). |
|
275 |
|
|
276 |
=item * C<per_page> |
|
277 |
|
|
278 |
Number of models per page (at least 1). |
|
279 |
|
|
280 |
=item * C<num_pages> |
|
281 |
|
|
282 |
Number of pages to display (at least 1). |
|
283 |
|
|
284 |
=item * C<common_pages> |
|
285 |
|
|
286 |
An array reference with one hash reference for each possible |
|
287 |
page. Each hash ref contains the keys C<active> (C<1> if that page is |
|
288 |
the currently active page), C<page> (the page number this hash |
|
289 |
reference describes) and C<visible> (whether or not it should be |
|
290 |
displayed). |
|
291 |
|
|
292 |
=back |
|
293 |
|
|
294 |
=item C<get_current_paginate_params> |
|
295 |
|
|
296 |
Returns a hash reference to the paginate spec structure given in the call |
|
297 |
to L<make_paginated> after normalization (hash reference construction, |
|
298 |
applying default parameters etc). |
|
299 |
|
|
300 |
=item C<disable_pagination> |
|
301 |
|
|
302 |
Disable pagination for the duration of the current action. Can be used |
|
303 |
when using the attribute C<ONLY> to L<make_paginated> does not |
|
304 |
cover all cases. |
|
305 |
|
|
306 |
=back |
|
307 |
|
|
308 |
=head1 BUGS |
|
309 |
|
|
310 |
Nothing here yet. |
|
311 |
|
|
312 |
=head1 AUTHOR |
|
313 |
|
|
314 |
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt> |
|
315 |
|
|
316 |
=cut |
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) ], |
|
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 |
get_models => sub { shift; $self->_get_models_handler_for_sorted(@_) }, |
|
41 |
); |
|
42 |
|
|
43 |
# $::lxdebug->dump(0, "CONSPEC", \%specs); |
|
44 |
} |
|
45 |
|
|
46 |
sub get_current_sort_params { |
|
47 |
my ($self, %params) = @_; |
|
48 |
|
|
49 |
my %sort_params; |
|
50 |
my ($by, $dir) = @{ $self->form_params }; |
|
51 |
|
|
52 |
if ($::form->{ $by }) { |
|
53 |
%sort_params = ( |
|
54 |
sort_by => $::form->{$by}, |
|
55 |
sort_dir => defined($::form->{$dir}) ? $::form->{$dir} * 1 : undef, |
|
56 |
); |
|
57 |
} elsif (!$self->by) { |
|
58 |
%sort_params = %params; |
|
59 |
} else { |
|
60 |
%sort_params = ( |
|
61 |
sort_by => $self->by, |
|
62 |
sort_dir => $self->dir, |
|
63 |
); |
|
64 |
} |
|
65 |
|
|
66 |
return %sort_params; |
|
67 |
} |
|
68 |
|
|
69 |
sub set_report_generator_sort_options { |
|
70 |
my ($self, %params) = @_; |
|
71 |
|
|
72 |
$params{$_} or croak("Missing parameter '$_'") for qw(report sortable_columns); |
|
73 |
|
|
74 |
my %current_sort_params = $self->get_current_sort_params; |
|
75 |
|
|
76 |
foreach my $col (@{ $params{sortable_columns} }) { |
|
77 |
$params{report}->{columns}->{$col}->{link} = $self->get_models->get_callback( |
|
78 |
sort_by => $col, |
|
79 |
sort_dir => ($current_sort_params{sort_by} eq $col ? 1 - $current_sort_params{sort_dir} : $current_sort_params{sort_dir}), |
|
80 |
); |
|
81 |
} |
|
82 |
|
|
83 |
$params{report}->set_sort_indicator($current_sort_params{sort_by}, 1 - $current_sort_params{sort_dir}); |
|
84 |
|
|
85 |
if ($params{report}->{export}) { |
|
86 |
$params{report}->{export}->{variable_list} = [ uniq( |
|
87 |
@{ $params{report}->{export}->{variable_list} }, |
|
88 |
@{ $self->form_params } |
|
89 |
)]; |
|
90 |
} |
|
91 |
} |
|
92 |
|
|
93 |
# |
|
94 |
# private functions |
|
95 |
# |
|
96 |
|
|
97 |
sub _callback_handler_for_sorted { |
|
98 |
my ($self, %params) = @_; |
|
99 |
my %spec = $self->get_current_sort_params; |
|
100 |
|
|
101 |
if ($spec{sort_by}) { |
|
102 |
$params{ $self->form_params->[0] } = $spec{sort_by}; |
|
103 |
$params{ $self->form_params->[1] } = $spec{sort_dir}; |
|
104 |
} |
|
105 |
|
|
106 |
# $::lxdebug->dump(0, "CB handler for sorted; params nach modif:", \%params); |
|
107 |
|
|
108 |
return %params; |
|
109 |
} |
|
110 |
|
|
111 |
sub _get_models_handler_for_sorted { |
|
112 |
my ($self, %params) = @_; |
|
113 |
|
|
114 |
my %sort_params = $self->get_current_sort_params; |
|
115 |
my $sort_spec = $self->specs->{ $sort_params{sort_by} }; |
|
116 |
|
|
117 |
$params{sort_by} = "SL::DB::Manager::$sort_spec->{model}"->make_sort_string(sort_by => $sort_spec->{model_column}, sort_dir => $sort_params{sort_dir}); |
|
118 |
|
|
119 |
# $::lxdebug->dump(0, "GM handler for sorted; params nach modif:", \%params); |
|
120 |
|
|
121 |
return %params; |
|
122 |
} |
|
123 |
|
|
124 |
|
|
125 |
sub init_form_params { |
|
126 |
[ qw(sort_by sort_dir) ] |
|
127 |
} |
|
128 |
|
|
129 |
1; |
|
130 |
__END__ |
|
131 |
|
|
132 |
=pod |
|
133 |
|
|
134 |
=encoding utf8 |
|
135 |
|
|
136 |
=head1 NAME |
|
137 |
|
|
138 |
SL::Controller::Helper::Sorted - A helper for semi-automatic handling |
|
139 |
of sorting lists of database models in a controller |
|
140 |
|
|
141 |
=head1 SYNOPSIS |
|
142 |
|
|
143 |
In a controller: |
|
144 |
|
|
145 |
use SL::Controller::Helper::GetModels; |
|
146 |
use SL::Controller::Helper::Sorted; |
|
147 |
|
|
148 |
__PACKAGE__->make_sorted( |
|
149 |
DEFAULT_BY => 'run_at', |
|
150 |
DEFAULT_DIR => 1, |
|
151 |
MODEL => 'BackgroundJobHistory', |
|
152 |
ONLY => [ qw(list) ], |
|
153 |
|
|
154 |
error => $::locale->text('Error'), |
|
155 |
package_name => $::locale->text('Package name'), |
|
156 |
run_at => $::locale->text('Run at'), |
|
157 |
); |
|
158 |
|
|
159 |
sub action_list { |
|
160 |
my ($self) = @_; |
|
161 |
|
|
162 |
my $sorted_models = $self->get_models; |
|
163 |
$self->render('controller/list', ENTRIES => $sorted_models); |
|
164 |
} |
|
165 |
|
|
166 |
In said template: |
|
167 |
|
|
168 |
[% USE L %] |
|
169 |
|
|
170 |
<table> |
|
171 |
<tr> |
|
172 |
<th>[% L.sortable_table_header('package_name') %]</th> |
|
173 |
<th>[% L.sortable_table_header('run_at') %]</th> |
|
174 |
<th>[% L.sortable_table_header('error') %]</th> |
|
175 |
</tr> |
|
176 |
|
|
177 |
[% FOREACH entry = ENTRIES %] |
|
178 |
<tr> |
|
179 |
<td>[% HTML.escape(entry.package_name) %]</td> |
|
180 |
<td>[% HTML.escape(entry.run_at) %]</td> |
|
181 |
<td>[% HTML.escape(entry.error) %]</td> |
|
182 |
</tr> |
|
183 |
[% END %] |
|
184 |
</table> |
|
185 |
|
|
186 |
=head1 OVERVIEW |
|
187 |
|
|
188 |
This specialized helper module enables controllers to display a |
|
189 |
sortable list of database models with as few lines as possible. |
|
190 |
|
|
191 |
For this to work the controller has to provide the information which |
|
192 |
indexes are eligible for sorting etc. by a call to L<make_sorted> at |
|
193 |
compile time. |
|
194 |
|
|
195 |
The underlying functionality that enables the use of more than just |
|
196 |
the sort helper is provided by the controller helper C<GetModels>. It |
|
197 |
provides mechanisms for helpers like this one to hook into certain |
|
198 |
calls made by the controller (C<get_callback> and C<get_models>) so |
|
199 |
that the specialized helpers can inject their parameters into the |
|
200 |
calls to e.g. C<SL::DB::Manager::SomeModel::get_all>. |
|
201 |
|
|
202 |
A template on the other hand can use the method |
|
203 |
C<sortable_table_header> from the layout helper module C<L>. |
|
204 |
|
|
205 |
This module requires that the Rose model managers use their C<Sorted> |
|
206 |
helper. |
|
207 |
|
|
208 |
The C<Sorted> helper hooks into the controller call to the action via |
|
209 |
a C<run_before> hook. This is done so that it can remember the sort |
|
210 |
parameters that were used in the current view. |
|
211 |
|
|
212 |
=head1 PACKAGE FUNCTIONS |
|
213 |
|
|
214 |
=over 4 |
|
215 |
|
|
216 |
=item C<make_sorted %sort_spec> |
|
217 |
|
|
218 |
This function must be called by a controller at compile time. It is |
|
219 |
uesd to set the various parameters required for this helper to do its |
|
220 |
magic. |
|
221 |
|
|
222 |
There are two sorts of keys in the hash C<%sort_spec>. The first kind |
|
223 |
is written in all upper-case. Those parameters are control |
|
224 |
parameters. The second kind are all lower-case and represent indexes |
|
225 |
that can be used for sorting (similar to database column names). The |
|
226 |
second kind are also the indexes you use in a template when calling |
|
227 |
C<[% L.sorted_table_header(...) %]>. |
|
228 |
|
|
229 |
Control parameters include the following: |
|
230 |
|
|
231 |
=over 4 |
|
232 |
|
|
233 |
=item * C<MODEL> |
|
234 |
|
|
235 |
Optional. A string: the name of the Rose database model that is used |
|
236 |
as a default in certain cases. If this parameter is missing then it is |
|
237 |
derived from the controller's package (e.g. for the controller |
|
238 |
C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to |
|
239 |
C<BackgroundJobHistory>). |
|
240 |
|
|
241 |
=item * C<DEFAULT_BY> |
|
242 |
|
|
243 |
Optional. A string: the index to sort by if the user hasn't clicked on |
|
244 |
any column yet (meaning: if the C<$::form> parameters for sorting do |
|
245 |
not contain a valid index). |
|
246 |
|
|
247 |
Defaults to the underlying database model's default sort column name. |
|
248 |
|
|
249 |
=item * C<DEFAULT_DIR> |
|
250 |
|
|
251 |
Optional. Default sort direction (ascending for trueish values, |
|
252 |
descrending for falsish values). |
|
253 |
|
|
254 |
Defaults to the underlying database model's default sort direction. |
|
255 |
|
|
256 |
=item * C<FORM_PARAMS> |
|
257 |
|
|
258 |
Optional. An array reference with exactly two strings that name the |
|
259 |
indexes in C<$::form> in which the sort index (the first element in |
|
260 |
the array) and sort direction (the second element in the array) are |
|
261 |
stored. |
Auch abrufbar als: Unified diff
Erste Version GetModels rewrite
known bugs:
disable pagination funktioniert nicht
compiletime optimizations werden noch nicht benutzt
doku fehlt