File Coverage

blib/lib/RapidApp/Module/Grid.pm
Criterion Covered Total %
statement 72 111 64.8
branch 42 74 56.7
condition 8 17 47.0
subroutine 17 22 77.2
pod 0 13 0.0
total 139 237 58.6


line stmt bran cond sub pod time code
1             package RapidApp::Module::Grid;
2              
3 4     4   2084 use strict;
  4         9  
  4         109  
4 4     4   20 use warnings;
  4         10  
  4         87  
5              
6 4     4   19 use Moose;
  4         6  
  4         22  
7             extends 'RapidApp::Module::StorCmp';
8              
9 4     4   22015 use RapidApp::Util qw(:all);
  4         10  
  4         1671  
10              
11 4     4   29 use Try::Tiny;
  4         16  
  4         6688  
12              
13             #has 'record_pk' => ( is => 'ro', default => 'id' );
14             #has 'DataStore_class' => ( is => 'ro', default => 'RapidApp::Module::DatStor', isa => 'ClassName' );
15              
16              
17             has 'title' => ( is => 'ro', default => undef );
18             has 'title_icon_href' => ( is => 'ro', default => undef );
19              
20             has 'open_record_class' => ( is => 'ro', default => undef, isa => 'Maybe[ClassName|HashRef]' );
21             has 'add_record_class' => ( is => 'ro', default => undef, isa => 'Maybe[ClassName|HashRef]' );
22              
23             has 'open_record_via_rest', is => 'ro', isa => 'Bool', default => 1;
24             has 'open_record_rest_key', is => 'ro', isa => 'Maybe[Str]', lazy => 1, default => undef;
25              
26             # ---
27             # If true, a special "open" link column will be prepended to the column list. This is useful for clients
28             # that cannot double-click, such as iPads
29             has 'open_record_column', is => 'ro', isa => 'Bool', default => 1;
30             has 'open_record_column_hidden', is => 'ro', isa => 'Bool', default => 1;
31             # ---
32              
33             # autoLoad needs to be false for the paging toolbar to not load the whole
34             # data set
35             has 'store_autoLoad' => ( is => 'ro', default => sub {\0} );
36              
37             has 'add_loadContentCnf' => ( is => 'ro', default => sub {
38             {
39             title => 'Add',
40             iconCls => 'ra-icon-add'
41             }
42             });
43              
44             has 'add_button_cnf' => ( is => 'ro', default => sub {
45             {
46             text => 'Add',
47             iconCls => 'ra-icon-add'
48             }
49             });
50              
51             # Either based on open_record_class, or can be set manually in the constructor:
52             has 'open_record_url' => ( is => 'ro', isa => 'Maybe[Str]', lazy => 1, default => sub {
53             my $self = shift;
54             return $self->Module('item',1)->base_url if ($self->open_record_class);
55             return undef;
56             });
57              
58              
59             # get_record_loadContentCnf is used on a per-row basis to set the
60             # options used to load the row in a tab when double-clicked
61             # This should be overridden in the subclass:
62             sub get_record_loadContentCnf {
63 0     0 0 0 my ($self, $record) = @_;
64            
65 0         0 local $_ = $record;
66 0         0 my $display = $self->get_record_display->($self);
67 0         0 %$record = %$_;
68            
69 0 0       0 $display = { title => $display } unless (ref $display);
70            
71 0         0 return $display;
72             }
73             has 'get_record_display' => ( is => 'ro', isa => 'CodeRef', lazy => 1, default => sub { sub {
74             my $self = shift;
75             $self->record_pk . ': ' . $_->{$self->record_pk}
76             }});
77              
78             has 'init_pagesize' => ( is => 'ro', isa => 'Int', default => 25 );
79             has '+max_pagesize' => ( default => 500 );
80             has 'use_column_summaries', is => 'ro', isa => 'Bool', default => 0;
81              
82             # -- autosize columns options:
83             has 'use_autosize_columns', is => 'ro', isa => 'Bool', default => 1;
84             has 'auto_autosize_columns', is => 'ro', isa => 'Bool', default => 0;
85             has 'auto_autosize_columns_deep', is => 'ro', isa => 'Bool', default => 0;
86             has 'autosize_hidden', is => 'ro', isa => 'Bool', default => 0;
87             has 'autosize_maxwidth', is => 'ro', isa => 'Int', default => 450;
88             # --
89              
90             # -- multisort options:
91             has 'use_multisort', is => 'ro', isa => 'Bool', default => 1;
92             # --
93              
94             has 'allow_edit_frozen', is => 'ro', isa => 'Bool', default => 1;
95              
96             sub BUILD {
97 52     52 0 1119 my $self = shift;
98            
99 52 100       1710 if (defined $self->open_record_class) {
100 45         1338 $self->apply_init_modules( item => $self->open_record_class );
101            
102             # reach into the new sub-module and add a write listener to its store to
103             # make it call our store.load() whenever it changes:
104             ### Temp disabled because this can cause a big load/hit ##
105             #$self->Module('item',1)->DataStore->add_listener( write => $self->DataStore->store_load_fn ) if (
106             # $self->Module('item',1)->isa('RapidApp::Module::StorCmp')
107             #);
108             }
109            
110 52 50       1371 if (defined $self->add_record_class) {
111 0         0 $self->apply_init_modules( add => $self->add_record_class );
112            
113             # reach into the new sub-module and add a write listener to its store to
114             # make it call our store.load() whenever it changes:
115            
116             ### Temp disabled because this can cause a big load/hit ##
117             #$self->Module('add',1)->DataStore->add_listener( write => $self->DataStore->store_load_fn ) if (
118             # $self->Module('add',1)->isa('RapidApp::Module::StorCmp')
119             #);
120             }
121            
122            
123             $self->apply_extconfig(
124 52 100       1323 xtype => 'appgrid2',
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
125             pageSize => $self->init_pagesize,
126             maxPageSize => $self->max_pagesize,
127             stripeRows => \1,
128             columnLines => \1,
129             gridsearch => \1,
130             column_allow_save_properties => [qw(width hidden)],
131             use_column_summaries => $self->use_column_summaries ? \1 : \0,
132             use_autosize_columns => $self->use_autosize_columns ? \1 : \0,
133             use_multisort => $self->use_multisort ? \1 : \0,
134             auto_autosize_columns => $self->auto_autosize_columns ? \1 : \0,
135             auto_autosize_columns_deep => $self->auto_autosize_columns_deep ? \1 : \0,
136             autosize_hidden => $self->autosize_hidden ? \1 : \0,
137             autosize_maxwidth => $self->autosize_maxwidth,
138             allow_edit_frozen => $self->allow_edit_frozen ? \1 : \0,
139             open_record_via_rest => $self->open_record_via_rest ? \1 : \0,
140             open_record_url => $self->open_record_url,
141             open_record_rest_key => $self->open_record_rest_key,
142             open_record_column => $self->open_record_column ? \1 : \0,
143             open_record_column_hidden => $self->open_record_column_hidden ? \1 : \0,
144             );
145            
146             # The record_pk is forced to be added/included as a column:
147 52 50       1831 if (defined $self->record_pk) {
148 52         1245 $self->apply_columns( $self->record_pk => {} );
149 52 50       118 push @{ $self->include_columns }, $self->record_pk if (scalar @{ $self->include_columns } > 0);
  0         0  
  52         371  
150             #$self->meta->find_attribute_by_name('include_columns_hash')->clear_value($self);
151 52         110 %{ $self->include_columns_hash } = ();
  52         295  
152             }
153            
154 52 100 66     1222 if (defined $self->open_record_url or defined $self->add_record_class) {
155 45         1288 $self->add_listener( beforerender => RapidApp::JSONFunc->new( raw => 1, func =>
156             'Ext.ux.RapidApp.AppTab.cnt_init_loadTarget'
157             ));
158             }
159            
160             # -- Moved into Ext.ux.RapidApp.AppTab.AppGrid2Def:
161             #if (defined $self->open_record_url) {
162             # $self->add_listener( rowdblclick => RapidApp::JSONFunc->new( raw => 1, func =>
163             # 'Ext.ux.RapidApp.AppTab.gridrow_nav'
164             # ));
165             #}
166             # --
167            
168 52 100       1734 $self->apply_actions( save_search => 'save_search' ) if ( $self->can('save_search') );
169 52 100       1540 $self->apply_actions( delete_search => 'delete_search' ) if ( $self->can('delete_search') );
170            
171 52         1258 $self->DataStore->add_read_raw_mungers(RapidApp::Handler->new( scope => $self, method => 'add_loadContentCnf_read_munger' ));
172            
173 52         292 $self->add_ONREQUEST_calls('init_onrequest');
174 52         253 $self->add_ONREQUEST_calls_late('init_delete_enable');
175             }
176              
177             sub init_onrequest {
178 22     22 0 46 my $self = shift;
179            
180             $self->apply_extconfig( preload_quick_search => $self->c->req->params->{quick_search} )
181 22 50   22   152 if (try{$self->c->req->params->{quick_search}});
  22         580  
182            
183 22     22   1900 my $quick_search_cols = try{$self->c->req->params->{quick_search_cols}};
  22         470  
184 22 50 33     1647 if($quick_search_cols && $quick_search_cols ne '') {
185 0         0 $quick_search_cols = lc($quick_search_cols);
186 0         0 my @cols = split(/\s*,\s*/,$quick_search_cols);
187 0         0 $self->apply_extconfig( init_quick_search_columns => \@cols );
188             }
189            
190             # Added for #94:
191 22 50   22   118 if(my $qs_mode = try{$self->c->req->params->{quick_search_mode}}) {
  22         455  
192 0         0 $self->apply_extconfig( init_quick_search_mode => $qs_mode );
193             }
194            
195            
196             #$self->apply_config(store => $self->JsonStore);
197 22 100       1645 $self->apply_extconfig(tbar => $self->tbar_items) if (defined $self->tbar_items);
198             }
199              
200              
201              
202             sub init_delete_enable {
203 22     22 0 50 my $self = shift;
204 22 50 33     217 if($self->can('action_delete_records') and $self->has_flag('can_delete')) {
205             #if($self->can('action_delete_records')) {
206 0         0 my $act_name = 'delete_rows';
207 0         0 $self->apply_actions( $act_name => 'action_delete_records' );
208 0         0 $self->apply_extconfig( delete_url => $self->suburl($act_name) );
209             }
210             }
211              
212              
213              
214              
215             sub add_loadContentCnf_read_munger {
216 8     8 0 20 my $self = shift;
217 8         15 my $result = shift;
218            
219             # Add a 'loadContentCnf' field to store if open_record_class is defined.
220             # This data is used when a row is double clicked on to open the open_record_class
221             # module in the loadContent handler (JS side object). This is currently AppTab
222             # but could be other JS classes that support the same API
223 8 100       216 if (defined $self->open_record_url) {
224 1         2 foreach my $record (@{$result->{rows}}) {
  1         7  
225 0         0 my $loadCfg = {};
226             # support merging from existing loadContentCnf already contained in the record data:
227 0 0       0 $loadCfg = $self->json->decode($record->{loadContentCnf}) if (defined $record->{loadContentCnf});
228            
229 0         0 %{ $loadCfg } = (
230 0         0 %{ $self->get_record_loadContentCnf($record) },
231 0         0 %{ $loadCfg }
  0         0  
232             );
233            
234 0 0       0 unless (defined $loadCfg->{autoLoad}) {
235 0         0 $loadCfg->{autoLoad} = {};
236 0 0       0 $loadCfg->{autoLoad}->{url} = $loadCfg->{url} if ($loadCfg->{url});
237             }
238            
239 0 0       0 $loadCfg->{autoLoad}->{url} = $self->open_record_url unless (defined $loadCfg->{autoLoad}->{url});
240            
241 0         0 $record->{loadContentCnf} = $self->json->encode($loadCfg);
242             }
243             }
244             }
245              
246              
247             #around 'store_read_raw' => sub {
248             # my $orig = shift;
249             # my $self = shift;
250             #
251             # my $result = $self->$orig(@_);
252             #
253             # # Add a 'loadContentCnf' field to store if open_record_class is defined.
254             # # This data is used when a row is double clicked on to open the open_record_class
255             # # module in the loadContent handler (JS side object). This is currently AppTab
256             # # but could be other JS classes that support the same API
257             # if (defined $self->open_record_class) {
258             # foreach my $record (@{$result->{rows}}) {
259             # my $loadCfg = {};
260             # # support merging from existing loadContentCnf already contained in the record data:
261             # $loadCfg = $self->json->decode($record->{loadContentCnf}) if (defined $record->{loadContentCnf});
262             #
263             # %{ $loadCfg } = (
264             # %{ $self->get_record_loadContentCnf($record) },
265             # %{ $loadCfg }
266             # );
267             #
268             # $loadCfg->{autoLoad} = {} unless (defined $loadCfg->{autoLoad});
269             # $loadCfg->{autoLoad}->{url} = $self->Module('item')->base_url unless (defined $loadCfg->{autoLoad}->{url});
270             #
271             #
272             # $record->{loadContentCnf} = $self->json->encode($loadCfg);
273             # }
274             # }
275             #
276             # return $result;
277             #};
278              
279             sub options_menu_button_Id {
280 84     84 0 150 my $self = shift;
281 84         1938 return $self->instance_id . '-options-menu-btn';
282             }
283              
284             sub options_menu_items {
285 43     43 0 71 my $self = shift;
286 43         86 return undef;
287             }
288              
289              
290             sub options_menu {
291 43     43 0 68 my $self = shift;
292            
293 43 100       140 my $items = $self->options_menu_items or return undef;
294 42 50 50     254 return undef unless (ref($items) eq 'ARRAY') && scalar(@$items);
295            
296             # Make it easy to find the options menu on the client side (JS):
297 42         959 my $menu_id = $self->instance_id . '-options-menu';
298 42         1298 $self->apply_extconfig('options_menu_id' => $menu_id );
299            
300             return {
301 42         310 xtype => 'button',
302             id => $self->options_menu_button_Id,
303             text => 'Options',
304             iconCls => 'ra-icon-gears',
305             itemId => 'options-button',
306             menu => {
307             items => $items,
308             id => $menu_id
309             }
310             };
311             }
312              
313              
314              
315             sub tbar_items {
316 43     43 0 75 my $self = shift;
317            
318 43         73 my $arrayref = [];
319            
320 43 50       1075 push @$arrayref, '<img src="' . $self->title_icon_href . '" />' if (defined $self->title_icon_href);
321 43 50       985 push @$arrayref, '<b>' . $self->title . '</b>' if (defined $self->title);
322              
323 43         120 my $menu = $self->options_menu;
324 43 50 66     189 push @$arrayref, ' ', '-' if (defined $menu and scalar(@$arrayref) > 0);
325 43 100       111 push @$arrayref, $menu if (defined $menu);
326            
327 43         67 push @$arrayref, '->';
328            
329 43 50 33     1058 push @$arrayref, $self->add_button if (
330             defined $self->add_record_class and
331             $self->show_add_button
332             );
333              
334 43 100       737 return (scalar @$arrayref > 1) ? $arrayref : undef;
335             }
336 0     0 0   sub show_add_button { 1 }
337              
338             sub add_button {
339 0     0 0   my $self = shift;
340            
341             my $loadCfg = {
342             url => $self->suburl('add'),
343 0           %{ $self->add_loadContentCnf }
  0            
344             };
345            
346 0           my $handler = RapidApp::JSONFunc->new( raw => 1, func =>
347             'function(btn) { btn.ownerCt.ownerCt.loadTargetObj.loadContent(' . $self->json->encode($loadCfg) . '); }'
348             );
349            
350             return RapidApp::JSONFunc->new( func => 'new Ext.Button', parm => {
351             handler => $handler,
352 0           %{ $self->add_button_cnf }
  0            
353             });
354             }
355              
356              
357              
358              
359             sub set_all_columns_hidden {
360 0     0 0   my $self = shift;
361 0           return $self->apply_to_all_columns(
362             hidden => \1
363             );
364             }
365              
366              
367             sub set_columns_visible {
368 0     0 0   my $self = shift;
369 0 0         my @cols = (ref($_[0]) eq 'ARRAY') ? @{ $_[0] } : @_; # <-- arg as array or arrayref
  0            
370 0           return $self->apply_columns_list(\@cols,{
371             hidden => \0
372             });
373             }
374              
375              
376              
377             #### --------------------- ####
378              
379              
380 4     4   57 no Moose;
  4         12  
  4         20  
381             #__PACKAGE__->meta->make_immutable;
382             1;