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
Pull Request — integration (#2604)
by Brendan
05:01
created

contentBlueprintsEvents   F

Complexity

Total Complexity 102

Size/Duplication

Total Lines 710
Duplicated Lines 30.56 %

Coupling/Cohesion

Components 0
Dependencies 17

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 217
loc 710
rs 1.263
wmc 102
lcom 0
cbo 17

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __viewIndex() 7 9 1
A __viewNew() 0 4 1
F __form() 63 336 52
A __viewEdit() 0 4 1
A __viewInfo() 0 4 1
A __actionNew() 0 6 2
F __formAction() 103 272 31
A __injectAboutInformation() 10 10 4
A __injectFilters() 0 9 3
B __actionEdit() 34 37 5
A __actionIndex() 0 4 1

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

1
<?php
2
    /**
3
     * @package content
4
     */
5
6
    /**
7
     * The Event Editor allows a developer to create events that typically
8
     * allow Frontend forms to populate Sections or edit Entries.
9
     */
10
    class contentBlueprintsEvents extends ResourcesPage
11
    {
12
        public $_errors = array();
13
14 View Code Duplication
        public function __viewIndex($resource_type)
15
        {
16
            parent::__viewIndex(ResourceManager::RESOURCE_TYPE_EVENT);
17
18
            $this->setTitle(__('%1$s &ndash; %2$s', array(__('Events'), __('Symphony'))));
19
            $this->appendSubheading(__('Events'),
20
                Widget::Anchor(__('Create New'), Administration::instance()->getCurrentPageURL() . 'new/',
21
                    __('Create a new event'), 'create button', null, array('accesskey' => 'c')));
22
        }
23
24
        public function __viewNew()
25
        {
26
            $this->__form();
27
        }
28
29
        public function __form($readonly = false)
0 ignored issues
show
Coding Style introduced by
__form 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...
30
        {
31
            $formHasErrors = (is_array($this->_errors) && !empty($this->_errors));
32
33 View Code Duplication
            if ($formHasErrors) {
34
                $this->pageAlert(
35
                    __('An error occurred while processing this form. See below for details.'),
36
                    Alert::ERROR
37
                );
38
39
                // These alerts are only valid if the form doesn't have errors
40
            } elseif (isset($this->_context['flag'])) {
41
                $time = Widget::Time();
42
43
                switch ($this->_context['flag']) {
44
                    case 'saved':
45
                        $message = __('Event updated at %s.', array($time->generate()));
46
                        break;
47
                    case 'created':
48
                        $message = __('Event created at %s.', array($time->generate()));
49
                }
50
51
                $this->pageAlert(
52
                    $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...
53
                    . ' <a href="' . SYMPHONY_URL . '/blueprints/events/new/" accesskey="c">'
54
                    . __('Create another?')
55
                    . '</a> <a href="' . SYMPHONY_URL . '/blueprints/events/" accesskey="a">'
56
                    . __('View all Events')
57
                    . '</a>',
58
                    Alert::SUCCESS
59
                );
60
            }
61
62
            $isEditing = ($readonly ? true : false);
63
            $fields = array("name" => null, "filters" => null);
64
            $about = array("name" => null);
65
            $providers = Symphony::ExtensionManager()->getProvidersOf(iProvider::EVENT);
66
67
            if (isset($_POST['fields'])) {
68
                $fields = $_POST['fields'];
69
70
                if ($this->_context['action'] === 'edit') {
71
                    $isEditing = true;
72
                }
73
            } elseif ($this->_context['action'] === 'edit' || $this->_context['action'] === 'info') {
74
                $isEditing = true;
75
                $handle = $this->_context['handle'];
76
                $existing = EventManager::create($handle);
77
                $about = $existing->about();
0 ignored issues
show
Bug introduced by
The method about() does not seem to exist on object<Event>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
78
79
                if ($this->_context['action'] === 'edit' && !$existing->allowEditorToParse()) {
80
                    redirect(SYMPHONY_URL . '/blueprints/events/info/' . $handle . '/');
81
                }
82
83
                $fields['name'] = $about['name'];
84
                $fields['source'] = $existing->getSource();
85
                $provided = false;
86
87 View Code Duplication
                if (!empty($providers)) {
88
                    foreach ($providers as $providerClass => $provider) {
89
                        if ($fields['source'] === call_user_func(array($providerClass, 'getClass'))) {
90
                            $fields = array_merge($fields, $existing->settings());
0 ignored issues
show
Bug introduced by
The method settings() does not seem to exist on object<Event>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
91
                            $provided = true;
92
                            break;
93
                        }
94
                    }
95
                }
96
97
                if (!$provided) {
98
                    if (isset($existing->eParamFILTERS)) {
99
                        $fields['filters'] = $existing->eParamFILTERS;
0 ignored issues
show
Bug introduced by
The property eParamFILTERS does not seem to exist in Event.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
100
                    }
101
                }
102
            }
103
104
            // Handle name on edited changes, or from reading an edited datasource
105 View Code Duplication
            if (isset($about['name'])) {
106
                $name = $about['name'];
107
            } elseif (isset($fields['name'])) {
108
                $name = $fields['name'];
109
            }
110
111
            $this->setPageType('form');
112
            $this->setTitle(__(($isEditing ? '%1$s &ndash; %2$s &ndash; %3$s' : '%2$s &ndash; %3$s'),
113
                array($name, __('Events'), __('Symphony'))));
0 ignored issues
show
Bug introduced by
The variable $name 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...
114
            $this->appendSubheading(($isEditing ? $about['name'] : __('Untitled')));
115
            $this->insertBreadcrumbs(array(
116
                Widget::Anchor(__('Events'), SYMPHONY_URL . '/blueprints/events/'),
117
            ));
118
119
            if (!$readonly) {
120
                $fieldset = new XMLElement('fieldset');
121
                $fieldset->setAttribute('class', 'settings');
122
                $fieldset->appendChild(new XMLElement('legend', __('Essentials')));
123
124
                // Target
125
                $sources = new XMLElement('div', null, array('class' => 'apply actions'));
126
                $div = new XMLElement('div');
127
                $label = Widget::Label(__('Target'), null, 'apply-label-left');
128
                $sources->appendChild($label);
129
                $sources->appendChild($div);
130
131
                $sections = SectionManager::fetch(null, 'ASC', 'name');
132
                $options = array();
133
                $section_options = array();
134
                $source = isset($fields['source']) ? $fields['source'] : null;
135
136
                if (is_array($sections) && !empty($sections)) {
137
                    $section_options = array('label' => __('Sections'), 'options' => array());
138
139 View Code Duplication
                    foreach ($sections as $s) {
140
                        $section_options['options'][] = array(
141
                            $s->get('id'),
142
                            $source === $s->get('id'),
143
                            General::sanitize($s->get('name'))
144
                        );
145
                    }
146
                }
147
148
                $options[] = $section_options;
149
150
                // Loop over the event providers
151
                if (!empty($providers)) {
152
                    $p = array('label' => __('From extensions'), 'options' => array());
153
154
                    foreach ($providers as $providerClass => $provider) {
155
                        $p['options'][] = array(
156
                            $providerClass,
157
                            ($fields['source'] === $providerClass),
158
                            $provider
159
                        );
160
                    }
161
162
                    $options[] = $p;
163
                }
164
165
                $div->appendChild(
166
                    Widget::Select('source', $options, array('id' => 'event-context'))
167
                );
168
169
                if (isset($this->_errors['source'])) {
170
                    $this->Context->prependChild(Widget::Error($sources, $this->_errors['source']));
171
                } else {
172
                    $this->Context->prependChild($sources);
173
                }
174
175
                $this->Form->appendChild(
176
                    Widget::Input('fields[source]', $options[0]['options'][0][0], 'hidden',
177
                        array('id' => 'event-source'))
178
                );
179
180
                // Name
181
                $group = new XMLElement('div');
182
                $label = Widget::Label(__('Name'));
183
                $label->appendChild(Widget::Input('fields[name]', General::sanitize($fields['name'])));
184
185
                $div = new XMLElement('div');
186
                $div->setAttribute('class', 'column');
187
188 View Code Duplication
                if (isset($this->_errors['name'])) {
189
                    $div->appendChild(Widget::Error($label, $this->_errors['name']));
190
                } else {
191
                    $div->appendChild($label);
192
                }
193
                $group->appendChild($div);
194
                $fieldset->appendChild($group);
195
                $this->Form->appendChild($fieldset);
196
197
                // Filters
198
                $fieldset = new XMLElement('fieldset');
199
                $fieldset->setAttribute('class', 'settings pickable');
200
                $fieldset->appendChild(new XMLElement('legend', __('Filters')));
201
                $p = new XMLElement('p', __('Event Filters add additional conditions or actions to an event.'));
202
                $p->setAttribute('class', 'help');
203
                $fieldset->appendChild($p);
204
205
                $filters = isset($fields['filters']) ? $fields['filters'] : array();
206
                $options = array(
207
                    array('admin-only', in_array('admin-only', $filters), __('Admin Only')),
208
                    array('send-email', in_array('send-email', $filters), __('Send Notification Email')),
209
                    array('expect-multiple', in_array('expect-multiple', $filters), __('Allow Multiple')),
210
                );
211
212
                /**
213
                 * Allows adding of new filter rules to the Event filter rule select box
214
                 *
215
                 * @delegate AppendEventFilter
216
                 * @param string $context
217
                 * '/blueprints/events/(edit|new|info)/'
218
                 * @param array $selected
219
                 *  An array of all the selected filters for this Event
220
                 * @param array $options
221
                 *  An array of all the filters that are available, passed by reference
222
                 */
223
                Symphony::ExtensionManager()->notifyMembers('AppendEventFilter',
224
                    '/blueprints/events/' . $this->_context['action'] . '/', array(
225
                        'selected' => $filters,
226
                        'options' => &$options
227
                    ));
228
229
                $fieldset->appendChild(Widget::Select('fields[filters][]', $options,
230
                    array('multiple' => 'multiple', 'id' => 'event-filters')));
231
                $this->Form->appendChild($fieldset);
232
233
                // Connections
234
                $fieldset = new XMLElement('fieldset');
235
                $fieldset->setAttribute('class', 'settings');
236
                $fieldset->appendChild(new XMLElement('legend', __('Attach to Pages')));
237
                $p = new XMLElement('p', __('The event will only be available on the selected pages.'));
238
                $p->setAttribute('class', 'help');
239
                $fieldset->appendChild($p);
240
241
                $div = new XMLElement('div');
242
                $label = Widget::Label(__('Pages'));
243
244
                $pages = PageManager::fetch();
245
                $event_handle = str_replace('-', '_', Lang::createHandle($fields['name']));
246
                $connections = ResourceManager::getAttachedPages(ResourceManager::RESOURCE_TYPE_EVENT, $event_handle);
247
                $selected = array();
248
249
                foreach ($connections as $connection) {
250
                    $selected[] = $connection['id'];
251
                }
252
253
                $options = array();
254
255 View Code Duplication
                foreach ($pages as $page) {
0 ignored issues
show
Bug introduced by
The expression $pages of type array|null 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...
256
                    $options[] = array(
257
                        $page['id'],
258
                        in_array($page['id'], $selected),
259
                        PageManager::resolvePageTitle($page['id'])
260
                    );
261
                }
262
263
                $label->appendChild(Widget::Select('fields[connections][]', $options, array('multiple' => 'multiple')));
264
                $div->appendChild($label);
265
266
                $fieldset->appendChild($div);
267
                $this->Form->appendChild($fieldset);
268
269
                // Providers
270
                if (!empty($providers)) {
271
                    foreach ($providers as $providerClass => $provider) {
272
                        if ($isEditing && $fields['source'] !== call_user_func(array($providerClass, 'getSource'))) {
273
                            continue;
274
                        }
275
276
                        call_user_func_array(array($providerClass, 'buildEditor'),
277
                            array($this->Form, &$this->_errors, $fields, $handle));
0 ignored issues
show
Bug introduced by
The variable $handle 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...
278
                    }
279
                }
280
            } else {
281
                // Author
282
                if (isset($about['author']['website'])) {
283
                    $link = Widget::Anchor($about['author']['name'], General::validateURL($about['author']['website']));
284
                } elseif (isset($about['author']['email'])) {
285
                    $link = Widget::Anchor($about['author']['name'], 'mailto:' . $about['author']['email']);
286
                } else {
287
                    $link = $about['author']['name'];
288
                }
289
290
                if ($link) {
291
                    $fieldset = new XMLElement('fieldset');
292
                    $fieldset->setAttribute('class', 'settings');
293
                    $fieldset->appendChild(new XMLElement('legend', __('Author')));
294
                    $fieldset->appendChild(new XMLElement('p', $link->generate(false)));
295
                    $this->Form->appendChild($fieldset);
296
                }
297
298
                // Version
299
                $fieldset = new XMLElement('fieldset');
300
                $fieldset->setAttribute('class', 'settings');
301
                $fieldset->appendChild(new XMLElement('legend', __('Version')));
302
                $version = array_key_exists('version', $about) ? $about['version'] : null;
303
                $release_date = array_key_exists('release-date',
304
                    $about) ? $about['release-date'] : filemtime(EventManager::__getDriverPath($handle));
305
306
                if (preg_match('/^\d+(\.\d+)*$/', $version)) {
307
                    $fieldset->appendChild(
308
                        new XMLElement('p', __('%1$s released on %2$s',
309
                            array($version, DateTimeObj::format($release_date, __SYM_DATE_FORMAT__))))
310
                    );
311 View Code Duplication
                } elseif (!is_null($version)) {
312
                    $fieldset->appendChild(
313
                        new XMLElement('p', __('Created by %1$s at %2$s',
314
                            array($version, DateTimeObj::format($release_date, __SYM_DATE_FORMAT__))))
315
                    );
316
                } else {
317
                    $fieldset->appendChild(
318
                        new XMLElement('p',
319
                            __('Last modified on %s', array(DateTimeObj::format($release_date, __SYM_DATE_FORMAT__))))
320
                    );
321
                }
322
                $this->Form->appendChild($fieldset);
323
            }
324
325
            // If we are editing an event, it assumed that the event has documentation
326
            $fieldset = new XMLElement('fieldset');
327
            $fieldset->setAttribute('id', 'event-documentation');
328
            $fieldset->setAttribute('class', 'settings');
329
330
            if ($isEditing && method_exists($existing, 'documentation')) {
331
                $doc = $existing->documentation();
0 ignored issues
show
Bug introduced by
The variable $existing 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...
332
333
                if ($doc) {
334
                    $fieldset->setValue(
335
                        '<legend>' . __('Documentation') . '</legend>' . PHP_EOL .
336
                        General::tabsToSpaces(is_object($doc) ? $doc->generate(true, 4) : $doc)
337
                    );
338
                }
339
            }
340
341
            $this->Form->appendChild($fieldset);
342
343
            $div = new XMLElement('div');
344
            $div->setAttribute('class', 'actions');
345
            $div->appendChild(Widget::Input('action[save]', ($isEditing ? __('Save Changes') : __('Create Event')),
346
                'submit', array('accesskey' => 's')));
347
348 View Code Duplication
            if ($isEditing) {
349
                $button = new XMLElement('button', __('Delete'));
350
                $button->setAttributeArray(array(
351
                    'name' => 'action[delete]',
352
                    'class' => 'button confirm delete',
353
                    'title' => __('Delete this event'),
354
                    'type' => 'submit',
355
                    'accesskey' => 'd',
356
                    'data-message' => __('Are you sure you want to delete this event?')
357
                ));
358
                $div->appendChild($button);
359
            }
360
361
            if (!$readonly) {
362
                $this->Form->appendChild($div);
363
            }
364
        }
365
366
        public function __viewEdit()
367
        {
368
            $this->__form();
369
        }
370
371
        public function __viewInfo()
372
        {
373
            $this->__form(true);
374
        }
375
376
        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...
377
        {
378
            if (array_key_exists('save', $_POST['action'])) {
379
                return $this->__formAction();
380
            }
381
        }
382
383
        public function __formAction()
0 ignored issues
show
Coding Style introduced by
__formAction 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...
384
        {
385
            $fields = $_POST['fields'];
386
            $this->_errors = array();
387
            $providers = Symphony::ExtensionManager()->getProvidersOf(iProvider::EVENT);
388
            $providerClass = null;
389
390 View Code Duplication
            if (trim($fields['name']) === '') {
391
                $this->_errors['name'] = __('This is a required field');
392
            }
393
394 View Code Duplication
            if (trim($fields['source']) === '') {
395
                $this->_errors['source'] = __('This is a required field');
396
            }
397
398
            $filters = isset($fields['filters']) ? $fields['filters'] : array();
399
400
            // See if a Provided Datasource is saved
401 View Code Duplication
            if (!empty($providers)) {
402
                foreach ($providers as $providerClass => $provider) {
403
                    if ($fields['source'] === call_user_func(array($providerClass, 'getSource'))) {
404
                        call_user_func_array(array($providerClass, 'validate'), array(&$fields, &$this->_errors));
405
                        break;
406
                    }
407
408
                    unset($providerClass);
409
                }
410
            }
411
412
            $classname = Lang::createHandle($fields['name'], 255, '_', false, true,
413
                array('@^[^a-z\d]+@i' => '', '/[^\w-\.]/i' => ''));
414
            $rootelement = str_replace('_', '-', $classname);
415
            $extends = 'SectionEvent';
416
417
            // Check to make sure the classname is not empty after handlisation.
418 View Code Duplication
            if (empty($classname) && !isset($this->_errors['name'])) {
419
                $this->_errors['name'] = __('Please ensure name contains at least one Latin-based character.',
420
                    array($classname));
421
            }
422
423
            $file = EVENTS . '/event.' . $classname . '.php';
424
            $isDuplicate = false;
425
            $queueForDeletion = null;
426
427 View Code Duplication
            if ($this->_context['action'] === 'new' && is_file($file)) {
428
                $isDuplicate = true;
429
            } elseif ($this->_context['action'] === 'edit') {
430
                $existing_handle = $this->_context['handle'];
431
432
                if ($classname !== $existing_handle && is_file($file)) {
433
                    $isDuplicate = true;
434
                } elseif ($classname !== $existing_handle) {
435
                    $queueForDeletion = EVENTS . '/event.' . $existing_handle . '.php';
436
                }
437
            }
438
439
            // Duplicate
440 View Code Duplication
            if ($isDuplicate) {
441
                $this->_errors['name'] = __('An Event with the name %s already exists',
442
                    array('<code>' . $classname . '</code>'));
443
            }
444
445
            if (empty($this->_errors)) {
446
                $source = $fields['source'];
447
                $params = array(
448
                    'rootelement' => $rootelement,
449
                );
450
451
                $about = array(
452
                    'name' => $fields['name'],
453
                    'version' => 'Symphony ' . Symphony::Configuration()->get('version', 'symphony'),
454
                    'release date' => DateTimeObj::getGMT('c'),
455
                    'author name' => Symphony::Author()->getFullName(),
456
                    'author website' => URL,
457
                    'author email' => Symphony::Author()->get('email')
458
                );
459
460
                // If there is a provider, get their template
461
                if ($providerClass) {
462
                    $eventShell = file_get_contents(call_user_func(array($providerClass, 'getTemplate')));
463
                } else {
464
                    $eventShell = file_get_contents($this->getTemplate('blueprints.event'));
465
                    $about['trigger condition'] = $rootelement;
466
                }
467
468
                $this->__injectAboutInformation($eventShell, $about);
469
470
                // Replace the name
471
                $eventShell = str_replace('<!-- CLASS NAME -->', $classname, $eventShell);
472
473
                // Build the templates
474
                if ($providerClass) {
475
                    $eventShell = call_user_func(array($providerClass, 'prepare'), $fields, $params, $eventShell);
476
                } else {
477
                    $this->__injectFilters($eventShell, $filters);
478
479
                    // Add Documentation
480
                    $ajaxEventDoc = new contentAjaxEventDocumentation();
481
                    $doc_parts = array();
482
483
                    // Add Documentation (Success/Failure)
484
                    $ajaxEventDoc->addEntrySuccessDoc($doc_parts, $rootelement, $filters);
485
                    $ajaxEventDoc->addEntryFailureDoc($doc_parts, $rootelement, $filters);
486
487
                    // Filters
488
                    $ajaxEventDoc->addDefaultFiltersDoc($doc_parts, $rootelement, $filters);
489
490
                    // Frontend Markup
491
                    $ajaxEventDoc->addFrontendMarkupDoc($doc_parts, $rootelement, $fields['source'], $filters);
492
                    $ajaxEventDoc->addSendMailFilterDoc($doc_parts, $filters);
493
494
                    /**
495
                     * Allows adding documentation for new filters. A reference to the $documentation
496
                     * array is provided, along with selected filters
497
                     *
498
                     * @delegate AppendEventFilterDocumentation
499
                     * @param string $context
500
                     * '/blueprints/events/(edit|new|info)/'
501
                     * @param array $selected
502
                     *  An array of all the selected filters for this Event
503
                     * @param array $documentation
504
                     *  An array of all the documentation XMLElements, passed by reference
505
                     * @param string $rootelment
506
                     *  The name of this event, as a handle.
507
                     */
508
                    Symphony::ExtensionManager()->notifyMembers('AppendEventFilterDocumentation', '/blueprints/events/',
509
                        array(
510
                            'selected' => $filters,
511
                            'documentation' => &$doc_parts,
512
                            'rootelement' => $rootelement
513
                        ));
514
515
                    $documentation = join(PHP_EOL, array_map(function ($part) {
516
                        return rtrim($part->generate(true, 4));
517
                    }, $doc_parts));
518
                    $documentation = str_replace('\'', '\\\'', $documentation);
519
520
                    $eventShell = str_replace('<!-- CLASS EXTENDS -->', $extends, $eventShell);
521
                    $eventShell = str_replace('<!-- DOCUMENTATION -->', General::tabsToSpaces($documentation, 4),
522
                        $eventShell);
523
                }
524
525
                $eventShell = str_replace('<!-- ROOT ELEMENT -->', $rootelement, $eventShell);
526
                $eventShell = str_replace('<!-- CLASS NAME -->', $classname, $eventShell);
527
                $eventShell = str_replace('<!-- SOURCE -->', $source, $eventShell);
528
529
                // Remove left over placeholders
530
                $eventShell = preg_replace(array('/<!--[\w ]++-->/'), '', $eventShell);
531
532
                if ($this->_context['action'] === 'new') {
533
                    /**
534
                     * Prior to creating an Event, the file path where it will be written to
535
                     * is provided and well as the contents of that file.
536
                     *
537
                     * @delegate EventsPreCreate
538
                     * @since Symphony 2.2
539
                     * @param string $context
540
                     * '/blueprints/events/'
541
                     * @param string $file
542
                     *  The path to the Event file
543
                     * @param string $contents
544
                     *  The contents for this Event as a string passed by reference
545
                     * @param array $filters
546
                     *  An array of the filters attached to this event
547
                     */
548
                    Symphony::ExtensionManager()->notifyMembers('EventPreCreate', '/blueprints/events/', array(
549
                        'file' => $file,
550
                        'contents' => &$eventShell,
551
                        'filters' => $filters
552
                    ));
553
                } else {
554
                    /**
555
                     * Prior to editing an Event, the file path where it will be written to
556
                     * is provided and well as the contents of that file.
557
                     *
558
                     * @delegate EventPreEdit
559
                     * @since Symphony 2.2
560
                     * @param string $context
561
                     * '/blueprints/events/'
562
                     * @param string $file
563
                     *  The path to the Event file
564
                     * @param string $contents
565
                     *  The contents for this Event as a string passed by reference
566
                     * @param array $filters
567
                     *  An array of the filters attached to this event
568
                     */
569
                    Symphony::ExtensionManager()->notifyMembers('EventPreEdit', '/blueprints/events/', array(
570
                        'file' => $file,
571
                        'contents' => &$eventShell,
572
                        'filters' => $filters
573
                    ));
574
                }
575
576
                // Write the file
577 View Code Duplication
                if (!is_writable(dirname($file)) || !General::writeFile($file, $eventShell,
578
                        Symphony::Configuration()->get('write_mode', 'file'))
579
                ) {
580
                    $this->pageAlert(
581
                        __('Failed to write Event to disk.')
582
                        . ' ' . __('Please check permissions on %s.', array('<code>/workspace/events</code>')),
583
                        Alert::ERROR
584
                    );
585
586
                    // Write successful
587
                } else {
588
                    if (function_exists('opcache_invalidate')) {
589
                        opcache_invalidate($file, true);
590
                    }
591
592
                    // Attach this event to pages
593
                    $connections = $fields['connections'];
594
                    ResourceManager::setPages(ResourceManager::RESOURCE_TYPE_EVENT,
595
                        is_null($existing_handle) ? $classname : $existing_handle, $connections);
0 ignored issues
show
Bug introduced by
The variable $existing_handle 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...
596
597
                    if ($queueForDeletion) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $queueForDeletion of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
598
                        General::deleteFile($queueForDeletion);
599
600
                        $pages = PageManager::fetch(false, array('events', 'id'), array(
601
                            "
602
                        `events` REGEXP '[[:<:]]" . $existing_handle . "[[:>:]]'
603
                    "
604
                        ));
605
606
                        if (is_array($pages) && !empty($pages)) {
607
                            foreach ($pages as $page) {
608
                                $page['events'] = preg_replace('/\b' . $existing_handle . '\b/i', $classname,
609
                                    $page['events']);
610
611
                                PageManager::edit($page['id'], $page);
612
                            }
613
                        }
614
                    }
615
616
                    if ($this->_context['action'] === 'new') {
617
                        /**
618
                         * After creating the Event, the path to the Event file is provided
619
                         *
620
                         * @delegate EventPostCreate
621
                         * @since Symphony 2.2
622
                         * @param string $context
623
                         * '/blueprints/events/'
624
                         * @param string $file
625
                         *  The path to the Event file
626
                         */
627
                        Symphony::ExtensionManager()->notifyMembers('EventPostCreate', '/blueprints/events/', array(
628
                            'file' => $file
629
                        ));
630
                    } else {
631
                        /**
632
                         * After editing the Event, the path to the Event file is provided
633
                         *
634
                         * @delegate EventPostEdit
635
                         * @since Symphony 2.2
636
                         * @param string $context
637
                         * '/blueprints/events/'
638
                         * @param string $file
639
                         *  The path to the Event file
640
                         * @param string $previous_file
641
                         *  The path of the previous Event file in the case where an Event may
642
                         *  have been renamed. To get the handle from this value, see
643
                         *  `EventManager::__getHandleFromFilename`
644
                         */
645
                        Symphony::ExtensionManager()->notifyMembers('EventPostEdit', '/blueprints/events/', array(
646
                            'file' => $file,
647
                            'previous_file' => ($queueForDeletion) ? $queueForDeletion : null
648
                        ));
649
                    }
650
651
                    redirect(SYMPHONY_URL . '/blueprints/events/edit/' . $classname . '/' . ($this->_context['action'] === 'new' ? 'created' : 'saved') . '/');
652
                }
653
            }
654
        }
655
656 View Code Duplication
        public function __injectAboutInformation(&$shell, $details)
657
        {
658
            if (!is_array($details) || empty($details)) {
659
                return;
660
            }
661
662
            foreach ($details as $key => $val) {
663
                $shell = str_replace('<!-- ' . strtoupper($key) . ' -->', addslashes($val), $shell);
664
            }
665
        }
666
667
        public function __injectFilters(&$shell, $elements)
668
        {
669
            if (!is_array($elements) || empty($elements)) {
670
                return;
671
            }
672
673
            $shell = str_replace('<!-- FILTERS -->', "'" . implode("'," . PHP_EOL . "\t\t\t\t'", $elements) . "'",
674
                $shell);
675
        }
676
677 View Code Duplication
        public function __actionEdit()
0 ignored issues
show
Coding Style introduced by
__actionEdit uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
678
        {
679
            if (array_key_exists('save', $_POST['action'])) {
680
                return $this->__formAction();
681
            } elseif (array_key_exists('delete', $_POST['action'])) {
682
                /**
683
                 * Prior to deleting the Event file. Target file path is provided.
684
                 *
685
                 * @delegate EventPreDelete
686
                 * @since Symphony 2.2
687
                 * @param string $context
688
                 * '/blueprints/events/'
689
                 * @param string $file
690
                 *  The path to the Event file
691
                 */
692
                Symphony::ExtensionManager()->notifyMembers('EventPreDelete', '/blueprints/events/',
693
                    array('file' => EVENTS . "/event." . $this->_context['handle'] . ".php"));
694
695
                if (!General::deleteFile(EVENTS . '/event.' . $this->_context['handle'] . '.php')) {
696
                    $this->pageAlert(
697
                        __('Failed to delete %s.', array('<code>' . $this->_context['handle'] . '</code>'))
698
                        . ' ' . __('Please check permissions on %s.', array('<code>/workspace/events</code>')),
699
                        Alert::ERROR
700
                    );
701
                } else {
702
                    $pages = ResourceManager::getAttachedPages(ResourceManager::RESOURCE_TYPE_EVENT,
703
                        $this->_context['handle']);
704
705
                    foreach ($pages as $page) {
706
                        ResourceManager::detach(ResourceManager::RESOURCE_TYPE_EVENT, $this->_context['handle'],
707
                            $page['id']);
708
                    }
709
710
                    redirect(SYMPHONY_URL . '/blueprints/events/');
711
                }
712
            }
713
        }
714
715
        public function __actionIndex($resource_type)
716
        {
717
            return parent::__actionIndex(ResourceManager::RESOURCE_TYPE_EVENT);
718
        }
719
    }
720