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 ( 4ccb09...2f9dc2 )
by Nicolas
03:16
created

contentBlueprintsSections   F

Complexity

Total Complexity 157

Size/Duplication

Total Lines 992
Duplicated Lines 16.33 %

Coupling/Cohesion

Components 1
Dependencies 16

Importance

Changes 0
Metric Value
dl 162
loc 992
rs 1.263
c 0
b 0
f 0
wmc 157
lcom 1
cbo 16

9 Methods

Rating   Name   Duplication   Size   Complexity  
A build() 12 12 2
D __viewIndex() 0 118 10
F __viewNew() 45 164 30
F __viewEdit() 81 222 33
C __actionIndex() 0 78 11
F __actionNew() 4 307 59
A __actionEdit() 0 4 1
B addSectionOptions() 0 44 1
B validateTimestamp() 20 20 10

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

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
630
                }
631
            }
632
633
            // Check handle to ensure it is unique
634
            $meta['handle'] = $_POST['meta']['handle'] = Lang::createHandle((isset($meta['handle']) && !empty($meta['handle']))
635
                ? $meta['handle']
636
                : $meta['name']);
637
638
            // Check to ensure all the required section fields are filled
639
            if (!isset($meta['name']) || strlen(trim($meta['name'])) == 0) {
640
                $this->_errors['name'] = __('This is a required field.');
641
                $canProceed = false;
642
643
                // Check for duplicate section handle during edit
644
            } elseif ($edit) {
645
                $s = SectionManager::fetchIDFromHandle(Lang::createHandle($meta['handle']));
646
647
                if (
648
                    $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...
649
                    && !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...
650
                ) {
651
                    $this->_errors['handle'] = __('A Section with the handle %s already exists', array('<code>' . $meta['handle'] . '</code>'));
652
                    $canProceed = false;
653
                }
654
655
                // Existing section during creation
656
            } elseif (!is_null(SectionManager::fetchIDFromHandle(Lang::createHandle($meta['handle'])))) {
657
                $this->_errors['handle'] = __('A Section with the handle %s already exists', array('<code>' . $meta['handle'] . '</code>'));
658
                $canProceed = false;
659
            }
660
661
            // Check to ensure all the required section fields are filled
662 View Code Duplication
            if (!isset($meta['navigation_group']) || strlen(trim($meta['navigation_group'])) == 0) {
663
                $this->_errors['navigation_group'] = __('This is a required field.');
664
                $canProceed = false;
665
            }
666
667
            // Basic custom field checking
668
            if (is_array($fields) && !empty($fields)) {
669
                // Check for duplicate CF names
670
                if ($canProceed) {
671
                    $name_list = array();
672
673
                    foreach ($fields as $position => $data) {
674
                        if (trim($data['element_name']) == '') {
675
                            $data['element_name'] = $fields[$position]['element_name'] = $_POST['fields'][$position]['element_name'] = Lang::createHandle($data['label'], 255, '-', false, true, array('@^[\d-]+@i' => ''));
676
                        }
677
678
                        if (trim($data['element_name']) != '' && in_array($data['element_name'], $name_list)) {
679
                            $this->_errors[$position] = array('element_name' => __('A field with this handle already exists. All handle must be unique.'));
680
                            $canProceed = false;
681
                            break;
682
                        }
683
684
                        $name_list[] = $data['element_name'];
685
                    }
686
                }
687
688
                if ($canProceed) {
689
                    $unique = array();
690
691
                    foreach ($fields as $position => $data) {
692
                        $field = FieldManager::create($data['type']);
693
                        $field->setFromPOST($data);
694
695
                        if (isset($existing_section)) {
696
                            $field->set('parent_section', $existing_section->get('id'));
697
                        }
698
699
                        if ($field->mustBeUnique() && !in_array($field->get('type'), $unique)) {
700
                            $unique[] = $field->get('type');
701
                        } elseif ($field->mustBeUnique() && in_array($field->get('type'), $unique)) {
702
                            // Warning. cannot have 2 of this field!
703
                            $canProceed = false;
704
                            $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>')));
705
                        }
706
707
                        $errors = array();
708
709
                        if (Field::__OK__ != $field->checkFields($errors, false) && !empty($errors)) {
710
                            $this->_errors[$position] = $errors;
711
                            $canProceed = false;
712
                        }
713
                    }
714
                }
715
            }
716
717
            if ($canProceed) {
718
                // If we are creating a new Section
719
                if (!$edit) {
720
                    $meta['sortorder'] = SectionManager::fetchNextSortOrder();
721
722
                    /**
723
                     * Just prior to saving the Section settings. Use with caution as
724
                     * there is no additional processing to ensure that Field's or Section's
725
                     * are unique.
726
                     *
727
                     * @delegate SectionPreCreate
728
                     * @since Symphony 2.2
729
                     * @param string $context
730
                     * '/blueprints/sections/'
731
                     * @param array $meta
732
                     *  The section's settings, passed by reference
733
                     * @param array $fields
734
                     *  An associative array of the fields that will be saved to this
735
                     *  section with the key being the position in the Section Editor
736
                     *  and the value being a Field object, passed by reference
737
                     */
738
                    Symphony::ExtensionManager()->notifyMembers('SectionPreCreate', '/blueprints/sections/', array('meta' => &$meta, 'fields' => &$fields));
739
740
                    $meta['author_id'] = Symphony::Author()->get('id');
741
                    $meta['modification_author_id'] = $meta['author_id'];
742
743
                    if (!$section_id = SectionManager::add($meta)) {
744
                        $this->pageAlert(__('An unknown database occurred while attempting to create the section.'), Alert::ERROR);
745
                    }
746
747
                    // We are editing a Section
748
                } else {
749
750
                    /**
751
                     * Just prior to updating the Section settings. Use with caution as
752
                     * there is no additional processing to ensure that Field's or Section's
753
                     * are unique.
754
                     *
755
                     * @delegate SectionPreEdit
756
                     * @since Symphony 2.2
757
                     * @param string $context
758
                     * '/blueprints/sections/'
759
                     * @param integer $section_id
760
                     *  The Section ID that is about to be edited.
761
                     * @param array $meta
762
                     *  The section's settings, passed by reference
763
                     * @param array $fields
764
                     *  An associative array of the fields that will be saved to this
765
                     *  section with the key being the position in the Section Editor
766
                     *  and the value being a Field object, passed by reference
767
                     */
768
                    Symphony::ExtensionManager()->notifyMembers('SectionPreEdit', '/blueprints/sections/', array('section_id' => $section_id, 'meta' => &$meta, 'fields' => &$fields));
769
770
                    $meta['modification_author_id'] = Symphony::Author()->get('id');
771
772
                    if (!SectionManager::edit($section_id, $meta)) {
773
                        $canProceed = false;
774
                        $this->pageAlert(__('An unknown database occurred while attempting to create the section.'), Alert::ERROR);
775
                    }
776
                }
777
778
                if ($section_id && $canProceed) {
779
                    if ($edit) {
780
                        // Delete missing CF's
781
                        $id_list = array();
782
783
                        if (is_array($fields) && !empty($fields)) {
784
                            foreach ($fields as $position => $data) {
785
                                if (isset($data['id'])) {
786
                                    $id_list[] = $data['id'];
787
                                }
788
                            }
789
                        }
790
791
                        $missing_cfs = Symphony::Database()->fetchCol('id', "SELECT `id` FROM `tbl_fields` WHERE `parent_section` = '$section_id' AND `id` NOT IN ('".@implode("', '", $id_list)."')");
792
793
                        if (is_array($missing_cfs) && !empty($missing_cfs)) {
794
                            foreach ($missing_cfs as $id) {
795
                                FieldManager::delete($id);
796
                            }
797
                        }
798
                    }
799
800
                    // Save each custom field
801
                    if (is_array($fields) && !empty($fields)) {
802
                        foreach ($fields as $position => $data) {
803
                            $field = FieldManager::create($data['type']);
804
                            $field->setFromPOST($data);
805
                            $field->set('sortorder', (string)$position);
806
                            $field->set('parent_section', $section_id);
807
808
                            // It is possible that the "existing" field has been deleted
809
                            // so we are dealing with an invalid id.
810
                            // First make sure the field table still exists and that
811
                            // there is a field with the corresponding id in it
812
                            $newField = !((boolean)$field->get('id') && $field->tableExists() && $field->exists());
813
                            // If the field has not been found, erase the id from $_POST
814
                            if ($newField) {
815
                                $field->set('id', false);
816
                            }
817
818
                            // Save data
819
                            $field->commit();
820
                            // Get the new id
821
                            $field_id = $field->get('id');
822
823
                            if ($field_id) {
824
                                if ($newField) {
825
                                    /**
826
                                     * After creation of a Field.
827
                                     *
828
                                     * @delegate FieldPostCreate
829
                                     * @param string $context
830
                                     * '/blueprints/sections/'
831
                                     * @param Field $field
832
                                     *  The Field object, passed by reference
833
                                     * @param array $data
834
                                     *  The settings for ths `$field`, passed by reference
835
                                     */
836
                                    Symphony::ExtensionManager()->notifyMembers('FieldPostCreate', '/blueprints/sections/', array('field' => &$field, 'data' => &$data));
837
                                } else {
838
                                    /**
839
                                     * After editing of a Field.
840
                                     *
841
                                     * @delegate FieldPostEdit
842
                                     * @param string $context
843
                                     * '/blueprints/sections/'
844
                                     * @param Field $field
845
                                     *  The Field object, passed by reference
846
                                     * @param array $data
847
                                     *  The settings for ths `$field`, passed by reference
848
                                     */
849
                                    Symphony::ExtensionManager()->notifyMembers('FieldPostEdit', '/blueprints/sections/', array('field' => &$field, 'data' => &$data));
850
                                }
851
                            }
852
                        }
853
                    }
854
855
                    if (!$edit) {
856
                        /**
857
                         * After the Section has been created, and all the Field's have been
858
                         * created for this section, but just before the redirect
859
                         *
860
                         * @delegate SectionPostCreate
861
                         * @since Symphony 2.2
862
                         * @param string $context
863
                         * '/blueprints/sections/'
864
                         * @param integer $section_id
865
                         *  The newly created Section ID.
866
                         */
867
                        Symphony::ExtensionManager()->notifyMembers('SectionPostCreate', '/blueprints/sections/', array('section_id' => $section_id));
868
869
                        redirect(SYMPHONY_URL . "/blueprints/sections/edit/$section_id/created/");
870
                    } else {
871
                        /**
872
                         * After the Section has been updated, and all the Field's have been
873
                         * updated for this section, but just before the redirect
874
                         *
875
                         * @delegate SectionPostEdit
876
                         * @since Symphony 2.2
877
                         * @param string $context
878
                         * '/blueprints/sections/'
879
                         * @param integer $section_id
880
                         *  The edited Section ID.
881
                         */
882
                        Symphony::ExtensionManager()->notifyMembers('SectionPostEdit', '/blueprints/sections/', array('section_id' => $section_id));
883
884
                        redirect(SYMPHONY_URL . "/blueprints/sections/edit/$section_id/saved/");
885
                    }
886
                }
887
            }
888
        }
889
890
        if (@array_key_exists('delete', $_POST['action'])) {
891
            $section_id = array($this->_context[1]);
892
            $canProceed = $this->validateTimestamp($section_id);
0 ignored issues
show
Documentation introduced by
$section_id is of type array<integer,?,{"0":"?"}>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
893
894
            if ($canProceed) {
895
                /**
896
                 * Just prior to calling the Section Manager's delete function
897
                 *
898
                 * @delegate SectionPreDelete
899
                 * @since Symphony 2.2
900
                 * @param string $context
901
                 * '/blueprints/sections/'
902
                 * @param array $section_ids
903
                 *  An array of Section ID's passed by reference
904
                 */
905
                Symphony::ExtensionManager()->notifyMembers('SectionPreDelete', '/blueprints/sections/', array('section_ids' => &$section_id));
906
907
                foreach ($section_id as $section) {
908
                    SectionManager::delete($section);
909
                }
910
911
                redirect(SYMPHONY_URL . '/blueprints/sections/');
912
            } else {
913
                $this->addTimestampValidationPageAlert(
914
                    $this->_errors['timestamp'],
915
                    SectionManager::fetch($section_id),
0 ignored issues
show
Bug introduced by
It seems like \SectionManager::fetch($section_id) targeting SectionManager::fetch() can also be of type array; however, AdministrationPage::addT...mpValidationPageAlert() does only seem to accept object<Entry>|object<Section>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
916
                    'delete'
917
                );
918
            }
919
        }
920
    }
921
922
    public function __actionEdit()
923
    {
924
        return $this->__actionNew();
925
    }
926
927
    public function addSectionOptions(array &$meta = null)
928
    {
929
        $fieldset = new XMLElement('fieldset');
930
        $fieldset->setAttribute('class', 'settings');
931
        $fieldset->appendChild(new XMLElement('legend', __('Options')));
932
933
        $div = new XMLElement('div', null, array('class' => 'two columns'));
934
935
        $hidediv = new XMLElement('div', null, array('class' => 'column'));
936
        $label = Widget::Checkbox('meta[hidden]', $meta['hidden'], __('Hide this section from the back-end menu'));
937
        $hidediv->appendChild($label);
938
        $div->appendChild($hidediv);
939
940
        $filterdiv = new XMLElement('div', null, array('class' => 'column'));
941
        $label = Widget::Checkbox('meta[filter]', $meta['filter'], __('Allow filtering of section entries'));
942
        $filterdiv->appendChild($label);
943
944
        $div->appendChild($filterdiv);
945
        $fieldset->appendChild($div);
946
        $this->Form->appendChild($fieldset);
947
948
        /**
949
         * Allows extensions to add elements to the header of the Section Editor
950
         * form. Usually for section settings, this delegate is passed the current
951
         * `$meta` array and the `$this->_errors` array.
952
         *
953
         * @delegate AddSectionElements
954
         * @since Symphony 2.2
955
         * @param string $context
956
         * '/blueprints/sections/'
957
         * @param XMLElement $form
958
         *  An XMLElement of the current `$this->Form`, just after the Section
959
         *  settings have been appended, but before the Fields duplicator
960
         * @param array $meta
961
         *  The current $_POST['meta'] array
962
         * @param array $errors
963
         *  The current errors array
964
         */
965
        Symphony::ExtensionManager()->notifyMembers('AddSectionElements', '/blueprints/sections/', array(
966
            'form' => &$this->Form,
967
            'meta' => &$meta,
968
            'errors' => &$this->_errors
969
        ));
970
    }
971
972
    /**
973
     * Given $_POST values, this function will validate the current timestamp
974
     * and set the proper error messages.
975
     *
976
     * @since Symphony 2.7.0
977
     * @param  int $section_id
978
     *   The entry id to validate
979
     * @return boolean
980
     *   true if the timestamp is valid
981
     */
982 View Code Duplication
    protected function validateTimestamp($section_id, $checkMissing = false)
983
    {
984
        if (!isset($_POST['action']['ignore-timestamp'])) {
985
            if ($checkMissing && !isset($_POST['action']['timestamp'])) {
986
                if (isset($this->_errors) && is_array($this->_errors)) {
987
                    $this->_errors['timestamp'] = __('The section could not be saved due to conflicting changes');
988
                }
989
                return false;
990
            } elseif (isset($_POST['action']['timestamp'])) {
991
                $tv = new TimestampValidator('sections');
992
                if (!$tv->check($section_id, $_POST['action']['timestamp'])) {
993
                    if (isset($this->_errors) && is_array($this->_errors)) {
994
                        $this->_errors['timestamp'] = __('The section could not be saved due to conflicting changes');
995
                    }
996
                    return false;
997
                }
998
            }
999
        }
1000
        return true;
1001
    }
1002
}
1003