File Coverage

blib/lib/Mojolicious/Plugin/Tables/Controller/Tables.pm
Criterion Covered Total %
statement 53 214 24.7
branch 10 76 13.1
condition 7 48 14.5
subroutine 8 15 53.3
pod 0 14 0.0
total 78 367 21.2


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Tables::Controller::Tables;
2              
3 3     3   6222 use Mojo::Base 'Mojolicious::Controller';
  3         8  
  3         20  
4              
5             # 'shipped' is a special stash slot for passing serialisable things to json or templates.
6             # Other stash slots can be complex objects for use by templates.
7              
8             sub ok {
9 6     6 0 5340 my $c = shift;
10 6         33 my $log = $c->app->log;
11 6   50     64 my $user_id = $c->stash('user_id') || die 'no user_id in stash';
12 6 50       102 if ($c->allowed($user_id, 'tables')) {
13 6   33     26 my $logourl = $c->app->config('logourl') || $c->url_for('/img/tables.png');
14 6         2112 my $model = $c->app->config('model');
15             $c->shipped( tablist => $model->{tablist},
16             bytable => $model->{bytable},
17 6         138 logourl => $logourl );
18 6         37 $c->stash( schema=>$model->{schema} );
19 6         116 return 1;
20             }
21 0         0 my $error = "tables activity unauthorised";
22 0 0 0     0 $c->fail($error) if (($c->stash('format')||'x') eq 'json');
23 0         0 $c->add_flash(errors=>$error);
24 0         0 $c->redirect_to('/');
25 0         0 return;
26             }
27              
28             # more fine-grained permission control..
29 6     6 0 27 sub allowed { 1 }
30              
31             sub page {
32 2     2 0 3373 my $c = shift;
33 2 50       16 $c->shipped(start_with=>$c->param('start_with')) if $c->param('start_with')
34             }
35              
36             sub table_ok {
37 4     4 0 6049 my $c = shift;
38 4         22 my $table = $c->stash('table');
39 4         68 my $schema = $c->stash('schema');
40 4         56 my $bytable= $c->shipped('bytable');
41 4   50     31 my $tinfo = $bytable->{$table} || die "no table $table";
42 4         19 my $source = $tinfo->{source};
43 4   50     119 my $rs = $schema->resultset($source) || die "no source $source";
44 4         3822 $c->stash(rs=>$rs);
45 4         97 $c->shipped(table=>$table, tinfo=>$tinfo);
46 4         17 1;
47             }
48              
49             sub id_ok {
50 4     4 0 6214 my $c = shift;
51 4         21 my $id = $c->stash('id');
52 4         55 my $rs = $c->stash('rs');
53 4         49 my @ids = split(/-\|-/, $id); # as made by see Row::compound_ids
54 4 50       29 if (my $row = $rs->find(@ids)) {
55 4         31 $c->stash(row => $row);
56 4         94 $c->shipped(id => $id);
57 4         27 return 1;
58             }
59 0         0 my $table = $c->stash('table');
60 0         0 die "no entry [$id] for table [$table]\n";
61             }
62              
63             sub table {
64 0     0 0 0 my $c = shift;
65 0         0 my $log = $c->app->log;
66 0         0 my $table = $c->stash('table');
67 0         0 my $rs = $c->stash('rs');
68 0         0 my $tinfo = $c->shipped('tinfo');
69              
70 0 0 0     0 if (($c->stash('format')||'html') eq 'html') {
71 0         0 $c->redirect_to("/tables?start_with=$table");
72 0         0 return;
73             }
74              
75 0         0 my $columns = $tinfo->{columns};
76 0         0 my $bycolumn = $tinfo->{bycolumn};
77 0         0 my $has_a = $tinfo->{has_a};
78 0         0 my @has_a = map { $_->{parent} } values %$has_a;
  0         0  
79              
80 0         0 my @order_bys;
81 0         0 for (my $i=0; 1; $i++) {
82 0   0     0 my $col = $c->param("order[$i][column]") // last;
83 0   0     0 my $dir = $c->param("order[$i][dir]" ) // last;
84 0         0 push @order_bys, {col=>$col, dir=>$dir};
85             }
86 0   0     0 my $draw = $c->param('draw') || 0;
87 0   0     0 my $start = $c->param('start') || 0;
88 0   0     0 my $length = $c->param('length') || -1;
89 0         0 my $search_v = $c->param('search[value]');
90 0         0 my $search_r = $c->param('search[regex]');
91              
92             # q1: count all candiates
93 0         0 my $recordsTotal = $rs->count;
94 0         0 my $recordsFiltered = $recordsTotal;
95            
96 0         0 my $attrs = {};
97 0         0 my $where = [];
98              
99             # q2: count filtered candidates
100 0 0       0 if ($search_v) {
101 0         0 my $like_v = { ilike => "%$search_v%" };
102 0         0 for my $col (@$columns) {
103 0         0 my $info = $bycolumn->{$col};
104 0 0       0 next if $info->{fkey};
105 0 0       0 next unless $info->{data_type} eq 'varchar';
106 0         0 push @$where, {$col=>$like_v};
107             }
108 0         0 $recordsFiltered = $rs->search($where, $attrs)->count;
109             }
110              
111             # q3: full
112 0 0       0 $attrs->{order_by} = [ map { {"-$_->{dir}" => $columns->[$_->{col}] } } @order_bys ]
  0         0  
113             if @order_bys;
114 0         0 $attrs->{offset} = $start;
115 0 0       0 $attrs->{rows} = $length if $length>0;
116             #$log->debug("where " . $c->dumper($where));
117             #$log->debug("attrs " . $c->dumper($columns));
118             #$log->debug("odbys " . $c->dumper(\@order_bys));
119             #$log->debug("attrs " . $c->dumper($attrs));
120 0         0 my @rows = $rs->search($where, $attrs);
121              
122             my @data = map {
123 0         0 my $row = $_;
  0         0  
124 0         0 my $datum = {DT_RowId=>$row->compound_ids};
125 0         0 for (@$columns) {
126 0         0 my $info = $bycolumn->{$_};
127 0 0       0 if ($info->{fkey}) {
128 0         0 $datum->{$_} = undef;
129 0 0       0 if (my $frow = $row->$_) {
130 0         0 $datum->{$_} = sprintf('%s',$frow)
131             }
132 0         0 next;
133             }
134 0         0 $datum->{$_} = $row->present($_, $info);
135             }
136             $datum
137 0         0 } @rows;
138              
139 0         0 my $bundle = {
140             draw => $draw,
141             recordsTotal => $recordsTotal,
142             recordsFiltered => $recordsFiltered,
143             data => \@data,
144             };
145              
146 0 0 0     0 if (($c->stash('format')||'data') eq 'json') {
147 0         0 $c->render( json => $bundle );
148 0         0 return;
149             }
150 0         0 $c->render(data => $c->dumper($bundle));
151             }
152              
153       0 0   sub edit {}
154       3 0   sub view {}
155 0     0 0 0 sub add { shift->render(template=>'tables/edit', dml_mode=>'add', row=>undef) }
156             sub del {
157 0     0 0 0 my $c = shift;
158 0         0 $c->add_stash(messages => "Confirm.. all you see here will be removed");
159 0         0 $c->render(template=>'tables/view', dml_mode=>'del');
160             }
161              
162             sub nuke {
163 0     0 0 0 my $c = shift;
164 0         0 my $table= $c->stash('table');
165 0         0 my $row = $c->stash('row');
166 0         0 my $id = $c->shipped('id');
167 0         0 my $row0 = "$row";
168 0         0 my $hits = $row->nuke;
169 0 0       0 $c->add_flash(messages=>"REMOVED $row0 at key $id and all subordinate info.. $hits records in total") if $hits;
170 0         0 my $url = $c->url_for('/tables')->query(start_with=>$table);
171 0         0 $c->redirect_to($url);
172              
173             }
174              
175             sub save {
176 0     0 0 0 my $c = shift;
177 0         0 my $table = $c->stash('table');
178 0         0 my $rs = $c->stash('rs');
179 0         0 my $row = $c->stash('row');
180 0         0 my $schema = $c->stash('schema');
181 0         0 my $id = $c->shipped('id');
182 0         0 my $tinfo = $c->shipped('tinfo');
183 0         0 my $bytable= $c->shipped('bytable');
184              
185 0         0 my $columns = $tinfo->{columns};
186 0         0 my $bycolumn = $tinfo->{bycolumn};
187              
188 0 0       0 if ($row) { # update
189 0         0 for my $col (@$columns) {
190 0         0 my $info = $bycolumn->{$col};
191 0 0       0 if (defined (my $val = $c->param($col))) {
    0          
192 0 0 0     0 $val = undef if $val eq '' && $info->{is_nullable};
193 0         0 $row->$col($val);
194             } elsif ($c->param("${col}_pre_checkbox")) {
195 0 0       0 my $val = $info->{is_nullable}? undef: 'f';
196 0         0 $row->$col($val);
197             }
198             }
199 0         0 $row->update;
200 0         0 $c->add_flash(messages=>"updated: $row");
201              
202             } else { # insert
203 0         0 $row = $rs->new_result({});
204 0         0 for my $col (@$columns) {
205 0         0 my $info = $bycolumn->{$col};
206 0 0       0 if (defined (my $val = $c->param($col))) {
207 0 0 0     0 next if $val eq '' && $info->{is_nullable};
208 0         0 $row->$col($val);
209             }
210             }
211 0 0       0 if (eval { $row->insert }) {
  0         0  
212 0         0 $row->discard_changes;
213 0         0 $id = $row->id;
214 0         0 $c->add_flash(messages=>"inserted at key $id: $row");
215             } else {
216 0         0 my $err = $@;
217 0         0 $c->app->log->error("inserting a $table: $err");
218 0         0 $c->add_stash(errors => "Database rejected the new record: $err");
219             # if came from an add-child..
220 0 0 0     0 if (my $psource = $c->param('psource') and
      0        
221             my $child = $c->param('child') and
222             my $parent_id = $c->param('parent_id')) {
223 0         0 my $prs = $schema->resultset($psource);
224 0         0 my $prow = $prs->find($parent_id);
225 0         0 my $ptable = $prow->table;
226 0         0 my $pinfo = $bytable->{$ptable};
227 0         0 $c->shipped(id=>$parent_id, table=>$ptable, tinfo=>$pinfo);
228 0         0 $c->render(template=>'tables/view', row=>$prow, child=>$child);
229             # else a stand-alone add.
230             } else {
231 0         0 $c->render(template=>'tables/edit', dml_mode=>'add', row=>undef);
232             }
233 0         0 return;
234             }
235             }
236 0   0     0 my $redirect_to = $c->param('redirect_to') || "/tables/$table/$id/view";
237 0         0 $c->redirect_to($redirect_to);
238             }
239              
240             sub children {
241 1     1 0 1707 my $c = shift;
242 1         4 my $tinfo = $c->shipped('tinfo');
243 1         4 my $has_many = $tinfo->{has_many};
244 1         4 my $children = $c->stash('children');
245 1         12 my $row = $c->stash('row');
246 1   50     18 my $offset = $c->param('offset') || 0;
247 1   50     343 my $limit = $c->param('limit') || 10;
248 1 50       69 die "$children: unknown has-many collection" unless $has_many->{$children};
249 1         4 my $cpkey = $has_many->{$children}->{cpkey};
250 1         4 my $dir = 'asc';
251 1         2 my $attrs = {};
252 1 50       8 if ($offset > 0) {
    50          
    50          
253 0         0 $attrs->{offset} = $offset;
254             } elsif ($offset == -1) { # get last page
255 0         0 $dir = 'desc';
256             } elsif ($offset == -2) { # get all.. in fact, apply safety valve of 1000
257 0         0 $limit = 1000;
258             }
259 1         6 $attrs->{order_by} = {"-$dir"=>$cpkey};
260 1 50       5 $attrs->{rows} = $limit if $limit;
261             #$c->app->log->debug("invoke child group $children with " . $c->dumper($attrs));
262 1         7 my @rows = map { +{id=>$_->id, label=>"$_"} } $row->$children({}, $attrs);
  2         12151  
263 1 50       8 @rows = reverse @rows if $dir eq 'desc';
264 1 50 50     48 if (($c->stash('format')||'html') eq 'json') {
265 1         26 $c->render( json => \@rows );
266 1         855 return;
267             }
268 0           $c->render(data => $c->dumper(\@rows));
269             }
270              
271             sub navigate {
272 0     0 0   my $c = shift;
273 0           my $log = $c->app->log;
274 0   0       my $to = $c->param('to') || 'next';
275 0           my $table = $c->stash('table');
276 0           my $rs = $c->stash('rs');
277 0           my $row = $c->stash('row');
278 0           my $tinfo = $c->shipped('tinfo');
279 0           my $id = $c->shipped('id');
280 0           my $pkeys = $tinfo->{pkeys};
281 0 0         die "cannot navigate to $to" unless $to =~ /start|end|next|prev/;
282 0 0         die "not supported for multi-barrel pkeys" if @$pkeys > 1;
283 0           my $pkey = $pkeys->[0];
284 0           my $attrs = { rows=>1, select=>[$pkey] };
285 0           my $rhs;
286 0           for ($to) {
287 0 0         if (/next/ ) { $attrs->{order_by} = {-asc =>$pkey }; $rhs = {'>'=>$id}; last }
  0            
  0            
  0            
288 0 0         if (/prev/ ) { $attrs->{order_by} = {-desc=>$pkey }; $rhs = {'<'=>$id}; last }
  0            
  0            
  0            
289 0 0         if (/start/) { $attrs->{order_by} = {-asc =>$pkey }; last }
  0            
  0            
290 0 0         if (/end/ ) { $attrs->{order_by} = {-desc=>$pkey }; last }
  0            
  0            
291             }
292 0           my $newid = $id;
293 0           my $where = {};
294 0 0         $where->{$pkey} = $rhs if $rhs;
295 0 0         if (my $hit = $rs->search($where, $attrs)->first) {
296 0           $newid = $hit->id;
297             }
298 0           $c->redirect_to("/tables/$table/$newid/view");
299             }
300              
301             1;
302