Test Failed
Push — develop ( b901bb...90b3ce )
by Felipe
04:12
created

DisplayController::_getKeyAndActions()   A

Complexity

Conditions 5
Paths 12

Size

Total Lines 69
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 37
nc 12
nop 5
dl 0
loc 69
rs 9.0168
c 1
b 0
f 0

How to fix   Long Method   

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

631
        foreach (array_keys(/** @scrutinizer ignore-type */ $resultset->fields) as $index => $key) {
Loading history...
632
            if (($key === $data->id) && (!($withOid && $this->conf['show_oids']))) {
633
                continue;
634
            }
635
            $finfo = $resultset->fetchField($index);
636
637
            if (false === $args) {
638
                echo '<th class="data">', $this->misc->printVal($finfo->name), '</th>'.PHP_EOL;
639
640
                continue;
641
            }
642
            $args['page']    = $_REQUEST['page'];
643
            $args['sortkey'] = $index + 1;
644
            // Sort direction opposite to current direction, unless it's currently ''
645
            $args['sortdir'] = ('asc' == $_REQUEST['sortdir'] && $_REQUEST['sortkey'] == ($index + 1)) ? 'desc' : 'asc';
646
647
            $sortLink = http_build_query($args);
648
649
            echo "<th class=\"data\"><a href=\"?{$sortLink}\">";
650
            echo $this->misc->printVal($finfo->name);
651
            if ($_REQUEST['sortkey'] == ($index + 1)) {
652
                $icon = ('asc' == $_REQUEST['sortdir']) ? $this->misc->icon('RaiseArgument') : $this->misc->icon('LowerArgument');
653
                echo sprintf('<img src="%s" alt="%s">', $icon, $_REQUEST['sortdir']);
654
            }
655
            echo '</a></th>'.PHP_EOL;
656
        }
657
658
        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

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