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

contentPublish   F

Complexity

Total Complexity 264

Size/Duplication

Total Lines 1903
Duplicated Lines 7.15 %

Coupling/Cohesion

Components 1
Dependencies 22

Importance

Changes 13
Bugs 2 Features 0
Metric Value
c 13
b 2
f 0
dl 136
loc 1903
rs 0.6314
wmc 264
lcom 1
cbo 22

27 Methods

Rating   Name   Duplication   Size   Complexity  
A parseContext() 0 16 2
C sort() 6 53 12
A build() 13 13 2
A action() 0 4 1
B __switchboard() 0 24 5
A view() 0 4 1
A createFilteringInterface() 0 17 3
A createFilteringDrawer() 0 7 1
A createFilteringDuplicator() 0 17 1
B createFieldFilters() 0 34 4
A createFilterComparisons() 0 18 3
A getFilterQuery() 0 14 4
B createFilter() 0 42 1
A createFilterSuggestions() 0 15 2
A createFilterHelp() 0 13 2
B createSystemDateFilters() 0 41 3
C __actionIndex() 0 112 8
D __viewNew() 34 123 21
A getPrepopulateString() 0 19 4
B getFilterString() 0 29 6
B __wrapFieldWithDiv() 0 40 5
B isFieldHidden() 0 14 5
D prepareAssociationsDrawer() 0 209 36
D __actionNew() 7 99 20
F __viewEdit() 41 217 30
D __actionEdit() 14 106 10
F __viewIndex() 21 517 72

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

1
<?php
2
3
    /**
4
     * @package content
5
     */
6
7
    /**
8
     * The Publish page is where the majority of an Authors time will
9
     * be spent in Symphony with adding, editing and removing entries
10
     * from Sections. This Page controls the entries table as well as
11
     * the Entry creation screens.
12
     */
13
    class contentPublish extends AdministrationPage
14
    {
15
        public $_errors = array();
16
17
        /**
18
         * The Pages page has /action/id/flag/ context.
19
         * eg. /edit/1/saved/
20
         *
21
         * @param array $context
22
         * @param array $parts
23
         * @return array
24
         */
25
        public function parseContext(array &$context, array $parts)
26
        {
27
            // Order is important!
28
            $params = array_fill_keys(array('section_handle', 'page', 'entry_id', 'flag'), null);
29
            $params['section_handle'] = $parts[1];
30
31
            if (isset($parts[2])) {
32
                $extras = preg_split('/\//', $parts[2], -1, PREG_SPLIT_NO_EMPTY);
33
                list($params['page'], $params['entry_id'], $params['flag']) = $extras;
34
                $params['entry_id'] = (int)$params['entry_id'];
35
            } else {
36
                $params['page'] = 'index';
37
            }
38
39
            $context = array_filter($params);
40
        }
41
42
        public function sort(&$sort, &$order, $params)
43
        {
44
            $section = $params['current-section'];
45
46
            // If `?unsort` is appended to the URL, then sorting is reverted
47
            // to 'none', aka. by 'entry-id'.
48
            if ($params['unsort']) {
49
                $section->setSortingField('id', false);
50
                $section->setSortingOrder('desc');
51
52
                redirect(Administration::instance()->getCurrentPageURL());
53
            }
54
55
            // By default, sorting information are retrieved from
56
            // the filesystem and stored inside the `Configuration` object
57
            if (is_null($sort) && is_null($order)) {
58
                $sort = $section->getSortingField();
59
                $order = $section->getSortingOrder();
60
61
                // Set the sorting in the `EntryManager` for subsequent use
62
                EntryManager::setFetchSorting($sort, $order);
63
            } else {
64
                $sort = General::sanitize($sort);
65
66
                // Ensure that this field is infact sortable, otherwise
67
                // fallback to IDs
68
                if (($field = FieldManager::fetch($sort)) instanceof Field && !$field->isSortable()) {
69
                    $sort = $section->getDefaultSortingField();
70
                }
71
72
                // If the sort order or direction differs from what is saved,
73
                // update the config file and reload the page
74
                if ($sort !== $section->getSortingField() || $order !== $section->getSortingOrder()) {
75
                    $section->setSortingField($sort, false);
76
                    $section->setSortingOrder($order);
77
78 View Code Duplication
                    if ($params['filters']) {
79
                        $params['filters'] = '?' . trim($params['filters'], '&amp;');
80
                    }
81
82
                    redirect(Administration::instance()->getCurrentPageURL() . $params['filters']);
83
                }
84
85
                // If the sort order or direction remains the same, reload the page
86
                if ($sort === $section->getSortingField() && $order === $section->getSortingOrder()) {
87 View Code Duplication
                    if ($params['filters']) {
88
                        $params['filters'] = '?' . trim($params['filters'], '&amp;');
89
                    }
90
91
                    redirect(Administration::instance()->getCurrentPageURL() . $params['filters']);
92
                }
93
            }
94
        }
95
96 View Code Duplication
        public function build(array $context = array())
97
        {
98
            $section_id = SectionManager::fetchIDFromHandle($context['section_handle']);
99
100
            if ($section_id) {
101
                $context['associations'] = array(
102
                    'parent' => SectionManager::fetchParentAssociations($section_id),
103
                    'child' => SectionManager::fetchChildAssociations($section_id)
104
                );
105
            }
106
107
            return parent::build($context);
108
        }
109
110
        public function action()
111
        {
112
            $this->__switchboard('action');
113
        }
114
115
        public function __switchboard($type = 'view')
116
        {
117
            $function = ($type === 'action' ? '__action' : '__view') . ucfirst($this->_context['page']);
118
119
            if (!method_exists($this, $function)) {
120
                // If there is no action function, just return without doing anything
121
                if ($type === 'action') {
122
                    return;
123
                }
124
125
                Administration::instance()->errorPageNotFound();
126
            }
127
128
            // Is this request allowed by server?
129
            if ($this->isRequestValid() === false) {
130
                $this->pageAlert(__('This request exceeds the maximum allowed request size of %s specified by your host.',
131
                    array(
132
                        ini_get('post_max_size')
133
                    )),
134
                    Alert::ERROR
135
                );
136
            }
137
            $this->$function();
138
        }
139
140
        public function view()
141
        {
142
            $this->__switchboard();
143
        }
144
145
        public function __viewIndex()
146
        {
147
            if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
148
                Administration::instance()->throwCustomError(
149
                    __('The Section, %s, could not be found.',
150
                        array('<code>' . $this->_context['section_handle'] . '</code>')),
151
                    __('Unknown Section'),
152
                    Page::HTTP_STATUS_NOT_FOUND
153
                );
154
            } elseif (!is_writable(CONFIG)) {
155
                $this->pageAlert(__('The Symphony configuration file, %s, is not writable. The sort order cannot be modified.',
156
                    array('<code>/manifest/config.php</code>')), Alert::NOTICE);
157
            }
158
159
            $section = SectionManager::fetch($section_id);
160
161
            $this->setPageType('table');
162
            $this->setTitle(__('%1$s &ndash; %2$s', array($section->get('name'), __('Symphony'))));
163
164
            $filters = array();
165
            $filter_querystring = $prepopulate_querystring = $where = $joins = null;
166
            $current_page = (isset($_REQUEST['pg']) && is_numeric($_REQUEST['pg']) ? max(1,
167
                intval($_REQUEST['pg'])) : 1);
168
169
            if (isset($_REQUEST['filter'])) {
170
                // legacy implementation, convert single filter to an array
171
                // split string in the form ?filter=handle:value
172
                if (!is_array($_REQUEST['filter'])) {
173
                    list($field_handle, $filter_value) = explode(':', $_REQUEST['filter'], 2);
174
                    $filters[$field_handle] = rawurldecode($filter_value);
175
                } else {
176
                    $filters = $_REQUEST['filter'];
177
                }
178
179
                foreach ($filters as $handle => $value) {
180
                    // Handle multiple values through filtering. RE: #2290
181
                    if ((is_array($value) && empty($value)) || trim($value) === '') {
182
                        continue;
183
                    }
184
185 View Code Duplication
                    if (!is_array($value)) {
186
                        $filter_type = Datasource::determineFilterType($value);
187
                        $value = preg_split('/' . ($filter_type === Datasource::FILTER_AND ? '\+' : '(?<!\\\\),') . '\s*/',
188
                            $value, -1, PREG_SPLIT_NO_EMPTY);
189
                        $value = array_map('trim', $value);
190
                        $value = array_map(array('Datasource', 'removeEscapedCommas'), $value);
191
                    }
192
193
                    // Handle date meta data #2003
194
                    $handle = Symphony::Database()->cleanValue($handle);
195
                    if (in_array($handle, array('system:creation-date', 'system:modification-date'))) {
196
                        $date_joins = '';
197
                        $date_where = '';
198
                        $date = new FieldDate();
199
                        $date->buildDSRetrievalSQL($value, $date_joins, $date_where,
200
                            ($filter_type === Datasource::FILTER_AND ? true : false));
0 ignored issues
show
Bug introduced by
The variable $filter_type 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...
201
202
                        // Replace the date field where with the `creation_date` or `modification_date`.
203
                        $date_where = preg_replace('/`t\d+`.date/',
204
                            ($field_id !== 'system:modification-date') ? '`e`.creation_date_gmt' : '`e`.modification_date_gmt',
0 ignored issues
show
Bug introduced by
The variable $field_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...
205
                            $date_where);
206
                        $where .= $date_where;
207
                    } else {
208
                        // Handle normal fields
209
                        $field_id = FieldManager::fetchFieldIDFromElementName(
210
                            $handle,
211
                            $section->get('id')
212
                        );
213
214
                        $field = FieldManager::fetch($field_id);
215
                        if ($field instanceof Field) {
216
                            $field->buildDSRetrievalSQL($value, $joins, $where,
217
                                ($filter_type === Datasource::FILTER_AND ? true : false));
218
219
                            $value = implode(',', $value);
220
                            $encoded_value = rawurlencode($value);
221
                            $filter_querystring .= sprintf("filter[%s]=%s&amp;", $handle, $encoded_value);
222
223
                            // Some fields require that prepopulation be done via ID. RE: #2331
224
                            if (!is_numeric($value) && method_exists($field, 'fetchIDfromValue')) {
225
                                $encoded_value = $field->fetchIDfromValue($value);
0 ignored issues
show
Bug introduced by
The method fetchIDfromValue() does not seem to exist on object<Field>.

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...
226
                            }
227
                            $prepopulate_querystring .= sprintf("prepopulate[%d]=%s&amp;", $field_id, $encoded_value);
228
                        } else {
229
                            unset($filters[$handle]);
230
                        }
231
                    }
232
                }
233
234
                $filter_querystring = preg_replace("/&amp;$/", '', $filter_querystring);
235
                $prepopulate_querystring = preg_replace("/&amp;$/", '', $prepopulate_querystring);
236
            }
237
238
            Sortable::initialize($this, $entries, $sort, $order, array(
239
                'current-section' => $section,
240
                'filters' => ($filter_querystring ? "&amp;" . $filter_querystring : ''),
241
                'unsort' => isset($_REQUEST['unsort'])
242
            ));
243
244
            $this->Form->setAttribute('action',
245
                Administration::instance()->getCurrentPageURL() . '?pg=' . $current_page . ($filter_querystring ? "&amp;" . $filter_querystring : ''));
246
247
            // Build filtering interface
248
            $this->createFilteringInterface();
249
250
            $subheading_buttons = array(
251
                Widget::Anchor(__('Create New'),
252
                    Administration::instance()->getCurrentPageURL() . 'new/' . ($prepopulate_querystring ? '?' . $prepopulate_querystring : ''),
253
                    __('Create a new entry'), 'create button', null, array('accesskey' => 'c'))
254
            );
255
256
            // Only show the Edit Section button if the Author is a developer. #938 ^BA
257
            if (Symphony::Author()->isDeveloper()) {
258
                array_unshift($subheading_buttons,
259
                    Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/',
260
                        __('Edit Section Configuration'), 'button'));
261
            }
262
263
            $this->appendSubheading($section->get('name'), $subheading_buttons);
264
265
            /**
266
             * Allows adjustments to be made to the SQL where and joins statements
267
             * before they are used to fetch the entries for the page
268
             *
269
             * @delegate AdjustPublishFiltering
270
             * @since Symphony 2.3.3
271
             * @param string $context
272
             * '/publish/'
273
             * @param integer $section_id
274
             * An array of the current columns, passed by reference
275
             * @param string $where
276
             * The current where statement, or null if not set
277
             * @param string $joins
278
             */
279
            Symphony::ExtensionManager()->notifyMembers('AdjustPublishFiltering', '/publish/',
280
                array('section-id' => $section_id, 'where' => &$where, 'joins' => &$joins));
281
282
            // Check that the filtered query fails that the filter is dropped and an
283
            // error is logged. #841 ^BA
284
            try {
285
                $entries = EntryManager::fetchByPage($current_page, $section_id,
286
                    Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), $where, $joins, true);
287
            } catch (DatabaseException $ex) {
288
                $this->pageAlert(__('An error occurred while retrieving filtered entries. Showing all entries instead.'),
289
                    Alert::ERROR);
290
                $filter_querystring = null;
291
                Symphony::Log()->warning(sprintf(
292
                    '%s - %s%s%s',
293
                    $section->get('name') . ' Publish Index',
294
                    $ex->getMessage(),
295
                    ($ex->getFile() ? " in file " . $ex->getFile() : null),
296
                    ($ex->getLine() ? " on line " . $ex->getLine() : null)
297
                ));
298
                $entries = EntryManager::fetchByPage($current_page, $section_id,
299
                    Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'));
300
            }
301
302
            // Flag filtering
303
            if (isset($_REQUEST['filter'])) {
304
                $filter_stats = new XMLElement('p', '<span>– ' . __('%d of %d entries (filtered)',
305
                        array($entries['total-entries'], EntryManager::fetchCount($section_id))) . '</span>',
306
                    array('class' => 'inactive'));
307
            } else {
308
                $filter_stats = new XMLElement('p',
309
                    '<span>– ' . __('%d entries', array($entries['total-entries'])) . '</span>',
310
                    array('class' => 'inactive'));
311
            }
312
            $this->Breadcrumbs->appendChild($filter_stats);
313
314
            // Build table
315
            $visible_columns = $section->fetchVisibleColumns();
316
            $columns = array();
317
318
            if (is_array($visible_columns) && !empty($visible_columns)) {
319
                foreach ($visible_columns as $column) {
320
                    $columns[] = array(
321
                        'label' => $column->get('label'),
322
                        'sortable' => $column->isSortable(),
323
                        'handle' => $column->get('id'),
324
                        'attrs' => array(
325
                            'id' => 'field-' . $column->get('id'),
326
                            'class' => 'field-' . $column->get('type')
327
                        )
328
                    );
329
                }
330
            } else {
331
                $columns[] = array(
332
                    'label' => __('ID'),
333
                    'sortable' => true,
334
                    'handle' => 'id'
335
                );
336
            }
337
338
            $aTableHead = Sortable::buildTableHeaders($columns, $sort, $order,
339
                ($filter_querystring) ? "&amp;" . $filter_querystring : '');
340
341
            $child_sections = array();
342
            $associated_sections = $section->fetchChildAssociations(true);
343
344
            if (is_array($associated_sections) && !empty($associated_sections)) {
345
                foreach ($associated_sections as $key => $as) {
346
                    $child_sections[$key] = SectionManager::fetch($as['child_section_id']);
347
                    $aTableHead[] = array($child_sections[$key]->get('name'), 'col');
348
                }
349
            }
350
351
            /**
352
             * Allows the creation of custom table columns for each entry. Called
353
             * after all the Section Visible columns have been added as well
354
             * as the Section Associations
355
             *
356
             * @delegate AddCustomPublishColumn
357
             * @since Symphony 2.2
358
             * @param string $context
359
             * '/publish/'
360
             * @param array $tableHead
361
             * An array of the current columns, passed by reference
362
             * @param integer $section_id
363
             * The current Section ID
364
             */
365
            Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumn', '/publish/',
366
                array('tableHead' => &$aTableHead, 'section_id' => $section->get('id')));
367
368
            // Table Body
369
            $aTableBody = array();
370
371
            if (!is_array($entries['records']) || empty($entries['records'])) {
372
                $aTableBody = array(
373
                    Widget::TableRow(array(Widget::TableData(__('None found.'), 'inactive', null, count($aTableHead))),
374
                        'odd')
375
                );
376
            } else {
377
                $field_pool = array();
378
379
                if (is_array($visible_columns) && !empty($visible_columns)) {
380
                    foreach ($visible_columns as $column) {
381
                        $field_pool[$column->get('id')] = $column;
382
                    }
383
                }
384
385
                $link_column = array_reverse($visible_columns);
386
                $link_column = end($link_column);
387
                reset($visible_columns);
388
389
                foreach ($entries['records'] as $entry) {
390
                    $tableData = array();
391
392
                    // Setup each cell
393
                    if (!is_array($visible_columns) || empty($visible_columns)) {
394
                        $tableData[] = Widget::TableData(Widget::Anchor($entry->get('id'),
395
                            Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/'));
396
                    } else {
397
                        $link = Widget::Anchor(
398
                            '',
399
                            Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/' . ($filter_querystring ? '?' . $prepopulate_querystring : ''),
400
                            $entry->get('id'),
401
                            'content'
402
                        );
403
404
                        foreach ($visible_columns as $position => $column) {
405
                            $data = $entry->getData($column->get('id'));
406
                            $field = $field_pool[$column->get('id')];
407
408
                            $value = $field->prepareTableValue($data, ($column === $link_column) ? $link : null,
409
                                $entry->get('id'));
410
411
                            if (!is_object($value) && (strlen(trim($value)) === 0 || $value === __('None'))) {
412
                                $value = ($position === 0 ? $link->generate() : __('None'));
413
                            }
414
415
                            if ($value === __('None')) {
416
                                $tableData[] = Widget::TableData($value,
417
                                    'inactive field-' . $column->get('type') . ' field-' . $column->get('id'));
418
                            } else {
419
                                $tableData[] = Widget::TableData($value,
420
                                    'field-' . $column->get('type') . ' field-' . $column->get('id'));
421
                            }
422
423
                            unset($field);
424
                        }
425
                    }
426
427
                    if (is_array($child_sections) && !empty($child_sections)) {
428
                        foreach ($child_sections as $key => $as) {
429
                            $field = FieldManager::fetch((int)$associated_sections[$key]['child_section_field_id']);
430
                            $parent_section_field_id = (int)$associated_sections[$key]['parent_section_field_id'];
431
432
                            if (!is_null($parent_section_field_id)) {
433
                                $search_value = $field->fetchAssociatedEntrySearchValue(
434
                                    $entry->getData($parent_section_field_id),
435
                                    $parent_section_field_id,
436
                                    $entry->get('id')
437
                                );
438
                            } else {
439
                                $search_value = $entry->get('id');
440
                            }
441
442
                            if (!is_array($search_value)) {
443
                                $associated_entry_count = $field->fetchAssociatedEntryCount($search_value);
444
445
                                $tableData[] = Widget::TableData(
446
                                    Widget::Anchor(
447
                                        sprintf('%d &rarr;', max(0, intval($associated_entry_count))),
448
                                        sprintf(
449
                                            '%s/publish/%s/?filter[%s]=%s',
450
                                            SYMPHONY_URL,
451
                                            $as->get('handle'),
452
                                            $field->get('element_name'),
453
                                            rawurlencode($search_value)
454
                                        ),
455
                                        $entry->get('id'),
456
                                        'content'
457
                                    )
458
                                );
459
                            }
460
                        }
461
                    }
462
463
                    /**
464
                     * Allows Extensions to inject custom table data for each Entry
465
                     * into the Publish Index
466
                     *
467
                     * @delegate AddCustomPublishColumnData
468
                     * @since Symphony 2.2
469
                     * @param string $context
470
                     * '/publish/'
471
                     * @param array $tableData
472
                     *  An array of `Widget::TableData`, passed by reference
473
                     * @param integer $section_id
474
                     *  The current Section ID
475
                     * @param Entry $entry_id
476
                     *  The entry object, please note that this is by error and this will
477
                     *  be removed in Symphony 2.4. The entry object is available in
478
                     *  the 'entry' key as of Symphony 2.3.1.
479
                     * @param Entry $entry
480
                     *  The entry object for this row
481
                     */
482
                    Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumnData', '/publish/', array(
483
                        'tableData' => &$tableData,
484
                        'section_id' => $section->get('id'),
485
                        'entry_id' => $entry,
486
                        'entry' => $entry
487
                    ));
488
489
                    $tableData[count($tableData) - 1]->appendChild(Widget::Label(__('Select Entry %d',
490
                        array($entry->get('id'))), null, 'accessible', null, array(
491
                        'for' => 'entry-' . $entry->get('id')
492
                    )));
493
                    $tableData[count($tableData) - 1]->appendChild(Widget::Input('items[' . $entry->get('id') . ']',
494
                        null, 'checkbox', array(
495
                            'id' => 'entry-' . $entry->get('id')
496
                        )));
497
498
                    // Add a row to the body array, assigning each cell to the row
499
                    $aTableBody[] = Widget::TableRow($tableData, null, 'id-' . $entry->get('id'));
500
                }
501
            }
502
503
            $table = Widget::Table(
504
                Widget::TableHead($aTableHead),
505
                null,
506
                Widget::TableBody($aTableBody),
507
                'selectable',
508
                null,
509
                array(
510
                    'role' => 'directory',
511
                    'aria-labelledby' => 'symphony-subheading',
512
                    'data-interactive' => 'data-interactive'
513
                )
514
            );
515
516
            $this->Form->appendChild($table);
517
518
            $tableActions = new XMLElement('div');
519
            $tableActions->setAttribute('class', 'actions');
520
521
            $options = array(
522
                array(null, false, __('With Selected...')),
523
                array(
524
                    'delete',
525
                    false,
526
                    __('Delete'),
527
                    'confirm',
528
                    null,
529
                    array(
530
                        'data-message' => __('Are you sure you want to delete the selected entries?')
531
                    )
532
                )
533
            );
534
535
            $toggable_fields = $section->fetchToggleableFields();
536
537
            if (is_array($toggable_fields) && !empty($toggable_fields)) {
538
                $index = 2;
539
540
                foreach ($toggable_fields as $field) {
541
                    $toggle_states = $field->getToggleStates();
542
543
                    if (is_array($toggle_states)) {
544
                        $options[$index] = array(
545
                            'label' => __('Set %s', array($field->get('label'))),
546
                            'options' => array()
547
                        );
548
549
                        foreach ($toggle_states as $value => $state) {
550
                            $options[$index]['options'][] = array(
551
                                'toggle-' . $field->get('id') . '-' . $value,
552
                                false,
553
                                $state
554
                            );
555
                        }
556
                    }
557
558
                    $index++;
559
                }
560
            }
561
562
            /**
563
             * Allows an extension to modify the existing options for this page's
564
             * With Selected menu. If the `$options` parameter is an empty array,
565
             * the 'With Selected' menu will not be rendered.
566
             *
567
             * @delegate AddCustomActions
568
             * @since Symphony 2.3.2
569
             * @param string $context
570
             * '/publish/'
571
             * @param array $options
572
             *  An array of arrays, where each child array represents an option
573
             *  in the With Selected menu. Options should follow the same format
574
             *  expected by `Widget::__SelectBuildOption`. Passed by reference.
575
             */
576
            Symphony::ExtensionManager()->notifyMembers('AddCustomActions', '/publish/', array(
577
                'options' => &$options
578
            ));
579
580
            if (!empty($options)) {
581
                $tableActions->appendChild(Widget::Apply($options));
582
                $this->Form->appendChild($tableActions);
583
            }
584
585
            if ($entries['total-pages'] > 1) {
586
                $ul = new XMLElement('ul');
587
                $ul->setAttribute('class', 'page');
588
589
                // First
590
                $li = new XMLElement('li');
591
592
                if ($current_page > 1) {
593
                    $li->appendChild(Widget::Anchor(__('First'),
594
                        Administration::instance()->getCurrentPageURL() . '?pg=1' . ($filter_querystring ? "&amp;" . $filter_querystring : '')));
595
                } else {
596
                    $li->setValue(__('First'));
597
                }
598
599
                $ul->appendChild($li);
600
601
                // Previous
602
                $li = new XMLElement('li');
603
604 View Code Duplication
                if ($current_page > 1) {
605
                    $li->appendChild(Widget::Anchor(__('&larr; Previous'),
606
                        Administration::instance()->getCurrentPageURL() . '?pg=' . ($current_page - 1) . ($filter_querystring ? "&amp;" . $filter_querystring : '')));
607
                } else {
608
                    $li->setValue(__('&larr; Previous'));
609
                }
610
611
                $ul->appendChild($li);
612
613
                // Summary
614
                $li = new XMLElement('li');
615
616
                $li->setAttribute('title', __('Viewing %1$s - %2$s of %3$s entries', array(
617
                    $entries['start'],
618
                    ($current_page !== $entries['total-pages']) ? $current_page * Symphony::Configuration()->get('pagination_maximum_rows',
619
                            'symphony') : $entries['total-entries'],
620
                    $entries['total-entries']
621
                )));
622
623
                $pgform = Widget::Form(Administration::instance()->getCurrentPageURL(), 'get', 'paginationform');
624
625
                $pgmax = max($current_page, $entries['total-pages']);
626
                $pgform->appendChild(Widget::Input('pg', null, 'text', array(
627
                    'data-active' => __('Go to page …'),
628
                    'data-inactive' => __('Page %1$s of %2$s', array((string)$current_page, $pgmax)),
629
                    'data-max' => $pgmax
630
                )));
631
632
                $li->appendChild($pgform);
633
                $ul->appendChild($li);
634
635
                // Next
636
                $li = new XMLElement('li');
637
638 View Code Duplication
                if ($current_page < $entries['total-pages']) {
639
                    $li->appendChild(Widget::Anchor(__('Next &rarr;'),
640
                        Administration::instance()->getCurrentPageURL() . '?pg=' . ($current_page + 1) . ($filter_querystring ? "&amp;" . $filter_querystring : '')));
641
                } else {
642
                    $li->setValue(__('Next &rarr;'));
643
                }
644
645
                $ul->appendChild($li);
646
647
                // Last
648
                $li = new XMLElement('li');
649
650 View Code Duplication
                if ($current_page < $entries['total-pages']) {
651
                    $li->appendChild(Widget::Anchor(__('Last'),
652
                        Administration::instance()->getCurrentPageURL() . '?pg=' . $entries['total-pages'] . ($filter_querystring ? "&amp;" . $filter_querystring : '')));
653
                } else {
654
                    $li->setValue(__('Last'));
655
                }
656
657
                $ul->appendChild($li);
658
659
                $this->Contents->appendChild($ul);
660
            }
661
        }
662
663
        /**
664
         * Append filtering interface
665
         */
666
        public function createFilteringInterface()
667
        {
668
            //Check if section has filtering enabled
669
            $context = $this->getContext();
670
            $handle = $context['section_handle'];
671
            $section_id = SectionManager::fetchIDFromHandle($handle);
672
            $section = SectionManager::fetch($section_id);
673
            $filter = $section->get('filter');
674
            $count = EntryManager::fetchCount($section_id);
675
676
            if ($filter !== 'no' && $count > 1) {
677
                $drawer = Widget::Drawer('filtering-' . $section_id, __('Filter Entries'),
678
                    $this->createFilteringDrawer($section));
0 ignored issues
show
Bug introduced by
It seems like $section defined by \SectionManager::fetch($section_id) on line 672 can also be of type array; however, contentPublish::createFilteringDrawer() does only seem to accept 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...
679
                $drawer->addClass('drawer-filtering');
680
                $this->insertDrawer($drawer);
681
            }
682
        }
683
684
        /**
685
         * Create filtering drawer
686
         *
687
         * @param Section $section
688
         * @return XMLElement
689
         */
690
        public function createFilteringDrawer(Section $section)
691
        {
692
            $this->filteringForm = Widget::Form(null, 'get', 'filtering');
0 ignored issues
show
Bug introduced by
The property filteringForm does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
693
            $this->createFilteringDuplicator($section);
694
695
            return $this->filteringForm;
696
        }
697
698
        public function createFilteringDuplicator($section)
699
        {
700
            $div = new XMLElement('div');
701
            $div->setAttribute('class', 'frame filters-duplicator');
702
            $div->setAttribute('data-interactive', 'data-interactive');
703
704
            $ol = new XMLElement('ol');
705
            $ol->setAttribute('data-add', __('Add filter'));
706
            $ol->setAttribute('data-remove', __('Clear filter'));
707
            $ol->setAttribute('data-empty', __('No filters applied yet.'));
708
709
            $this->createFieldFilters($ol, $section);
710
            $this->createSystemDateFilters($ol);
711
712
            $div->appendChild($ol);
713
            $this->filteringForm->appendChild($div);
714
        }
715
716
        private function createFieldFilters(&$wrapper, $section)
717
        {
718
            $filters = $_GET['filter'];
719
720
            foreach ($section->fetchFilterableFields() as $field) {
721
                if (!$field->canPublishFilter()) {
722
                    continue;
723
                }
724
725
                $filter = $filters[$field->get('element_name')];
726
727
                // Filter data
728
                $data = array();
729
                $data['type'] = $field->get('element_name');
730
                $data['name'] = $field->get('label');
731
                $data['filter'] = $filter;
732
                $data['instance'] = 'unique';
733
                $data['search'] = $field->fetchSuggestionTypes();
734
                $data['operators'] = $field->fetchFilterableOperators();
735
                $data['comparisons'] = $this->createFilterComparisons($data);
736
                $data['query'] = $this->getFilterQuery($data);
737
                $data['field-id'] = $field->get('id');
738
739
                // Add existing filter
740
                if (isset($filter)) {
741
                    $this->createFilter($wrapper, $data);
742
                }
743
744
                // Add filter template
745
                $data['instance'] = 'unique template';
746
                $data['query'] = '';
747
                $this->createFilter($wrapper, $data);
748
            }
749
        }
750
751
        private function createFilterComparisons($data)
752
        {
753
            // Default comparison
754
            $comparisons = array();
755
756
            // Custom field comparisons
757
            foreach ($data['operators'] as $operator) {
758
                $filter = trim($operator['filter']);
759
760
                $comparisons[] = array(
761
                    $filter,
762
                    (!empty($filter) && strpos($data['filter'], $filter) === 0),
763
                    __($operator['title'])
764
                );
765
            }
766
767
            return $comparisons;
768
        }
769
770
        private function getFilterQuery($data)
771
        {
772
            $query = $data['filter'];
773
774
            foreach ($data['operators'] as $operator) {
775
                $filter = trim($operator['filter']);
776
777
                if (!empty($filter) && strpos($data['filter'], $filter) === 0) {
778
                    $query = substr($data['filter'], strlen($filter));
779
                }
780
            }
781
782
            return (string)$query;
783
        }
784
785
        private function createFilter(&$wrapper, $data)
786
        {
787
            $li = new XMLElement('li');
788
            $li->setAttribute('class', $data['instance']);
789
            $li->setAttribute('data-type', $data['type']);
790
791
            // Header
792
            $li->appendChild(new XMLElement('header', $data['name'], array(
793
                'data-name' => $data['name']
794
            )));
795
796
            // Settings
797
            $div = new XMLElement('div', null, array('class' => 'two columns'));
798
799
            // Comparisons
800
            $label = Widget::Label();
801
            $label->setAttribute('class', 'column secondary');
802
803
            $select = Widget::Select($data['type'] . '-comparison', $data['comparisons'], array(
804
                'class' => 'comparison'
805
            ));
806
807
            $label->appendChild($select);
808
            $div->appendChild($label);
809
810
            // Query
811
            $label = Widget::Label();
812
            $label->setAttribute('class', 'column primary');
813
814
            $input = Widget::Input($data['type'], $data['query'], 'text', array(
815
                'placeholder' => __('Type and hit enter to apply filter…'),
816
                'autocomplete' => 'off'
817
            ));
818
            $input->setAttribute('class', 'filter');
819
            $label->appendChild($input);
820
821
            $this->createFilterSuggestions($label, $data);
822
823
            $div->appendChild($label);
824
            $li->appendChild($div);
825
            $wrapper->appendChild($li);
826
        }
827
828
        private function createFilterSuggestions(&$wrapper, $data)
829
        {
830
            $ul = new XMLElement('ul');
831
            $ul->setAttribute('class', 'suggestions');
832
            $ul->setAttribute('data-field-id', $data['field-id']);
833
            $ul->setAttribute('data-associated-ids', '0');
834
            $ul->setAttribute('data-search-types', implode($data['search'], ','));
835
836
            // Add help text for each filter operator
837
            foreach ($data['operators'] as $operator) {
838
                $this->createFilterHelp($ul, $operator);
839
            }
840
841
            $wrapper->appendChild($ul);
842
        }
843
844
        private function createFilterHelp(&$wrapper, $operator)
845
        {
846
            if (empty($operator['help'])) {
847
                return;
848
            }
849
850
            $li = new XMLElement('li', __('Comparison mode') . ': ' . $operator['help'], array(
851
                'class' => 'help',
852
                'data-comparison' => trim($operator['filter'])
853
            ));
854
855
            $wrapper->appendChild($li);
856
        }
857
858
        private function createSystemDateFilters(&$wrapper)
859
        {
860
            $filters = $_GET['filter'];
861
            $dateField = new FieldDate;
862
863
            $fields = array(
864
                array(
865
                    'type' => 'system:creation-date',
866
                    'label' => __('System Creation Date')
867
                ),
868
                array(
869
                    'type' => 'system:modification-date',
870
                    'label' => __('System Modification Date')
871
                )
872
            );
873
874
            foreach ($fields as $field) {
875
                $filter = $filters[$field['type']];
876
877
                // Filter data
878
                $data = array();
879
                $data['type'] = $field['type'];
880
                $data['name'] = $field['label'];
881
                $data['filter'] = $filter;
882
                $data['instance'] = 'unique';
883
                $data['search'] = $dateField->fetchSuggestionTypes();
884
                $data['operators'] = $dateField->fetchFilterableOperators();
885
                $data['comparisons'] = $this->createFilterComparisons($data);
886
                $data['query'] = $this->getFilterQuery($data);
887
888
                // Add existing filter
889
                if (isset($filter)) {
890
                    $this->createFilter($wrapper, $data);
891
                }
892
893
                // Add filter template
894
                $data['instance'] = 'unique template';
895
                $data['query'] = '';
896
                $this->createFilter($wrapper, $data);
897
            }
898
        }
899
900
        public function __actionIndex()
0 ignored issues
show
Coding Style introduced by
__actionIndex uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__actionIndex uses the super-global variable $_SERVER 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...
901
        {
902
            $checked = (is_array($_POST['items'])) ? array_keys($_POST['items']) : null;
903
904
            if (is_array($checked) && !empty($checked)) {
905
                /**
906
                 * Extensions can listen for any custom actions that were added
907
                 * through `AddCustomPreferenceFieldsets` or `AddCustomActions`
908
                 * delegates.
909
                 *
910
                 * @delegate CustomActions
911
                 * @since Symphony 2.3.2
912
                 * @param string $context
913
                 *  '/publish/'
914
                 * @param array $checked
915
                 *  An array of the selected rows. The value is usually the ID of the
916
                 *  the associated object.
917
                 */
918
                Symphony::ExtensionManager()->notifyMembers('CustomActions', '/publish/', array(
919
                    'checked' => $checked
920
                ));
921
922
                switch ($_POST['with-selected']) {
923
                    case 'delete':
924
                        /**
925
                         * Prior to deletion of entries. An array of Entry ID's is provided which
926
                         * can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete`
927
                         * in Symphony 2.3.
928
                         *
929
                         * @delegate EntryPreDelete
930
                         * @param string $context
931
                         * '/publish/'
932
                         * @param array $entry_id
933
                         *  An array of Entry ID's passed by reference
934
                         */
935
                        Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/',
936
                            array('entry_id' => &$checked));
937
938
                        EntryManager::delete($checked);
939
940
                        /**
941
                         * After the deletion of entries, this delegate provides an array of Entry ID's
942
                         * that were deleted.
943
                         *
944
                         * @since Symphony 2.3
945
                         * @delegate EntryPostDelete
946
                         * @param string $context
947
                         * '/publish/'
948
                         * @param array $entry_id
949
                         *  An array of Entry ID's that were deleted.
950
                         */
951
                        Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/',
952
                            array('entry_id' => $checked));
953
954
                        redirect($_SERVER['REQUEST_URI']);
955
                        break;
956
                    default:
957
                        list($option, $field_id, $value) = explode('-', $_POST['with-selected'], 3);
958
959
                        if ($option === 'toggle') {
960
                            $field = FieldManager::fetch($field_id);
961
                            $fields = array($field->get('element_name') => $value);
962
963
                            $section = SectionManager::fetch($field->get('parent_section'));
964
965
                            foreach ($checked as $entry_id) {
966
                                $entry = EntryManager::fetch($entry_id);
967
                                $existing_data = $entry[0]->getData($field_id);
968
                                $entry[0]->setData($field_id,
969
                                    $field->toggleFieldData(is_array($existing_data) ? $existing_data : array(), $value,
970
                                        $entry_id));
971
972
                                /**
973
                                 * Just prior to editing of an Entry
974
                                 *
975
                                 * @delegate EntryPreEdit
976
                                 * @param string $context
977
                                 * '/publish/edit/'
978
                                 * @param Section $section
979
                                 * @param Entry $entry
980
                                 * @param array $fields
981
                                 */
982
                                Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/', array(
983
                                    'section' => $section,
984
                                    'entry' => &$entry[0],
985
                                    'fields' => $fields
986
                                ));
987
988
                                $entry[0]->commit();
989
990
                                /**
991
                                 * Editing an entry. Entry object is provided.
992
                                 *
993
                                 * @delegate EntryPostEdit
994
                                 * @param string $context
995
                                 * '/publish/edit/'
996
                                 * @param Section $section
997
                                 * @param Entry $entry
998
                                 * @param array $fields
999
                                 */
1000
                                Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/', array(
1001
                                    'section' => $section,
1002
                                    'entry' => $entry[0],
1003
                                    'fields' => $fields
1004
                                ));
1005
                            }
1006
1007
                            redirect($_SERVER['REQUEST_URI']);
1008
                        }
1009
                }
1010
            }
1011
        }
1012
1013
        public function __viewNew()
0 ignored issues
show
Coding Style introduced by
__viewNew uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1014
        {
1015 View Code Duplication
            if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
1016
                Administration::instance()->throwCustomError(
1017
                    __('The Section, %s, could not be found.',
1018
                        array('<code>' . $this->_context['section_handle'] . '</code>')),
1019
                    __('Unknown Section'),
1020
                    Page::HTTP_STATUS_NOT_FOUND
1021
                );
1022
            }
1023
1024
            $section = SectionManager::fetch($section_id);
1025
1026
            $this->setPageType('form');
1027
            $this->setTitle(__('%1$s &ndash; %2$s', array($section->get('name'), __('Symphony'))));
1028
1029
            // Ensure errored entries still maintain any prepopulated values [#2211]
1030
            $this->Form->setAttribute('action', $this->Form->getAttribute('action') . $this->getPrepopulateString());
1031
            $this->Form->setAttribute('enctype', 'multipart/form-data');
1032
1033
            $sidebar_fields = $section->fetchFields(null, 'sidebar');
1034
            $main_fields = $section->fetchFields(null, 'main');
1035
1036 View Code Duplication
            if (!empty($sidebar_fields) && !empty($main_fields)) {
1037
                $this->Form->setAttribute('class', 'two columns');
1038
            } else {
1039
                $this->Form->setAttribute('class', 'columns');
1040
            }
1041
1042
            // Only show the Edit Section button if the Author is a developer. #938 ^BA
1043
            if (Symphony::Author()->isDeveloper()) {
1044
                $this->appendSubheading(__('Untitled'),
1045
                    Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/',
1046
                        __('Edit Section Configuration'), 'button')
1047
                );
1048
            } else {
1049
                $this->appendSubheading(__('Untitled'));
1050
            }
1051
1052
            // Build filtered breadcrumb [#1378}
1053
            $this->insertBreadcrumbs(array(
1054
                Widget::Anchor($section->get('name'),
1055
                    SYMPHONY_URL . '/publish/' . $this->_context['section_handle'] . '/' . $this->getFilterString()),
1056
            ));
1057
1058
            $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE',
1059
                Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
1060
1061
            // If there is post data floating around, due to errors, create an entry object
1062
            if (isset($_POST['fields'])) {
1063
                $entry = EntryManager::create();
1064
                $entry->set('section_id', $section_id);
1065
                $entry->setDataFromPost($_POST['fields'], $error, true);
1066
1067
                // Brand new entry, so need to create some various objects
1068
            } else {
1069
                $entry = EntryManager::create();
1070
                $entry->set('section_id', $section_id);
1071
            }
1072
1073
            // Check if there is a field to prepopulate
1074
            if (isset($_REQUEST['prepopulate'])) {
1075
                foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1076
                    $this->Form->prependChild(Widget::Input(
1077
                        "prepopulate[{$field_id}]",
1078
                        rawurlencode($value),
1079
                        'hidden'
1080
                    ));
1081
1082
                    // The actual pre-populating should only happen if there is not existing fields post data
1083
                    if (!isset($_POST['fields']) && $field = FieldManager::fetch($field_id)) {
1084
                        $entry->setData(
1085
                            $field->get('id'),
1086
                            $field->processRawFieldData($value, $error, $message, true)
0 ignored issues
show
Bug introduced by
The variable $error 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...
Bug introduced by
The variable $message seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Documentation introduced by
$error is of type array|null, 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...
1087
                        );
1088
                    }
1089
                }
1090
            }
1091
1092
            $primary = new XMLElement('fieldset');
1093
            $primary->setAttribute('class', 'primary column');
1094
1095
            if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) {
1096
                $message = __('Fields must be added to this section before an entry can be created.');
1097
1098 View Code Duplication
                if (Symphony::Author()->isDeveloper()) {
1099
                    $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . $section->get('id') . '/" accesskey="c">'
1100
                        . __('Add fields')
1101
                        . '</a>';
1102
                }
1103
1104
                $this->pageAlert($message, Alert::ERROR);
1105
            } else {
1106 View Code Duplication
                if (is_array($main_fields) && !empty($main_fields)) {
1107
                    foreach ($main_fields as $field) {
1108
                        $primary->appendChild($this->__wrapFieldWithDiv($field, $entry));
1109
                    }
1110
1111
                    $this->Form->appendChild($primary);
1112
                }
1113
1114 View Code Duplication
                if (is_array($sidebar_fields) && !empty($sidebar_fields)) {
1115
                    $sidebar = new XMLElement('fieldset');
1116
                    $sidebar->setAttribute('class', 'secondary column');
1117
1118
                    foreach ($sidebar_fields as $field) {
1119
                        $sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry));
1120
                    }
1121
1122
                    $this->Form->appendChild($sidebar);
1123
                }
1124
1125
                $div = new XMLElement('div');
1126
                $div->setAttribute('class', 'actions');
1127
                $div->appendChild(Widget::Input('action[save]', __('Create Entry'), 'submit',
1128
                    array('accesskey' => 's')));
1129
1130
                $this->Form->appendChild($div);
1131
1132
                // Create a Drawer for Associated Sections
1133
                $this->prepareAssociationsDrawer($section);
0 ignored issues
show
Bug introduced by
It seems like $section defined by \SectionManager::fetch($section_id) on line 1024 can also be of type array; however, contentPublish::prepareAssociationsDrawer() does only seem to accept 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...
1134
            }
1135
        }
1136
1137
        /**
1138
         * If this entry is being prepopulated, this function will return the prepopulated
1139
         * fields and values as a query string.
1140
         *
1141
         * @since Symphony 2.5.2
1142
         * @return string
1143
         */
1144
        public function getPrepopulateString()
1145
        {
1146
            $prepopulate_querystring = '';
1147
1148
            if (isset($_REQUEST['prepopulate'])) {
1149
                foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1150
                    $prepopulate_querystring .= sprintf("prepopulate[%s]=%s&", $field_id, rawurldecode($value));
1151
                }
1152
                $prepopulate_querystring = trim($prepopulate_querystring, '&');
1153
            }
1154
1155
            // This is to prevent the value being interpreted as an additional GET
1156
            // parameter. eg. prepopulate[cat]=Minx&June, would come through as:
1157
            // $_GET['cat'] = Minx
1158
            // $_GET['June'] = ''
1159
            $prepopulate_querystring = preg_replace("/&amp;$/", '', $prepopulate_querystring);
1160
1161
            return $prepopulate_querystring ? '?' . $prepopulate_querystring : null;
1162
        }
1163
1164
        /**
1165
         * If the entry is being prepopulated, we may want to filter other views by this entry's
1166
         * value. This function will create that filter query string.
1167
         *
1168
         * @since Symphony 2.5.2
1169
         * @return string
1170
         */
1171
        public function getFilterString()
1172
        {
1173
            $filter_querystring = '';
1174
1175
            if (isset($_REQUEST['prepopulate'])) {
1176
                foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1177
                    $handle = FieldManager::fetchHandleFromID($field_id);
1178
1179
                    //This is in case it is an Association so the filter reads the text value instead of the ID
1180
                    $field = FieldManager::fetch($field_id);
1181
                    if ($field instanceof Field) {
1182
                        if (method_exists($field, 'fetchValueFromID')) {
1183
                            $value = $field->fetchValueFromID($value);
0 ignored issues
show
Bug introduced by
The method fetchValueFromID() does not seem to exist on object<Field>.

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...
1184
                        }
1185
                    }
1186
1187
                    $filter_querystring .= sprintf("filter[%s]=%s&", $handle, rawurldecode($value));
1188
                }
1189
                $filter_querystring = trim($filter_querystring, '&');
1190
            }
1191
1192
            // This is to prevent the value being interpreted as an additional GET
1193
            // parameter. eg. filter[cat]=Minx&June, would come through as:
1194
            // $_GET['cat'] = Minx
1195
            // $_GET['June'] = ''
1196
            $filter_querystring = preg_replace("/&amp;$/", '', $filter_querystring);
1197
1198
            return $filter_querystring ? '?' . $filter_querystring : null;
1199
        }
1200
1201
        /**
1202
         * Given a Field and Entry object, this function will wrap
1203
         * the Field's displayPublishPanel result with a div that
1204
         * contains some contextual information such as the Field ID,
1205
         * the Field handle and whether it is required or not.
1206
         *
1207
         * @param Field $field
1208
         * @param Entry $entry
1209
         * @return XMLElement
1210
         */
1211
        private function __wrapFieldWithDiv(Field $field, Entry $entry)
1212
        {
1213
            $is_hidden = $this->isFieldHidden($field);
1214
            $div = new XMLElement('div', null, array(
1215
                'id' => 'field-' . $field->get('id'),
1216
                'class' => 'field field-' . $field->handle() . ($field->get('required') === 'yes' ? ' required' : '') . ($is_hidden === true ? ' irrelevant' : '')
1217
            ));
1218
1219
            $field->setAssociationContext($div);
1220
1221
            $field->displayPublishPanel(
1222
                $div, $entry->getData($field->get('id')),
1223
                (isset($this->_errors[$field->get('id')]) ? $this->_errors[$field->get('id')] : null),
1224
                null, null, (is_numeric($entry->get('id')) ? $entry->get('id') : null)
1225
            );
1226
1227
            /**
1228
             * Allows developers modify the field before it is rendered in the publish
1229
             * form. Passes the `Field` object, `Entry` object, the `XMLElement` div and
1230
             * any errors for the entire `Entry`. Only the `$div` element
1231
             * will be altered before appending to the page, the rest are read only.
1232
             *
1233
             * @since Symphony 2.5.0
1234
             * @delegate ModifyFieldPublishWidget
1235
             * @param string $context
1236
             * '/backend/'
1237
             * @param Field $field
1238
             * @param Entry $entry
1239
             * @param array $errors
1240
             * @param Widget $widget
1241
             */
1242
            Symphony::ExtensionManager()->notifyMembers('ModifyFieldPublishWidget', '/backend/', array(
1243
                'field' => $field,
1244
                'entry' => $entry,
1245
                'errors' => $this->_errors,
1246
                'widget' => &$div
1247
            ));
1248
1249
            return $div;
1250
        }
1251
1252
        /**
1253
         * Check whether the given `$field` will be hidden because it's been
1254
         * prepopulated.
1255
         *
1256
         * @param  Field $field
1257
         * @return boolean
1258
         */
1259
        public function isFieldHidden(Field $field)
1260
        {
1261
            if ($field->get('hide_when_prepopulated') === 'yes') {
1262
                if (isset($_REQUEST['prepopulate'])) {
1263
                    foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1264
                        if ($field_id === $field->get('id')) {
1265
                            return true;
1266
                        }
1267
                    }
1268
                }
1269
            }
1270
1271
            return false;
1272
        }
1273
1274
        /**
1275
         * Prepare a Drawer to visualize section associations
1276
         *
1277
         * @param  Section $section The current Section object
1278
         * @throws InvalidArgumentException
1279
         * @throws Exception
1280
         */
1281
        private function prepareAssociationsDrawer($section)
1282
        {
1283
            $entry_id = (!is_null($this->_context['entry_id'])) ? $this->_context['entry_id'] : null;
1284
            $show_entries = Symphony::Configuration()->get('association_maximum_rows', 'symphony');
1285
1286
            if (is_null($entry_id) && !isset($_GET['prepopulate']) || is_null($show_entries) || $show_entries === 0) {
1287
                return;
1288
            }
1289
1290
            $parent_associations = SectionManager::fetchParentAssociations($section->get('id'), true);
0 ignored issues
show
Documentation introduced by
$section->get('id') is of type array|string, 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...
1291
            $child_associations = SectionManager::fetchChildAssociations($section->get('id'), true);
0 ignored issues
show
Documentation introduced by
$section->get('id') is of type array|string, 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...
1292
            $content = null;
1293
            $drawer_position = 'vertical-right';
1294
1295
            /**
1296
             * Prepare Associations Drawer from an Extension
1297
             *
1298
             * @since Symphony 2.3.3
1299
             * @delegate PrepareAssociationsDrawer
1300
             * @param string $context
1301
             * '/publish/'
1302
             * @param integer $entry_id
1303
             *  The entry ID or null
1304
             * @param array $parent_associations
1305
             *  Array of Sections
1306
             * @param array $child_associations
1307
             *  Array of Sections
1308
             * @param string $drawer_position
1309
             *  The position of the Drawer, defaults to `vertical-right`. Available
1310
             *  values of `vertical-left, `vertical-right` and `horizontal`
1311
             */
1312
            Symphony::ExtensionManager()->notifyMembers('PrepareAssociationsDrawer', '/publish/', array(
1313
                'entry_id' => $entry_id,
1314
                'parent_associations' => &$parent_associations,
1315
                'child_associations' => &$child_associations,
1316
                'content' => &$content,
1317
                'drawer-position' => &$drawer_position
1318
            ));
1319
1320
            // If there are no associations, return now.
1321
            if (
1322
                (is_null($parent_associations) || empty($parent_associations))
1323
                &&
1324
                (is_null($child_associations) || empty($child_associations))
1325
            ) {
1326
                return;
1327
            }
1328
1329
            if (!($content instanceof XMLElement)) {
1330
                $content = new XMLElement('div', null, array('class' => 'content'));
1331
                $content->setSelfClosingTag(false);
1332
1333
                // Process Parent Associations
1334
                if (!is_null($parent_associations) && !empty($parent_associations)) {
1335
                    foreach ($parent_associations as $as) {
0 ignored issues
show
Bug introduced by
The expression $parent_associations of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

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

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

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

Loading history...
1336
                        if ($field = FieldManager::fetch($as['parent_section_field_id'])) {
1337
                            if (isset($_GET['prepopulate'])) {
1338
                                $prepopulate_field = key($_GET['prepopulate']);
1339
                            }
1340
1341
                            // get associated entries if entry exists,
1342
                            if ($entry_id) {
1343
                                $entry_ids = $field->findParentRelatedEntries($as['child_section_field_id'], $entry_id);
1344
1345
                                // get prepopulated entry otherwise
1346
                            } elseif (isset($_GET['prepopulate'])) {
1347
                                $entry_ids = array(intval(current($_GET['prepopulate'])));
1348
                            } else {
1349
                                $entry_ids = array();
1350
                            }
1351
1352
                            // Use $schema for perf reasons
1353
                            $schema = array($field->get('element_name'));
1354
                            $where = (!empty($entry_ids)) ? sprintf(' AND `e`.`id` IN (%s)',
1355
                                implode(', ', $entry_ids)) : null;
1356
                            $entries = (!empty($entry_ids) || isset($_GET['prepopulate']) && $field->get('id') === $prepopulate_field)
0 ignored issues
show
Bug introduced by
The variable $prepopulate_field 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...
1357
                                ? EntryManager::fetchByPage(1, $as['parent_section_id'], $show_entries, $where, null,
1358
                                    false, false, true, $schema)
1359
                                : array();
1360
                            $has_entries = !empty($entries) && $entries['total-entries'] !== 0;
1361
1362
                            if ($has_entries) {
1363
                                $element = new XMLElement('section', null, array('class' => 'association parent'));
1364
                                $header = new XMLElement('header');
1365
                                $header->appendChild(new XMLElement('p', __('Linked to %s in',
1366
                                    array('<a class="association-section" href="' . SYMPHONY_URL . '/publish/' . $as['handle'] . '/">' . $as['name'] . '</a>'))));
1367
                                $element->appendChild($header);
1368
1369
                                $ul = new XMLElement('ul', null, array(
1370
                                    'class' => 'association-links',
1371
                                    'data-section-id' => $as['child_section_id'],
1372
                                    'data-association-ids' => implode(', ', $entry_ids)
1373
                                ));
1374
1375
                                foreach ($entries['records'] as $e) {
1376
                                    // let the field create the mark up
1377
                                    $li = $field->prepareAssociationsDrawerXMLElement($e, $as);
1378
                                    // add it to the unordered list
1379
                                    $ul->appendChild($li);
1380
                                }
1381
1382
                                $element->appendChild($ul);
1383
                                $content->appendChild($element);
1384
                            }
1385
                        }
1386
                    }
1387
                }
1388
1389
                // Process Child Associations
1390
                if (!is_null($child_associations) && !empty($child_associations)) {
1391
                    foreach ($child_associations as $as) {
0 ignored issues
show
Bug introduced by
The expression $child_associations of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

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

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

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

Loading history...
1392
                        // Get the related section
1393
                        $child_section = SectionManager::fetch($as['child_section_id']);
1394
1395
                        if (!($child_section instanceof Section)) {
1396
                            continue;
1397
                        }
1398
1399
                        // Get the visible field instance (using the sorting field, this is more flexible than visibleColumns())
1400
                        // Get the link field instance
1401
                        $visible_field = current($child_section->fetchVisibleColumns());
1402
                        $relation_field = FieldManager::fetch($as['child_section_field_id']);
1403
1404
                        // Get entries, using $schema for performance reasons.
1405
                        $entry_ids = $relation_field->findRelatedEntries($entry_id, $as['parent_section_field_id']);
1406
                        $schema = $visible_field ? array($visible_field->get('element_name')) : array();
1407
                        $where = sprintf(' AND `e`.`id` IN (%s)', implode(', ', $entry_ids));
1408
1409
                        $entries = (!empty($entry_ids)) ? EntryManager::fetchByPage(1, $as['child_section_id'],
1410
                            $show_entries, $where, null, false, false, true, $schema) : array();
1411
                        $has_entries = !empty($entries) && $entries['total-entries'] !== 0;
1412
1413
                        // Build the HTML of the relationship
1414
                        $element = new XMLElement('section', null, array('class' => 'association child'));
1415
                        $header = new XMLElement('header');
1416
                        $filter = '?filter[' . $relation_field->get('element_name') . ']=' . $entry_id;
1417
                        $prepopulate = '?prepopulate[' . $as['child_section_field_id'] . ']=' . $entry_id;
1418
1419
                        // Create link with filter or prepopulate
1420
                        $link = SYMPHONY_URL . '/publish/' . $as['handle'] . '/' . $filter;
1421
                        $a = new XMLElement('a', $as['name'], array(
1422
                            'class' => 'association-section',
1423
                            'href' => $link
1424
                        ));
1425
1426
                        // Create new entries
1427
                        $create = new XMLElement('a', __('Create New'), array(
1428
                            'class' => 'button association-new',
1429
                            'href' => SYMPHONY_URL . '/publish/' . $as['handle'] . '/new/' . $prepopulate
1430
                        ));
1431
1432
                        // Display existing entries
1433
                        if ($has_entries) {
1434
                            $header->appendChild(new XMLElement('p', __('Links in %s', array($a->generate()))));
1435
1436
                            $ul = new XMLElement('ul', null, array(
1437
                                'class' => 'association-links',
1438
                                'data-section-id' => $as['child_section_id'],
1439
                                'data-association-ids' => implode(', ', $entry_ids)
1440
                            ));
1441
1442
                            foreach ($entries['records'] as $key => $e) {
1443
                                // let the first visible field create the mark up
1444
                                if ($visible_field) {
1445
                                    $li = $visible_field->prepareAssociationsDrawerXMLElement($e, $as, $prepopulate);
1446
                                } // or use the system:id if no visible field exists.
1447
                                else {
1448
                                    $li = Field::createAssociationsDrawerXMLElement($e->get('id'), $e, $as,
1449
                                        $prepopulate);
1450
                                }
1451
1452
                                // add it to the unordered list
1453
                                $ul->appendChild($li);
1454
                            }
1455
1456
                            $element->appendChild($ul);
1457
1458
                            // If we are only showing 'some' of the entries, then show this on the UI
1459
                            if ($entries['total-entries'] > $show_entries) {
1460
                                $pagination = new XMLElement('li', null, array(
1461
                                    'class' => 'association-more',
1462
                                    'data-current-page' => '1',
1463
                                    'data-total-pages' => ceil($entries['total-entries'] / $show_entries),
1464
                                    'data-total-entries' => $entries['total-entries']
1465
                                ));
1466
                                $counts = new XMLElement('a', __('Show more entries'), array(
1467
                                    'href' => $link
1468
                                ));
1469
1470
                                $pagination->appendChild($counts);
1471
                                $ul->appendChild($pagination);
1472
                            }
1473
1474
                            // No entries
1475
                        } else {
1476
                            $element->setAttribute('class', 'association child empty');
1477
                            $header->appendChild(new XMLElement('p', __('No links in %s', array($a->generate()))));
1478
                        }
1479
1480
                        $header->appendChild($create);
1481
                        $element->prependChild($header);
1482
                        $content->appendChild($element);
1483
                    }
1484
                }
1485
            }
1486
1487
            $drawer = Widget::Drawer('section-associations', __('Show Associations'), $content);
1488
            $this->insertDrawer($drawer, $drawer_position, 'prepend');
1489
        }
1490
1491
        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...
Coding Style introduced by
__actionNew uses the super-global variable $_FILES 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...
1492
        {
1493
            if (array_key_exists('save', $_POST['action']) || array_key_exists("done", $_POST['action'])) {
1494
                $section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle']);
1495
1496
                if (!$section = SectionManager::fetch($section_id)) {
1497
                    Administration::instance()->throwCustomError(
1498
                        __('The Section, %s, could not be found.',
1499
                            array('<code>' . $this->_context['section_handle'] . '</code>')),
1500
                        __('Unknown Section'),
1501
                        Page::HTTP_STATUS_NOT_FOUND
1502
                    );
1503
                }
1504
1505
                $entry = EntryManager::create();
1506
                $entry->set('author_id', Symphony::Author()->get('id'));
1507
                $entry->set('section_id', $section_id);
1508
                $entry->set('creation_date', DateTimeObj::get('c'));
1509
                $entry->set('modification_date', DateTimeObj::get('c'));
1510
1511
                $fields = $_POST['fields'];
1512
1513
                // Combine FILES and POST arrays, indexed by their custom field handles
1514
                if (isset($_FILES['fields'])) {
1515
                    $filedata = General::processFilePostData($_FILES['fields']);
1516
1517
                    foreach ($filedata as $handle => $data) {
1518
                        if (!isset($fields[$handle])) {
1519
                            $fields[$handle] = $data;
1520
                        } elseif (isset($data['error']) && $data['error'] === UPLOAD_ERR_NO_FILE) {
1521
                            $fields[$handle] = null;
1522
                        } else {
1523
                            foreach ($data as $ii => $d) {
1524
                                if (isset($d['error']) && $d['error'] === UPLOAD_ERR_NO_FILE) {
1525
                                    $fields[$handle][$ii] = null;
1526
                                } elseif (is_array($d) && !empty($d)) {
1527
                                    foreach ($d as $key => $val) {
1528
                                        $fields[$handle][$ii][$key] = $val;
1529
                                    }
1530
                                }
1531
                            }
1532
                        }
1533
                    }
1534
                }
1535
1536
                // Initial checks to see if the Entry is ok
1537
                if (Entry::__ENTRY_FIELD_ERROR__ === $entry->checkPostData($fields, $this->_errors)) {
1538
                    $this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR);
1539
1540
                    // Secondary checks, this will actually process the data and attempt to save
1541 View Code Duplication
                } elseif (Entry::__ENTRY_OK__ !== $entry->setDataFromPost($fields, $errors)) {
1542
                    foreach ($errors as $field_id => $message) {
0 ignored issues
show
Bug introduced by
The expression $errors 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...
1543
                        $this->pageAlert($message, Alert::ERROR);
1544
                    }
1545
1546
                    // Everything is awesome. Dance.
1547
                } else {
1548
                    /**
1549
                     * Just prior to creation of an Entry
1550
                     *
1551
                     * @delegate EntryPreCreate
1552
                     * @param string $context
1553
                     * '/publish/new/'
1554
                     * @param Section $section
1555
                     * @param Entry $entry
1556
                     * @param array $fields
1557
                     */
1558
                    Symphony::ExtensionManager()->notifyMembers('EntryPreCreate', '/publish/new/',
1559
                        array('section' => $section, 'entry' => &$entry, 'fields' => &$fields));
1560
1561
                    // Check to see if the dancing was premature
1562
                    if (!$entry->commit()) {
1563
                        $this->pageAlert(null, Alert::ERROR);
1564
                    } else {
1565
                        /**
1566
                         * Creation of an Entry. New Entry object is provided.
1567
                         *
1568
                         * @delegate EntryPostCreate
1569
                         * @param string $context
1570
                         * '/publish/new/'
1571
                         * @param Section $section
1572
                         * @param Entry $entry
1573
                         * @param array $fields
1574
                         */
1575
                        Symphony::ExtensionManager()->notifyMembers('EntryPostCreate', '/publish/new/',
1576
                            array('section' => $section, 'entry' => $entry, 'fields' => $fields));
1577
1578
                        $prepopulate_querystring = $this->getPrepopulateString();
1579
                        redirect(sprintf(
1580
                            '%s/publish/%s/edit/%d/created/%s',
1581
                            SYMPHONY_URL,
1582
                            $this->_context['section_handle'],
1583
                            $entry->get('id'),
1584
                            (!empty($prepopulate_querystring) ? $prepopulate_querystring : null)
1585
                        ));
1586
                    }
1587
                }
1588
            }
1589
        }
1590
1591
        public function __viewEdit()
0 ignored issues
show
Coding Style introduced by
__viewEdit uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1592
        {
1593 View Code Duplication
            if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
1594
                Administration::instance()->throwCustomError(
1595
                    __('The Section, %s, could not be found.',
1596
                        array('<code>' . $this->_context['section_handle'] . '</code>')),
1597
                    __('Unknown Section'),
1598
                    Page::HTTP_STATUS_NOT_FOUND
1599
                );
1600
            }
1601
1602
            $section = SectionManager::fetch($section_id);
1603
            $entry_id = intval($this->_context['entry_id']);
1604
            $base = '/publish/' . $this->_context['section_handle'] . '/';
1605
            $new_link = $base . 'new/';
1606
            $filter_link = $base;
1607
1608
            EntryManager::setFetchSorting('id', 'DESC');
1609
1610 View Code Duplication
            if (!$existingEntry = EntryManager::fetch($entry_id)) {
1611
                Administration::instance()->throwCustomError(
1612
                    __('Unknown Entry'),
1613
                    __('The Entry, %s, could not be found.', array($entry_id)),
1614
                    Page::HTTP_STATUS_NOT_FOUND
1615
                );
1616
            }
1617
            $existingEntry = $existingEntry[0];
1618
1619
            // If there is post data floating around, due to errors, create an entry object
1620
            if (isset($_POST['fields'])) {
1621
                $fields = $_POST['fields'];
1622
1623
                $entry = EntryManager::create();
1624
                $entry->set('id', $entry_id);
1625
                $entry->set('author_id', $existingEntry->get('author_id'));
1626
                $entry->set('section_id', $existingEntry->get('section_id'));
1627
                $entry->set('creation_date', $existingEntry->get('creation_date'));
1628
                $entry->set('modification_date', $existingEntry->get('modification_date'));
1629
                $entry->setDataFromPost($fields, $errors, true);
1630
1631
                // Editing an entry, so need to create some various objects
1632
            } else {
1633
                $entry = $existingEntry;
1634
                $fields = array();
1635
1636
                if (!$section) {
1637
                    $section = SectionManager::fetch($entry->get('section_id'));
1638
                }
1639
            }
1640
1641
            /**
1642
             * Just prior to rendering of an Entry edit form.
1643
             *
1644
             * @delegate EntryPreRender
1645
             * @param string $context
1646
             * '/publish/edit/'
1647
             * @param Section $section
1648
             * @param Entry $entry
1649
             * @param array $fields
1650
             */
1651
            Symphony::ExtensionManager()->notifyMembers('EntryPreRender', '/publish/edit/', array(
1652
                'section' => $section,
1653
                'entry' => &$entry,
1654
                'fields' => $fields
1655
            ));
1656
1657
            // Iterate over the `prepopulate` parameters to build a URL
1658
            // to remember this state for Create New, View all Entries and
1659
            // Breadcrumb links. If `prepopulate` doesn't exist, this will
1660
            // just use the standard pages (ie. no filtering)
1661
            if (isset($_REQUEST['prepopulate'])) {
1662
                $new_link .= $this->getPrepopulateString();
1663
                $filter_link .= $this->getFilterString();
1664
            }
1665
1666
            if (isset($this->_context['flag'])) {
1667
                // These flags are only relevant if there are no errors
1668
                if (empty($this->_errors)) {
1669
                    $time = Widget::Time();
1670
1671
                    switch ($this->_context['flag']) {
1672
                        case 'saved':
1673
                            $message = __('Entry updated at %s.', array($time->generate()));
1674
                            break;
1675
                        case 'created':
1676
                            $message = __('Entry created at %s.', array($time->generate()));
1677
                    }
1678
1679
                    $this->pageAlert(
1680
                        $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...
1681
                        . ' <a href="' . SYMPHONY_URL . $new_link . '" accesskey="c">'
1682
                        . __('Create another?')
1683
                        . '</a> <a href="' . SYMPHONY_URL . $filter_link . '" accesskey="a">'
1684
                        . __('View all Entries')
1685
                        . '</a>',
1686
                        Alert::SUCCESS
1687
                    );
1688
                }
1689
            }
1690
1691
            // Determine the page title
1692
            $field_id = Symphony::Database()->fetchVar('id', 0, "
1693
            SELECT `id`
1694
            FROM `tbl_fields`
1695
            WHERE `parent_section` = ?
1696
            ORDER BY `sortorder` LIMIT 1",
1697
                array($section->get('id'))
1698
            );
1699
            if (!is_null($field_id)) {
1700
                $field = FieldManager::fetch($field_id);
1701
            }
1702
1703
            if ($field) {
1704
                $title = $field->prepareReadableValue($existingEntry->getData($field->get('id')), $entry_id, true);
0 ignored issues
show
Bug introduced by
The variable $field 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...
1705
            } else {
1706
                $title = '';
1707
            }
1708
1709
            if (trim($title) === '') {
1710
                $title = __('Untitled');
1711
            }
1712
1713
            // Check if there is a field to prepopulate
1714
            if (isset($_REQUEST['prepopulate'])) {
1715
                foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1716
                    $this->Form->prependChild(Widget::Input(
1717
                        "prepopulate[{$field_id}]",
1718
                        rawurlencode($value),
1719
                        'hidden'
1720
                    ));
1721
                }
1722
            }
1723
1724
            $this->setPageType('form');
1725
            $this->Form->setAttribute('enctype', 'multipart/form-data');
1726
            $this->setTitle(__('%1$s &ndash; %2$s &ndash; %3$s', array($title, $section->get('name'), __('Symphony'))));
1727
1728
            $sidebar_fields = $section->fetchFields(null, 'sidebar');
1729
            $main_fields = $section->fetchFields(null, 'main');
1730
1731 View Code Duplication
            if (!empty($sidebar_fields) && !empty($main_fields)) {
1732
                $this->Form->setAttribute('class', 'two columns');
1733
            } else {
1734
                $this->Form->setAttribute('class', 'columns');
1735
            }
1736
1737
            // Only show the Edit Section button if the Author is a developer. #938 ^BA
1738
            if (Symphony::Author()->isDeveloper()) {
1739
                $this->appendSubheading($title,
1740
                    Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/',
1741
                        __('Edit Section Configuration'), 'button'));
1742
            } else {
1743
                $this->appendSubheading($title);
1744
            }
1745
1746
            $this->insertBreadcrumbs(array(
1747
                Widget::Anchor($section->get('name'), SYMPHONY_URL . (isset($filter_link) ? $filter_link : $base)),
1748
            ));
1749
1750
            $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE',
1751
                Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
1752
1753
            $primary = new XMLElement('fieldset');
1754
            $primary->setAttribute('class', 'primary column');
1755
1756
            if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) {
1757
                $message = __('Fields must be added to this section before an entry can be created.');
1758
1759 View Code Duplication
                if (Symphony::Author()->isDeveloper()) {
1760
                    $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . $section->get('id') . '/" accesskey="c">'
1761
                        . __('Add fields')
1762
                        . '</a>';
1763
                }
1764
1765
                $this->pageAlert($message, Alert::ERROR);
1766
            } else {
1767 View Code Duplication
                if (is_array($main_fields) && !empty($main_fields)) {
1768
                    foreach ($main_fields as $field) {
1769
                        $primary->appendChild($this->__wrapFieldWithDiv($field, $entry));
1770
                    }
1771
1772
                    $this->Form->appendChild($primary);
1773
                }
1774
1775 View Code Duplication
                if (is_array($sidebar_fields) && !empty($sidebar_fields)) {
1776
                    $sidebar = new XMLElement('fieldset');
1777
                    $sidebar->setAttribute('class', 'secondary column');
1778
1779
                    foreach ($sidebar_fields as $field) {
1780
                        $sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry));
1781
                    }
1782
1783
                    $this->Form->appendChild($sidebar);
1784
                }
1785
1786
                $div = new XMLElement('div');
1787
                $div->setAttribute('class', 'actions');
1788
                $div->appendChild(Widget::Input('action[save]', __('Save Changes'), 'submit',
1789
                    array('accesskey' => 's')));
1790
1791
                $button = new XMLElement('button', __('Delete'));
1792
                $button->setAttributeArray(array(
1793
                    'name' => 'action[delete]',
1794
                    'class' => 'button confirm delete',
1795
                    'title' => __('Delete this entry'),
1796
                    'type' => 'submit',
1797
                    'accesskey' => 'd',
1798
                    'data-message' => __('Are you sure you want to delete this entry?')
1799
                ));
1800
                $div->appendChild($button);
1801
1802
                $this->Form->appendChild($div);
1803
1804
                // Create a Drawer for Associated Sections
1805
                $this->prepareAssociationsDrawer($section);
0 ignored issues
show
Bug introduced by
It seems like $section can also be of type array; however, contentPublish::prepareAssociationsDrawer() does only seem to accept 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...
1806
            }
1807
        }
1808
1809
        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...
1810
        {
1811
            $entry_id = intval($this->_context['entry_id']);
1812
1813
            if (@array_key_exists('save', $_POST['action']) || @array_key_exists("done", $_POST['action'])) {
1814 View Code Duplication
                if (!$ret = EntryManager::fetch($entry_id)) {
1815
                    Administration::instance()->throwCustomError(
1816
                        __('The Entry, %s, could not be found.', array($entry_id)),
1817
                        __('Unknown Entry'),
1818
                        Page::HTTP_STATUS_NOT_FOUND
1819
                    );
1820
                }
1821
1822
                $entry = $ret[0];
1823
1824
                $section = SectionManager::fetch($entry->get('section_id'));
1825
1826
                $post = General::getPostData();
1827
                $fields = $post['fields'];
1828
1829
                // Initial checks to see if the Entry is ok
1830
                if (Entry::__ENTRY_FIELD_ERROR__ === $entry->checkPostData($fields, $this->_errors)) {
1831
                    $this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR);
1832
1833
                    // Secondary checks, this will actually process the data and attempt to save
1834 View Code Duplication
                } elseif (Entry::__ENTRY_OK__ !== $entry->setDataFromPost($fields, $errors)) {
0 ignored issues
show
Bug introduced by
The variable $errors does not exist. Did you forget to declare it?

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

Loading history...
1835
                    foreach ($errors as $field_id => $message) {
1836
                        $this->pageAlert($message, Alert::ERROR);
1837
                    }
1838
1839
                    // Everything is awesome. Dance.
1840
                } else {
1841
                    /**
1842
                     * Just prior to editing of an Entry.
1843
                     *
1844
                     * @delegate EntryPreEdit
1845
                     * @param string $context
1846
                     * '/publish/edit/'
1847
                     * @param Section $section
1848
                     * @param Entry $entry
1849
                     * @param array $fields
1850
                     */
1851
                    Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/',
1852
                        array('section' => $section, 'entry' => &$entry, 'fields' => $fields));
1853
1854
                    // Check to see if the dancing was premature
1855
                    if (!$entry->commit()) {
1856
                        $this->pageAlert(null, Alert::ERROR);
1857
                    } else {
1858
                        /**
1859
                         * Just after the editing of an Entry
1860
                         *
1861
                         * @delegate EntryPostEdit
1862
                         * @param string $context
1863
                         * '/publish/edit/'
1864
                         * @param Section $section
1865
                         * @param Entry $entry
1866
                         * @param array $fields
1867
                         */
1868
                        Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/',
1869
                            array('section' => $section, 'entry' => $entry, 'fields' => $fields));
1870
1871
                        redirect(sprintf(
1872
                            '%s/publish/%s/edit/%d/saved/%s',
1873
                            SYMPHONY_URL,
1874
                            $this->_context['section_handle'],
1875
                            $entry->get('id'),
1876
                            $this->getPrepopulateString()
1877
                        ));
1878
                    }
1879
                }
1880
            } elseif (@array_key_exists('delete', $_POST['action']) && is_numeric($entry_id)) {
1881
                /**
1882
                 * Prior to deletion of entries. An array of Entry ID's is provided which
1883
                 * can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete`
1884
                 * in Symphony 2.3.
1885
                 *
1886
                 * @delegate EntryPreDelete
1887
                 * @param string $context
1888
                 * '/publish/'
1889
                 * @param array $entry_id
1890
                 *    An array of Entry ID's passed by reference
1891
                 */
1892
                $checked = array($entry_id);
1893
                Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/',
1894
                    array('entry_id' => &$checked));
1895
1896
                EntryManager::delete($checked);
1897
1898
                /**
1899
                 * After the deletion of entries, this delegate provides an array of Entry ID's
1900
                 * that were deleted.
1901
                 *
1902
                 * @since Symphony 2.3
1903
                 * @delegate EntryPostDelete
1904
                 * @param string $context
1905
                 * '/publish/'
1906
                 * @param array $entry_id
1907
                 *  An array of Entry ID's that were deleted.
1908
                 */
1909
                Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/',
1910
                    array('entry_id' => $checked));
1911
1912
                redirect(SYMPHONY_URL . '/publish/' . $this->_context['section_handle'] . '/');
1913
            }
1914
        }
1915
    }
1916