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 — integration ( 45cc9f...98bc42 )
by Brendan
05:52
created

contentBlueprintsSections   F

Complexity

Total Complexity 142

Size/Duplication

Total Lines 960
Duplicated Lines 16.25 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 6
Bugs 0 Features 1
Metric Value
c 6
b 0
f 1
dl 156
loc 960
rs 1.263
wmc 142
lcom 1
cbo 15

9 Methods

Rating   Name   Duplication   Size   Complexity  
A parseContext() 13 13 2
C __actionIndex() 0 87 11
A build() 13 13 2
D __viewIndex() 0 118 10
A __actionEdit() 0 4 1
B addSectionOptions() 0 44 1
F __viewNew() 45 164 29
F __viewEdit() 81 211 32
F __actionNew() 4 285 54

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 contentBlueprintsSections 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 contentBlueprintsSections, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package content
4
 */
5
6
/**
7
 * This page controls the creation and maintenance of Symphony
8
 * Sections through the Section Index and Section Editor.
9
 */
10
11
class contentBlueprintsSections extends AdministrationPage
12
{
13
    public $_errors = array();
14
15
    /**
16
     * The Sections page has /action/id/flag/ context.
17
     * eg. /edit/1/saved/
18
     *
19
     * @param array $context
20
     * @param array $parts
21
     * @return array
22
     */
23 View Code Duplication
    public function parseContext(array &$context, array $parts)
24
    {
25
        // Order is important!
26
        $params = array_fill_keys(array('action', 'id', 'flag'), null);
27
28
        if (isset($parts[2])) {
29
            $extras = preg_split('/\//', $parts[2], -1, PREG_SPLIT_NO_EMPTY);
30
            list($params['action'], $params['id'], $params['flag']) = $extras;
31
            $params['id'] = (int)$params['id'];
32
        }
33
34
        $context = array_filter($params);
35
    }
36
37 View Code Duplication
    public function build(array $context = array())
38
    {
39
        $section_id = $context['id'];
40
41
        if (isset($section_id)) {
42
            $context['associations'] = array(
43
                'parent' => SectionManager::fetchParentAssociations($section_id),
44
                'child' => SectionManager::fetchChildAssociations($section_id)
45
            );
46
        }
47
48
        return parent::build($context);
49
    }
50
51
    public function __viewIndex()
52
    {
53
        $this->setPageType('table');
54
        $this->setTitle(__('%1$s &ndash; %2$s', array(__('Sections'), __('Symphony'))));
55
        $this->appendSubheading(__('Sections'), Widget::Anchor(__('Create New'), Administration::instance()->getCurrentPageURL().'new/', __('Create a section'), 'create button', null, array('accesskey' => 'c')));
56
57
        $sections = SectionManager::fetch(null, 'ASC', 'sortorder');
58
59
        $aTableHead = array(
60
            array(__('Name'), 'col'),
61
            array(__('Entries'), 'col'),
62
            array(__('Navigation Group'), 'col')
63
        );
64
65
        $aTableBody = array();
66
67
        if (!is_array($sections) || empty($sections)) {
68
            $aTableBody = array(
69
                Widget::TableRow(array(Widget::TableData(__('None found.'), 'inactive', null, count($aTableHead))), 'odd')
70
            );
71
        } else {
72
            foreach ($sections as $s) {
73
                $entry_count = EntryManager::fetchCount($s->get('id'));
74
75
                // Setup each cell
76
                $td1 = Widget::TableData(Widget::Anchor($s->get('name'), Administration::instance()->getCurrentPageURL() . 'edit/' . $s->get('id') .'/', null, 'content'));
77
                $td1->appendChild(Widget::Label(__('Select Section %s', array($s->get('name'))), null, 'accessible', null, array(
78
                    'for' => 'section-' . $s->get('id')
79
                )));
80
                $td1->appendChild(Widget::Input('items['.$s->get('id').']', 'on', 'checkbox', array(
81
                    'id' => 'section-' . $s->get('id')
82
                )));
83
84
                $td2 = Widget::TableData(Widget::Anchor("$entry_count", SYMPHONY_URL . '/publish/' . $s->get('handle') . '/'));
85
                $td3 = Widget::TableData($s->get('navigation_group'));
86
87
                // Create row
88
                $tr = Widget::TableRow(array($td1, $td2, $td3));
89
90
                if ($s->get('hidden') === 'yes') {
91
                    $tr->setAttribute('class', 'inactive');
92
                }
93
94
                $aTableBody[] = $tr;
95
            }
96
        }
97
98
        $table = Widget::Table(
99
            Widget::TableHead($aTableHead),
100
            null,
101
            Widget::TableBody($aTableBody),
102
            'orderable selectable',
103
            null,
104
            array('role' => 'directory', 'aria-labelledby' => 'symphony-subheading', 'data-interactive' => 'data-interactive')
105
        );
106
107
        $this->Form->appendChild($table);
108
109
        $version = new XMLElement('p', 'Symphony ' . Symphony::Configuration()->get('version', 'symphony'), array(
110
            'id' => 'version'
111
        ));
112
113
        $this->Form->appendChild($version);
114
115
        $tableActions = new XMLElement('div');
116
        $tableActions->setAttribute('class', 'actions');
117
118
        $options = array(
119
            array(null, false, __('With Selected...')),
120
            array('delete', false, __('Delete'), 'confirm', null, array(
121
                'data-message' => __('Are you sure you want to delete the selected sections?')
122
            )),
123
            array('delete-entries', false, __('Delete Entries'), 'confirm', null, array(
124
                'data-message' => __('Are you sure you want to delete all entries in the selected sections?')
125
            ))
126
        );
127
128
        if (is_array($sections) && !empty($sections)) {
129
            $index = 3;
130
            $options[$index] = array('label' => __('Set navigation group'), 'options' => array());
131
132
            $groups = array();
133
134
            foreach ($sections as $s) {
135
                if (in_array($s->get('navigation_group'), $groups)) {
136
                    continue;
137
                }
138
139
                $groups[] = $s->get('navigation_group');
140
141
                $value = 'set-navigation-group-' . urlencode($s->get('navigation_group'));
142
                $options[$index]['options'][] = array($value, false, $s->get('navigation_group'));
143
            }
144
        }
145
146
        /**
147
         * Allows an extension to modify the existing options for this page's
148
         * With Selected menu. If the `$options` parameter is an empty array,
149
         * the 'With Selected' menu will not be rendered.
150
         *
151
         * @delegate AddCustomActions
152
         * @since Symphony 2.3.2
153
         * @param string $context
154
         * '/blueprints/sections/'
155
         * @param array $options
156
         *  An array of arrays, where each child array represents an option
157
         *  in the With Selected menu. Options should follow the same format
158
         *  expected by `Widget::__SelectBuildOption`. Passed by reference.
159
         */
160
        Symphony::ExtensionManager()->notifyMembers('AddCustomActions', '/blueprints/sections/', array(
161
            'options' => &$options
162
        ));
163
164
        if (!empty($options)) {
165
            $tableActions->appendChild(Widget::Apply($options));
166
            $this->Form->appendChild($tableActions);
167
        }
168
    }
169
170
    public function __viewNew()
0 ignored issues
show
Coding Style introduced by
__viewNew 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...
171
    {
172
        $this->setPageType('form');
173
        $this->setTitle(__('%1$s &ndash; %2$s', array(__('Sections'), __('Symphony'))));
174
        $this->appendSubheading(__('Untitled'));
175
        $this->insertBreadcrumbs(array(
176
            Widget::Anchor(__('Sections'), SYMPHONY_URL . '/blueprints/sections/'),
177
        ));
178
179
        $types = array();
180
181
        $fields = (isset($_POST['fields']) && is_array($_POST['fields'])) ? $_POST['fields'] : array();
182
        $meta = (isset($_POST['meta']) && is_array($_POST['meta'])) ? $_POST['meta'] : array('name'=>null);
183
184
        $formHasErrors = (is_array($this->_errors) && !empty($this->_errors));
185
186
        if ($formHasErrors) {
187
            $this->pageAlert(
188
                __('An error occurred while processing this form. See below for details.'),
189
                Alert::ERROR
190
            );
191
        }
192
193
        $showEmptyTemplate = (is_array($fields) && !empty($fields) ? false : true);
194
195
        if (!$showEmptyTemplate) {
196
            ksort($fields);
197
        }
198
199
        // Set navigation group, if not already set
200
        if (!isset($meta['navigation_group'])) {
201
            $meta['navigation_group'] = (isset($this->_navigation[0]['name']) ? $this->_navigation[0]['name'] : __('Content'));
202
        }
203
204
        $fieldset = new XMLElement('fieldset');
205
        $fieldset->setAttribute('class', 'settings');
206
        $fieldset->appendChild(new XMLElement('legend', __('Essentials')));
207
208
        $namediv = new XMLElement('div', null, array('class' => 'column'));
209
210
        $label = Widget::Label(__('Name'));
211
        $label->appendChild(Widget::Input('meta[name]', (isset($meta['name']) ? General::sanitize($meta['name']) : null)));
212
213 View Code Duplication
        if (isset($this->_errors['name'])) {
214
            $namediv->appendChild(Widget::Error($label, $this->_errors['name']));
215
        } else {
216
            $namediv->appendChild($label);
217
        }
218
219
        $fieldset->appendChild($namediv);
220
221
        $div = new XMLElement('div', null, array('class' => 'two columns'));
222
223
        $handlediv = new XMLElement('div', null, array('class' => 'column'));
224
225
        $label = Widget::Label(__('Handle'));
226
        $label->appendChild(Widget::Input('meta[handle]', (isset($meta['handle']) ? General::sanitize($meta['handle']) : null)));
227
228 View Code Duplication
        if (isset($this->_errors['handle'])) {
229
            $handlediv->appendChild(Widget::Error($label, $this->_errors['handle']));
230
        } else {
231
            $handlediv->appendChild($label);
232
        }
233
234
        $div->appendChild($handlediv);
235
236
        $navgroupdiv = new XMLElement('div', null, array('class' => 'column'));
237
238
        $sections = SectionManager::fetch(null, 'ASC', 'sortorder');
239
        $label = Widget::Label(__('Navigation Group'));
240
        $label->appendChild(Widget::Input('meta[navigation_group]', $meta['navigation_group']));
241
242 View Code Duplication
        if (isset($this->_errors['navigation_group'])) {
243
            $navgroupdiv->appendChild(Widget::Error($label, $this->_errors['navigation_group']));
244
        } else {
245
            $navgroupdiv->appendChild($label);
246
        }
247
248 View Code Duplication
        if (is_array($sections) && !empty($sections)) {
249
            $ul = new XMLElement('ul', null, array('class' => 'tags singular', 'data-interactive' => 'data-interactive'));
250
            $groups = array();
251
252
            foreach ($sections as $s) {
253
                if (in_array($s->get('navigation_group'), $groups)) {
254
                    continue;
255
                }
256
257
                $ul->appendChild(new XMLElement('li', $s->get('navigation_group')));
258
                $groups[] = $s->get('navigation_group');
259
            }
260
261
            $navgroupdiv->appendChild($ul);
262
        }
263
264
        $div->appendChild($navgroupdiv);
265
        $fieldset->appendChild($div);
266
        $this->Form->appendChild($fieldset);
267
268
        $this->addSectionOptions($meta);
269
270
        $fieldset = new XMLElement('fieldset');
271
        $fieldset->setAttribute('class', 'settings');
272
273
        $legend = new XMLElement('legend', __('Fields'));
274
        $legend->setAttribute('id', 'fields-legend');
275
        $fieldset->appendChild($legend);
276
277
        $div = new XMLElement('div', null, array('class' => 'frame', 'id' => 'fields-duplicator'));
278
279
        $ol = new XMLElement('ol');
280
        $ol->setAttribute('data-add', __('Add field'));
281
        $ol->setAttribute('data-remove', __('Remove field'));
282
283
        if (!$showEmptyTemplate) {
284
            foreach ($fields as $position => $data) {
285
                if ($input = FieldManager::create($data['type'])) {
286
                    $input->setArray($data);
287
288
                    $wrapper = new XMLElement('li');
289
290
                    $input->set('sortorder', $position);
291
                    $input->displaySettingsPanel($wrapper, (isset($this->_errors[$position]) ? $this->_errors[$position] : null));
292
                    $ol->appendChild($wrapper);
293
                }
294
            }
295
        }
296
297
        foreach (FieldManager::listAll() as $type) {
298
            if ($type = FieldManager::create($type)) {
299
                $types[] = $type;
300
            }
301
        }
302
303
        uasort($types, function($a, $b) {
304
            return strnatcasecmp($a->_name, $b->_name);
305
        });
306
307 View Code Duplication
        foreach ($types as $type) {
308
            $defaults = array();
309
310
            $type->findDefaults($defaults);
311
            $type->setArray($defaults);
312
313
            $wrapper = new XMLElement('li');
314
            $wrapper->setAttribute('class', 'template field-' . $type->handle() . ($type->mustBeUnique() ? ' unique' : null));
315
            $wrapper->setAttribute('data-type', $type->handle());
316
317
            $type->set('sortorder', '-1');
318
            $type->displaySettingsPanel($wrapper);
319
320
            $ol->appendChild($wrapper);
321
        }
322
323
        $div->appendChild($ol);
324
        $fieldset->appendChild($div);
325
326
        $this->Form->appendChild($fieldset);
327
328
        $div = new XMLElement('div');
329
        $div->setAttribute('class', 'actions');
330
        $div->appendChild(Widget::Input('action[save]', __('Create Section'), 'submit', array('accesskey' => 's')));
331
332
        $this->Form->appendChild($div);
333
    }
334
335
    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...
336
    {
337
        $section_id = $this->_context['id'];
338
339 View Code Duplication
        if (!$section = SectionManager::fetch($section_id)) {
340
            Administration::instance()->throwCustomError(
341
                __('The Section, %s, could not be found.', array($section_id)),
342
                __('Unknown Section'),
343
                Page::HTTP_STATUS_NOT_FOUND
344
            );
345
        }
346
347
        $meta = $section->get();
348
        $section_id = $meta['id'];
349
        $types = array();
350
351
        $formHasErrors = (is_array($this->_errors) && !empty($this->_errors));
352
353 View Code Duplication
        if ($formHasErrors) {
354
            $this->pageAlert(
355
                __('An error occurred while processing this form. See below for details.'),
356
                Alert::ERROR
357
            );
358
359
            // These alerts are only valid if the form doesn't have errors
360
        } elseif (isset($this->_context['flag'])) {
361
            $time = Widget::Time();
362
363
            switch ($this->_context['flag']) {
364
                case 'saved':
365
                    $message = __('Section updated at %s.', array($time->generate()));
366
                    break;
367
                case 'created':
368
                    $message = __('Section created at %s.', array($time->generate()));
369
            }
370
371
            $this->pageAlert(
372
                $message
0 ignored issues
show
Bug introduced by
The variable $message 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...
373
                . ' <a href="' . SYMPHONY_URL . '/blueprints/sections/new/" accesskey="c">'
374
                . __('Create another?')
375
                . '</a> <a href="' . SYMPHONY_URL . '/blueprints/sections/" accesskey="a">'
376
                . __('View all Sections')
377
                . '</a>',
378
                Alert::SUCCESS
379
            );
380
        }
381
382
        if (isset($_POST['fields'])) {
383
            $fields = array();
384
385
            if (is_array($_POST['fields']) && !empty($_POST['fields'])) {
386
                foreach ($_POST['fields'] as $position => $data) {
387
                    if ($fields[$position] = FieldManager::create($data['type'])) {
388
                        $fields[$position]->setArray($data);
389
                        $fields[$position]->set('sortorder', $position);
390
                    }
391
                }
392
            }
393
        } else {
394
            $fields = FieldManager::fetch(null, $section_id);
395
            $fields = array_values($fields);
396
        }
397
398
        if (isset($_POST['meta'])) {
399
            $meta = $_POST['meta'];
400
            if ($meta['name'] === '') {
401
                $meta['name'] = $section->get('name');
402
            }
403
        }
404
405
        $this->setPageType('form');
406
        $this->setTitle(__('%1$s &ndash; %2$s &ndash; %3$s', array($meta['name'], __('Sections'), __('Symphony'))));
407
        $this->appendSubheading($meta['name'],
408
            Widget::Anchor(__('View Entries'), SYMPHONY_URL . '/publish/' . $section->get('handle'), __('View Section Entries'), 'button')
409
        );
410
        $this->insertBreadcrumbs(array(
411
            Widget::Anchor(__('Sections'), SYMPHONY_URL . '/blueprints/sections/'),
412
        ));
413
414
        $fieldset = new XMLElement('fieldset');
415
        $fieldset->setAttribute('class', 'settings');
416
        $fieldset->appendChild(new XMLElement('legend', __('Essentials')));
417
418
        $namediv = new XMLElement('div', null, array('class' => 'column'));
419
420
        $label = Widget::Label(__('Name'));
421
        $label->appendChild(Widget::Input('meta[name]', (isset($meta['name']) ? General::sanitize($meta['name']) : null)));
422
423 View Code Duplication
        if (isset($this->_errors['name'])) {
424
            $namediv->appendChild(Widget::Error($label, $this->_errors['name']));
425
        } else {
426
            $namediv->appendChild($label);
427
        }
428
429
        $fieldset->appendChild($namediv);
430
431
        $div = new XMLElement('div', null, array('class' => 'two columns'));
432
433
        $handlediv = new XMLElement('div', null, array('class' => 'column'));
434
435
        $label = Widget::Label(__('Handle'));
436
        $label->appendChild(Widget::Input('meta[handle]', (isset($meta['handle']) ? General::sanitize($meta['handle']) : null)));
437
438 View Code Duplication
        if (isset($this->_errors['handle'])) {
439
            $handlediv->appendChild(Widget::Error($label, $this->_errors['handle']));
440
        } else {
441
            $handlediv->appendChild($label);
442
        }
443
444
        $div->appendChild($handlediv);
445
446
        $navgroupdiv = new XMLElement('div', null, array('class' => 'column'));
447
448
        $sections = SectionManager::fetch(null, 'ASC', 'sortorder');
449
        $label = Widget::Label(__('Navigation Group'));
450
        $label->appendChild(Widget::Input('meta[navigation_group]', $meta['navigation_group']));
451
452 View Code Duplication
        if (isset($this->_errors['navigation_group'])) {
453
            $navgroupdiv->appendChild(Widget::Error($label, $this->_errors['navigation_group']));
454
        } else {
455
            $navgroupdiv->appendChild($label);
456
        }
457
458 View Code Duplication
        if (is_array($sections) && !empty($sections)) {
459
            $ul = new XMLElement('ul', null, array('class' => 'tags singular', 'data-interactive' => 'data-interactive'));
460
            $groups = array();
461
462
            foreach ($sections as $s) {
463
                if (in_array($s->get('navigation_group'), $groups)) {
464
                    continue;
465
                }
466
467
                $ul->appendChild(new XMLElement('li', $s->get('navigation_group')));
468
                $groups[] = $s->get('navigation_group');
469
            }
470
471
            $navgroupdiv->appendChild($ul);
472
        }
473
474
        $div->appendChild($navgroupdiv);
475
        $fieldset->appendChild($div);
476
        $this->Form->appendChild($fieldset);
477
478
        $this->addSectionOptions($meta);
479
480
        $fieldset = new XMLElement('fieldset');
481
        $fieldset->setAttribute('class', 'settings');
482
483
        $legend = new XMLElement('legend', __('Fields'));
484
        $legend->setAttribute('id', 'fields-legend');
485
        $fieldset->appendChild($legend);
486
487
        $div = new XMLElement('div', null, array('class' => 'frame', 'id' => 'fields-duplicator'));
488
489
        $ol = new XMLElement('ol');
490
        $ol->setAttribute('data-add', __('Add field'));
491
        $ol->setAttribute('data-remove', __('Remove field'));
492
493
        if (is_array($fields) && !empty($fields)) {
494
            foreach ($fields as $position => $field) {
495
                $wrapper = new XMLElement('li', null, array('class' => 'field-' . $field->handle() . ($field->mustBeUnique() ? ' unique' : null)));
496
                $wrapper->setAttribute('data-type', $field->handle());
497
498
                $field->set('sortorder', $position);
499
                $field->displaySettingsPanel($wrapper, (isset($this->_errors[$position]) ? $this->_errors[$position] : null));
500
                $ol->appendChild($wrapper);
501
            }
502
        }
503
504
        foreach (FieldManager::listAll() as $type) {
505
            if ($type = FieldManager::create($type)) {
506
                array_push($types, $type);
507
            }
508
        }
509
510
        uasort($types, function($a, $b) {
511
            return strnatcasecmp($a->_name, $b->_name);
512
        });
513
514 View Code Duplication
        foreach ($types as $type) {
515
            $defaults = array();
516
517
            $type->findDefaults($defaults);
518
            $type->setArray($defaults);
519
520
            $wrapper = new XMLElement('li');
521
522
            $wrapper->setAttribute('class', 'template field-' . $type->handle() . ($type->mustBeUnique() ? ' unique' : null));
523
            $wrapper->setAttribute('data-type', $type->handle());
524
525
            $type->set('sortorder', '-1');
526
            $type->displaySettingsPanel($wrapper);
527
528
            $ol->appendChild($wrapper);
529
        }
530
531
        $div->appendChild($ol);
532
        $fieldset->appendChild($div);
533
534
        $this->Form->appendChild($fieldset);
535
536
        $div = new XMLElement('div');
537
        $div->setAttribute('class', 'actions');
538
        $div->appendChild(Widget::Input('action[save]', __('Save Changes'), 'submit', array('accesskey' => 's')));
539
540
        $button = new XMLElement('button', __('Delete'));
541
        $button->setAttributeArray(array('name' => 'action[delete]', 'class' => 'button confirm delete', 'title' => __('Delete this section'), 'type' => 'submit', 'accesskey' => 'd', 'data-message' => __('Are you sure you want to delete this section?')));
542
        $div->appendChild($button);
543
544
        $this->Form->appendChild($div);
545
    }
546
547
    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...
548
    {
549
        $checked = (is_array($_POST['items'])) ? array_keys($_POST['items']) : null;
550
551
        if (is_array($checked) && !empty($checked)) {
552
            /**
553
             * Extensions can listen for any custom actions that were added
554
             * through `AddCustomPreferenceFieldsets` or `AddCustomActions`
555
             * delegates.
556
             *
557
             * @delegate CustomActions
558
             * @since Symphony 2.3.2
559
             * @param string $context
560
             *  '/blueprints/sections/'
561
             * @param array $checked
562
             *  An array of the selected rows. The value is usually the ID of the
563
             *  the associated object.
564
             */
565
            Symphony::ExtensionManager()->notifyMembers('CustomActions', '/blueprints/sections/', array(
566
                'checked' => $checked
567
            ));
568
569
            if ($_POST['with-selected'] === 'delete') {
570
                /**
571
                 * Just prior to calling the Section Manager's delete function
572
                 *
573
                 * @delegate SectionPreDelete
574
                 * @since Symphony 2.2
575
                 * @param string $context
576
                 * '/blueprints/sections/'
577
                 * @param array $section_ids
578
                 *  An array of Section ID's passed by reference
579
                 */
580
                Symphony::ExtensionManager()->notifyMembers('SectionPreDelete', '/blueprints/sections/', array('section_ids' => &$checked));
581
582
                foreach ($checked as $section_id) {
583
                    SectionManager::delete($section_id);
584
                }
585
586
                /**
587
                 * Just after calling the Section Manager's delete function
588
                 *
589
                 * @delegate SectionPostDelete
590
                 * @since Symphony 3.0.0
591
                 * @param string $context
592
                 * '/blueprints/sections/'
593
                 * @param array $section_ids
594
                 *  An array of Section ID's that were deleted
595
                 */
596
                Symphony::ExtensionManager()->notifyMembers('SectionPostDelete', '/blueprints/sections/', array('section_ids' => $checked));
597
598
                redirect(SYMPHONY_URL . '/blueprints/sections/');
599
            } elseif ($_POST['with-selected'] === 'delete-entries') {
600
                foreach ($checked as $section_id) {
601
                    $entries = EntryManager::fetch(null, $section_id, null, null, null, null, false, false, null, false);
602
                    $entry_ids = array();
603
604
                    foreach ($entries as $entry) {
0 ignored issues
show
Bug introduced by
The expression $entries of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
605
                        $entry_ids[] = $entry['id'];
606
                    }
607
608
                    /**
609
                     * Prior to deletion of entries.
610
                     *
611
                     * @delegate Delete
612
                     * @param string $context
613
                     * '/publish/'
614
                     * @param array $entry_id
615
                     *  An array of Entry ID's that are about to be deleted, passed by reference
616
                     */
617
                    Symphony::ExtensionManager()->notifyMembers('Delete', '/publish/', array('entry_id' => &$entry_ids));
618
619
                    EntryManager::delete($entry_ids, $section_id);
620
                }
621
622
                redirect(SYMPHONY_URL . '/blueprints/sections/');
623
            } elseif (preg_match('/^set-navigation-group-/', $_POST['with-selected'])) {
624
                $navigation_group = preg_replace('/^set-navigation-group-/', null, $_POST['with-selected']);
625
626
                foreach ($checked as $section_id) {
627
                    SectionManager::edit($section_id, array('navigation_group' => urldecode($navigation_group)));
628
                }
629
630
                redirect(SYMPHONY_URL . '/blueprints/sections/');
631
            }
632
        }
633
    }
634
635
    public function __actionNew()
0 ignored issues
show
Coding Style introduced by
__actionNew 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...
636
    {
637
        if (@array_key_exists('save', $_POST['action']) || @array_key_exists('done', $_POST['action'])) {
638
            $canProceed = true;
639
            $edit = ($this->_context['action'] === "edit");
640
            $this->_errors = array();
641
642
            $fields = isset($_POST['fields']) ? $_POST['fields'] : array();
643
            $meta = $_POST['meta'];
644
645
            if ($edit) {
646
                $section_id = $this->_context['id'];
647
                $existing_section = SectionManager::fetch($section_id);
648
            }
649
650
            // Check handle to ensure it is unique
651
            $meta['handle'] = $_POST['meta']['handle'] = Lang::createHandle((isset($meta['handle']) && !empty($meta['handle']))
652
                ? $meta['handle']
653
                : $meta['name']);
654
655
            // Check to ensure all the required section fields are filled
656
            if (!isset($meta['name']) || strlen(trim($meta['name'])) === 0) {
657
                $this->_errors['name'] = __('This is a required field.');
658
                $canProceed = false;
659
660
                // Check for duplicate section handle during edit
661
            } elseif ($edit) {
662
                $s = SectionManager::fetchIDFromHandle(Lang::createHandle($meta['handle']));
663
664
                if (
665
                    $meta['handle'] !== $existing_section->get('handle')
0 ignored issues
show
Bug introduced by
The variable $existing_section 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...
666
                    && !is_null($s) && $s !== $section_id
0 ignored issues
show
Bug introduced by
The variable $section_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...
667
                ) {
668
                    $this->_errors['handle'] = __('A Section with the handle %s already exists', array('<code>' . $meta['handle'] . '</code>'));
669
                    $canProceed = false;
670
                }
671
672
                // Existing section during creation
673
            } elseif (!is_null(SectionManager::fetchIDFromHandle(Lang::createHandle($meta['handle'])))) {
674
                $this->_errors['handle'] = __('A Section with the handle %s already exists', array('<code>' . $meta['handle'] . '</code>'));
675
                $canProceed = false;
676
            }
677
678
            // Check to ensure all the required section fields are filled
679 View Code Duplication
            if (!isset($meta['navigation_group']) || strlen(trim($meta['navigation_group'])) === 0) {
680
                $this->_errors['navigation_group'] = __('This is a required field.');
681
                $canProceed = false;
682
            }
683
684
            // Basic custom field checking
685
            if (is_array($fields) && !empty($fields)) {
686
                // Check for duplicate CF names
687
                if ($canProceed) {
688
                    $name_list = array();
689
690
                    foreach ($fields as $position => $data) {
691
                        if (trim($data['element_name']) === '') {
692
                            $data['element_name'] = $fields[$position]['element_name'] = $_POST['fields'][$position]['element_name'] = Lang::createHandle($data['label'], 255, '-', false, true, array('@^[\d-]+@i' => ''));
693
                        }
694
695
                        if (trim($data['element_name']) !== '' && in_array($data['element_name'], $name_list)) {
696
                            $this->_errors[$position] = array('element_name' => __('A field with this handle already exists. All handle must be unique.'));
697
                            $canProceed = false;
698
                            break;
699
                        }
700
701
                        $name_list[] = $data['element_name'];
702
                    }
703
                }
704
705
                if ($canProceed) {
706
                    $unique = array();
707
708
                    foreach ($fields as $position => $data) {
709
                        $field = FieldManager::create($data['type']);
710
                        $field->setFromPOST($data);
711
712
                        if (isset($existing_section)) {
713
                            $field->set('parent_section', $existing_section->get('id'));
714
                        }
715
716
                        if ($field->mustBeUnique() && !in_array($field->get('type'), $unique)) {
717
                            $unique[] = $field->get('type');
718
                        } elseif ($field->mustBeUnique() && in_array($field->get('type'), $unique)) {
719
                            // Warning. cannot have 2 of this field!
720
                            $canProceed = false;
721
                            $this->_errors[$position] = array('label' => __('There is already a field of type %s. There can only be one per section.', array('<code>' . $field->handle() . '</code>')));
722
                        }
723
724
                        $errors = array();
725
726
                        if (Field::__OK__ !== $field->checkFields($errors, false) && !empty($errors)) {
727
                            $this->_errors[$position] = $errors;
728
                            $canProceed = false;
729
                        }
730
                    }
731
                }
732
            }
733
734
            if ($canProceed) {
735
                // If we are creating a new Section
736
                if (!$edit) {
737
                    $meta['sortorder'] = SectionManager::fetchNextSortOrder();
738
739
                    /**
740
                     * Just prior to saving the Section settings. Use with caution as
741
                     * there is no additional processing to ensure that Field's or Section's
742
                     * are unique.
743
                     *
744
                     * @delegate SectionPreCreate
745
                     * @since Symphony 2.2
746
                     * @param string $context
747
                     * '/blueprints/sections/'
748
                     * @param array $meta
749
                     *  The section's settings, passed by reference
750
                     * @param array $fields
751
                     *  An associative array of the fields that will be saved to this
752
                     *  section with the key being the position in the Section Editor
753
                     *  and the value being a Field object, passed by reference
754
                     */
755
                    Symphony::ExtensionManager()->notifyMembers('SectionPreCreate', '/blueprints/sections/', array('meta' => &$meta, 'fields' => &$fields));
756
757
                    if (!$section_id = SectionManager::add($meta)) {
758
                        $this->pageAlert(__('An unknown database occurred while attempting to create the section.'), Alert::ERROR);
759
                    }
760
761
                    // We are editing a Section
762
                } else {
763
764
                    /**
765
                     * Just prior to updating the Section settings. Use with caution as
766
                     * there is no additional processing to ensure that Field's or Section's
767
                     * are unique.
768
                     *
769
                     * @delegate SectionPreEdit
770
                     * @since Symphony 2.2
771
                     * @param string $context
772
                     * '/blueprints/sections/'
773
                     * @param integer $section_id
774
                     *  The Section ID that is about to be edited.
775
                     * @param array $meta
776
                     *  The section's settings, passed by reference
777
                     * @param array $fields
778
                     *  An associative array of the fields that will be saved to this
779
                     *  section with the key being the position in the Section Editor
780
                     *  and the value being a Field object, passed by reference
781
                     */
782
                    Symphony::ExtensionManager()->notifyMembers('SectionPreEdit', '/blueprints/sections/', array('section_id' => $section_id, 'meta' => &$meta, 'fields' => &$fields));
783
784
                    if (!SectionManager::edit($section_id, $meta)) {
785
                        $canProceed = false;
786
                        $this->pageAlert(__('An unknown database occurred while attempting to create the section.'), Alert::ERROR);
787
                    }
788
                }
789
790
                if ($section_id && $canProceed) {
791
                    if ($edit) {
792
                        // Delete missing CF's
793
                        $id_list = array();
794
795
                        if (is_array($fields) && !empty($fields)) {
796
                            foreach ($fields as $position => $data) {
797
                                if (isset($data['id'])) {
798
                                    $id_list[] = (int)$data['id'];
799
                                }
800
                            }
801
                        }
802
803
                        $q = Database::addPlaceholders($id_list);
804
                        $missing_cfs = Symphony::Database()->fetchCol('id', "
805
                            SELECT `id` 
806
                            FROM `tbl_fields` 
807
                            WHERE `parent_section` = ? AND `id` NOT IN (".$q.")",
808
                            array_merge(array($section_id), $id_list)
809
                        );
810
811
                        if (is_array($missing_cfs) && !empty($missing_cfs)) {
812
                            foreach ($missing_cfs as $id) {
813
                                FieldManager::delete($id);
814
                            }
815
                        }
816
                    }
817
818
                    // Save each custom field
819
                    if (is_array($fields) && !empty($fields)) {
820
                        foreach ($fields as $position => $data) {
821
                            $field = FieldManager::create($data['type']);
822
                            $field->setFromPOST($data);
823
                            $field->set('sortorder', (string)$position);
824
                            $field->set('parent_section', $section_id);
825
826
                            $newField = !(boolean)$field->get('id');
827
828
                            $field->commit();
829
                            $field_id = $field->get('id');
830
831
                            if ($field_id) {
832
                                if ($newField) {
833
                                    /**
834
                                     * After creation of a Field.
835
                                     *
836
                                     * @delegate FieldPostCreate
837
                                     * @param string $context
838
                                     * '/blueprints/sections/'
839
                                     * @param Field $field
840
                                     *  The Field object, passed by reference
841
                                     * @param array $data
842
                                     *  The settings for ths `$field`, passed by reference
843
                                     */
844
                                    Symphony::ExtensionManager()->notifyMembers('FieldPostCreate', '/blueprints/sections/', array('field' => &$field, 'data' => &$data));
845
                                } else {
846
                                    /**
847
                                     * After editing of a Field.
848
                                     *
849
                                     * @delegate FieldPostEdit
850
                                     * @param string $context
851
                                     * '/blueprints/sections/'
852
                                     * @param Field $field
853
                                     *  The Field object, passed by reference
854
                                     * @param array $data
855
                                     *  The settings for ths `$field`, passed by reference
856
                                     */
857
                                    Symphony::ExtensionManager()->notifyMembers('FieldPostEdit', '/blueprints/sections/', array('field' => &$field, 'data' => &$data));
858
                                }
859
                            }
860
                        }
861
                    }
862
863
                    if (!$edit) {
864
                        /**
865
                         * After the Section has been created, and all the Field's have been
866
                         * created for this section, but just before the redirect
867
                         *
868
                         * @delegate SectionPostCreate
869
                         * @since Symphony 2.2
870
                         * @param string $context
871
                         * '/blueprints/sections/'
872
                         * @param integer $section_id
873
                         *  The newly created Section ID.
874
                         */
875
                        Symphony::ExtensionManager()->notifyMembers('SectionPostCreate', '/blueprints/sections/', array('section_id' => $section_id));
876
877
                        redirect(SYMPHONY_URL . "/blueprints/sections/edit/$section_id/created/");
878
                    } else {
879
                        /**
880
                         * After the Section has been updated, and all the Field's have been
881
                         * updated for this section, but just before the redirect
882
                         *
883
                         * @delegate SectionPostEdit
884
                         * @since Symphony 2.2
885
                         * @param string $context
886
                         * '/blueprints/sections/'
887
                         * @param integer $section_id
888
                         *  The edited Section ID.
889
                         */
890
                        Symphony::ExtensionManager()->notifyMembers('SectionPostEdit', '/blueprints/sections/', array('section_id' => $section_id));
891
892
                        redirect(SYMPHONY_URL . "/blueprints/sections/edit/$section_id/saved/");
893
                    }
894
                }
895
            }
896
        }
897
898
        if (@array_key_exists("delete", $_POST['action'])) {
899
            $section_id = array($this->_context['id']);
900
901
            /**
902
             * Just prior to calling the Section Manager's delete function
903
             *
904
             * @delegate SectionPreDelete
905
             * @since Symphony 2.2
906
             * @param string $context
907
             * '/blueprints/sections/'
908
             * @param array $section_ids
909
             *  An array of Section ID's passed by reference
910
             */
911
            Symphony::ExtensionManager()->notifyMembers('SectionPreDelete', '/blueprints/sections/', array('section_ids' => &$section_id));
912
913
            foreach ($section_id as $section) {
914
                SectionManager::delete($section);
915
            }
916
917
            redirect(SYMPHONY_URL . '/blueprints/sections/');
918
        }
919
    }
920
921
    public function __actionEdit()
922
    {
923
        return $this->__actionNew();
924
    }
925
926
    public function addSectionOptions(array &$meta = null)
927
    {
928
        $fieldset = new XMLElement('fieldset');
929
        $fieldset->setAttribute('class', 'settings');
930
        $fieldset->appendChild(new XMLElement('legend', __('Options')));
931
932
        $div = new XMLElement('div', null, array('class' => 'two columns'));
933
934
        $hidediv = new XMLElement('div', null, array('class' => 'column'));
935
        $label = Widget::Checkbox('meta[hidden]', $meta['hidden'], __('Hide this section from the back-end menu'));
936
        $hidediv->appendChild($label);
937
        $div->appendChild($hidediv);
938
939
        $filterdiv = new XMLElement('div', null, array('class' => 'column'));
940
        $label = Widget::Checkbox('meta[filter]', $meta['filter'], __('Allow filtering of section entries'));
941
        $filterdiv->appendChild($label);
942
943
        $div->appendChild($filterdiv);
944
        $fieldset->appendChild($div);
945
        $this->Form->appendChild($fieldset);
946
947
        /**
948
         * Allows extensions to add elements to the header of the Section Editor
949
         * form. Usually for section settings, this delegate is passed the current
950
         * `$meta` array and the `$this->_errors` array.
951
         *
952
         * @delegate AddSectionElements
953
         * @since Symphony 2.2
954
         * @param string $context
955
         * '/blueprints/sections/'
956
         * @param XMLElement $form
957
         *  An XMLElement of the current `$this->Form`, just after the Section
958
         *  settings have been appended, but before the Fields duplicator
959
         * @param array $meta
960
         *  The current $_POST['meta'] array
961
         * @param array $errors
962
         *  The current errors array
963
         */
964
        Symphony::ExtensionManager()->notifyMembers('AddSectionElements', '/blueprints/sections/', array(
965
            'form' => &$this->Form,
966
            'meta' => &$meta,
967
            'errors' => &$this->_errors
968
        ));
969
    }
970
}
971