GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 0e9349...d2bdfd )
by Nicolas
08:54 queued 04:10
created

contentBlueprintsPages   F

Complexity

Total Complexity 131

Size/Duplication

Total Lines 959
Duplicated Lines 10.64 %

Coupling/Cohesion

Components 1
Dependencies 11
Metric Value
wmc 131
lcom 1
cbo 11
dl 102
loc 959
rs 3.9999

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __viewNew() 0 4 1
F __viewEdit() 30 303 42
A __compare_pages() 0 4 1
A __actionNew() 0 4 1
F __actionEdit() 62 276 41
B insertBreadcrumbsUsingPageIdentifier() 0 43 6
D __viewIndex() 10 149 20
B __actionIndex() 0 29 5
B __actionTemplate() 0 51 6
C __actionDelete() 0 81 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like contentBlueprintsPages often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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

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

1
<?php
2
3
/**
4
 * @package content
5
 */
6
7
/**
8
 * Developers can create new Frontend pages from this class. It provides
9
 * an index view of all the pages in this Symphony install as well as the
10
 * forms for the creation/editing of a Page
11
 */
12
13
class contentBlueprintsPages extends AdministrationPage
14
{
15
    public $_errors = array();
16
    protected $_hilights = array();
17
18
    public function insertBreadcrumbsUsingPageIdentifier($page_id, $preserve_last = true)
19
    {
20
        if ($page_id == 0) {
21
            return $this->insertBreadcrumbs(
22
                array(Widget::Anchor(__('Pages'), SYMPHONY_URL . '/blueprints/pages/'))
23
            );
24
        }
25
26
        $pages = PageManager::resolvePage($page_id, 'handle');
27
28
        foreach ($pages as &$page) {
29
            // If we are viewing the Page Editor, the Breadcrumbs should link
30
            // to the parent's Page Editor.
31
            if ($this->_context[0] == 'edit') {
32
                $page = Widget::Anchor(
33
                    PageManager::fetchTitleFromHandle($page),
34
                    SYMPHONY_URL . '/blueprints/pages/edit/' . PageManager::fetchIDFromHandle($page) . '/'
35
                );
36
37
                // If the pages index is nested, the Breadcrumb should link to the
38
                // Pages Index filtered by parent
39
            } elseif (Symphony::Configuration()->get('pages_table_nest_children', 'symphony') == 'yes') {
40
                $page = Widget::Anchor(
41
                    PageManager::fetchTitleFromHandle($page),
42
                    SYMPHONY_URL . '/blueprints/pages/?parent=' . PageManager::fetchIDFromHandle($page)
43
                );
44
45
                // If there is no nesting on the Pages Index, the breadcrumb is
46
                // not a link, just plain text
47
            } else {
48
                $page = new XMLElement('span', PageManager::fetchTitleFromHandle($page));
49
            }
50
        }
51
52
        if (!$preserve_last) {
53
            array_pop($pages);
54
        }
55
56
        $this->insertBreadcrumbs(array_merge(
57
            array(Widget::Anchor(__('Pages'), SYMPHONY_URL . '/blueprints/pages/')),
58
            $pages
59
        ));
60
    }
61
62
    public function __viewIndex()
63
    {
64
        $this->setPageType('table');
65
        $this->setTitle(__('%1$s &ndash; %2$s', array(__('Pages'), __('Symphony'))));
66
67
        $nesting = Symphony::Configuration()->get('pages_table_nest_children', 'symphony') == 'yes';
68
69
        if ($nesting && isset($_GET['parent']) && is_numeric($_GET['parent'])) {
70
            $parent = PageManager::fetchPageByID((int)$_GET['parent'], array('title', 'id'));
71
        }
72
73
        $this->appendSubheading(isset($parent) ? $parent['title'] : __('Pages'), Widget::Anchor(
74
            __('Create New'), Administration::instance()->getCurrentPageURL() . 'new/' . ($nesting && isset($parent) ? "?parent={$parent['id']}" : null),
75
            __('Create a new page'), 'create button', null, array('accesskey' => 'c')
76
        ));
77
78
        if (isset($parent)) {
79
            $this->insertBreadcrumbsUsingPageIdentifier($parent['id'], false);
80
        }
81
82
        $aTableHead = array(
83
            array(__('Name'), 'col'),
84
            array(__('Template'), 'col'),
85
            array('<abbr title="' . __('Universal Resource Locator') . '">' . __('URL') . '</abbr>', 'col'),
86
            array(__('Parameters'), 'col'),
87
            array(__('Type'), 'col')
88
        );
89
        $aTableBody = array();
90
91
        if ($nesting) {
92
            $aTableHead[] = array(__('Children'), 'col');
93
            $where = array(
94
                'parent ' . (isset($parent) ? " = {$parent['id']} " : ' IS NULL ')
95
            );
96
        } else {
97
            $where = array();
98
        }
99
100
        $pages = PageManager::fetch(true, array('*'), $where);
101
102
        if (!is_array($pages) || empty($pages)) {
103
            $aTableBody = array(Widget::TableRow(array(
104
                Widget::TableData(__('None found.'), 'inactive', null, count($aTableHead))
105
            ), 'odd'));
106
        } else {
107
            foreach ($pages as $page) {
108
                $class = array();
109
110
                $page_title = ($nesting ? $page['title'] : PageManager::resolvePageTitle($page['id']));
111
                $page_url = URL . '/' . PageManager::resolvePagePath($page['id']) . '/';
112
                $page_edit_url = Administration::instance()->getCurrentPageURL() . 'edit/' . $page['id'] . '/';
113
                $page_template = PageManager::createFilePath($page['path'], $page['handle']);
114
115
                $col_title = Widget::TableData(Widget::Anchor($page_title, $page_edit_url, $page['handle']));
116
                $col_title->appendChild(Widget::Label(__('Select Page %s', array($page_title)), null, 'accessible', null, array(
117
                    'for' => 'page-' . $page['id']
118
                )));
119
                $col_title->appendChild(Widget::Input('items['.$page['id'].']', 'on', 'checkbox', array(
120
                    'id' => 'page-' . $page['id']
121
                )));
122
123
                $col_template = Widget::TableData($page_template . '.xsl');
124
125
                $col_url = Widget::TableData(Widget::Anchor($page_url, $page_url));
126
127 View Code Duplication
                if ($page['params']) {
128
                    $col_params = Widget::TableData(trim($page['params'], '/'));
129
                } else {
130
                    $col_params = Widget::TableData(__('None'), 'inactive');
131
                }
132
133 View Code Duplication
                if (!empty($page['type'])) {
134
                    $col_types = Widget::TableData(implode(', ', $page['type']));
135
                } else {
136
                    $col_types = Widget::TableData(__('None'), 'inactive');
137
                }
138
139
                if (in_array($page['id'], $this->_hilights)) {
140
                    $class[] = 'failed';
141
                }
142
143
                $columns = array($col_title, $col_template, $col_url, $col_params, $col_types);
144
145
                if ($nesting) {
146
                    if (PageManager::hasChildPages($page['id'])) {
147
                        $col_children = Widget::TableData(
148
                            Widget::Anchor(PageManager::getChildPagesCount($page['id']) . ' &rarr;',
149
                            SYMPHONY_URL . '/blueprints/pages/?parent=' . $page['id'])
150
                        );
151
                    } else {
152
                        $col_children = Widget::TableData(__('None'), 'inactive');
153
                    }
154
155
                    $columns[] = $col_children;
156
                }
157
158
                $aTableBody[] = Widget::TableRow(
159
                    $columns,
160
                    implode(' ', $class)
161
                );
162
            }
163
        }
164
165
        $table = Widget::Table(
166
            Widget::TableHead($aTableHead), null,
167
            Widget::TableBody($aTableBody), 'orderable selectable',
168
            null, array('role' => 'directory', 'aria-labelledby' => 'symphony-subheading', 'data-interactive' => 'data-interactive')
169
        );
170
171
        $this->Form->appendChild($table);
172
173
        $version = new XMLElement('p', 'Symphony ' . Symphony::Configuration()->get('version', 'symphony'), array(
174
            'id' => 'version'
175
        ));
176
        $this->Form->appendChild($version);
177
178
        $tableActions = new XMLElement('div');
179
        $tableActions->setAttribute('class', 'actions');
180
181
        $options = array(
182
            array(null, false, __('With Selected...')),
183
            array('delete', false, __('Delete'), 'confirm', null, array(
184
                'data-message' => __('Are you sure you want to delete the selected pages?')
185
            ))
186
        );
187
188
        /**
189
         * Allows an extension to modify the existing options for this page's
190
         * With Selected menu. If the `$options` parameter is an empty array,
191
         * the 'With Selected' menu will not be rendered.
192
         *
193
         * @delegate AddCustomActions
194
         * @since Symphony 2.3.2
195
         * @param string $context
196
         * '/blueprints/pages/'
197
         * @param array $options
198
         *  An array of arrays, where each child array represents an option
199
         *  in the With Selected menu. Options should follow the same format
200
         *  expected by `Widget::__SelectBuildOption`. Passed by reference.
201
         */
202
        Symphony::ExtensionManager()->notifyMembers('AddCustomActions', '/blueprints/pages/', array(
203
            'options' => &$options
204
        ));
205
206
        if (!empty($options)) {
207
            $tableActions->appendChild(Widget::Apply($options));
208
            $this->Form->appendChild($tableActions);
209
        }
210
    }
211
212
    public function __viewNew()
213
    {
214
        $this->__viewEdit();
215
    }
216
217
    public function __viewEdit()
0 ignored issues
show
Coding Style introduced by
__viewEdit 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...
218
    {
219
        $this->setPageType('form');
220
        $fields = array("title"=>null, "handle"=>null, "parent"=>null, "params"=>null, "type"=>null, "data_sources"=>null);
221
        $existing = $fields;
222
223
        $nesting = (Symphony::Configuration()->get('pages_table_nest_children', 'symphony') == 'yes');
224
225
        // Verify page exists:
226
        if ($this->_context[0] === 'edit') {
227
            if (!$page_id = (int)$this->_context[1]) {
228
                redirect(SYMPHONY_URL . '/blueprints/pages/');
229
            }
230
231
            $existing = PageManager::fetchPageByID($page_id);
232
233
            if (!$existing) {
234
                Administration::instance()->errorPageNotFound();
235
            } else {
236
                $existing['type'] = PageManager::fetchPageTypes($page_id);
237
            }
238
        }
239
240
        // Status message:
241
        if (isset($this->_context[2])) {
242
            $flag = $this->_context[2];
243
            $parent_link_suffix = $message = '';
244
            $time = Widget::Time();
245
246
            if (isset($_REQUEST['parent']) && is_numeric($_REQUEST['parent'])) {
247
                $parent_link_suffix = "?parent=" . $_REQUEST['parent'];
248
            } elseif ($nesting && isset($existing) && !is_null($existing['parent'])) {
249
                $parent_link_suffix = '?parent=' . $existing['parent'];
250
            }
251
252
            switch ($flag) {
253
                case 'saved':
254
                    $message = __('Page updated at %s.', array($time->generate()));
255
                    break;
256
                case 'created':
257
                    $message = __('Page created at %s.', array($time->generate()));
258
            }
259
260
            $this->pageAlert(
261
                $message
262
                . ' <a href="' . SYMPHONY_URL . '/blueprints/pages/new/' . $parent_link_suffix . '" accesskey="c">'
263
                . __('Create another?')
264
                . '</a> <a href="' . SYMPHONY_URL . '/blueprints/pages/" accesskey="a">'
265
                . __('View all Pages')
266
                . '</a>',
267
                Alert::SUCCESS
268
            );
269
        }
270
271
        // Find values:
272
        if (isset($_POST['fields'])) {
273
            $fields = $_POST['fields'];
274
        } elseif ($this->_context[0] == 'edit') {
275
            $fields = $existing;
276
277
            if (!is_null($fields['type'])) {
278
                $fields['type'] = implode(', ', $fields['type']);
279
            }
280
281
            $fields['data_sources'] = preg_split('/,/i', $fields['data_sources'], -1, PREG_SPLIT_NO_EMPTY);
282
            $fields['events'] = preg_split('/,/i', $fields['events'], -1, PREG_SPLIT_NO_EMPTY);
283 View Code Duplication
        } elseif (isset($_REQUEST['parent']) && is_numeric($_REQUEST['parent'])) {
284
            $fields['parent'] = $_REQUEST['parent'];
285
        }
286
287
        $title = $fields['title'];
288
289
        if (trim($title) == '') {
290
            $title = $existing['title'];
291
        }
292
293
        $this->setTitle(__(
294
            ($title ? '%1$s &ndash; %2$s &ndash; %3$s' : '%2$s &ndash; %3$s'),
295
            array(
296
                $title,
297
                __('Pages'),
298
                __('Symphony')
299
            )
300
        ));
301
302
        $page_id = isset($page_id) ? $page_id : null;
303
304
        if (!empty($title)) {
305
            $page_url = URL . '/' . PageManager::resolvePagePath($page_id) . '/';
306
307
            $this->appendSubheading($title, array(
308
                Widget::Anchor(__('View Page'), $page_url, __('View Page on Frontend'), 'button', null, array('target' => '_blank', 'accesskey' => 'v'))
309
            ));
310
        } else {
311
            $this->appendSubheading(!empty($title) ? $title : __('Untitled'));
312
        }
313
314
        if (isset($page_id)) {
315
            $this->insertBreadcrumbsUsingPageIdentifier($page_id, false);
316
        } else {
317
            $_GET['parent'] = isset($_GET['parent']) ? $_GET['parent'] : null;
318
            $this->insertBreadcrumbsUsingPageIdentifier((int)$_GET['parent'], true);
319
        }
320
321
        // Title --------------------------------------------------------------
322
323
        $fieldset = new XMLElement('fieldset');
324
        $fieldset->setAttribute('class', 'settings');
325
        $fieldset->appendChild(new XMLElement('legend', __('Page Settings')));
326
327
        $label = Widget::Label(__('Name'));
328
        $label->appendChild(Widget::Input(
329
            'fields[title]', General::sanitize($fields['title'])
330
        ));
331
332
        if (isset($this->_errors['title'])) {
333
            $label = Widget::Error($label, $this->_errors['title']);
334
        }
335
336
        $fieldset->appendChild($label);
337
338
        // Handle -------------------------------------------------------------
339
340
        $group = new XMLElement('div');
341
        $group->setAttribute('class', 'two columns');
342
        $column = new XMLElement('div');
343
        $column->setAttribute('class', 'column');
344
345
        $label = Widget::Label(__('Handle'));
346
        $label->appendChild(Widget::Input(
347
            'fields[handle]', $fields['handle']
348
        ));
349
350
        if (isset($this->_errors['handle'])) {
351
            $label = Widget::Error($label, $this->_errors['handle']);
352
        }
353
354
        $column->appendChild($label);
355
356
        // Parent ---------------------------------------------------------
357
358
        $label = Widget::Label(__('Parent Page'));
359
360
        $where = array(
361
            sprintf('id != %d', $page_id)
362
        );
363
        $pages = PageManager::fetch(false, array('id'), $where, 'title ASC');
364
365
        $options = array(
366
            array('', false, '/')
367
        );
368
369
        if (!empty($pages)) {
370
            foreach ($pages as $page) {
371
                $options[] = array(
372
                    $page['id'], $fields['parent'] == $page['id'],
373
                    '/' . PageManager::resolvePagePath($page['id'])
374
                );
375
            }
376
377
            usort($options, array($this, '__compare_pages'));
378
        }
379
380
        $label->appendChild(Widget::Select(
381
            'fields[parent]', $options
382
        ));
383
        $column->appendChild($label);
384
        $group->appendChild($column);
385
386
        // Parameters ---------------------------------------------------------
387
388
        $column = new XMLElement('div');
389
        $column->setAttribute('class', 'column');
390
391
        $label = Widget::Label(__('Parameters'));
392
        $label->appendChild(Widget::Input(
393
            'fields[params]', $fields['params'], 'text', array('placeholder' => 'param1/param2')
394
        ));
395
        $column->appendChild($label);
396
397
        // Type -----------------------------------------------------------
398
399
        $label = Widget::Label(__('Type'));
400
        $label->appendChild(Widget::Input('fields[type]', $fields['type']));
401
402
        if (isset($this->_errors['type'])) {
403
            $label = Widget::Error($label, $this->_errors['type']);
404
        }
405
406
        $column->appendChild($label);
407
408
        $tags = new XMLElement('ul');
409
        $tags->setAttribute('class', 'tags');
410
        $tags->setAttribute('data-interactive', 'data-interactive');
411
412
        $types = PageManager::fetchAvailablePageTypes();
413
414
        foreach ($types as $type) {
415
            $tags->appendChild(new XMLElement('li', $type));
416
        }
417
418
        $column->appendChild($tags);
419
        $group->appendChild($column);
420
        $fieldset->appendChild($group);
421
        $this->Form->appendChild($fieldset);
422
423
        // Events -------------------------------------------------------------
424
425
        $fieldset = new XMLElement('fieldset');
426
        $fieldset->setAttribute('class', 'settings');
427
        $fieldset->appendChild(new XMLElement('legend', __('Page Resources')));
428
429
        $group = new XMLElement('div');
430
        $group->setAttribute('class', 'two columns');
431
432
        $label = Widget::Label(__('Events'));
433
        $label->setAttribute('class', 'column');
434
435
        $events = ResourceManager::fetch(ResourceManager::RESOURCE_TYPE_EVENT, array(), array(), 'name ASC');
436
        $options = array();
437
438 View Code Duplication
        if (is_array($events) && !empty($events)) {
439
            if (!isset($fields['events'])) {
440
                $fields['events'] = array();
441
            }
442
443
            foreach ($events as $name => $about) {
444
                $options[] = array(
445
                    $name, in_array($name, $fields['events']), $about['name']
446
                );
447
            }
448
        }
449
450
        $label->appendChild(Widget::Select('fields[events][]', $options, array('multiple' => 'multiple')));
451
        $group->appendChild($label);
452
453
        // Data Sources -------------------------------------------------------
454
455
        $label = Widget::Label(__('Data Sources'));
456
        $label->setAttribute('class', 'column');
457
458
        $datasources = ResourceManager::fetch(ResourceManager::RESOURCE_TYPE_DS, array(), array(), 'name ASC');
459
        $options = array();
460
461 View Code Duplication
        if (is_array($datasources) && !empty($datasources)) {
462
            if (!isset($fields['data_sources'])) {
463
                $fields['data_sources'] = array();
464
            }
465
466
            foreach ($datasources as $name => $about) {
467
                $options[] = array(
468
                    $name, in_array($name, $fields['data_sources']), $about['name']
469
                );
470
            }
471
        }
472
473
        $label->appendChild(Widget::Select('fields[data_sources][]', $options, array('multiple' => 'multiple')));
474
        $group->appendChild($label);
475
        $fieldset->appendChild($group);
476
        $this->Form->appendChild($fieldset);
477
478
        // Controls -----------------------------------------------------------
479
480
        /**
481
         * After all Page related Fields have been added to the DOM, just before the
482
         * actions.
483
         *
484
         * @delegate AppendPageContent
485
         * @param string $context
486
         *  '/blueprints/pages/'
487
         * @param XMLElement $form
488
         * @param array $fields
489
         * @param array $errors
490
         */
491
        Symphony::ExtensionManager()->notifyMembers(
492
            'AppendPageContent',
493
            '/blueprints/pages/',
494
            array(
495
                'form'        => &$this->Form,
496
                'fields'    => &$fields,
497
                'errors'    => $this->_errors
498
            )
499
        );
500
501
        $div = new XMLElement('div');
502
        $div->setAttribute('class', 'actions');
503
        $div->appendChild(Widget::Input(
504
            'action[save]', ($this->_context[0] == 'edit' ? __('Save Changes') : __('Create Page')),
505
            'submit', array('accesskey' => 's')
506
        ));
507
508 View Code Duplication
        if ($this->_context[0] == 'edit') {
509
            $button = new XMLElement('button', __('Delete'));
510
            $button->setAttributeArray(array('name' => 'action[delete]', 'class' => 'button confirm delete', 'title' => __('Delete this page'), 'accesskey' => 'd', 'data-message' => __('Are you sure you want to delete this page?')));
511
            $div->appendChild($button);
512
        }
513
514
        $this->Form->appendChild($div);
515
516
        if (isset($_REQUEST['parent']) && is_numeric($_REQUEST['parent'])) {
517
            $this->Form->appendChild(new XMLElement('input', null, array('type' => 'hidden', 'name' => 'parent', 'value' => $_REQUEST['parent'])));
518
        }
519
    }
520
521
    public function __compare_pages($a, $b)
522
    {
523
        return strnatcasecmp($a[2], $b[2]);
524
    }
525
526
    public function __actionIndex()
0 ignored issues
show
Coding Style introduced by
__actionIndex 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...
527
    {
528
        $checked = (is_array($_POST['items'])) ? array_keys($_POST['items']) : null;
529
530
        if (is_array($checked) && !empty($checked)) {
531
            /**
532
             * Extensions can listen for any custom actions that were added
533
             * through `AddCustomPreferenceFieldsets` or `AddCustomActions`
534
             * delegates.
535
             *
536
             * @delegate CustomActions
537
             * @since Symphony 2.3.2
538
             * @param string $context
539
             *  '/blueprints/pages/'
540
             * @param array $checked
541
             *  An array of the selected rows. The value is usually the ID of the
542
             *  the associated object.
543
             */
544
            Symphony::ExtensionManager()->notifyMembers('CustomActions', '/blueprints/pages/', array(
545
                'checked' => $checked
546
            ));
547
548
            switch ($_POST['with-selected']) {
549
                case 'delete':
550
                    $this->__actionDelete($checked, SYMPHONY_URL . '/blueprints/pages/');
551
                    break;
552
            }
553
        }
554
    }
555
556
    public function __actionTemplate()
0 ignored issues
show
Coding Style introduced by
__actionTemplate 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...
557
    {
558
        $filename = $this->_context[1] . '.xsl';
559
        $file_abs = PAGES . '/' . $filename;
560
        $fields = $_POST['fields'];
561
        $this->_errors = array();
562
563
        if (!isset($fields['body']) || trim($fields['body']) == '') {
564
            $this->_errors['body'] = __('This is a required field.');
565
        } elseif (!General::validateXML($fields['body'], $errors, false, new XSLTProcess())) {
566
            $this->_errors['body'] = __('This document is not well formed.') . ' ' . __('The following error was returned:') . ' <code>' . $errors[0]['message'] . '</code>';
567
        }
568
569
        if (empty($this->_errors)) {
570
            /**
571
             * Just before a Page Template is about to written to disk
572
             *
573
             * @delegate PageTemplatePreEdit
574
             * @since Symphony 2.2.2
575
             * @param string $context
576
             * '/blueprints/pages/template/'
577
             * @param string $file
578
             *  The path to the Page Template file
579
             * @param string $contents
580
             *  The contents of the `$fields['body']`, passed by reference
581
             */
582
            Symphony::ExtensionManager()->notifyMembers('PageTemplatePreEdit', '/blueprints/pages/template/', array('file' => $file_abs, 'contents' => &$fields['body']));
583
584
            if (!PageManager::writePageFiles($file_abs, $fields['body'])) {
585
                $this->pageAlert(
586
                    __('Page Template could not be written to disk.')
587
                    . ' ' . __('Please check permissions on %s.', array('<code>/workspace/pages</code>')),
588
                    Alert::ERROR
589
                );
590
            } else {
591
                /**
592
                 * Just after a Page Template has been edited and written to disk
593
                 *
594
                 * @delegate PageTemplatePostEdit
595
                 * @since Symphony 2.2.2
596
                 * @param string $context
597
                 * '/blueprints/pages/template/'
598
                 * @param string $file
599
                 *  The path to the Page Template file
600
                 */
601
                Symphony::ExtensionManager()->notifyMembers('PageTemplatePostEdit', '/blueprints/pages/template/', array('file' => $file_abs));
602
603
                redirect(SYMPHONY_URL . '/blueprints/pages/template/' . $this->_context[1] . '/saved/');
604
            }
605
        }
606
    }
607
608
    public function __actionNew()
609
    {
610
        $this->__actionEdit();
611
    }
612
613
    public function __actionEdit()
0 ignored issues
show
Coding Style introduced by
__actionEdit 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...
614
    {
615
        if ($this->_context[0] != 'new' && !$page_id = (integer)$this->_context[1]) {
616
            redirect(SYMPHONY_URL . '/blueprints/pages/');
617
        }
618
619
        $parent_link_suffix = null;
620
621 View Code Duplication
        if (isset($_REQUEST['parent']) && is_numeric($_REQUEST['parent'])) {
622
            $parent_link_suffix = '?parent=' . $_REQUEST['parent'];
623
        }
624
625
        if (@array_key_exists('delete', $_POST['action'])) {
626
            $this->__actionDelete($page_id, SYMPHONY_URL  . '/blueprints/pages/' . $parent_link_suffix);
0 ignored issues
show
Bug introduced by
The variable $page_id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
627
        }
628
629
        if (@array_key_exists('save', $_POST['action'])) {
630
            $fields = $_POST['fields'];
631
            $this->_errors = array();
632
            $autogenerated_handle = false;
0 ignored issues
show
Unused Code introduced by
$autogenerated_handle is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
633
634 View Code Duplication
            if (!isset($fields['title']) || trim($fields['title']) == '') {
635
                $this->_errors['title'] = __('This is a required field');
636
            }
637
638
            if (trim($fields['type']) != '' && preg_match('/(index|404|403)/i', $fields['type'])) {
639
                $types = preg_split('/\s*,\s*/', strtolower($fields['type']), -1, PREG_SPLIT_NO_EMPTY);
640
641
                if (in_array('index', $types) && PageManager::hasPageTypeBeenUsed($page_id, 'index')) {
642
                    $this->_errors['type'] = __('An index type page already exists.');
643
                } elseif (in_array('404', $types) && PageManager::hasPageTypeBeenUsed($page_id, '404')) {
644
                    $this->_errors['type'] = __('A 404 type page already exists.');
645
                } elseif (in_array('403', $types) && PageManager::hasPageTypeBeenUsed($page_id, '403')) {
646
                    $this->_errors['type'] = __('A 403 type page already exists.');
647
                }
648
            }
649
650
            if (trim($fields['handle']) == '') {
651
                $fields['handle'] = $fields['title'];
652
                $autogenerated_handle = true;
0 ignored issues
show
Unused Code introduced by
$autogenerated_handle is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
653
            }
654
655
            $fields['handle'] = PageManager::createHandle($fields['handle']);
656
657
            if (empty($fields['handle']) && !isset($this->_errors['title'])) {
658
                $this->_errors['handle'] = __('Please ensure handle contains at least one Latin-based character.');
659
            }
660
661
            /**
662
             * Just after the Symphony validation has run, allows Developers
663
             * to run custom validation logic on a Page
664
             *
665
             * @delegate PagePostValidate
666
             * @since Symphony 2.2
667
             * @param string $context
668
             * '/blueprints/pages/'
669
             * @param array $fields
670
             *  The `$_POST['fields']` array. This should be read-only and not changed
671
             *  through this delegate.
672
             * @param array $errors
673
             *  An associative array of errors, with the key matching a key in the
674
             *  `$fields` array, and the value being the string of the error. `$errors`
675
             *  is passed by reference.
676
             */
677
            Symphony::ExtensionManager()->notifyMembers('PagePostValidate', '/blueprints/pages/', array('fields' => $fields, 'errors' => &$errors));
0 ignored issues
show
Bug introduced by
The variable $errors does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
678
679
            if (empty($this->_errors)) {
680
                $autogenerated_handle = false;
681
682
                if ($fields['params']) {
683
                    $fields['params'] = trim(preg_replace('@\/{2,}@', '/', $fields['params']), '/');
684
                }
685
686
                // Clean up type list
687
                $types = preg_split('/\s*,\s*/', $fields['type'], -1, PREG_SPLIT_NO_EMPTY);
688
                $types = @array_map('trim', $types);
689
                unset($fields['type']);
690
691
                $fields['parent'] = ($fields['parent'] != __('None') ? $fields['parent'] : null);
692
                $fields['data_sources'] = is_array($fields['data_sources']) ? implode(',', $fields['data_sources']) : null;
693
                $fields['events'] = is_array($fields['events']) ? implode(',', $fields['events']) : null;
694
                $fields['path'] = null;
695
696
                if ($fields['parent']) {
697
                    $fields['path'] = PageManager::resolvePagePath((integer)$fields['parent']);
698
                }
699
700
                // Check for duplicates:
701
                $current = PageManager::fetchPageByID($page_id);
702
703
                if (empty($current)) {
704
                    $fields['sortorder'] = PageManager::fetchNextSortOrder();
705
                }
706
707
                $where = array();
708
709
                if (!empty($current)) {
710
                    $where[] = "p.id != {$page_id}";
711
                }
712
713
                $where[] = "p.handle = '" . $fields['handle'] . "'";
714
                $where[] = (is_null($fields['path']))
715
                    ? "p.path IS null"
716
                    : "p.path = '" . $fields['path'] . "'";
717
                $duplicate = PageManager::fetch(false, array('*'), $where);
718
719
                // If duplicate
720
                if (!empty($duplicate)) {
721
                    if ($autogenerated_handle) {
722
                        $this->_errors['title'] = __('A page with that title already exists');
723
                    } else {
724
                        $this->_errors['handle'] = __('A page with that handle already exists');
725
                    }
726
727
                    // Create or move files:
728
                } else {
729
                    // New page?
730
                    if (empty($current)) {
731
                        $file_created = PageManager::createPageFiles(
732
                            $fields['path'],
733
                            $fields['handle']
734
                        );
735
736
                        // Existing page, potentially rename files
737
                    } else {
738
                        $file_created = PageManager::createPageFiles(
739
                            $fields['path'],
740
                            $fields['handle'],
741
                            $current['path'],
742
                            $current['handle']
743
                        );
744
                    }
745
746
                    // If the file wasn't created, it's usually permissions related
747
                    if (!$file_created) {
748
                        $redirect = null;
0 ignored issues
show
Unused Code introduced by
$redirect is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
749
                        return $this->pageAlert(
750
                            __('Page Template could not be written to disk.')
751
                            . ' ' . __('Please check permissions on %s.', array('<code>/workspace/pages</code>')),
752
                            Alert::ERROR
753
                        );
754
                    }
755
756
                    // Insert the new data:
757
                    if (empty($current)) {
758
                        /**
759
                         * Just prior to creating a new Page record in `tbl_pages`, provided
760
                         * with the `$fields` associative array. Use with caution, as no
761
                         * duplicate page checks are run after this delegate has fired
762
                         *
763
                         * @delegate PagePreCreate
764
                         * @since Symphony 2.2
765
                         * @param string $context
766
                         * '/blueprints/pages/'
767
                         * @param array $fields
768
                         *  The `$_POST['fields']` array passed by reference
769
                         */
770
                        Symphony::ExtensionManager()->notifyMembers('PagePreCreate', '/blueprints/pages/', array('fields' => &$fields));
771
772 View Code Duplication
                        if (!$page_id = PageManager::add($fields)) {
773
                            $this->pageAlert(
774
                                __('Unknown errors occurred while attempting to save.')
775
                                . '<a href="' . SYMPHONY_URL . '/system/log/">'
776
                                . __('Check your activity log')
777
                                . '</a>.',
778
                                Alert::ERROR
779
                            );
780
                        } else {
781
                            /**
782
                             * Just after the creation of a new page in `tbl_pages`
783
                             *
784
                             * @delegate PagePostCreate
785
                             * @since Symphony 2.2
786
                             * @param string $context
787
                             * '/blueprints/pages/'
788
                             * @param integer $page_id
789
                             *  The ID of the newly created Page
790
                             * @param array $fields
791
                             *  An associative array of data that was just saved for this page
792
                             */
793
                            Symphony::ExtensionManager()->notifyMembers('PagePostCreate', '/blueprints/pages/', array('page_id' => $page_id, 'fields' => &$fields));
794
795
                            $redirect = "/blueprints/pages/edit/{$page_id}/created/{$parent_link_suffix}";
796
                        }
797
798
                        // Update existing:
799
                    } else {
800
                        /**
801
                         * Just prior to updating a Page record in `tbl_pages`, provided
802
                         * with the `$fields` associative array. Use with caution, as no
803
                         * duplicate page checks are run after this delegate has fired
804
                         *
805
                         * @delegate PagePreEdit
806
                         * @since Symphony 2.2
807
                         * @param string $context
808
                         * '/blueprints/pages/'
809
                         * @param integer $page_id
810
                         *  The ID of the Page that is about to be updated
811
                         * @param array $fields
812
                         *  The `$_POST['fields']` array passed by reference
813
                         */
814
                        Symphony::ExtensionManager()->notifyMembers('PagePreEdit', '/blueprints/pages/', array('page_id' => $page_id, 'fields' => &$fields));
815
816 View Code Duplication
                        if (!PageManager::edit($page_id, $fields, true)) {
817
                            return $this->pageAlert(
818
                                __('Unknown errors occurred while attempting to save.')
819
                                . '<a href="' . SYMPHONY_URL . '/system/log/">'
820
                                . __('Check your activity log')
821
                                . '</a>.',
822
                                Alert::ERROR
823
                            );
824
                        } else {
825
                            /**
826
                             * Just after updating a page in `tbl_pages`
827
                             *
828
                             * @delegate PagePostEdit
829
                             * @since Symphony 2.2
830
                             * @param string $context
831
                             * '/blueprints/pages/'
832
                             * @param integer $page_id
833
                             *  The ID of the Page that was just updated
834
                             * @param array $fields
835
                             *  An associative array of data that was just saved for this page
836
                             */
837
                            Symphony::ExtensionManager()->notifyMembers('PagePostEdit', '/blueprints/pages/', array('page_id' => $page_id, 'fields' => $fields));
838
839
                            $redirect = "/blueprints/pages/edit/{$page_id}/saved/{$parent_link_suffix}";
840
                        }
841
                    }
842
                }
843
844
                // Only proceed if there was no errors saving/creating the page
845
                if (empty($this->_errors)) {
846
                    /**
847
                     * Just before the page's types are saved into `tbl_pages_types`.
848
                     * Use with caution as no further processing is done on the `$types`
849
                     * array to prevent duplicate `$types` from occurring (ie. two index
850
                     * page types). Your logic can use the PageManger::hasPageTypeBeenUsed
851
                     * function to perform this logic.
852
                     *
853
                     * @delegate PageTypePreCreate
854
                     * @since Symphony 2.2
855
                     * @see toolkit.PageManager#hasPageTypeBeenUsed
856
                     * @param string $context
857
                     * '/blueprints/pages/'
858
                     * @param integer $page_id
859
                     *  The ID of the Page that was just created or updated
860
                     * @param array $types
861
                     *  An associative array of the types for this page passed by reference.
862
                     */
863
                    Symphony::ExtensionManager()->notifyMembers('PageTypePreCreate', '/blueprints/pages/', array('page_id' => $page_id, 'types' => &$types));
864
865
                    // Assign page types:
866
                    PageManager::addPageTypesToPage($page_id, $types);
0 ignored issues
show
Security Bug introduced by
It seems like $page_id defined by \PageManager::add($fields) on line 772 can also be of type false; however, PageManager::addPageTypesToPage() does only seem to accept integer|null, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
867
868
                    // Find and update children:
869
                    if ($this->_context[0] == 'edit') {
870
                        PageManager::editPageChildren($page_id, $fields['path'] . '/' . $fields['handle']);
0 ignored issues
show
Security Bug introduced by
It seems like $page_id defined by \PageManager::add($fields) on line 772 can also be of type false; however, PageManager::editPageChildren() does only seem to accept integer|null, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
871
                    }
872
873
                    if ($redirect) {
874
                        redirect(SYMPHONY_URL . $redirect);
0 ignored issues
show
Bug introduced by
The variable $redirect does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
875
                    }
876
                }
877
            }
878
879
            // If there was any errors, either with pre processing or because of a
880
            // duplicate page, return.
881 View Code Duplication
            if (is_array($this->_errors) && !empty($this->_errors)) {
882
                return $this->pageAlert(
883
                    __('An error occurred while processing this form. See below for details.'),
884
                    Alert::ERROR
885
                );
886
            }
887
        }
888
    }
889
890
    public function __actionDelete($pages, $redirect)
891
    {
892
        $success = true;
893
        $deleted_page_ids = array();
894
895
        if (!is_array($pages)) {
896
            $pages = array($pages);
897
        }
898
899
        /**
900
         * Prior to deleting Pages
901
         *
902
         * @delegate PagePreDelete
903
         * @since Symphony 2.2
904
         * @param string $context
905
         * '/blueprints/pages/'
906
         * @param array $page_ids
907
         *  An array of Page ID's that are about to be deleted, passed
908
         *  by reference
909
         * @param string $redirect
910
         *  The absolute path that the Developer will be redirected to
911
         *  after the Pages are deleted
912
         */
913
        Symphony::ExtensionManager()->notifyMembers('PagePreDelete', '/blueprints/pages/', array('page_ids' => &$pages, 'redirect' => &$redirect));
914
915
        foreach ($pages as $page_id) {
916
            $page = PageManager::fetchPageByID($page_id);
917
918
            if (empty($page)) {
919
                $success = false;
920
                $this->pageAlert(
921
                    __('Page could not be deleted because it does not exist.'),
922
                    Alert::ERROR
923
                );
924
925
                break;
926
            }
927
928
            if (PageManager::hasChildPages($page_id)) {
929
                $this->_hilights[] = $page['id'];
930
                $success = false;
931
                $this->pageAlert(
932
                    __('Page could not be deleted because it has children.'),
933
                    Alert::ERROR
934
                );
935
936
                continue;
937
            }
938
939
            if (!PageManager::deletePageFiles($page['path'], $page['handle'])) {
940
                $this->_hilights[] = $page['id'];
941
                $success = false;
942
                $this->pageAlert(
943
                    __('One or more pages could not be deleted.')
944
                    . ' ' . __('Please check permissions on %s.', array('<code>/workspace/pages</code>')),
945
                    Alert::ERROR
946
                );
947
948
                continue;
949
            }
950
951
            if (PageManager::delete($page_id, false)) {
952
                $deleted_page_ids[] = $page_id;
953
            }
954
        }
955
956
        if ($success) {
957
            /**
958
             * Fires after all Pages have been deleted
959
             *
960
             * @delegate PagePostDelete
961
             * @since Symphony 2.3
962
             * @param string $context
963
             * '/blueprints/pages/'
964
             * @param array $page_ids
965
             *  The page ID's that were just deleted
966
             */
967
            Symphony::ExtensionManager()->notifyMembers('PagePostDelete', '/blueprints/pages/', array('page_ids' => $deleted_page_ids));
968
            redirect($redirect);
969
        }
970
    }
971
}
972