Passed
Push — develop ( 94ce57...e08851 )
by Felipe
03:48
created

DisplayController::printTableHeaderCells()   C

Complexity

Conditions 12
Paths 16

Size

Total Lines 36
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 36
rs 6.9666
c 0
b 0
f 0
cc 12
nc 16
nop 3

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.50
5
 */
6
7
namespace PHPPgAdmin\Controller;
8
9
/**
10
 * Base controller class.
11
 *
12
 * @package PHPPgAdmin
13
 */
14
class DisplayController extends BaseController
15
{
16
    use \PHPPgAdmin\Traits\InsertEditRowTrait;
17
18
    /**
19
     * Default method to render the controller according to the action parameter.
20
     */
21
    public function render()
22
    {
23
        $this->misc     = $this->misc;
24
        $plugin_manager = $this->plugin_manager;
25
26
        if ('dobrowsefk' == $this->action) {
27
            return $this->doBrowseFK();
28
        }
29
30
        set_time_limit(0);
31
32
        $scripts = '<script src="'.\SUBFOLDER.'/assets/js/display.js" type="text/javascript"></script>';
33
34
        $scripts .= '<script type="text/javascript">'.PHP_EOL;
35
        $scripts .= "var Display = {\n";
36
        $scripts .= "errmsg: '".str_replace("'", "\\'", $this->lang['strconnectionfail'])."'\n";
37
        $scripts .= "};\n";
38
        $scripts .= '</script>'.PHP_EOL;
39
40
        $footer_template = 'footer.twig';
41
        $header_template = 'header.twig';
42
43
        ob_start();
44
        switch ($this->action) {
45
            case 'editrow':
46
                $header_template = 'header_sqledit.twig';
47
                $footer_template = 'footer_sqledit.twig';
48
                if (isset($_POST['save'])) {
49
                    $this->doEditRow();
50
                } else {
51
                    $this->doBrowse();
52
                }
53
54
                break;
55
            case 'confeditrow':
56
                $this->formEditRow();
57
58
                break;
59
            case 'delrow':
60
                $header_template = 'header_sqledit.twig';
61
                $footer_template = 'footer_sqledit.twig';
62
                if (isset($_POST['yes'])) {
63
                    $this->doDelRow(false);
64
                } else {
65
                    $this->doBrowse();
66
                }
67
68
                break;
69
            case 'confdelrow':
70
                $this->doDelRow(true);
71
72
                break;
73
            default:
74
                $header_template = 'header_sqledit.twig';
75
                $footer_template = 'footer_sqledit.twig';
76
                $this->doBrowse();
77
78
                break;
79
        }
80
        $output = ob_get_clean();
81
82
        $subject = $this->coalesceArr($_REQUEST, 'subject', 'table')['subject'];
83
84
        $object = null;
85
        $object = $this->setIfIsset($object, $_REQUEST[$subject]);
86
87
        // Set the title based on the subject of the request
88
        if ('table' == $subject) {
89
            $title = $this->headerTitle('strtables', '', $object);
90
        } elseif ('view' == $subject) {
91
            $title = $this->headerTitle('strviews', '', $object);
92
        } elseif ('matview' == $subject) {
93
            $title = $this->headerTitle('strviews', 'M', $object);
94
        } elseif ('column' == $subject) {
95
            $title = $this->headerTitle('strcolumn', '', $object);
96
        } else {
97
            $title = $this->headerTitle('strqueryresults');
98
        }
99
100
        $this->printHeader($title, $scripts, true, $header_template);
101
102
        $this->printBody();
103
104
        echo $output;
105
106
        $this->printFooter(true, $footer_template);
107
    }
108
109
    /**
110
     * Displays requested data.
111
     *
112
     * @param mixed $msg
113
     */
114
    public function doBrowse($msg = '')
115
    {
116
        $this->misc = $this->misc;
117
        $data       = $this->misc->getDatabaseAccessor();
118
119
        // If current page is not set, default to first page
120
        $page = $this->coalesceArr($_REQUEST, 'page', 1)['page'];
121
122
        $save_history = !isset($_REQUEST['nohistory']);
123
124
        $subject = $this->coalesceArr($_REQUEST, 'subject', 'table')['subject'];
125
126
        $object = $this->coalesceArr($_REQUEST, $subject)[$subject];
127
128
        if ($subject === 'column' && $object && isset($_REQUEST['f_schema'], $_REQUEST['f_table'])) {
129
            $f_schema = $_REQUEST['f_schema'];
130
            $f_table  = $_REQUEST['f_table'];
131
132
            $_REQUEST['query'] = "SELECT \"{$object}\",
133
            count(*) AS \"count\"
134
            FROM \"{$f_schema}\".\"{$f_table}\"
135
            GROUP BY \"{$object}\" ORDER BY \"{$object}\"";
136
        }
137
138
        //$object = $this->setIfIsset($object, $_REQUEST[$subject]);
139
140
        $this->printTrail($subject);
141
142
        $tabsPosition = 'browse';
143
        if ($subject === 'database') {
144
            $tabsPosition = 'sql';
145
        } elseif ($subject === 'column') {
146
            $tabsPosition = 'colproperties';
147
        }
148
149
        $this->printTabs($subject, $tabsPosition);
150
151
        list($query, $title, $type) = $this->getQueryTitleAndType($data, $object);
152
        $this->printTitle($title);
153
154
        //$this->prtrace($subject, $object, $query, $_SESSION['sqlquery']);
155
156
        $this->printMsg($msg);
157
158
        // If 'sortkey' is not set, default to ''
159
        $sortkey = $this->coalesceArr($_REQUEST, 'sortkey', '')['sortkey'];
160
161
        // If 'sortdir' is not set, default to ''
162
        $sortdir = $this->coalesceArr($_REQUEST, 'sortdir', '')['sortdir'];
163
164
        // If 'strings' is not set, default to collapsed
165
        $strings = $this->coalesceArr($_REQUEST, 'strings', 'collapsed')['strings'];
166
167
        $this->coalesceArr($_REQUEST, 'schema')['schema'];
168
        $search_path = $this->coalesceArr($_REQUEST, 'search_path')['search_path'];
169
170
        // Set the schema search path
171
        if (isset($search_path) && (0 != $data->setSearchPath(array_map('trim', explode(',', $search_path))))) {
172
            return;
173
        }
174
175
        try {
176
            // Retrieve page from query.  $max_pages is returned by reference.
177
            $resultset = $data->browseQuery(
178
                $type,
179
                $object,
180
                $query,
181
                $sortkey,
182
                $sortdir,
183
                $page,
184
                $this->conf['max_rows'],
185
                $max_pages
186
            );
187
        } catch (\PHPPgAdmin\ADOdbException $e) {
188
            return $this->halt($e->getMessage());
189
        }
190
191
        // Build strings for GETs in array
192
        $_gets = [
193
            'server'   => $_REQUEST['server'],
194
            'database' => $_REQUEST['database'],
195
        ];
196
197
        $this->coalesceArr($_REQUEST, 'query');
198
        $this->coalesceArr($_REQUEST, 'count');
199
        $this->coalesceArr($_REQUEST, 'return');
200
        $this->coalesceArr($_REQUEST, 'table');
201
        $this->coalesceArr($_REQUEST, 'nohistory');
202
203
        $this->setIfIsset($_gets['schema'], $_REQUEST['schema'], null, false);
204
        $this->setIfIsset($_gets[$subject], $object, null, false);
205
        $this->setIfIsset($_gets['subject'], $subject, null, false);
206
        $this->setIfIsset($_gets['query'], $_REQUEST['query'], null, false);
207
        $this->setIfIsset($_gets['count'], $_REQUEST['count'], null, false);
208
        $this->setIfIsset($_gets['return'], $_REQUEST['return'], null, false);
209
        $this->setIfIsset($_gets['search_path'], $_REQUEST['search_path'], null, false);
210
        $this->setIfIsset($_gets['table'], $_REQUEST['table'], null, false);
211
        $this->setIfIsset($_gets['nohistory'], $_REQUEST['nohistory'], null, false);
212
        $_gets['sortkey'] = $sortkey;
213
        $_gets['sortdir'] = $sortdir;
214
        $_gets['strings'] = $strings;
215
216
        if ($save_history && is_object($resultset) && ('QUERY' == $type)) {
217
            //{
218
            $this->misc->saveScriptHistory($_REQUEST['query']);
219
        }
220
221
        $query = $query ? $query : sprintf('SELECT * FROM %s.%s', $_REQUEST['schema'], $object);
222
223
        //$query = isset($_REQUEST['query'])? $_REQUEST['query'] : "select * from {$_REQUEST['schema']}.{$_REQUEST['table']};";
224
        //$this->prtrace($query);
225
226
        //die(htmlspecialchars($query));
227
228
        echo '<form method="post" id="sqlform" action="'.$_SERVER['REQUEST_URI'].'">';
229
        echo $this->misc->form;
230
        if ($object) {
231
            echo '<input type="hidden" name="'.$subject.'" value="', htmlspecialchars($object), '" />'.PHP_EOL;
232
        }
233
        echo '<textarea width="90%" name="query"  id="query" rows="5" cols="100" resizable="true">';
234
        echo htmlspecialchars($query);
235
        echo '</textarea><br><input type="submit"/>';
236
237
        echo '</form>';
238
239
        $this->printResultsTable($resultset, $page, $max_pages, $_gets, $object);
240
        // Navigation links
241
242
        $navlinks = $this->getBrowseNavLinks($type, $_gets, $page, $subject, $object, $resultset);
243
        $this->printNavLinks($navlinks, 'display-browse', get_defined_vars());
244
    }
245
246
    public function getQueryTitleAndType($data, $object)
247
    {
248
        $fkey = $this->coalesceArr($_REQUEST, 'fkey')['fkey'];
249
250
        $query = $this->coalesceArr($_REQUEST, 'query')['query'];
251
        // This code is used when browsing FK in pure-xHTML (without js)
252
        if ($fkey) {
253
            $ops = [];
254
            foreach (array_keys($fkey) as $x) {
255
                $ops[$x] = '=';
256
            }
257
            $query             = $data->getSelectSQL($_REQUEST['table'], [], $fkey, $ops);
258
            $_REQUEST['query'] = $query;
259
        }
260
261
        $title = 'strqueryresults';
262
        $type  = 'QUERY';
263
264
        if ($object && $query) {
265
            $_SESSION['sqlquery'] = $query;
266
            $title                = 'strselect';
267
            $type                 = 'SELECT';
268
        } elseif ($object) {
269
            $title = 'strselect';
270
            $type  = 'TABLE';
271
        } elseif (isset($_SESSION['sqlquery'])) {
272
            $query = $_SESSION['sqlquery'];
273
        }
274
275
        return [$query, $title, $type];
276
    }
277
278
    public function getBrowseNavLinks($type, $_gets, $page, $subject, $object, $resultset)
279
    {
280
        $fields = [
281
            'server'   => $_REQUEST['server'],
282
            'database' => $_REQUEST['database'],
283
        ];
284
285
        $this->setIfIsset($fields['schema'], $_REQUEST['schema'], null, false);
286
287
        $navlinks = [];
288
        $strings  = $_gets['strings'];
289
        // Return
290
        if (isset($_REQUEST['return'])) {
291
            $urlvars = $this->misc->getSubjectParams($_REQUEST['return']);
292
293
            $navlinks['back'] = [
294
                'attr'    => [
295
                    'href' => [
296
                        'url'     => $urlvars['url'],
297
                        'urlvars' => $urlvars['params'],
298
                    ],
299
                ],
300
                'content' => $this->lang['strback'],
301
            ];
302
        }
303
304
        // Edit SQL link
305
        if ('QUERY' == $type) {
306
            $navlinks['edit'] = [
307
                'attr'    => [
308
                    'href' => [
309
                        'url'     => 'database',
310
                        'urlvars' => array_merge(
311
                            $fields,
312
                            [
313
                                'action'   => 'sql',
314
                                'paginate' => 'on',
315
                            ]
316
                        ),
317
                    ],
318
                ],
319
                'content' => $this->lang['streditsql'],
320
            ];
321
        }
322
323
        $navlinks['collapse'] = [
324
            'attr'    => [
325
                'href' => [
326
                    'url'     => 'display',
327
                    'urlvars' => array_merge(
328
                        $_gets,
329
                        [
330
                            'strings' => 'expanded',
331
                            'page'    => $page,
332
                        ]
333
                    ),
334
                ],
335
            ],
336
            'content' => $this->lang['strexpand'],
337
        ];
338
        // Expand/Collapse
339
        if ('expanded' == $strings) {
340
            $navlinks['collapse'] = [
341
                'attr'    => [
342
                    'href' => [
343
                        'url'     => 'display',
344
                        'urlvars' => array_merge(
345
                            $_gets,
346
                            [
347
                                'strings' => 'collapsed',
348
                                'page'    => $page,
349
                            ]
350
                        ),
351
                    ],
352
                ],
353
                'content' => $this->lang['strcollapse'],
354
            ];
355
        }
356
357
        // Create view and download
358
        if (isset($_REQUEST['query'], $resultset) && is_object($resultset) && $resultset->recordCount() > 0) {
359
            // Report views don't set a schema, so we need to disable create view in that case
360
            if (isset($_REQUEST['schema'])) {
361
                $navlinks['createview'] = [
362
                    'attr'    => [
363
                        'href' => [
364
                            'url'     => 'views',
365
                            'urlvars' => array_merge(
366
                                $fields,
367
                                [
368
                                    'action'         => 'create',
369
                                    'formDefinition' => $_REQUEST['query'],
370
                                ]
371
                            ),
372
                        ],
373
                    ],
374
                    'content' => $this->lang['strcreateview'],
375
                ];
376
            }
377
378
            $urlvars = [];
379
380
            $this->setIfIsset($urlvars['search_path'], $_REQUEST['search_path'], null, false);
381
382
            $navlinks['download'] = [
383
                'attr'    => [
384
                    'href' => [
385
                        'url'     => 'dataexport',
386
                        'urlvars' => array_merge($fields, $urlvars),
387
                    ],
388
                ],
389
                'content' => $this->lang['strdownload'],
390
            ];
391
        }
392
393
        // Insert
394
        if (isset($object) && (isset($subject) && 'table' == $subject)) {
395
            $navlinks['insert'] = [
396
                'attr'    => [
397
                    'href' => [
398
                        'url'     => 'tables',
399
                        'urlvars' => array_merge(
400
                            $fields,
401
                            [
402
                                'action' => 'confinsertrow',
403
                                'table'  => $object,
404
                            ]
405
                        ),
406
                    ],
407
                ],
408
                'content' => $this->lang['strinsert'],
409
            ];
410
        }
411
412
        // Refresh
413
        $navlinks['refresh'] = [
414
            'attr'    => [
415
                'href' => [
416
                    'url'     => 'display',
417
                    'urlvars' => array_merge(
418
                        $_gets,
419
                        [
420
                            'strings' => $strings,
421
                            'page'    => $page,
422
                        ]
423
                    ),
424
                ],
425
            ],
426
            'content' => $this->lang['strrefresh'],
427
        ];
428
429
        return $navlinks;
430
    }
431
432
    private function _getKeyAndActions($resultset, $object, $data, $page, $_gets)
433
    {
434
        $key            = [];
435
        $strings        = $_gets['strings'];
436
        $plugin_manager = $this->plugin_manager;
437
        // Fetch unique row identifier, if this is a table browse request.
438
        if ($object) {
439
            $key = $data->getRowIdentifier($object);
440
        }
441
        // Check that the key is actually in the result set.  This can occur for select
442
        // operations where the key fields aren't part of the select.  XXX:  We should
443
        // be able to support this, somehow.
444
        foreach ($key as $v) {
445
            // If a key column is not found in the record set, then we
446
            // can't use the key.
447
            if (!array_key_exists($v, $resultset->fields)) {
448
                $key = [];
449
450
                break;
451
            }
452
        }
453
454
        $buttons = [
455
            'edit'   => [
456
                'content' => $this->lang['stredit'],
457
                'attr'    => [
458
                    'href' => [
459
                        'url'     => 'display',
460
                        'urlvars' => array_merge(
461
                            [
462
                                'action'  => 'confeditrow',
463
                                'strings' => $strings,
464
                                'page'    => $page,
465
                            ],
466
                            $_gets
467
                        ),
468
                    ],
469
                ],
470
            ],
471
            'delete' => [
472
                'content' => $this->lang['strdelete'],
473
                'attr'    => [
474
                    'href' => [
475
                        'url'     => 'display',
476
                        'urlvars' => array_merge(
477
                            [
478
                                'action'  => 'confdelrow',
479
                                'strings' => $strings,
480
                                'page'    => $page,
481
                            ],
482
                            $_gets
483
                        ),
484
                    ],
485
                ],
486
            ],
487
        ];
488
        $actions = [
489
            'actionbuttons' => &$buttons,
490
            'place'         => 'display-browse',
491
        ];
492
        $plugin_manager->doHook('actionbuttons', $actions);
493
494
        foreach (array_keys($actions['actionbuttons']) as $action) {
495
            $actions['actionbuttons'][$action]['attr']['href']['urlvars'] = array_merge(
496
                $actions['actionbuttons'][$action]['attr']['href']['urlvars'],
497
                $_gets
498
            );
499
        }
500
501
        return [$actions, $key];
502
    }
503
504
    public function printResultsTable($resultset, $page, $max_pages, $_gets, $object)
505
    {
506
        if (!is_object($resultset) || $resultset->recordCount() <= 0) {
507
            echo "<p>{$this->lang['strnodata']}</p>".PHP_EOL;
508
509
            return;
510
        }
511
512
        $data = $this->misc->getDatabaseAccessor();
513
514
        list($actions, $key) = $this->_getKeyAndActions($resultset, $object, $data, $page, $_gets);
515
516
        $fkey_information = $this->getFKInfo();
517
        // Show page navigation
518
        $paginator = $this->_printPages($page, $max_pages, $_gets);
519
520
        echo $paginator;
521
        echo '<table id="data">'.PHP_EOL;
522
        echo '<tr>';
523
524
        // Display edit and delete actions if we have a key
525
        $display_action_column = (count($actions['actionbuttons']) > 0 && count($key) > 0);
526
527
        echo $display_action_column ? "<th class=\"data\">{$this->lang['stractions']}</th>".PHP_EOL : '';
528
529
        // we show OIDs only if we are in TABLE or SELECT type browsing
530
        $this->printTableHeaderCells($resultset, $_gets, isset($object));
531
532
        echo '</tr>'.PHP_EOL;
533
534
        reset($resultset->fields);
535
536
        $trclass     = 'data2';
537
        $buttonclass = 'opbutton2';
538
539
        while (!$resultset->EOF) {
540
            $trclass     = ($trclass === 'data2') ? 'data1' : 'data2';
541
            $buttonclass = ($buttonclass === 'opbutton2') ? 'opbutton1' : 'opbutton2';
542
543
            echo sprintf('<tr class="%s">', $trclass).PHP_EOL;
544
545
            $this->_printResultsTableActionButtons($resultset, $key, $actions, $display_action_column, $buttonclass);
546
547
            $this->printTableRowCells($resultset, $fkey_information, isset($object));
548
549
            echo '</tr>'.PHP_EOL;
550
            $resultset->moveNext();
551
        }
552
        echo '</table>'.PHP_EOL;
553
554
        echo '<p>', $resultset->recordCount(), " {$this->lang['strrows']}</p>".PHP_EOL;
555
        // Show page navigation
556
        echo $paginator;
557
    }
558
559
    private function _printResultsTableActionButtons($resultset, $key, $actions, $display_action_column, $buttonclass)
560
    {
561
        if (!$display_action_column) {
562
            return;
563
        }
564
565
        $edit_params   = isset($actions['actionbuttons']['edit']) ? $actions['actionbuttons']['edit'] : [];
566
        $delete_params = isset($actions['actionbuttons']['delete']) ? $actions['actionbuttons']['delete'] : [];
567
568
        $keys_array = [];
569
        $has_nulls  = false;
570
        foreach ($key as $v) {
571
            if (null === $resultset->fields[$v]) {
572
                $has_nulls = true;
573
574
                break;
575
            }
576
            $keys_array["key[{$v}]"] = $resultset->fields[$v];
577
        }
578
        if ($has_nulls) {
579
            echo '<td>&nbsp;</td>'.PHP_EOL;
580
581
            return;
582
        }
583
        // Display edit and delete links if we have a key
584
        if (isset($actions['actionbuttons']['edit'])) {
585
            $actions['actionbuttons']['edit']                            = $edit_params;
586
            $actions['actionbuttons']['edit']['attr']['href']['urlvars'] = array_merge(
587
                $actions['actionbuttons']['edit']['attr']['href']['urlvars'],
588
                $keys_array
589
            );
590
        }
591
592
        if (isset($actions['actionbuttons']['delete'])) {
593
            $actions['actionbuttons']['delete']                            = $delete_params;
594
            $actions['actionbuttons']['delete']['attr']['href']['urlvars'] = array_merge(
595
                $actions['actionbuttons']['delete']['attr']['href']['urlvars'],
596
                $keys_array
597
            );
598
        }
599
        echo sprintf('<td class="%s" style="white-space:nowrap">', $buttonclass);
600
        foreach ($actions['actionbuttons'] as $action) {
601
            $this->printLink($action, true, __METHOD__);
602
        }
603
        echo '</td>'.PHP_EOL;
604
    }
605
606
    /**
607
     * Print table header cells.
608
     *
609
     * @param \PHPPgAdmin\ADORecordSet $resultset set of results from getRow operation
610
     * @param array|bool               $args      - associative array for sort link parameters, or false if there isn't any
611
     * @param bool                     $withOid   either to display OIDs or not
612
     */
613
    public function printTableHeaderCells(&$resultset, $args, $withOid)
614
    {
615
        $data = $this->misc->getDatabaseAccessor();
616
617
        if (!is_object($resultset) || $resultset->recordCount() <= 0) {
618
            return;
619
        }
620
621
        foreach (array_keys($resultset->fields) as $index => $key) {
0 ignored issues
show
Bug introduced by
$resultset->fields of type boolean is incompatible with the type array expected by parameter $input of array_keys(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

621
        foreach (array_keys(/** @scrutinizer ignore-type */ $resultset->fields) as $index => $key) {
Loading history...
622
            if (($key === $data->id) && (!($withOid && $this->conf['show_oids']))) {
623
                continue;
624
            }
625
            $finfo = $resultset->fetchField($index);
626
627
            if (false === $args) {
628
                echo '<th class="data">', $this->misc->printVal($finfo->name), '</th>'.PHP_EOL;
629
630
                continue;
631
            }
632
            $args['page']    = $_REQUEST['page'];
633
            $args['sortkey'] = $index + 1;
634
            // Sort direction opposite to current direction, unless it's currently ''
635
            $args['sortdir'] = ('asc' == $_REQUEST['sortdir'] && $_REQUEST['sortkey'] == ($index + 1)) ? 'desc' : 'asc';
636
637
            $sortLink = http_build_query($args);
638
639
            echo "<th class=\"data\"><a href=\"?{$sortLink}\">";
640
            echo $this->misc->printVal($finfo->name);
641
            if ($_REQUEST['sortkey'] == ($index + 1)) {
642
                $icon = ('asc' == $_REQUEST['sortdir']) ? $this->misc->icon('RaiseArgument') : $this->misc->icon('LowerArgument');
643
                echo sprintf('<img src="%s" alt="%s">', $icon, $_REQUEST['sortdir']);
644
            }
645
            echo '</a></th>'.PHP_EOL;
646
        }
647
648
        reset($resultset->fields);
0 ignored issues
show
Bug introduced by
$resultset->fields of type boolean is incompatible with the type array expected by parameter $array of reset(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

648
        reset(/** @scrutinizer ignore-type */ $resultset->fields);
Loading history...
649
    }
650
651
    /**
652
     * Print table rows.
653
     *
654
     * @param \PHPPgAdmin\ADORecordSet $resultset        The resultset
655
     * @param array                    $fkey_information The fkey information
656
     * @param bool                     $withOid          either to display OIDs or not
657
     */
658
    public function printTableRowCells(&$resultset, &$fkey_information, $withOid)
659
    {
660
        $data = $this->misc->getDatabaseAccessor();
661
        $j    = 0;
662
663
        $this->coalesceArr($_REQUEST, 'strings', 'collapsed');
664
665
        foreach ($resultset->fields as $k => $v) {
666
            $finfo = $resultset->fetchField($j++);
667
668
            if (($k === $data->id) && (!($withOid && $this->conf['show_oids']))) {
669
                continue;
670
            }
671
            $printvalOpts = ['null' => true, 'clip' => ('collapsed' == $_REQUEST['strings'])];
672
            if (null !== $v && '' == $v) {
673
                echo '<td>&nbsp;</td>';
674
            } else {
675
                echo '<td style="white-space:nowrap;">';
676
677
                $this->_printFKLinks($resultset, $fkey_information, $k, $v, $printvalOpts);
678
679
                $val = $this->misc->printVal($v, $finfo->type, $printvalOpts);
680
681
                echo $val;
682
                echo '</td>';
683
            }
684
        }
685
    }
686
687
    private function _printFKLinks($resultset, $fkey_information, $k, $v, &$printvalOpts)
688
    {
689
        if ((null === $v) || !isset($fkey_information['byfield'][$k])) {
690
            return;
691
        }
692
693
        foreach ($fkey_information['byfield'][$k] as $conid) {
694
            $query_params = $fkey_information['byconstr'][$conid]['url_data'];
695
696
            foreach ($fkey_information['byconstr'][$conid]['fkeys'] as $p_field => $f_field) {
697
                $query_params .= '&amp;'.urlencode("fkey[{$f_field}]").'='.urlencode($resultset->fields[$p_field]);
698
            }
699
700
            // $fkey_information['common_url'] is already urlencoded
701
            $query_params .= '&amp;'.$fkey_information['common_url'];
702
            $title = htmlentities($fkey_information['byconstr'][$conid]['consrc'], ENT_QUOTES, 'UTF-8');
703
            echo '<div style="display:inline-block;">';
704
            echo sprintf('<a class="fk fk_%s" href="display?%s">', htmlentities($conid, ENT_QUOTES, 'UTF-8'), $query_params);
705
            echo sprintf('<img src="%s" style="vertical-align:middle;" alt="[fk]" title="%s" />', $this->misc->icon('ForeignKey'), $title);
706
            echo '</a>';
707
            echo '</div>';
708
        }
709
        $printvalOpts['class'] = 'fk_value';
710
    }
711
712
    private function _unserializeIfNotArray($the_array, $key)
713
    {
714
        if (!isset($the_array[$key])) {
715
            return [];
716
        }
717
        if (is_array($the_array[$key])) {
718
            return $the_array[$key];
719
        }
720
721
        return unserialize(urldecode($the_array[$key]));
722
    }
723
724
    /**
725
     * Show form to edit row.
726
     *
727
     * @param string $msg message to display on top of the form or after performing edition
728
     */
729
    public function formEditRow($msg = '')
730
    {
731
        $data = $this->misc->getDatabaseAccessor();
732
733
        $key = $this->_unserializeIfNotArray($_REQUEST, 'key');
734
735
        $this->printTrail($_REQUEST['subject']);
736
        $this->printTitle($this->lang['streditrow']);
737
        $this->printMsg($msg);
738
739
        $attrs     = $data->getTableAttributes($_REQUEST['table']);
740
        $resultset = $data->browseRow($_REQUEST['table'], $key);
741
742
        $fksprops = $this->_getFKProps();
743
744
        echo '<form action="'.\SUBFOLDER.'/src/views/display" method="post" id="ac_form">'.PHP_EOL;
745
746
        $elements = 0;
747
        $error    = true;
748
        if (1 == $resultset->recordCount() && $attrs->recordCount() > 0) {
749
            echo '<table>'.PHP_EOL;
750
751
            // Output table header
752
            echo "<tr><th class=\"data\">{$this->lang['strcolumn']}</th><th class=\"data\">{$this->lang['strtype']}</th>";
753
            echo "<th class=\"data\">{$this->lang['strformat']}</th>".PHP_EOL;
754
            echo "<th class=\"data\">{$this->lang['strnull']}</th><th class=\"data\">{$this->lang['strvalue']}</th></tr>";
755
756
            $i = 0;
757
            while (!$attrs->EOF) {
758
                $attrs->fields['attnotnull'] = $data->phpBool($attrs->fields['attnotnull']);
759
                $id                          = (0 == ($i % 2) ? '1' : '2');
760
761
                // Initialise variables
762
                if (!isset($_REQUEST['format'][$attrs->fields['attname']])) {
763
                    $_REQUEST['format'][$attrs->fields['attname']] = 'VALUE';
764
                }
765
766
                echo "<tr class=\"data{$id}\">".PHP_EOL;
767
                echo '<td style="white-space:nowrap;">', $this->misc->printVal($attrs->fields['attname']), '</td>';
768
                echo '<td style="white-space:nowrap;">'.PHP_EOL;
769
                echo $this->misc->printVal($data->formatType($attrs->fields['type'], $attrs->fields['atttypmod']));
770
                echo '<input type="hidden" name="types[', htmlspecialchars($attrs->fields['attname']), ']" value="',
771
                htmlspecialchars($attrs->fields['type']), '" /></td>';
772
                ++$elements;
773
                echo '<td style="white-space:nowrap;">'.PHP_EOL;
774
                echo '<select name="format['.htmlspecialchars($attrs->fields['attname']), ']">'.PHP_EOL;
775
                echo '<option value="VALUE"', ($_REQUEST['format'][$attrs->fields['attname']] == 'VALUE') ? ' selected="selected"' : '', ">{$this->lang['strvalue']}</option>".PHP_EOL;
776
                $selected = ($_REQUEST['format'][$attrs->fields['attname']] == 'EXPRESSION') ? ' selected="selected"' : '';
777
                echo '<option value="EXPRESSION"'.$selected.">{$this->lang['strexpression']}</option>".PHP_EOL;
778
                echo "</select>\n</td>".PHP_EOL;
779
                ++$elements;
780
                echo '<td style="white-space:nowrap;">';
781
                // Output null box if the column allows nulls (doesn't look at CHECKs or ASSERTIONS)
782
                if (!$attrs->fields['attnotnull']) {
783
                    // Set initial null values
784
                    if ('confeditrow' == $_REQUEST['action'] && null === $resultset->fields[$attrs->fields['attname']]) {
785
                        $_REQUEST['nulls'][$attrs->fields['attname']] = 'on';
786
                    }
787
                    echo "<label><span><input type=\"checkbox\" class=\"nullcheckbox\" name=\"nulls[{$attrs->fields['attname']}]\"",
788
                    isset($_REQUEST['nulls'][$attrs->fields['attname']]) ? ' checked="checked"' : '', ' /></span></label></td>'.PHP_EOL;
789
                    ++$elements;
790
                } else {
791
                    echo '&nbsp;</td>';
792
                }
793
794
                echo "<td id=\"row_att_{$attrs->fields['attnum']}\" style=\"white-space:nowrap;\">";
795
796
                $extras = [];
797
798
                // If the column allows nulls, then we put a JavaScript action on the data field to unset the
799
                // NULL checkbox as soon as anything is entered in the field.  We use the $elements variable to
800
                // keep track of which element offset we're up to.  We can't refer to the null checkbox by name
801
                // as it contains '[' and ']' characters.
802
                if (!$attrs->fields['attnotnull']) {
803
                    $extras['class'] = 'insert_row_input';
804
                }
805
806
                if ((false !== $fksprops) && isset($fksprops['byfield'][$attrs->fields['attnum']])) {
807
                    $extras['id']           = "attr_{$attrs->fields['attnum']}";
808
                    $extras['autocomplete'] = 'off';
809
                }
810
811
                echo $data->printField("values[{$attrs->fields['attname']}]", $resultset->fields[$attrs->fields['attname']], $attrs->fields['type'], $extras);
812
813
                echo '</td>';
814
                ++$elements;
815
                echo '</tr>'.PHP_EOL;
816
                ++$i;
817
                $attrs->moveNext();
818
            }
819
            echo '</table>'.PHP_EOL;
820
821
            $error = false;
822
        } elseif (1 != $resultset->recordCount()) {
823
            echo "<p>{$this->lang['strrownotunique']}</p>".PHP_EOL;
824
        } else {
825
            echo "<p>{$this->lang['strinvalidparam']}</p>".PHP_EOL;
826
        }
827
828
        echo '<input type="hidden" name="action" value="editrow" />'.PHP_EOL;
829
        echo $this->misc->form;
830
        echo isset($_REQUEST['table']) ? sprintf('<input type="hidden" name="table" value="%s" />%s', htmlspecialchars($_REQUEST['table']), PHP_EOL) : '';
831
832
        echo isset($_REQUEST['subject']) ? sprintf('<input type="hidden" name="subject" value="%s" />%s', htmlspecialchars($_REQUEST['subject']), PHP_EOL) : '';
833
834
        echo isset($_REQUEST['query']) ? sprintf('<input type="hidden" name="query" value="%s" />%s', htmlspecialchars($_REQUEST['query']), PHP_EOL) : '';
835
836
        echo isset($_REQUEST['count']) ? sprintf('<input type="hidden" name="count" value="%s" />%s', htmlspecialchars($_REQUEST['count']), PHP_EOL) : '';
837
838
        echo isset($_REQUEST['return']) ? sprintf('<input type="hidden" name="return" value="%s" />%s', htmlspecialchars($_REQUEST['return']), PHP_EOL) : '';
839
840
        echo '<input type="hidden" name="page" value="', htmlspecialchars($_REQUEST['page']), '" />'.PHP_EOL;
841
        echo '<input type="hidden" name="sortkey" value="', htmlspecialchars($_REQUEST['sortkey']), '" />'.PHP_EOL;
842
        echo '<input type="hidden" name="sortdir" value="', htmlspecialchars($_REQUEST['sortdir']), '" />'.PHP_EOL;
843
        echo '<input type="hidden" name="strings" value="', htmlspecialchars($_REQUEST['strings']), '" />'.PHP_EOL;
844
        echo '<input type="hidden" name="key" value="', htmlspecialchars(urlencode(serialize($key))), '" />'.PHP_EOL;
845
        echo '<p>';
846
        if (!$error) {
847
            echo "<input type=\"submit\" name=\"save\" accesskey=\"r\" value=\"{$this->lang['strsave']}\" />".PHP_EOL;
848
        }
849
850
        echo "<input type=\"submit\" name=\"cancel\" value=\"{$this->lang['strcancel']}\" />".PHP_EOL;
851
852
        if (false !== $fksprops) {
853
            $autocomplete_string = "<input type=\"checkbox\" id=\"no_ac\" value=\"0\" /><label for=\"no_ac\">{$this->lang['strac']}</label>";
854
            if ('default off' != $this->conf['autocomplete']) {
855
                $autocomplete_string = "<input type=\"checkbox\" id=\"no_ac\" value=\"1\" checked=\"checked\" /><label for=\"no_ac\">{$this->lang['strac']}</label>";
856
            }
857
            echo $autocomplete_string.PHP_EOL;
858
        }
859
860
        echo '</p>'.PHP_EOL;
861
        echo '</form>'.PHP_EOL;
862
        echo '<script src="'.\SUBFOLDER.'/assets/js/insert_or_edit_row.js" type="text/javascript"></script>';
863
    }
864
865
    /**
866
     * Performs actual edition of row.
867
     */
868
    public function doEditRow()
869
    {
870
        $data = $this->misc->getDatabaseAccessor();
871
872
        $key = $this->_unserializeIfNotArray($_REQUEST, 'key');
873
874
        $this->coalesceArr($_POST, 'values', []);
875
876
        $this->coalesceArr($_POST, 'nulls', []);
877
878
        $status = $data->editRow(
879
            $_POST['table'],
880
            $_POST['values'],
881
            $_POST['nulls'],
882
            $_POST['format'],
883
            $_POST['types'],
884
            $key
885
        );
886
        if (0 == $status) {
887
            return $this->doBrowse($this->lang['strrowupdated']);
888
        }
889
        if ($status == -2) {
890
            return $this->formEditRow($this->lang['strrownotunique']);
891
        }
892
893
        return $this->formEditRow($this->lang['strrowupdatedbad']);
894
    }
895
896
    /**
897
     * Show confirmation of drop and perform actual drop.
898
     *
899
     * @param mixed $confirm
900
     */
901
    public function doDelRow($confirm)
902
    {
903
        $data = $this->misc->getDatabaseAccessor();
904
905
        if ($confirm) {
906
            $this->printTrail($_REQUEST['subject']);
907
            $this->printTitle($this->lang['strdeleterow']);
908
909
            $resultset = $data->browseRow($_REQUEST['table'], $_REQUEST['key']);
910
911
            echo '<form action="'.\SUBFOLDER.'/src/views/display" method="post">'.PHP_EOL;
912
            echo $this->misc->form;
913
914
            if (1 == $resultset->recordCount()) {
915
                echo "<p>{$this->lang['strconfdeleterow']}</p>".PHP_EOL;
916
917
                $fkinfo = [];
918
                echo '<table><tr>';
919
                $this->printTableHeaderCells($resultset, false, true);
920
                echo '</tr>';
921
                echo '<tr class="data1">'.PHP_EOL;
922
                $this->printTableRowCells($resultset, $fkinfo, true);
923
                echo '</tr>'.PHP_EOL;
924
                echo '</table>'.PHP_EOL;
925
                echo '<br />'.PHP_EOL;
926
927
                echo '<input type="hidden" name="action" value="delrow" />'.PHP_EOL;
928
                echo "<input type=\"submit\" name=\"yes\" value=\"{$this->lang['stryes']}\" />".PHP_EOL;
929
                echo "<input type=\"submit\" name=\"no\" value=\"{$this->lang['strno']}\" />".PHP_EOL;
930
            } elseif (1 != $resultset->recordCount()) {
931
                echo "<p>{$this->lang['strrownotunique']}</p>".PHP_EOL;
932
                echo "<input type=\"submit\" name=\"cancel\" value=\"{$this->lang['strcancel']}\" />".PHP_EOL;
933
            } else {
934
                echo "<p>{$this->lang['strinvalidparam']}</p>".PHP_EOL;
935
                echo "<input type=\"submit\" name=\"cancel\" value=\"{$this->lang['strcancel']}\" />".PHP_EOL;
936
            }
937
            if (isset($_REQUEST['table'])) {
938
                echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), '" />'.PHP_EOL;
939
            }
940
941
            if (isset($_REQUEST['subject'])) {
942
                echo '<input type="hidden" name="subject" value="', htmlspecialchars($_REQUEST['subject']), '" />'.PHP_EOL;
943
            }
944
945
            if (isset($_REQUEST['query'])) {
946
                echo '<input type="hidden" name="query" value="', htmlspecialchars($_REQUEST['query']), '" />'.PHP_EOL;
947
            }
948
949
            if (isset($_REQUEST['count'])) {
950
                echo '<input type="hidden" name="count" value="', htmlspecialchars($_REQUEST['count']), '" />'.PHP_EOL;
951
            }
952
953
            if (isset($_REQUEST['return'])) {
954
                echo '<input type="hidden" name="return" value="', htmlspecialchars($_REQUEST['return']), '" />'.PHP_EOL;
955
            }
956
957
            echo '<input type="hidden" name="page" value="', htmlspecialchars($_REQUEST['page']), '" />'.PHP_EOL;
958
            echo '<input type="hidden" name="sortkey" value="', htmlspecialchars($_REQUEST['sortkey']), '" />'.PHP_EOL;
959
            echo '<input type="hidden" name="sortdir" value="', htmlspecialchars($_REQUEST['sortdir']), '" />'.PHP_EOL;
960
            echo '<input type="hidden" name="strings" value="', htmlspecialchars($_REQUEST['strings']), '" />'.PHP_EOL;
961
            echo '<input type="hidden" name="key" value="', htmlspecialchars(urlencode(serialize($_REQUEST['key']))), '" />'.PHP_EOL;
962
            echo '</form>'.PHP_EOL;
963
        } else {
964
            $status = $data->deleteRow($_POST['table'], unserialize(urldecode($_POST['key'])));
965
            if (0 == $status) {
966
                $this->doBrowse($this->lang['strrowdeleted']);
967
            } elseif ($status == -2) {
968
                $this->doBrowse($this->lang['strrownotunique']);
969
            } else {
970
                $this->doBrowse($this->lang['strrowdeletedbad']);
971
            }
972
        }
973
    }
974
975
    /**
976
     * Build & return the FK information data structure
977
     * used when deciding if a field should have a FK link or not.
978
     *
979
     * @return array associative array describing the FK
980
     */
981
    public function &getFKInfo()
982
    {
983
        $data = $this->misc->getDatabaseAccessor();
984
985
        // Get the foreign key(s) information from the current table
986
        $fkey_information = ['byconstr' => [], 'byfield' => []];
987
988
        if (isset($_REQUEST['table'])) {
989
            $constraints = $data->getConstraintsWithFields($_REQUEST['table']);
990
            if ($constraints->recordCount() > 0) {
991
                $fkey_information['common_url'] = $this->misc->getHREF('schema').'&amp;subject=table';
992
993
                // build the FK constraints data structure
994
                while (!$constraints->EOF) {
995
                    $constr = &$constraints->fields;
996
                    if ('f' == $constr['contype']) {
997
                        if (!isset($fkey_information['byconstr'][$constr['conid']])) {
998
                            $fkey_information['byconstr'][$constr['conid']] = [
999
                                'url_data' => 'table='.urlencode($constr['f_table']).'&amp;schema='.urlencode($constr['f_schema']),
1000
                                'fkeys'    => [],
1001
                                'consrc'   => $constr['consrc'],
1002
                            ];
1003
                        }
1004
1005
                        $fkey_information['byconstr'][$constr['conid']]['fkeys'][$constr['p_field']] = $constr['f_field'];
1006
1007
                        if (!isset($fkey_information['byfield'][$constr['p_field']])) {
1008
                            $fkey_information['byfield'][$constr['p_field']] = [];
1009
                        }
1010
1011
                        $fkey_information['byfield'][$constr['p_field']][] = $constr['conid'];
1012
                    }
1013
                    $constraints->moveNext();
1014
                }
1015
            }
1016
        }
1017
1018
        return $fkey_information;
1019
    }
1020
1021
    // Print the FK row, used in ajax requests
1022
    public function doBrowseFK()
1023
    {
1024
        $data = $this->misc->getDatabaseAccessor();
1025
1026
        $ops = [];
1027
        foreach ($_REQUEST['fkey'] as $x => $y) {
1028
            $ops[$x] = '=';
1029
        }
1030
        $query             = $data->getSelectSQL($_REQUEST['table'], [], $_REQUEST['fkey'], $ops);
1031
        $_REQUEST['query'] = $query;
1032
1033
        $fkinfo = $this->getFKInfo();
1034
1035
        $max_pages = 1;
1036
        // Retrieve page from query.  $max_pages is returned by reference.
1037
        $resultset = $data->browseQuery(
1038
            'SELECT',
1039
            $_REQUEST['table'],
1040
            $_REQUEST['query'],
1041
            null,
1042
            null,
1043
            1,
1044
            1,
1045
            $max_pages
1046
        );
1047
1048
        echo '<a href="javascript:void(0);" style="display:table-cell;" class="fk_delete"><img alt="[delete]" src="'.$this->misc->icon('Delete').'" /></a>'.PHP_EOL;
1049
        echo '<div style="display:table-cell;">';
1050
1051
        if (is_object($resultset) && $resultset->recordCount() > 0) {
1052
            /* we are browsing a referenced table here
1053
             * we should show OID if show_oids is true
1054
             * so we give true to withOid in functions bellow
1055
             */
1056
            echo '<table><tr>';
1057
            $this->printTableHeaderCells($resultset, false, true);
1058
            echo '</tr>';
1059
            echo '<tr class="data1">'.PHP_EOL;
1060
            $this->printTableRowCells($resultset, $fkinfo, true);
1061
            echo '</tr>'.PHP_EOL;
1062
            echo '</table>'.PHP_EOL;
1063
        } else {
1064
            echo $this->lang['strnodata'];
1065
        }
1066
        echo '</div>';
1067
    }
1068
1069
    private function _getMinMaxPages($page, $pages)
1070
    {
1071
        $window = 10;
1072
        if ($page <= $window) {
1073
            $min_page = 1;
1074
            $max_page = min(2 * $window, $pages);
1075
        } elseif ($page > $window && $pages >= $page + $window) {
1076
            $min_page = ($page - $window) + 1;
1077
            $max_page = $page + $window;
1078
        } else {
1079
            $min_page = ($page - (2 * $window - ($pages - $page))) + 1;
1080
            $max_page = $pages;
1081
        }
1082
1083
        // Make sure min_page is always at least 1
1084
        // and max_page is never greater than $pages
1085
        $min_page = max($min_page, 1);
1086
        $max_page = min($max_page, $pages);
1087
1088
        return [$min_page, $max_page];
1089
    }
1090
1091
    /**
1092
     * Do multi-page navigation.  Displays the prev, next and page options.
1093
     *
1094
     * @param int   $page      - the page currently viewed
1095
     * @param int   $pages     - the maximum number of pages
1096
     * @param array $gets      -  the parameters to include in the link to the wanted page
1097
     * @param int   $max_width - the number of pages to make available at any one time (default = 20)
1098
     *
1099
     * @return string the pagination links
1100
     */
1101
    private function _printPages($page, $pages, $gets, $max_width = 20)
1102
    {
1103
        $lang = $this->lang;
1104
        $page = (int) $page;
1105
1106
        if ($page < 0 || $page > $pages || $pages <= 1 || $max_width <= 0) {
1107
            return;
1108
        }
1109
1110
        unset($gets['page']);
1111
        $url = http_build_query($gets);
1112
1113
        $result = '<p style="text-align: center">'.PHP_EOL;
1114
        if ($page != 1) {
1115
            $result .= sprintf('<a class="pagenav" href="?%s&page=1">%s</a>%s&nbsp;', $url, $lang['strfirst'], PHP_EOL);
1116
            $result .= sprintf('<a class="pagenav" href="?%s&page=%s">%s</a>%s', $url, $page - 1, $lang['strprev'], PHP_EOL);
1117
        }
1118
1119
        list($min_page, $max_page) = $this->_getMinMaxPages($page, $pages);
1120
1121
        for ($i = $min_page; $i <= $max_page; ++$i) {
1122
            $result .= (($i === $page) ? $i : sprintf('<a class="pagenav" href="display?%s&page=%s">%s</a>', $url, $i, $i)).PHP_EOL;
1123
        }
1124
1125
        if ($page != $pages) {
1126
            $result .= sprintf('<a class="pagenav" href="?%s&page=%s">%s</a>%s', $url, $page + 1, $lang['strnext'], PHP_EOL);
1127
            $result .= sprintf('&nbsp;<a class="pagenav" href="?%s&page=%s">%s</a>%s', $url, $pages, $lang['strlast'], PHP_EOL);
1128
        }
1129
        $result .= '</p>'.PHP_EOL;
1130
1131
        return $result;
1132
    }
1133
}
1134