Test Failed
Branch develop (db5506)
by Felipe
03:46
created

MaterializedviewsController::doSubTree()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 16

Duplication

Lines 26
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 16
nc 1
nop 0
dl 26
loc 26
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace PHPPgAdmin\Controller;
4
5
use \PHPPgAdmin\Decorators\Decorator;
6
7
/**
8
 * Base controller class
9
 */
10 View Code Duplication
class MaterializedviewsController extends BaseController
0 ignored issues
show
Duplication introduced by
This class seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
11
{
12
    public $script      = 'materializedviews.php';
13
    public $_name       = 'MaterializedviewsController';
14
    public $table_place = 'matviews-matviews';
15
16
    public function render()
0 ignored issues
show
Coding Style introduced by
render uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
render uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
17
    {
18
        $conf   = $this->conf;
19
        $misc   = $this->misc;
20
        $lang   = $this->lang;
21
        $action = $this->action;
22
23
        if ($action == 'tree') {
24
            return $this->doTree();
25
        } elseif ($action == 'subtree') {
26
            return $this->doSubTree();
27
        }
28
29
        $data = $misc->getDatabaseAccessor();
30
31
        $this->printHeader('M ' . $lang['strviews']);
32
        $this->printBody();
33
34
        switch ($action) {
35
            case 'selectrows':
36
                if (!isset($_REQUEST['cancel'])) {
37
                    $this->doSelectRows(false);
38
                } else {
39
                    $this->doDefault();
40
                }
41
42
                break;
43
            case 'confselectrows':
44
                $this->doSelectRows(true);
45
                break;
46
            case 'save_create_wiz':
47
                if (isset($_REQUEST['cancel'])) {
48
                    $this->doDefault();
49
                } else {
50
                    $this->doSaveCreateWiz();
51
                }
52
53
                break;
54
            case 'wiz_create':
55
                $this->doWizardCreate();
56
                break;
57
            case 'set_params_create':
58
                if (isset($_POST['cancel'])) {
59
                    $this->doDefault();
60
                } else {
61
                    $this->doSetParamsCreate();
62
                }
63
64
                break;
65
            case 'save_create':
66
                if (isset($_REQUEST['cancel'])) {
67
                    $this->doDefault();
68
                } else {
69
                    $this->doSaveCreate();
70
                }
71
72
                break;
73
            case 'create':
74
                $this->doCreate();
75
                break;
76
            case 'drop':
77
                if (isset($_POST['drop'])) {
78
                    $this->doDrop(false);
79
                } else {
80
                    $this->doDefault();
81
                }
82
83
                break;
84
            case 'confirm_drop':
85
                $this->doDrop(true);
86
                break;
87
            default:
88
                $this->doDefault();
89
                break;
90
        }
91
92
        $this->printFooter();
93
    }
94
95
    /**
96
     * Show default list of views in the database
97
     */
98
    public function doDefault($msg = '')
0 ignored issues
show
Coding Style introduced by
doDefault uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
99
    {
100
        $conf = $this->conf;
101
        $misc = $this->misc;
102
        $lang = $this->lang;
103
        $data = $misc->getDatabaseAccessor();
104
105
        $this->printTrail('schema');
106
        $this->printTabs('schema', 'matviews');
107
        $this->printMsg($msg);
108
109
        //$matviews = $data->getViews();
110
        $matviews = $data->getMaterializedViews();
111
112
        $columns = [
113
            'matview' => [
114
                'title' => 'M ' . $lang['strview'],
115
                'field' => Decorator::field('relname'),
116
                'url'   => SUBFOLDER . "/redirect/matview?{$misc->href}&amp;",
117
                'vars'  => ['matview' => 'relname'],
118
            ],
119
            'owner'   => [
120
                'title' => $lang['strowner'],
121
                'field' => Decorator::field('relowner'),
122
            ],
123
            'actions' => [
124
                'title' => $lang['stractions'],
125
            ],
126
            'comment' => [
127
                'title' => $lang['strcomment'],
128
                'field' => Decorator::field('relcomment'),
129
            ],
130
        ];
131
132
        $actions = [
133
            'multiactions' => [
134
                'keycols' => ['matview' => 'relname'],
135
                'url'     => 'materializedviews.php',
136
            ],
137
            'browse'       => [
138
                'content' => $lang['strbrowse'],
139
                'attr'    => [
140
                    'href' => [
141
                        'url'     => 'display.php',
142
                        'urlvars' => [
143
                            'action'  => 'confselectrows',
144
                            'subject' => 'matview',
145
                            'return'  => 'schema',
146
                            'matview' => Decorator::field('relname'),
147
                        ],
148
                    ],
149
                ],
150
            ],
151
            'select'       => [
152
                'content' => $lang['strselect'],
153
                'attr'    => [
154
                    'href' => [
155
                        'url'     => 'materializedviews.php',
156
                        'urlvars' => [
157
                            'action'  => 'confselectrows',
158
                            'matview' => Decorator::field('relname'),
159
                        ],
160
                    ],
161
                ],
162
            ],
163
164
            // Insert is possible if the relevant rule for the view has been created.
165
            //            'insert' => array(
166
            //                'title'    => $lang['strinsert'],
167
            //                'url'    => "materializedviews.php?action=confinsertrow&amp;{$misc->href}&amp;",
168
            //                'vars'    => array('view' => 'relname'),
169
            //            ),
170
171
            'alter'        => [
172
                'content' => $lang['stralter'],
173
                'attr'    => [
174
                    'href' => [
175
                        'url'     => 'materializedviewproperties.php',
176
                        'urlvars' => [
177
                            'action'  => 'confirm_alter',
178
                            'matview' => Decorator::field('relname'),
179
                        ],
180
                    ],
181
                ],
182
            ],
183
            'drop'         => [
184
                'multiaction' => 'confirm_drop',
185
                'content'     => $lang['strdrop'],
186
                'attr'        => [
187
                    'href' => [
188
                        'url'     => 'materializedviews.php',
189
                        'urlvars' => [
190
                            'action'  => 'confirm_drop',
191
                            'matview' => Decorator::field('relname'),
192
                        ],
193
                    ],
194
                ],
195
            ],
196
        ];
197
198
        echo $this->printTable($matviews, $columns, $actions, $this->table_place, $lang['strnoviews']);
199
200
        $navlinks = [
201
            'create'    => [
202
                'attr'    => [
203
                    'href' => [
204
                        'url'     => 'materializedviews.php',
205
                        'urlvars' => [
206
                            'action'   => 'create',
207
                            'server'   => $_REQUEST['server'],
208
                            'database' => $_REQUEST['database'],
209
                            'schema'   => $_REQUEST['schema'],
210
                        ],
211
                    ],
212
                ],
213
                'content' => $lang['strcreateview'],
214
            ],
215
            'createwiz' => [
216
                'attr'    => [
217
                    'href' => [
218
                        'url'     => 'materializedviews.php',
219
                        'urlvars' => [
220
                            'action'   => 'wiz_create',
221
                            'server'   => $_REQUEST['server'],
222
                            'database' => $_REQUEST['database'],
223
                            'schema'   => $_REQUEST['schema'],
224
                        ],
225
                    ],
226
                ],
227
                'content' => $lang['strcreateviewwiz'],
228
            ],
229
        ];
230
        $this->printNavLinks($navlinks, $this->table_place, get_defined_vars());
231
    }
232
233
    /**
234
     * Generate XML for the browser tree.
235
     */
236
    public function doTree()
237
    {
238
        $conf = $this->conf;
239
        $misc = $this->misc;
240
        $lang = $this->lang;
241
        $data = $misc->getDatabaseAccessor();
242
243
        $matviews = $data->getMaterializedViews();
244
245
        $reqvars = $misc->getRequestVars('matview');
246
247
        $attrs = [
248
            'text'       => Decorator::field('relname'),
249
            'icon'       => 'MView',
250
            'iconAction' => Decorator::url('display.php', $reqvars, ['matview' => Decorator::field('relname')]),
251
            'toolTip'    => Decorator::field('relcomment'),
252
            'action'     => Decorator::redirecturl('redirect.php', $reqvars, ['matview' => Decorator::field('relname')]),
253
            'branch'     => Decorator::url('materializedviews.php', $reqvars, ['action' => 'subtree', 'matview' => Decorator::field('relname')]),
254
        ];
255
256
        return $this->printTree($matviews, $attrs, 'matviews');
257
    }
258
259
    public function doSubTree()
0 ignored issues
show
Coding Style introduced by
doSubTree uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
260
    {
261
        $conf = $this->conf;
262
        $misc = $this->misc;
263
        $lang = $this->lang;
264
        $data = $misc->getDatabaseAccessor();
265
266
        $tabs    = $misc->getNavTabs('matview');
267
        $items   = $this->adjustTabsForTree($tabs);
268
        $reqvars = $misc->getRequestVars('matview');
269
270
        $attrs = [
271
            'text'   => Decorator::field('title'),
272
            'icon'   => Decorator::field('icon'),
273
            'action' => Decorator::actionurl(Decorator::field('url'), $reqvars, Decorator::field('urlvars'), ['matview' => $_REQUEST['matview']]),
274
            'branch' => Decorator::ifempty(
275
                Decorator::field('branch'), '', Decorator::url(Decorator::field('url'), Decorator::field('urlvars'), $reqvars,
276
                    [
277
                        'action'  => 'tree',
278
                        'matview' => $_REQUEST['matview'],
279
                    ]
280
                )
281
            ),
282
        ];
283
284
        return $this->printTree($items, $attrs, 'matviews');
285
    }
286
287
    /**
288
     * Ask for select parameters and perform select
289
     */
290
    public function doSelectRows($confirm, $msg = '')
0 ignored issues
show
Coding Style introduced by
doSelectRows uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
doSelectRows uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
291
    {
292
        $conf = $this->conf;
293
        $misc = $this->misc;
294
        $lang = $this->lang;
295
        $data = $misc->getDatabaseAccessor();
296
297
        if ($confirm) {
298
            $this->printTrail('view');
299
            $this->printTabs('matview', 'select');
300
            $this->printMsg($msg);
301
302
            $attrs = $data->getTableAttributes($_REQUEST['matview']);
303
304
            echo '<form action="' . SUBFOLDER . '/src/views/' . $this->script . '" method="post" id="selectform">';
305
            echo "\n";
306
307
            if ($attrs->recordCount() > 0) {
308
                // JavaScript for select all feature
309
                echo "<script type=\"text/javascript\">\n";
310
                echo "//<![CDATA[\n";
311
                echo "	function selectAll() {\n";
312
                echo "		for (var i=0; i<document.getElementById('selectform').elements.length; i++) {\n";
313
                echo "			var e = document.getElementById('selectform').elements[i];\n";
314
                echo "			if (e.name.indexOf('show') == 0) { \n ";
315
                echo "				e.checked = document.getElementById('selectform').selectall.checked;\n";
316
                echo "			}\n";
317
                echo "		}\n";
318
                echo "	}\n";
319
                echo "//]]>\n";
320
                echo "</script>\n";
321
322
                echo "<table>\n";
323
324
                // Output table header
325
                echo "<tr><th class=\"data\">{$lang['strshow']}</th><th class=\"data\">{$lang['strcolumn']}</th>";
326
                echo "<th class=\"data\">{$lang['strtype']}</th><th class=\"data\">{$lang['stroperator']}</th>";
327
                echo "<th class=\"data\">{$lang['strvalue']}</th></tr>";
328
329
                $i = 0;
330
                while (!$attrs->EOF) {
331
                    $attrs->fields['attnotnull'] = $data->phpBool($attrs->fields['attnotnull']);
332
                    // Set up default value if there isn't one already
333
                    if (!isset($_REQUEST['values'][$attrs->fields['attname']])) {
334
                        $_REQUEST['values'][$attrs->fields['attname']] = null;
335
                    }
336
337
                    if (!isset($_REQUEST['ops'][$attrs->fields['attname']])) {
338
                        $_REQUEST['ops'][$attrs->fields['attname']] = null;
339
                    }
340
341
                    // Continue drawing row
342
                    $id = (($i % 2) == 0 ? '1' : '2');
343
                    echo "<tr class=\"data{$id}\">\n";
344
                    echo '<td style="white-space:nowrap;">';
345
                    echo '<input type="checkbox" name="show[', htmlspecialchars($attrs->fields['attname']), ']"',
346
                    isset($_REQUEST['show'][$attrs->fields['attname']]) ? ' checked="checked"' : '', ' /></td>';
347
                    echo '<td style="white-space:nowrap;">', $misc->printVal($attrs->fields['attname']), '</td>';
348
                    echo '<td style="white-space:nowrap;">', $misc->printVal($data->formatType($attrs->fields['type'], $attrs->fields['atttypmod'])), '</td>';
349
                    echo '<td style="white-space:nowrap;">';
350
                    echo "<select name=\"ops[{$attrs->fields['attname']}]\">\n";
351
                    foreach (array_keys($data->selectOps) as $v) {
352
                        echo '<option value="', htmlspecialchars($v), '"', ($v == $_REQUEST['ops'][$attrs->fields['attname']]) ? ' selected="selected"' : '',
353
                        '>', htmlspecialchars($v), "</option>\n";
354
                    }
355
                    echo "</select></td>\n";
356
                    echo '<td style="white-space:nowrap;">', $data->printField("values[{$attrs->fields['attname']}]",
357
                        $_REQUEST['values'][$attrs->fields['attname']], $attrs->fields['type']), '</td>';
358
                    echo "</tr>\n";
359
                    $i++;
360
                    $attrs->moveNext();
361
                }
362
                // Select all checkbox
363
                echo "<tr><td colspan=\"5\"><input type=\"checkbox\" id=\"selectall\" name=\"selectall\" accesskey=\"a\" onclick=\"javascript:selectAll()\" /><label for=\"selectall\">{$lang['strselectallfields']}</label></td></tr>";
364
                echo "</table>\n";
365
            } else {
366
                echo "<p>{$lang['strinvalidparam']}</p>\n";
367
            }
368
369
            echo "<p><input type=\"hidden\" name=\"action\" value=\"selectrows\" />\n";
370
            echo '<input type="hidden" name="view" value="', htmlspecialchars($_REQUEST['matview']), "\" />\n";
371
            echo "<input type=\"hidden\" name=\"subject\" value=\"view\" />\n";
372
            echo $misc->form;
373
            echo "<input type=\"submit\" name=\"select\" accesskey=\"r\" value=\"{$lang['strselect']}\" />\n";
374
            echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
375
            echo "</form>\n";
376
            return;
377
        } else {
378
            if (!isset($_POST['show'])) {
379
                $_POST['show'] = [];
380
            }
381
382
            if (!isset($_POST['values'])) {
383
                $_POST['values'] = [];
384
            }
385
386
            if (!isset($_POST['nulls'])) {
387
                $_POST['nulls'] = [];
388
            }
389
390
            // Verify that they haven't supplied a value for unary operators
391
            foreach ($_POST['ops'] as $k => $v) {
392
                if ($data->selectOps[$v] == 'p' && $_POST['values'][$k] != '') {
393
                    $this->doSelectRows(true, $lang['strselectunary']);
394
                    return;
395
                }
396
            }
397
398
            if (sizeof($_POST['show']) == 0) {
399
                return $this->doSelectRows(true, $lang['strselectneedscol']);
400
            } else {
401
                // Generate query SQL
402
                $query = $data->getSelectSQL($_REQUEST['matview'], array_keys($_POST['show']), $_POST['values'], $_POST['ops']);
403
404
                $_REQUEST['query']  = $query;
405
                $_REQUEST['return'] = 'schema';
406
407
                $this->setNoOutput(true);
408
409
                $display_controller = new DisplayController($this->getContainer());
410
411
                return $display_controller->render();
412
            }
413
        }
414
    }
415
416
    /**
417
     * Show confirmation of drop and perform actual drop
418
     */
419
    public function doDrop($confirm)
0 ignored issues
show
Coding Style introduced by
doDrop uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
doDrop uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
420
    {
421
        $conf = $this->conf;
422
        $misc = $this->misc;
423
        $lang = $this->lang;
424
        $data = $misc->getDatabaseAccessor();
425
426
        if (empty($_REQUEST['matview']) && empty($_REQUEST['ma'])) {
427
            $this->doDefault($lang['strspecifyviewtodrop']);
428
            exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method doDrop() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
429
        }
430
431
        if ($confirm) {
432
            $this->printTrail('getTrail');
433
            $this->printTitle($lang['strdrop'], 'pg.matview.drop');
434
435
            echo '<form action="' . SUBFOLDER . "/src/views/materializedviews.php\" method=\"post\">\n";
436
437
            //If multi drop
438
            if (isset($_REQUEST['ma'])) {
439
                foreach ($_REQUEST['ma'] as $v) {
440
                    $a = unserialize(htmlspecialchars_decode($v, ENT_QUOTES));
0 ignored issues
show
Security introduced by
htmlspecialchars_decode(...\Controller\ENT_QUOTES) can contain request data and is used in unserialized context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_REQUEST, and $_REQUEST['ma'] is assigned to $v
    in src/controllers/MaterializedviewsController.php on line 439
  2. Data is passed through htmlspecialchars_decode()
    in src/controllers/MaterializedviewsController.php on line 440

Preventing Object Injection Attacks

If you pass raw user-data to unserialize() for example, this can be used to create an object of any class that is available in your local filesystem. For an attacker, classes that have magic methods like __destruct or __wakeup are particularly interesting in such a case, as they can be exploited very easily.

We recommend to not pass user data to such a function. In case of unserialize, better use JSON to transfer data.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
441
                    echo '<p>', sprintf($lang['strconfdropview'], $misc->printVal($a['view'])), "</p>\n";
442
                    echo '<input type="hidden" name="view[]" value="', htmlspecialchars($a['view']), "\" />\n";
443
                }
444
            } else {
445
                echo '<p>', sprintf($lang['strconfdropview'], $misc->printVal($_REQUEST['matview'])), "</p>\n";
446
                echo '<input type="hidden" name="view" value="', htmlspecialchars($_REQUEST['matview']), "\" />\n";
447
            }
448
449
            echo "<input type=\"hidden\" name=\"action\" value=\"drop\" />\n";
450
451
            echo $misc->form;
452
            echo "<p><input type=\"checkbox\" id=\"cascade\" name=\"cascade\" /> <label for=\"cascade\">{$lang['strcascade']}</label></p>\n";
453
            echo "<input type=\"submit\" name=\"drop\" value=\"{$lang['strdrop']}\" />\n";
454
            echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
455
            echo "</form>\n";
456
        } else {
457
            if (is_array($_POST['view'])) {
458
                $msg    = '';
459
                $status = $data->beginTransaction();
460
                if ($status == 0) {
461
                    foreach ($_POST['view'] as $s) {
462
                        $status = $data->dropView($s, isset($_POST['cascade']));
463
                        if ($status == 0) {
464
                            $msg .= sprintf('%s: %s<br />', htmlentities($s, ENT_QUOTES, 'UTF-8'), $lang['strviewdropped']);
465
                        } else {
466
                            $data->endTransaction();
467
                            $this->doDefault(sprintf('%s%s: %s<br />', $msg, htmlentities($s, ENT_QUOTES, 'UTF-8'), $lang['strviewdroppedbad']));
468
                            return;
469
                        }
470
                    }
471
                }
472
                if ($data->endTransaction() == 0) {
473
                    // Everything went fine, back to the Default page....
474
                    $this->misc->setReloadBrowser(true);
475
                    $this->doDefault($msg);
476
                } else {
477
                    $this->doDefault($lang['strviewdroppedbad']);
478
                }
479
            } else {
480
                $status = $data->dropView($_POST['view'], isset($_POST['cascade']));
481
                if ($status == 0) {
482
                    $this->misc->setReloadBrowser(true);
483
                    $this->doDefault($lang['strviewdropped']);
484
                } else {
485
                    $this->doDefault($lang['strviewdroppedbad']);
486
                }
487
            }
488
        }
489
    }
490
491
    /**
492
     * Sets up choices for table linkage, and which fields to select for the view we're creating
493
     */
494
    public function doSetParamsCreate($msg = '')
0 ignored issues
show
Coding Style introduced by
doSetParamsCreate uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
doSetParamsCreate uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
495
    {
496
        $conf = $this->conf;
497
        $misc = $this->misc;
498
        $lang = $this->lang;
499
        $data = $misc->getDatabaseAccessor();
500
501
        // Check that they've chosen tables for the view definition
502
        if (!isset($_POST['formTables'])) {
503
            $this->doWizardCreate($lang['strviewneedsdef']);
504
        } else {
505
            // Initialise variables
506
            if (!isset($_REQUEST['formView'])) {
507
                $_REQUEST['formView'] = '';
508
            }
509
510
            if (!isset($_REQUEST['formComment'])) {
511
                $_REQUEST['formComment'] = '';
512
            }
513
514
            $this->printTrail('schema');
515
            $this->printTitle($lang['strcreateviewwiz'], 'pg.matview.create');
516
            $this->printMsg($msg);
517
518
            $tblCount = sizeof($_POST['formTables']);
519
            //unserialize our schema/table information and store in arrSelTables
520
            for ($i = 0; $i < $tblCount; $i++) {
521
                $arrSelTables[] = unserialize($_POST['formTables'][$i]);
0 ignored issues
show
Security introduced by
$_POST['formTables'][$i] can contain request data and is used in unserialized context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST
    in src/controllers/MaterializedviewsController.php on line 521

Preventing Object Injection Attacks

If you pass raw user-data to unserialize() for example, this can be used to create an object of any class that is available in your local filesystem. For an attacker, classes that have magic methods like __destruct or __wakeup are particularly interesting in such a case, as they can be exploited very easily.

We recommend to not pass user data to such a function. In case of unserialize, better use JSON to transfer data.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
522
            }
523
524
            $linkCount = $tblCount;
525
526
            //get linking keys
527
            $rsLinkKeys = $data->getLinkingKeys($arrSelTables);
528
            $linkCount  = $rsLinkKeys->recordCount() > $tblCount ? $rsLinkKeys->recordCount() : $tblCount;
529
530
            $arrFields = []; //array that will hold all our table/field names
531
532
            //if we have schemas we need to specify the correct schema for each table we're retrieiving
533
            //with getTableAttributes
534
            $curSchema = $data->_schema;
535
            for ($i = 0; $i < $tblCount; $i++) {
536
                if ($data->_schema != $arrSelTables[$i]['schemaname']) {
537
                    $data->setSchema($arrSelTables[$i]['schemaname']);
538
                }
539
540
                $attrs = $data->getTableAttributes($arrSelTables[$i]['tablename']);
541
                while (!$attrs->EOF) {
542
                    $arrFields["{$arrSelTables[$i]['schemaname']}.{$arrSelTables[$i]['tablename']}.{$attrs->fields['attname']}"] = serialize([
543
                        'schemaname' => $arrSelTables[$i]['schemaname'],
544
                        'tablename'  => $arrSelTables[$i]['tablename'],
545
                        'fieldname'  => $attrs->fields['attname']]
546
                    );
547
                    $attrs->moveNext();
548
                }
549
550
                $data->setSchema($curSchema);
551
            }
552
            asort($arrFields);
553
554
            echo '<form action="' . SUBFOLDER . "/src/views/materializedviews.php\" method=\"post\">\n";
555
            echo "<table>\n";
556
            echo "<tr><th class=\"data\">{$lang['strviewname']}</th></tr>";
557
            echo "<tr>\n<td class=\"data1\">\n";
558
            // View name
559
            echo '<input name="formView" value="', htmlspecialchars($_REQUEST['formView']), "\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" />\n";
560
            echo "</td>\n</tr>\n";
561
            echo "<tr><th class=\"data\">{$lang['strcomment']}</th></tr>";
562
            echo "<tr>\n<td class=\"data1\">\n";
563
            // View comments
564
            echo '<textarea name="formComment" rows="3" cols="32">',
565
            htmlspecialchars($_REQUEST['formComment']), "</textarea>\n";
566
            echo "</td>\n</tr>\n";
567
            echo "</table>\n";
568
569
            // Output selector for fields to be retrieved from view
570
            echo "<table>\n";
571
            echo "<tr><th class=\"data\">{$lang['strcolumns']}</th></tr>";
572
            echo "<tr>\n<td class=\"data1\">\n";
573
            echo \PHPPgAdmin\XHtml\HTMLController::printCombo($arrFields, 'formFields[]', false, '', true);
574
            echo "</td>\n</tr>";
575
            echo "<tr><td><input type=\"radio\" name=\"dblFldMeth\" id=\"dblFldMeth1\" value=\"rename\" /><label for=\"dblFldMeth1\">{$lang['strrenamedupfields']}</label>";
576
            echo "<br /><input type=\"radio\" name=\"dblFldMeth\" id=\"dblFldMeth2\" value=\"drop\" /><label for=\"dblFldMeth2\">{$lang['strdropdupfields']}</label>";
577
            echo "<br /><input type=\"radio\" name=\"dblFldMeth\" id=\"dblFldMeth3\" value=\"\" checked=\"checked\" /><label for=\"dblFldMeth3\">{$lang['strerrordupfields']}</label></td></tr></table><br />";
578
579
            // Output the Linking keys combo boxes
580
            echo "<table>\n";
581
            echo "<tr><th class=\"data\">{$lang['strviewlink']}</th></tr>";
582
            $rowClass = 'data1';
583
            for ($i = 0; $i < $linkCount; $i++) {
584
                // Initialise variables
585
                if (!isset($formLink[$i]['operator'])) {
586
                    $formLink[$i]['operator'] = 'INNER JOIN';
587
                }
588
589
                echo "<tr>\n<td class=\"$rowClass\">\n";
590
591
                if (!$rsLinkKeys->EOF) {
592
                    $curLeftLink  = htmlspecialchars(serialize(['schemaname' => $rsLinkKeys->fields['p_schema'], 'tablename' => $rsLinkKeys->fields['p_table'], 'fieldname' => $rsLinkKeys->fields['p_field']]));
593
                    $curRightLink = htmlspecialchars(serialize(['schemaname' => $rsLinkKeys->fields['f_schema'], 'tablename' => $rsLinkKeys->fields['f_table'], 'fieldname' => $rsLinkKeys->fields['f_field']]));
594
                    $rsLinkKeys->moveNext();
595
                } else {
596
                    $curLeftLink  = '';
597
                    $curRightLink = '';
598
                }
599
600
                echo \PHPPgAdmin\XHtml\HTMLController::printCombo($arrFields, "formLink[$i][leftlink]", true, $curLeftLink, false);
601
                echo \PHPPgAdmin\XHtml\HTMLController::printCombo($data->joinOps, "formLink[$i][operator]", true, $formLink[$i]['operator']);
602
                echo \PHPPgAdmin\XHtml\HTMLController::printCombo($arrFields, "formLink[$i][rightlink]", true, $curRightLink, false);
603
                echo "</td>\n</tr>\n";
604
                $rowClass = $rowClass == 'data1' ? 'data2' : 'data1';
605
            }
606
            echo "</table>\n<br />\n";
607
608
            // Build list of available operators (infix only)
609
            $arrOperators = [];
610
            foreach ($data->selectOps as $k => $v) {
611
                if ($v == 'i') {
612
                    $arrOperators[$k] = $k;
613
                }
614
            }
615
616
            // Output additional conditions, note that this portion of the wizard treats the right hand side as literal values
617
            //(not as database objects) so field names will be treated as strings, use the above linking keys section to perform joins
618
            echo "<table>\n";
619
            echo "<tr><th class=\"data\">{$lang['strviewconditions']}</th></tr>";
620
            $rowClass = 'data1';
621
            for ($i = 0; $i < $linkCount; $i++) {
622
                echo "<tr>\n<td class=\"$rowClass\">\n";
623
                echo \PHPPgAdmin\XHtml\HTMLController::printCombo($arrFields, "formCondition[$i][field]");
624
                echo \PHPPgAdmin\XHtml\HTMLController::printCombo($arrOperators, "formCondition[$i][operator]", false, false);
625
                echo "<input type=\"text\" name=\"formCondition[$i][txt]\" />\n";
626
                echo "</td>\n</tr>\n";
627
                $rowClass = $rowClass == 'data1' ? 'data2' : 'data1';
628
            }
629
            echo "</table>\n";
630
            echo "<p><input type=\"hidden\" name=\"action\" value=\"save_create_wiz\" />\n";
631
632
            foreach ($arrSelTables as $curTable) {
633
                echo '<input type="hidden" name="formTables[]" value="' . htmlspecialchars(serialize($curTable)) . "\" />\n";
634
            }
635
636
            echo $misc->form;
637
            echo "<input type=\"submit\" value=\"{$lang['strcreate']}\" />\n";
638
            echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
639
            echo "</form>\n";
640
        }
641
    }
642
643
    /**
644
     * Display a wizard where they can enter a new view
645
     */
646
    public function doWizardCreate($msg = '')
647
    {
648
        $conf = $this->conf;
649
        $misc = $this->misc;
650
        $lang = $this->lang;
651
        $data = $misc->getDatabaseAccessor();
652
653
        $tables = $data->getTables(true);
654
655
        $this->printTrail('schema');
656
        $this->printTitle($lang['strcreateviewwiz'], 'pg.matview.create');
657
        $this->printMsg($msg);
658
659
        echo '<form action="' . SUBFOLDER . "/src/views/materializedviews.php\" method=\"post\">\n";
660
        echo "<table>\n";
661
        echo "<tr><th class=\"data\">{$lang['strtables']}</th></tr>";
662
        echo "<tr>\n<td class=\"data1\">\n";
663
664
        $arrTables = [];
665
        while (!$tables->EOF) {
666
            $arrTmp                                                                   = [];
667
            $arrTmp['schemaname']                                                     = $tables->fields['nspname'];
668
            $arrTmp['tablename']                                                      = $tables->fields['relname'];
669
            $arrTables[$tables->fields['nspname'] . '.' . $tables->fields['relname']] = serialize($arrTmp);
670
            $tables->moveNext();
671
        }
672
        echo \PHPPgAdmin\XHtml\HTMLController::printCombo($arrTables, 'formTables[]', false, '', true);
673
674
        echo "</td>\n</tr>\n";
675
        echo "</table>\n";
676
        echo "<p><input type=\"hidden\" name=\"action\" value=\"set_params_create\" />\n";
677
        echo $misc->form;
678
        echo "<input type=\"submit\" value=\"{$lang['strnext']}\" />\n";
679
        echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
680
        echo "</form>\n";
681
    }
682
683
    /**
684
     * Displays a screen where they can enter a new view
685
     */
686
    public function doCreate($msg = '')
0 ignored issues
show
Coding Style introduced by
doCreate uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
doCreate uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
687
    {
688
        $conf = $this->conf;
689
        $misc = $this->misc;
690
        $lang = $this->lang;
691
        $data = $misc->getDatabaseAccessor();
692
693
        if (!isset($_REQUEST['formView'])) {
694
            $_REQUEST['formView'] = '';
695
        }
696
697
        if (!isset($_REQUEST['formDefinition'])) {
698
            if (isset($_SESSION['sqlquery'])) {
699
                $_REQUEST['formDefinition'] = $_SESSION['sqlquery'];
700
            } else {
701
                $_REQUEST['formDefinition'] = 'SELECT ';
702
            }
703
        }
704
        if (!isset($_REQUEST['formComment'])) {
705
            $_REQUEST['formComment'] = '';
706
        }
707
708
        $this->printTrail('schema');
709
        $this->printTitle($lang['strcreateview'], 'pg.matview.create');
710
        $this->printMsg($msg);
711
712
        echo '<form action="' . SUBFOLDER . "/src/views/materializedviews.php\" method=\"post\">\n";
713
        echo "<table style=\"width: 100%\">\n";
714
        echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strname']}</th>\n";
715
        echo "\t<td class=\"data1\"><input name=\"formView\" size=\"32\" maxlength=\"{$data->_maxNameLen}\" value=\"",
716
        htmlspecialchars($_REQUEST['formView']), "\" /></td>\n\t</tr>\n";
717
        echo "\t<tr>\n\t\t<th class=\"data left required\">{$lang['strdefinition']}</th>\n";
718
        echo "\t<td class=\"data1\"><textarea style=\"width:100%;\" rows=\"10\" cols=\"50\" name=\"formDefinition\">",
719
        htmlspecialchars($_REQUEST['formDefinition']), "</textarea></td>\n\t</tr>\n";
720
        echo "\t<tr>\n\t\t<th class=\"data left\">{$lang['strcomment']}</th>\n";
721
        echo "\t\t<td class=\"data1\"><textarea name=\"formComment\" rows=\"3\" cols=\"32\">",
722
        htmlspecialchars($_REQUEST['formComment']), "</textarea></td>\n\t</tr>\n";
723
        echo "</table>\n";
724
        echo "<p><input type=\"hidden\" name=\"action\" value=\"save_create\" />\n";
725
        echo $misc->form;
726
        echo "<input type=\"submit\" value=\"{$lang['strcreate']}\" />\n";
727
        echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" /></p>\n";
728
        echo "</form>\n";
729
    }
730
731
    /**
732
     * Actually creates the new view in the database
733
     */
734
    public function doSaveCreate()
0 ignored issues
show
Coding Style introduced by
doSaveCreate uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
735
    {
736
        $conf = $this->conf;
737
        $misc = $this->misc;
738
        $lang = $this->lang;
739
        $data = $misc->getDatabaseAccessor();
740
741
        // Check that they've given a name and a definition
742
        if ($_POST['formView'] == '') {
743
            $this->doCreate($lang['strviewneedsname']);
744
        } elseif ($_POST['formDefinition'] == '') {
745
            $this->doCreate($lang['strviewneedsdef']);
746
        } else {
747
            $status = $data->createView($_POST['formView'], $_POST['formDefinition'], false, $_POST['formComment']);
748
            if ($status == 0) {
749
                $this->misc->setReloadBrowser(true);
750
                $this->doDefault($lang['strviewcreated']);
751
            } else {
752
                $this->doCreate($lang['strviewcreatedbad']);
753
            }
754
        }
755
    }
756
757
    /**
758
     * Actually creates the new wizard view in the database
759
     */
760
    public function doSaveCreateWiz()
0 ignored issues
show
Coding Style introduced by
doSaveCreateWiz uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
761
    {
762
        $conf = $this->conf;
763
        $misc = $this->misc;
764
        $lang = $this->lang;
765
        $data = $misc->getDatabaseAccessor();
766
767
        // Check that they've given a name and fields they want to select
768
769
        if (!strlen($_POST['formView'])) {
770
            $this->doSetParamsCreate($lang['strviewneedsname']);
771
        } elseif (!isset($_POST['formFields']) || !count($_POST['formFields'])) {
772
            $this->doSetParamsCreate($lang['strviewneedsfields']);
773
        } else {
774
            $selFields = '';
775
776
            if (!empty($_POST['dblFldMeth'])) {
777
                $tmpHsh = [];
778
            }
779
780
            foreach ($_POST['formFields'] as $curField) {
781
                $arrTmp = unserialize($curField);
782
                $data->fieldArrayClean($arrTmp);
783
                if (!empty($_POST['dblFldMeth'])) {
784
                    // doublon control
785
                    if (empty($tmpHsh[$arrTmp['fieldname']])) {
786
                        // field does not exist
787
                        $selFields .= "\"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\", ";
788
                        $tmpHsh[$arrTmp['fieldname']] = 1;
789
                    } elseif ($_POST['dblFldMeth'] == 'rename') {
790
                        // field exist and must be renamed
791
                        $tmpHsh[$arrTmp['fieldname']]++;
792
                        $selFields .= "\"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\" AS \"{$arrTmp['schemaname']}_{$arrTmp['tablename']}_{$arrTmp['fieldname']}{$tmpHsh[$arrTmp['fieldname']]}\", ";
793
                    }
794
                    /* field already exist, just ignore this one */
795
                } else {
796
                    // no doublon control
797
                    $selFields .= "\"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\", ";
798
                }
799
            }
800
801
            $selFields = substr($selFields, 0, -2);
802
            unset($arrTmp, $tmpHsh);
803
            $linkFields = '';
804
805
            // If we have links, out put the JOIN ... ON statements
806
            if (is_array($_POST['formLink'])) {
807
                // Filter out invalid/blank entries for our links
808
                $arrLinks = [];
809
                foreach ($_POST['formLink'] as $curLink) {
810
                    if (strlen($curLink['leftlink']) && strlen($curLink['rightlink']) && strlen($curLink['operator'])) {
811
                        $arrLinks[] = $curLink;
812
                    }
813
                }
814
                // We must perform some magic to make sure that we have a valid join order
815
                $count       = sizeof($arrLinks);
816
                $arrJoined   = [];
817
                $arrUsedTbls = [];
818
819
                // If we have at least one join condition, output it
820
                if ($count > 0) {
821
                    $j = 0;
822
                    while ($j < $count) {
823
                        foreach ($arrLinks as $curLink) {
824
                            $arrLeftLink  = unserialize($curLink['leftlink']);
825
                            $arrRightLink = unserialize($curLink['rightlink']);
826
                            $data->fieldArrayClean($arrLeftLink);
827
                            $data->fieldArrayClean($arrRightLink);
828
829
                            $tbl1 = "\"{$arrLeftLink['schemaname']}\".\"{$arrLeftLink['tablename']}\"";
830
                            $tbl2 = "\"{$arrRightLink['schemaname']}\".\"{$arrRightLink['tablename']}\"";
831
832
                            if ((!in_array($curLink, $arrJoined) && in_array($tbl1, $arrUsedTbls)) || !count($arrJoined)) {
833
834
                                // Make sure for multi-column foreign keys that we use a table alias tables joined to more than once
835
                                // This can (and should be) more optimized for multi-column foreign keys
836
                                $adj_tbl2 = in_array($tbl2, $arrUsedTbls) ? "$tbl2 AS alias_ppa_" . mktime() : $tbl2;
837
838
                                $linkFields .= strlen($linkFields) ? "{$curLink['operator']} $adj_tbl2 ON (\"{$arrLeftLink['schemaname']}\".\"{$arrLeftLink['tablename']}\".\"{$arrLeftLink['fieldname']}\" = \"{$arrRightLink['schemaname']}\".\"{$arrRightLink['tablename']}\".\"{$arrRightLink['fieldname']}\") "
839
                                : "$tbl1 {$curLink['operator']} $adj_tbl2 ON (\"{$arrLeftLink['schemaname']}\".\"{$arrLeftLink['tablename']}\".\"{$arrLeftLink['fieldname']}\" = \"{$arrRightLink['schemaname']}\".\"{$arrRightLink['tablename']}\".\"{$arrRightLink['fieldname']}\") ";
840
841
                                $arrJoined[] = $curLink;
842
                                if (!in_array($tbl1, $arrUsedTbls)) {
843
                                    $arrUsedTbls[] = $tbl1;
844
                                }
845
846
                                if (!in_array($tbl2, $arrUsedTbls)) {
847
                                    $arrUsedTbls[] = $tbl2;
848
                                }
849
                            }
850
                        }
851
                        $j++;
852
                    }
853
                }
854
            }
855
856
            //if linkfields has no length then either _POST['formLink'] was not set, or there were no join conditions
857
            //just select from all seleted tables - a cartesian join do a
858
            if (!strlen($linkFields)) {
859
                foreach ($_POST['formTables'] as $curTable) {
860
                    $arrTmp = unserialize($curTable);
861
                    $data->fieldArrayClean($arrTmp);
862
                    $linkFields .= strlen($linkFields) ? ", \"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\"" : "\"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\"";
863
                }
864
            }
865
866
            $addConditions = '';
867
            if (is_array($_POST['formCondition'])) {
868
                foreach ($_POST['formCondition'] as $curCondition) {
869
                    if (strlen($curCondition['field']) && strlen($curCondition['txt'])) {
870
                        $arrTmp = unserialize($curCondition['field']);
871
                        $data->fieldArrayClean($arrTmp);
872
                        $addConditions .= strlen($addConditions) ? " AND \"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\" {$curCondition['operator']} '{$curCondition['txt']}' "
873
                        : " \"{$arrTmp['schemaname']}\".\"{$arrTmp['tablename']}\".\"{$arrTmp['fieldname']}\" {$curCondition['operator']} '{$curCondition['txt']}' ";
874
                    }
875
                }
876
            }
877
878
            $viewQuery = "SELECT $selFields FROM $linkFields ";
879
880
            //add where from additional conditions
881
            if (strlen($addConditions)) {
882
                $viewQuery .= ' WHERE ' . $addConditions;
883
            }
884
885
            $status = $data->createView($_POST['formView'], $viewQuery, false, $_POST['formComment']);
886
            if ($status == 0) {
887
                $this->misc->setReloadBrowser(true);
888
                $this->doDefault($lang['strviewcreated']);
889
            } else {
890
                $this->doSetParamsCreate($lang['strviewcreatedbad']);
891
            }
892
        }
893
    }
894
}
895