Cancelled
Push — develop ( 8620b3...1fbc32 )
by Felipe
04:39
created

DisplayController   F

Complexity

Total Complexity 148

Size/Duplication

Total Lines 1090
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 148
dl 0
loc 1090
rs 0.6947
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
C render() 0 87 12
D printTableHeaderCells() 0 41 10
C printTableRowCells() 0 43 11
F doBrowse() 0 452 49
B doBrowseFK() 0 45 4
C getFKInfo() 0 38 7
A _getMinMaxPages() 0 20 4
C doDelRow() 0 70 11
D _printPages() 0 31 9
B doEditRow() 0 30 4
F formEditRow() 0 155 27

How to fix   Complexity   

Complex Class

Complex classes like DisplayController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DisplayController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.47
5
 */
6
7
namespace PHPPgAdmin\Controller;
8
9
/**
10
 * Base controller class.
11
 *
12
 * @package PHPPgAdmin
13
 */
14
class DisplayController extends BaseController
15
{
16
    /**
17
     * Default method to render the controller according to the action parameter.
18
     */
19
    public function render()
20
    {
21
        $conf           = $this->conf;
0 ignored issues
show
Unused Code introduced by
The assignment to $conf is dead and can be removed.
Loading history...
22
        $this->misc     = $this->misc;
23
        $plugin_manager = $this->plugin_manager;
24
25
        if ('dobrowsefk' == $this->action) {
26
            return $this->doBrowseFK();
27
        }
28
29
        set_time_limit(0);
30
31
        $scripts = '<script src="'.\SUBFOLDER.'/assets/js/display.js" type="text/javascript"></script>';
32
33
        $scripts .= '<script type="text/javascript">'."\n";
34
        $scripts .= "var Display = {\n";
35
        $scripts .= "errmsg: '".str_replace("'", "\\'", $this->lang['strconnectionfail'])."'\n";
36
        $scripts .= "};\n";
37
        $scripts .= '</script>'."\n";
38
39
        $footer_template = 'footer.twig';
40
        $header_template = 'header.twig';
41
42
        ob_start();
43
        switch ($this->action) {
44
            case 'editrow':
45
                $header_template = 'header_sqledit.twig';
46
                $footer_template = 'footer_sqledit.twig';
47
                if (isset($_POST['save'])) {
48
                    $this->doEditRow();
49
                } else {
50
                    $this->doBrowse();
51
                }
52
53
                break;
54
            case 'confeditrow':
55
                $this->formEditRow();
56
57
                break;
58
            case 'delrow':
59
                $header_template = 'header_sqledit.twig';
60
                $footer_template = 'footer_sqledit.twig';
61
                if (isset($_POST['yes'])) {
62
                    $this->doDelRow(false);
63
                } else {
64
                    $this->doBrowse();
65
                }
66
67
                break;
68
            case 'confdelrow':
69
                $this->doDelRow(true);
70
71
                break;
72
            default:
73
                $header_template = 'header_sqledit.twig';
74
                $footer_template = 'footer_sqledit.twig';
75
                $this->doBrowse();
76
77
                break;
78
        }
79
        $output = ob_get_clean();
80
81
        $subject = $this->coalesceArr($_REQUEST, 'subject', 'table')['subject'];
82
83
        $object = null;
84
        $object = $this->setIfIsset($object, $_REQUEST[$subject]);
85
86
        // Set the title based on the subject of the request
87
        if ('table' == $subject) {
88
            $title = $this->headerTitle('strtables', '', $object);
89
        } elseif ('view' == $subject) {
90
            $title = $this->headerTitle('strviews', '', $object);
91
        } elseif ('matview' == $subject) {
92
            $title = $this->headerTitle('strviews', 'M', $object);
93
        } elseif ('column' == $subject) {
94
            $title = $this->headerTitle('strcolumn', '', $object);
95
        } else {
96
            $title = $this->headerTitle('strqueryresults');
97
        }
98
99
        $this->printHeader($title, $scripts, true, $header_template);
100
101
        $this->printBody();
102
103
        echo $output;
104
105
        $this->printFooter(true, $footer_template);
106
    }
107
108
    /**
109
     * Displays requested data.
110
     *
111
     * @param mixed $msg
112
     */
113
    public function doBrowse($msg = '')
114
    {
115
        $conf           = $this->conf;
116
        $this->misc     = $this->misc;
117
        $plugin_manager = $this->plugin_manager;
118
        $data           = $this->misc->getDatabaseAccessor();
119
120
        // If current page is not set, default to first page
121
        $page = $this->coalesceArr($_REQUEST, 'page', 1)['page'];
122
123
        $save_history = !isset($_REQUEST['nohistory']);
124
125
        $subject = $this->coalesceArr($_REQUEST, 'subject', 'table')['subject'];
126
127
        $object = $this->coalesceArr($_REQUEST, $subject)[$subject];
128
129
        if ($subject === 'column' && $object && isset($_REQUEST['f_schema'], $_REQUEST['f_table'])) {
130
            $f_schema = $_REQUEST['f_schema'];
131
            $f_table  = $_REQUEST['f_table'];
132
133
            $_REQUEST['query'] = "SELECT \"{$object}\",
134
            count(*) AS \"count\"
135
            FROM \"{$f_schema}\".\"{$f_table}\"
136
            GROUP BY \"{$object}\" ORDER BY \"{$object}\"";
137
        }
138
139
        //$object = $this->setIfIsset($object, $_REQUEST[$subject]);
140
141
        //$this->prtrace($subject, $object);
142
143
        $this->printTrail($subject);
144
145
        $tabsPosition = 'browse';
146
        if ($subject === 'database') {
147
            $tabsPosition = 'sql';
148
        } elseif ($subject === 'column') {
149
            $tabsPosition = 'colproperties';
150
        }
151
152
        $this->printTabs($subject, $tabsPosition);
153
154
        $fkey = $this->coalesceArr($_REQUEST, 'fkey')['fkey'];
155
156
        $query = $this->coalesceArr($_REQUEST, 'query')['query'];
157
        // This code is used when browsing FK in pure-xHTML (without js)
158
        if ($fkey) {
159
            $ops = [];
160
            foreach ($fkey as $x => $y) {
161
                $ops[$x] = '=';
162
            }
163
            $query             = $data->getSelectSQL($_REQUEST['table'], [], $fkey, $ops);
164
            $_REQUEST['query'] = $query;
165
        }
166
167
        if ($object && $query) {
168
            $_SESSION['sqlquery'] = $query;
169
            $this->printTitle($this->lang['strselect']);
170
            $type = 'SELECT';
171
        } elseif ($object) {
172
            $type = 'TABLE';
173
        } else {
174
            $this->printTitle($this->lang['strqueryresults']);
175
            // we come from sql, $_SESSION['sqlquery'] has been set there
176
            $type = 'QUERY';
177
        }
178
179
        $this->printMsg($msg);
180
181
        // If 'sortkey' is not set, default to ''
182
        $sortkey = $this->coalesceArr($_REQUEST, 'sortkey', '')['sortkey'];
183
184
        // If 'sortdir' is not set, default to ''
185
        $sortdir = $this->coalesceArr($_REQUEST, 'sortdir', '')['sortdir'];
186
187
        // If 'strings' is not set, default to collapsed
188
        $strings = $this->coalesceArr($_REQUEST, 'strings', 'collapsed')['strings'];
189
190
        $schema      = $this->coalesceArr($_REQUEST, 'schema')['schema'];
191
        $search_path = $this->coalesceArr($_REQUEST, 'search_path')['search_path'];
192
193
        // Fetch unique row identifier, if this is a table browse request.
194
        if ($object) {
195
            $key = $data->getRowIdentifier($object);
196
        } else {
197
            $key = [];
198
        }
199
200
        // Set the schema search path
201
        if (isset($search_path) && (0 != $data->setSearchPath(array_map('trim', explode(',', $search_path))))) {
202
            return;
203
        }
204
205
        try {
206
            // Retrieve page from query.  $max_pages is returned by reference.
207
            $resultset = $data->browseQuery(
208
                $type,
209
                $object,
210
                $query,
211
                $sortkey,
212
                $sortdir,
213
                $page,
214
                $this->conf['max_rows'],
215
                $max_pages
216
            );
217
        } catch (\PHPPgAdmin\ADOdbException $e) {
218
            return;
219
        }
220
221
        $fkey_information = $this->getFKInfo();
222
223
        // Build strings for GETs in array
224
        $_gets = [
225
            'server'   => $_REQUEST['server'],
226
            'database' => $_REQUEST['database'],
227
        ];
228
229
        $this->coalesceArr($_REQUEST, 'query');
230
        $this->coalesceArr($_REQUEST, 'count');
231
        $this->coalesceArr($_REQUEST, 'return');
232
        $this->coalesceArr($_REQUEST, 'table');
233
        $this->coalesceArr($_REQUEST, 'nohistory');
234
235
        $this->setIfIsset($_gets['schema'], $_REQUEST['schema'], null, false);
236
        $this->setIfIsset($_gets[$subject], $object, null, false);
237
        $this->setIfIsset($_gets['subject'], $subject, null, false);
238
        $this->setIfIsset($_gets['query'], $_REQUEST['query'], null, false);
239
        $this->setIfIsset($_gets['count'], $_REQUEST['count'], null, false);
240
        $this->setIfIsset($_gets['return'], $_REQUEST['return'], null, false);
241
        $this->setIfIsset($_gets['search_path'], $_REQUEST['search_path'], null, false);
242
        $this->setIfIsset($_gets['table'], $_REQUEST['table'], null, false);
243
        $this->setIfIsset($_gets['nohistory'], $_REQUEST['nohistory'], null, false);
244
        $_gets['sortkey'] = $sortkey;
245
        $_gets['sortdir'] = $sortdir;
246
        $_gets['strings'] = $strings;
247
248
        if ($save_history && is_object($resultset) && ('QUERY' == $type)) {
249
            //{
250
            $this->misc->saveScriptHistory($_REQUEST['query']);
251
        }
252
253
        $query = $query ? $query : sprintf('SELECT * FROM %s.%s', $_REQUEST['schema'], $object);
254
255
        //$query = isset($_REQUEST['query'])? $_REQUEST['query'] : "select * from {$_REQUEST['schema']}.{$_REQUEST['table']};";
256
        //$this->prtrace($query);
257
258
        //die(htmlspecialchars($query));
259
260
        echo '<form method="post" id="sqlform" action="'.$_SERVER['REQUEST_URI'].'">';
261
        echo $this->misc->form;
262
        if ($object) {
263
            echo '<input type="hidden" name="'.$subject.'" value="', htmlspecialchars($object), '" />'."\n";
264
        }
265
        echo '<textarea width="90%" name="query"  id="query" rows="5" cols="100" resizable="true">';
266
        echo htmlspecialchars($query);
267
        echo '</textarea><br><input type="submit"/>';
268
269
        echo '</form>';
270
271
        if (is_object($resultset) && $resultset->recordCount() > 0) {
272
            // Show page navigation
273
            $paginator = $this->_printPages($page, $max_pages, $_gets);
274
275
            echo $paginator;
276
            echo "<table id=\"data\">\n<tr>";
277
278
            // Check that the key is actually in the result set.  This can occur for select
279
            // operations where the key fields aren't part of the select.  XXX:  We should
280
            // be able to support this, somehow.
281
            foreach ($key as $v) {
282
                // If a key column is not found in the record set, then we
283
                // can't use the key.
284
                if (!array_key_exists($v, $resultset->fields)) {
285
                    $key = [];
286
287
                    break;
288
                }
289
            }
290
291
            $buttons = [
292
                'edit'   => [
293
                    'content' => $this->lang['stredit'],
294
                    'attr'    => [
295
                        'href' => [
296
                            'url'     => 'display',
297
                            'urlvars' => array_merge(
298
                                [
299
                                    'action'  => 'confeditrow',
300
                                    'strings' => $strings,
301
                                    'page'    => $page,
302
                                ],
303
                                $_gets
304
                            ),
305
                        ],
306
                    ],
307
                ],
308
                'delete' => [
309
                    'content' => $this->lang['strdelete'],
310
                    'attr'    => [
311
                        'href' => [
312
                            'url'     => 'display',
313
                            'urlvars' => array_merge(
314
                                [
315
                                    'action'  => 'confdelrow',
316
                                    'strings' => $strings,
317
                                    'page'    => $page,
318
                                ],
319
                                $_gets
320
                            ),
321
                        ],
322
                    ],
323
                ],
324
            ];
325
            $actions = [
326
                'actionbuttons' => &$buttons,
327
                'place'         => 'display-browse',
328
            ];
329
            $plugin_manager->doHook('actionbuttons', $actions);
330
331
            foreach (array_keys($actions['actionbuttons']) as $this->action) {
332
                $actions['actionbuttons'][$this->action]['attr']['href']['urlvars'] = array_merge(
333
                    $actions['actionbuttons'][$this->action]['attr']['href']['urlvars'],
334
                    $_gets
335
                );
336
            }
337
338
            $edit_params = isset($actions['actionbuttons']['edit']) ?
339
            $actions['actionbuttons']['edit'] : [];
340
            $delete_params = isset($actions['actionbuttons']['delete']) ?
341
            $actions['actionbuttons']['delete'] : [];
342
343
            // Display edit and delete actions if we have a key
344
            $colspan = count($buttons);
345
            if ($colspan > 0 and count($key) > 0) {
346
                echo "<th colspan=\"{$colspan}\" class=\"data\">{$this->lang['stractions']}</th>"."\n";
347
            }
348
349
            // we show OIDs only if we are in TABLE or SELECT type browsing
350
            $this->printTableHeaderCells($resultset, $_gets, isset($object));
351
352
            echo '</tr>'."\n";
353
354
            $i = 0;
355
            reset($resultset->fields);
356
            while (!$resultset->EOF) {
357
                $id = (0 == ($i % 2) ? '1' : '2');
358
                echo "<tr class=\"data{$id}\">"."\n";
359
                // Display edit and delete links if we have a key
360
                if ($colspan > 0 and count($key) > 0) {
361
                    $keys_array = [];
362
                    $has_nulls  = false;
363
                    foreach ($key as $v) {
364
                        if (null === $resultset->fields[$v]) {
365
                            $has_nulls = true;
366
367
                            break;
368
                        }
369
                        $keys_array["key[{$v}]"] = $resultset->fields[$v];
370
                    }
371
                    if ($has_nulls) {
372
                        echo "<td colspan=\"{$colspan}\">&nbsp;</td>"."\n";
373
                    } else {
374
                        if (isset($actions['actionbuttons']['edit'])) {
375
                            $actions['actionbuttons']['edit']                            = $edit_params;
376
                            $actions['actionbuttons']['edit']['attr']['href']['urlvars'] = array_merge(
377
                                $actions['actionbuttons']['edit']['attr']['href']['urlvars'],
378
                                $keys_array
379
                            );
380
                        }
381
382
                        if (isset($actions['actionbuttons']['delete'])) {
383
                            $actions['actionbuttons']['delete']                            = $delete_params;
384
                            $actions['actionbuttons']['delete']['attr']['href']['urlvars'] = array_merge(
385
                                $actions['actionbuttons']['delete']['attr']['href']['urlvars'],
386
                                $keys_array
387
                            );
388
                        }
389
390
                        foreach ($actions['actionbuttons'] as $this->action) {
391
                            echo "<td class=\"opbutton{$id}\">";
392
                            $this->printLink($this->action, true, __METHOD__);
393
                            echo '</td>'."\n";
394
                        }
395
                    }
396
                }
397
398
                $this->printTableRowCells($resultset, $fkey_information, isset($object));
399
400
                echo '</tr>'."\n";
401
                $resultset->moveNext();
402
                ++$i;
403
            }
404
            echo '</table>'."\n";
405
406
            echo '<p>', $resultset->recordCount(), " {$this->lang['strrows']}</p>"."\n";
407
            // Show page navigation
408
            echo $paginator;
409
        } else {
410
            echo "<p>{$this->lang['strnodata']}</p>"."\n";
411
        }
412
413
        // Navigation links
414
        $navlinks = [];
415
416
        $fields = [
417
            'server'   => $_REQUEST['server'],
418
            'database' => $_REQUEST['database'],
419
        ];
420
421
        $this->setIfIsset($fields['schema'], $_REQUEST['schema'], null, false);
422
423
        // Return
424
        if (isset($_REQUEST['return'])) {
425
            $urlvars = $this->misc->getSubjectParams($_REQUEST['return']);
426
427
            $navlinks['back'] = [
428
                'attr'    => [
429
                    'href' => [
430
                        'url'     => $urlvars['url'],
431
                        'urlvars' => $urlvars['params'],
432
                    ],
433
                ],
434
                'content' => $this->lang['strback'],
435
            ];
436
        }
437
438
        // Edit SQL link
439
        if ('QUERY' == $type) {
440
            $navlinks['edit'] = [
441
                'attr'    => [
442
                    'href' => [
443
                        'url'     => 'database',
444
                        'urlvars' => array_merge(
445
                            $fields,
446
                            [
447
                                'action'   => 'sql',
448
                                'paginate' => 'on',
449
                            ]
450
                        ),
451
                    ],
452
                ],
453
                'content' => $this->lang['streditsql'],
454
            ];
455
        }
456
457
        // Expand/Collapse
458
        if ('expanded' == $strings) {
459
            $navlinks['collapse'] = [
460
                'attr'    => [
461
                    'href' => [
462
                        'url'     => 'display',
463
                        'urlvars' => array_merge(
464
                            $_gets,
465
                            [
466
                                'strings' => 'collapsed',
467
                                'page'    => $page,
468
                            ]
469
                        ),
470
                    ],
471
                ],
472
                'content' => $this->lang['strcollapse'],
473
            ];
474
        } else {
475
            $navlinks['collapse'] = [
476
                'attr'    => [
477
                    'href' => [
478
                        'url'     => 'display',
479
                        'urlvars' => array_merge(
480
                            $_gets,
481
                            [
482
                                'strings' => 'expanded',
483
                                'page'    => $page,
484
                            ]
485
                        ),
486
                    ],
487
                ],
488
                'content' => $this->lang['strexpand'],
489
            ];
490
        }
491
492
        // Create view and download
493
        if (isset($_REQUEST['query'], $resultset) && is_object($resultset) && $resultset->recordCount() > 0) {
494
            // Report views don't set a schema, so we need to disable create view in that case
495
            if (isset($_REQUEST['schema'])) {
496
                $navlinks['createview'] = [
497
                    'attr'    => [
498
                        'href' => [
499
                            'url'     => 'views',
500
                            'urlvars' => array_merge(
501
                                $fields,
502
                                [
503
                                    'action'         => 'create',
504
                                    'formDefinition' => $_REQUEST['query'],
505
                                ]
506
                            ),
507
                        ],
508
                    ],
509
                    'content' => $this->lang['strcreateview'],
510
                ];
511
            }
512
513
            $urlvars = [];
514
515
            $this->setIfIsset($urlvars['search_path'], $_REQUEST['search_path'], null, false);
516
517
            $navlinks['download'] = [
518
                'attr'    => [
519
                    'href' => [
520
                        'url'     => 'dataexport',
521
                        'urlvars' => array_merge($fields, $urlvars),
522
                    ],
523
                ],
524
                'content' => $this->lang['strdownload'],
525
            ];
526
        }
527
528
        // Insert
529
        if (isset($object) && (isset($subject) && 'table' == $subject)) {
530
            $navlinks['insert'] = [
531
                'attr'    => [
532
                    'href' => [
533
                        'url'     => 'tables',
534
                        'urlvars' => array_merge(
535
                            $fields,
536
                            [
537
                                'action' => 'confinsertrow',
538
                                'table'  => $object,
539
                            ]
540
                        ),
541
                    ],
542
                ],
543
                'content' => $this->lang['strinsert'],
544
            ];
545
        }
546
547
        // Refresh
548
        $navlinks['refresh'] = [
549
            'attr'    => [
550
                'href' => [
551
                    'url'     => 'display',
552
                    'urlvars' => array_merge(
553
                        $_gets,
554
                        [
555
                            'strings' => $strings,
556
                            'page'    => $page,
557
                        ]
558
                    ),
559
                ],
560
            ],
561
            'content' => $this->lang['strrefresh'],
562
        ];
563
564
        $this->printNavLinks($navlinks, 'display-browse', get_defined_vars());
565
    }
566
567
    /**
568
     * Print table header cells.
569
     *
570
     * @param \PHPPgAdmin\ADORecordSet $resultset set of results from getRow operation
571
     * @param array|bool               $args      - associative array for sort link parameters, or false if there isn't any
572
     * @param bool                     $withOid   either to display OIDs or not
573
     */
574
    public function printTableHeaderCells(&$resultset, $args, $withOid)
575
    {
576
        $data = $this->misc->getDatabaseAccessor();
577
        $j    = 0;
578
579
        foreach ($resultset->fields as $k => $v) {
580
            if (($k === $data->id) && (!($withOid && $this->conf['show_oids']))) {
581
                ++$j;
582
583
                continue;
584
            }
585
            $finfo = $resultset->fetchField($j);
586
587
            if (false === $args) {
588
                echo '<th class="data">', $this->misc->printVal($finfo->name), '</th>'."\n";
589
            } else {
590
                $args['page']    = $_REQUEST['page'];
591
                $args['sortkey'] = $j + 1;
592
                // Sort direction opposite to current direction, unless it's currently ''
593
                $args['sortdir'] = (
594
                    'asc' == $_REQUEST['sortdir']
595
                    and $_REQUEST['sortkey'] == ($j + 1)
596
                ) ? 'desc' : 'asc';
597
598
                $sortLink = http_build_query($args);
599
600
                echo "<th class=\"data\"><a href=\"?{$sortLink}\">"
601
                , $this->misc->printVal($finfo->name);
602
                if ($_REQUEST['sortkey'] == ($j + 1)) {
603
                    if ('asc' == $_REQUEST['sortdir']) {
604
                        echo '<img src="'.$this->misc->icon('RaiseArgument').'" alt="asc">';
605
                    } else {
606
                        echo '<img src="'.$this->misc->icon('LowerArgument').'" alt="desc">';
607
                    }
608
                }
609
                echo '</a></th>'."\n";
610
            }
611
            ++$j;
612
        }
613
614
        reset($resultset->fields);
615
    }
616
617
    /**
618
     * Print table rows.
619
     *
620
     * @param \PHPPgAdmin\ADORecordSet $resultset        The resultset
621
     * @param array                    $fkey_information The fkey information
622
     * @param bool                     $withOid          either to display OIDs or not
623
     */
624
    public function printTableRowCells(&$resultset, &$fkey_information, $withOid)
625
    {
626
        $data = $this->misc->getDatabaseAccessor();
627
        $j    = 0;
628
629
        $this->coalesceArr($_REQUEST, 'strings', 'collapsed');
630
631
        foreach ($resultset->fields as $k => $v) {
632
            $finfo = $resultset->fetchField($j++);
633
634
            if (($k === $data->id) && (!($withOid && $this->conf['show_oids']))) {
635
                continue;
636
            }
637
            $printvalOpts = ['null' => true, 'clip' => ('collapsed' == $_REQUEST['strings'])];
638
            if (null !== $v && '' == $v) {
639
                echo '<td>&nbsp;</td>';
640
            } else {
641
                echo '<td style="white-space:nowrap;">';
642
643
                if ((null !== $v) && isset($fkey_information['byfield'][$k])) {
644
                    foreach ($fkey_information['byfield'][$k] as $conid) {
645
                        $query_params = $fkey_information['byconstr'][$conid]['url_data'];
646
647
                        foreach ($fkey_information['byconstr'][$conid]['fkeys'] as $p_field => $f_field) {
648
                            $query_params .= '&amp;'.urlencode("fkey[{$f_field}]").'='.urlencode($resultset->fields[$p_field]);
649
                        }
650
651
                        // $fkey_information['common_url'] is already urlencoded
652
                        $query_params .= '&amp;'.$fkey_information['common_url'];
653
                        echo '<div style="display:inline-block;">';
654
                        echo '<a class="fk fk_'.htmlentities($conid, ENT_QUOTES, 'UTF-8')."\" href=\"display?{$query_params}\">";
655
                        echo '<img src="'.$this->misc->icon('ForeignKey').'" style="vertical-align:middle;" alt="[fk]" title="'
656
                        .htmlentities($fkey_information['byconstr'][$conid]['consrc'], ENT_QUOTES, 'UTF-8')
657
                            .'" />';
658
                        echo '</a>';
659
                        echo '</div>';
660
                    }
661
                    $printvalOpts['class'] = 'fk_value';
662
                }
663
                $val = $this->misc->printVal($v, $finfo->type, $printvalOpts);
664
665
                echo $val;
666
                echo '</td>';
667
            }
668
        }
669
    }
670
671
    /**
672
     * Show form to edit row.
673
     *
674
     * @param string $msg message to display on top of the form or after performing edition
675
     */
676
    public function formEditRow($msg = '')
677
    {
678
        $data = $this->misc->getDatabaseAccessor();
679
680
        if (is_array($_REQUEST['key'])) {
681
            $key = $_REQUEST['key'];
682
        } else {
683
            $key = unserialize(urldecode($_REQUEST['key']));
684
        }
685
686
        $this->printTrail($_REQUEST['subject']);
687
        $this->printTitle($this->lang['streditrow']);
688
        $this->printMsg($msg);
689
690
        $attrs     = $data->getTableAttributes($_REQUEST['table']);
691
        $resultset = $data->browseRow($_REQUEST['table'], $key);
692
693
        if (('disable' != $this->conf['autocomplete'])) {
694
            $fksprops = $this->misc->getAutocompleteFKProperties($_REQUEST['table']);
695
            if (false !== $fksprops) {
696
                echo $fksprops['code'];
697
            }
698
        } else {
699
            $fksprops = false;
700
        }
701
702
        echo '<form action="'.\SUBFOLDER.'/src/views/display" method="post" id="ac_form">'."\n";
703
704
        $elements = 0;
705
        $error    = true;
706
        if (1 == $resultset->recordCount() && $attrs->recordCount() > 0) {
707
            echo '<table>'."\n";
708
709
            // Output table header
710
            echo "<tr><th class=\"data\">{$this->lang['strcolumn']}</th><th class=\"data\">{$this->lang['strtype']}</th>";
711
            echo "<th class=\"data\">{$this->lang['strformat']}</th>"."\n";
712
            echo "<th class=\"data\">{$this->lang['strnull']}</th><th class=\"data\">{$this->lang['strvalue']}</th></tr>";
713
714
            $i = 0;
715
            while (!$attrs->EOF) {
716
                $attrs->fields['attnotnull'] = $data->phpBool($attrs->fields['attnotnull']);
717
                $id                          = (0 == ($i % 2) ? '1' : '2');
718
719
                // Initialise variables
720
                if (!isset($_REQUEST['format'][$attrs->fields['attname']])) {
721
                    $_REQUEST['format'][$attrs->fields['attname']] = 'VALUE';
722
                }
723
724
                echo "<tr class=\"data{$id}\">"."\n";
725
                echo '<td style="white-space:nowrap;">', $this->misc->printVal($attrs->fields['attname']), '</td>';
726
                echo '<td style="white-space:nowrap;">'."\n";
727
                echo $this->misc->printVal($data->formatType($attrs->fields['type'], $attrs->fields['atttypmod']));
728
                echo '<input type="hidden" name="types[', htmlspecialchars($attrs->fields['attname']), ']" value="',
729
                htmlspecialchars($attrs->fields['type']), '" /></td>';
730
                ++$elements;
731
                echo '<td style="white-space:nowrap;">'."\n";
732
                echo '<select name="format['.htmlspecialchars($attrs->fields['attname']), ']">'."\n";
733
                echo '<option value="VALUE"', ($_REQUEST['format'][$attrs->fields['attname']] == 'VALUE') ? ' selected="selected"' : '', ">{$this->lang['strvalue']}</option>"."\n";
734
                $selected = ($_REQUEST['format'][$attrs->fields['attname']] == 'EXPRESSION') ? ' selected="selected"' : '';
735
                echo '<option value="EXPRESSION"'.$selected.">{$this->lang['strexpression']}</option>"."\n";
736
                echo "</select>\n</td>"."\n";
737
                ++$elements;
738
                echo '<td style="white-space:nowrap;">';
739
                // Output null box if the column allows nulls (doesn't look at CHECKs or ASSERTIONS)
740
                if (!$attrs->fields['attnotnull']) {
741
                    // Set initial null values
742
                    if ('confeditrow' == $_REQUEST['action'] && null === $resultset->fields[$attrs->fields['attname']]) {
743
                        $_REQUEST['nulls'][$attrs->fields['attname']] = 'on';
744
                    }
745
                    echo "<label><span><input type=\"checkbox\" class=\"nullcheckbox\" name=\"nulls[{$attrs->fields['attname']}]\"",
746
                    isset($_REQUEST['nulls'][$attrs->fields['attname']]) ? ' checked="checked"' : '', ' /></span></label></td>'."\n";
747
                    ++$elements;
748
                } else {
749
                    echo '&nbsp;</td>';
750
                }
751
752
                echo "<td id=\"row_att_{$attrs->fields['attnum']}\" style=\"white-space:nowrap;\">";
753
754
                $extras = [];
755
756
                // If the column allows nulls, then we put a JavaScript action on the data field to unset the
757
                // NULL checkbox as soon as anything is entered in the field.  We use the $elements variable to
758
                // keep track of which element offset we're up to.  We can't refer to the null checkbox by name
759
                // as it contains '[' and ']' characters.
760
                if (!$attrs->fields['attnotnull']) {
761
                    $extras['class'] = 'insert_row_input';
762
                }
763
764
                if ((false !== $fksprops) && isset($fksprops['byfield'][$attrs->fields['attnum']])) {
765
                    $extras['id']           = "attr_{$attrs->fields['attnum']}";
766
                    $extras['autocomplete'] = 'off';
767
                }
768
769
                echo $data->printField("values[{$attrs->fields['attname']}]", $resultset->fields[$attrs->fields['attname']], $attrs->fields['type'], $extras);
770
771
                echo '</td>';
772
                ++$elements;
773
                echo '</tr>'."\n";
774
                ++$i;
775
                $attrs->moveNext();
776
            }
777
            echo '</table>'."\n";
778
779
            $error = false;
780
        } elseif (1 != $resultset->recordCount()) {
781
            echo "<p>{$this->lang['strrownotunique']}</p>"."\n";
782
        } else {
783
            echo "<p>{$this->lang['strinvalidparam']}</p>"."\n";
784
        }
785
786
        echo '<input type="hidden" name="action" value="editrow" />'."\n";
787
        echo $this->misc->form;
788
        if (isset($_REQUEST['table'])) {
789
            echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), '" />'."\n";
790
        }
791
792
        if (isset($_REQUEST['subject'])) {
793
            echo '<input type="hidden" name="subject" value="', htmlspecialchars($_REQUEST['subject']), '" />'."\n";
794
        }
795
796
        if (isset($_REQUEST['query'])) {
797
            echo '<input type="hidden" name="query" value="', htmlspecialchars($_REQUEST['query']), '" />'."\n";
798
        }
799
800
        if (isset($_REQUEST['count'])) {
801
            echo '<input type="hidden" name="count" value="', htmlspecialchars($_REQUEST['count']), '" />'."\n";
802
        }
803
804
        if (isset($_REQUEST['return'])) {
805
            echo '<input type="hidden" name="return" value="', htmlspecialchars($_REQUEST['return']), '" />'."\n";
806
        }
807
808
        echo '<input type="hidden" name="page" value="', htmlspecialchars($_REQUEST['page']), '" />'."\n";
809
        echo '<input type="hidden" name="sortkey" value="', htmlspecialchars($_REQUEST['sortkey']), '" />'."\n";
810
        echo '<input type="hidden" name="sortdir" value="', htmlspecialchars($_REQUEST['sortdir']), '" />'."\n";
811
        echo '<input type="hidden" name="strings" value="', htmlspecialchars($_REQUEST['strings']), '" />'."\n";
812
        echo '<input type="hidden" name="key" value="', htmlspecialchars(urlencode(serialize($key))), '" />'."\n";
813
        echo '<p>';
814
        if (!$error) {
815
            echo "<input type=\"submit\" name=\"save\" accesskey=\"r\" value=\"{$this->lang['strsave']}\" />"."\n";
816
        }
817
818
        echo "<input type=\"submit\" name=\"cancel\" value=\"{$this->lang['strcancel']}\" />"."\n";
819
820
        if (false !== $fksprops) {
821
            if ('default off' != $this->conf['autocomplete']) {
822
                echo "<input type=\"checkbox\" id=\"no_ac\" value=\"1\" checked=\"checked\" /><label for=\"no_ac\">{$this->lang['strac']}</label>"."\n";
823
            } else {
824
                echo "<input type=\"checkbox\" id=\"no_ac\" value=\"0\" /><label for=\"no_ac\">{$this->lang['strac']}</label>"."\n";
825
            }
826
        }
827
828
        echo '</p>'."\n";
829
        echo '</form>'."\n";
830
        echo '<script src="'.\SUBFOLDER.'/assets/js/insert_or_edit_row.js" type="text/javascript"></script>';
831
    }
832
833
    /**
834
     * Performs actual edition of row.
835
     */
836
    public function doEditRow()
837
    {
838
        $data = $this->misc->getDatabaseAccessor();
839
840
        if (is_array($_REQUEST['key'])) {
841
            $key = $_REQUEST['key'];
842
        } else {
843
            $key = unserialize(urldecode($_REQUEST['key']));
844
        }
845
846
        $this->coalesceArr($_POST, 'values', []);
847
848
        $this->coalesceArr($_POST, 'nulls', []);
849
850
        $status = $data->editRow(
851
            $_POST['table'],
852
            $_POST['values'],
853
            $_POST['nulls'],
854
            $_POST['format'],
855
            $_POST['types'],
856
            $key
857
        );
858
        if (0 == $status) {
859
            return $this->doBrowse($this->lang['strrowupdated']);
860
        }
861
        if ($status == -2) {
862
            return $this->formEditRow($this->lang['strrownotunique']);
863
        }
864
865
        return $this->formEditRow($this->lang['strrowupdatedbad']);
866
    }
867
868
    /**
869
     * Show confirmation of drop and perform actual drop.
870
     *
871
     * @param mixed $confirm
872
     */
873
    public function doDelRow($confirm)
874
    {
875
        $data = $this->misc->getDatabaseAccessor();
876
877
        if ($confirm) {
878
            $this->printTrail($_REQUEST['subject']);
879
            $this->printTitle($this->lang['strdeleterow']);
880
881
            $resultset = $data->browseRow($_REQUEST['table'], $_REQUEST['key']);
882
883
            echo '<form action="'.\SUBFOLDER.'/src/views/display" method="post">'."\n";
884
            echo $this->misc->form;
885
886
            if (1 == $resultset->recordCount()) {
887
                echo "<p>{$this->lang['strconfdeleterow']}</p>"."\n";
888
889
                $fkinfo = [];
890
                echo '<table><tr>';
891
                $this->printTableHeaderCells($resultset, false, true);
892
                echo '</tr>';
893
                echo '<tr class="data1">'."\n";
894
                $this->printTableRowCells($resultset, $fkinfo, true);
895
                echo '</tr>'."\n";
896
                echo '</table>'."\n";
897
                echo '<br />'."\n";
898
899
                echo '<input type="hidden" name="action" value="delrow" />'."\n";
900
                echo "<input type=\"submit\" name=\"yes\" value=\"{$this->lang['stryes']}\" />"."\n";
901
                echo "<input type=\"submit\" name=\"no\" value=\"{$this->lang['strno']}\" />"."\n";
902
            } elseif (1 != $resultset->recordCount()) {
903
                echo "<p>{$this->lang['strrownotunique']}</p>"."\n";
904
                echo "<input type=\"submit\" name=\"cancel\" value=\"{$this->lang['strcancel']}\" />"."\n";
905
            } else {
906
                echo "<p>{$this->lang['strinvalidparam']}</p>"."\n";
907
                echo "<input type=\"submit\" name=\"cancel\" value=\"{$this->lang['strcancel']}\" />"."\n";
908
            }
909
            if (isset($_REQUEST['table'])) {
910
                echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), '" />'."\n";
911
            }
912
913
            if (isset($_REQUEST['subject'])) {
914
                echo '<input type="hidden" name="subject" value="', htmlspecialchars($_REQUEST['subject']), '" />'."\n";
915
            }
916
917
            if (isset($_REQUEST['query'])) {
918
                echo '<input type="hidden" name="query" value="', htmlspecialchars($_REQUEST['query']), '" />'."\n";
919
            }
920
921
            if (isset($_REQUEST['count'])) {
922
                echo '<input type="hidden" name="count" value="', htmlspecialchars($_REQUEST['count']), '" />'."\n";
923
            }
924
925
            if (isset($_REQUEST['return'])) {
926
                echo '<input type="hidden" name="return" value="', htmlspecialchars($_REQUEST['return']), '" />'."\n";
927
            }
928
929
            echo '<input type="hidden" name="page" value="', htmlspecialchars($_REQUEST['page']), '" />'."\n";
930
            echo '<input type="hidden" name="sortkey" value="', htmlspecialchars($_REQUEST['sortkey']), '" />'."\n";
931
            echo '<input type="hidden" name="sortdir" value="', htmlspecialchars($_REQUEST['sortdir']), '" />'."\n";
932
            echo '<input type="hidden" name="strings" value="', htmlspecialchars($_REQUEST['strings']), '" />'."\n";
933
            echo '<input type="hidden" name="key" value="', htmlspecialchars(urlencode(serialize($_REQUEST['key']))), '" />'."\n";
934
            echo '</form>'."\n";
935
        } else {
936
            $status = $data->deleteRow($_POST['table'], unserialize(urldecode($_POST['key'])));
937
            if (0 == $status) {
938
                $this->doBrowse($this->lang['strrowdeleted']);
939
            } elseif ($status == -2) {
940
                $this->doBrowse($this->lang['strrownotunique']);
941
            } else {
942
                $this->doBrowse($this->lang['strrowdeletedbad']);
943
            }
944
        }
945
    }
946
947
    /**
948
     * Build & return the FK information data structure
949
     * used when deciding if a field should have a FK link or not.
950
     *
951
     * @return array associative array describing the FK
952
     */
953
    public function &getFKInfo()
954
    {
955
        $data = $this->misc->getDatabaseAccessor();
956
957
        // Get the foreign key(s) information from the current table
958
        $fkey_information = ['byconstr' => [], 'byfield' => []];
959
960
        if (isset($_REQUEST['table'])) {
961
            $constraints = $data->getConstraintsWithFields($_REQUEST['table']);
962
            if ($constraints->recordCount() > 0) {
963
                $fkey_information['common_url'] = $this->misc->getHREF('schema').'&amp;subject=table';
964
965
                // build the FK constraints data structure
966
                while (!$constraints->EOF) {
967
                    $constr = &$constraints->fields;
968
                    if ('f' == $constr['contype']) {
969
                        if (!isset($fkey_information['byconstr'][$constr['conid']])) {
970
                            $fkey_information['byconstr'][$constr['conid']] = [
971
                                'url_data' => 'table='.urlencode($constr['f_table']).'&amp;schema='.urlencode($constr['f_schema']),
972
                                'fkeys'    => [],
973
                                'consrc'   => $constr['consrc'],
974
                            ];
975
                        }
976
977
                        $fkey_information['byconstr'][$constr['conid']]['fkeys'][$constr['p_field']] = $constr['f_field'];
978
979
                        if (!isset($fkey_information['byfield'][$constr['p_field']])) {
980
                            $fkey_information['byfield'][$constr['p_field']] = [];
981
                        }
982
983
                        $fkey_information['byfield'][$constr['p_field']][] = $constr['conid'];
984
                    }
985
                    $constraints->moveNext();
986
                }
987
            }
988
        }
989
990
        return $fkey_information;
991
    }
992
993
    // Print the FK row, used in ajax requests
994
    public function doBrowseFK()
995
    {
996
        $data = $this->misc->getDatabaseAccessor();
997
998
        $ops = [];
999
        foreach ($_REQUEST['fkey'] as $x => $y) {
1000
            $ops[$x] = '=';
1001
        }
1002
        $query             = $data->getSelectSQL($_REQUEST['table'], [], $_REQUEST['fkey'], $ops);
1003
        $_REQUEST['query'] = $query;
1004
1005
        $fkinfo = $this->getFKInfo();
1006
1007
        $max_pages = 1;
1008
        // Retrieve page from query.  $max_pages is returned by reference.
1009
        $resultset = $data->browseQuery(
1010
            'SELECT',
1011
            $_REQUEST['table'],
1012
            $_REQUEST['query'],
1013
            null,
1014
            null,
1015
            1,
1016
            1,
1017
            $max_pages
1018
        );
1019
1020
        echo '<a href="javascript:void(0);" style="display:table-cell;" class="fk_delete"><img alt="[delete]" src="'.$this->misc->icon('Delete').'" /></a>'."\n";
1021
        echo '<div style="display:table-cell;">';
1022
1023
        if (is_object($resultset) && $resultset->recordCount() > 0) {
1024
            /* we are browsing a referenced table here
1025
             * we should show OID if show_oids is true
1026
             * so we give true to withOid in functions bellow
1027
             */
1028
            echo '<table><tr>';
1029
            $this->printTableHeaderCells($resultset, false, true);
1030
            echo '</tr>';
1031
            echo '<tr class="data1">'."\n";
1032
            $this->printTableRowCells($resultset, $fkinfo, true);
1033
            echo '</tr>'."\n";
1034
            echo '</table>'."\n";
1035
        } else {
1036
            echo $this->lang['strnodata'];
1037
        }
1038
        echo '</div>';
1039
    }
1040
1041
    private function _getMinMaxPages($page, $pages)
1042
    {
1043
        $window = 10;
1044
        if ($page <= $window) {
1045
            $min_page = 1;
1046
            $max_page = min(2 * $window, $pages);
1047
        } elseif ($page > $window && $pages >= $page + $window) {
1048
            $min_page = ($page - $window) + 1;
1049
            $max_page = $page + $window;
1050
        } else {
1051
            $min_page = ($page - (2 * $window - ($pages - $page))) + 1;
1052
            $max_page = $pages;
1053
        }
1054
1055
        // Make sure min_page is always at least 1
1056
        // and max_page is never greater than $pages
1057
        $min_page = max($min_page, 1);
1058
        $max_page = min($max_page, $pages);
1059
1060
        return [$min_page, $max_page];
1061
    }
1062
1063
    /**
1064
     * Do multi-page navigation.  Displays the prev, next and page options.
1065
     *
1066
     * @param int   $page      - the page currently viewed
1067
     * @param int   $pages     - the maximum number of pages
1068
     * @param array $gets      -  the parameters to include in the link to the wanted page
1069
     * @param int   $max_width - the number of pages to make available at any one time (default = 20)
1070
     *
1071
     * @return string the pagination links
1072
     */
1073
    private function _printPages($page, $pages, $gets, $max_width = 20)
1074
    {
1075
        $lang = $this->lang;
1076
        $page = (int) $page;
1077
1078
        if ($page < 0 || $page > $pages || $pages <= 1 || $max_width <= 0) {
1079
            return;
1080
        }
1081
1082
        unset($gets['page']);
1083
        $url = http_build_query($gets);
1084
1085
        $result = '<p style="text-align: center">'."\n";
1086
        if ($page != 1) {
1087
            $result .= sprintf('<a class="pagenav" href="?%s&page=1">%s</a>%s&nbsp;', $url, $lang['strfirst'], "\n");
1088
            $result .= sprintf('<a class="pagenav" href="?%s&page=%s">%s</a>%s', $url, $page - 1, $lang['strprev'], "\n");
1089
        }
1090
1091
        list($min_page, $max_page) = $this->_getMinMaxPages($page, $pages);
1092
1093
        for ($i = $min_page; $i <= $max_page; ++$i) {
1094
            $result .= (($i === $page) ? $i : sprintf('<a class="pagenav" href="display?%s&page=%s">%s</a>', $url, $i, $i))."\n";
1095
        }
1096
1097
        if ($page != $pages) {
1098
            $result .= sprintf('<a class="pagenav" href="?%s&page=%s">%s</a>%s', $url, $page + 1, $lang['strnext'], "\n");
1099
            $result .= sprintf('&nbsp;<a class="pagenav" href="?%s&page=%s">%s</a>%s', $url, $pages, $lang['strlast'], "\n");
1100
        }
1101
        $result .= "</p>\n";
1102
1103
        return $result;
1104
    }
1105
}
1106