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.
Passed
Pull Request — master (#2835)
by
unknown
06:01
created

contentPublish   F

Complexity

Total Complexity 290

Size/Duplication

Total Lines 1953
Duplicated Lines 9.88 %

Coupling/Cohesion

Components 2
Dependencies 23

Importance

Changes 0
Metric Value
dl 193
loc 1953
rs 0.6197
c 0
b 0
f 0
wmc 290
lcom 2
cbo 23

23 Methods

Rating   Name   Duplication   Size   Complexity  
A createFilteringInterface() 0 14 3
A createFilterHelp() 0 12 2
A createFilteringDuplicator() 0 16 1
B createSystemDateFilters() 0 39 3
A view() 0 3 1
B createFilter() 0 41 1
A createFilteringDrawer() 0 6 1
B __switchboard() 0 22 5
A build() 0 12 2
A action() 0 3 1
B isFieldHidden() 0 13 5
B __wrapFieldWithDiv() 0 36 5
F __viewEdit() 0 238 32
B createFieldFilters() 0 32 4
A createFilterSuggestions() 0 14 2
F __viewIndex() 0 561 77
C sort() 0 45 11
A getFilterQuery() 0 13 4
C __actionIndex() 0 109 8
B createFilterComparisons() 0 32 6
D __viewNew() 0 119 22
D __actionNew() 0 94 20
D __actionEdit() 0 119 14

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
 * The Publish page is where the majority of an Authors time will
8
 * be spent in Symphony with adding, editing and removing entries
9
 * from Sections. This Page controls the entries table as well as
10
 * the Entry creation screens.
11
 */
12
13
class contentPublish extends AdministrationPage
14
{
15
    public $_errors = array();
16
17
    public function sort(&$sort, &$order, $params)
18
    {
19
        $section = $params['current-section'];
20
        $filters = '';
21
        // Format the filter query string
22
        if (isset($params['filters']) && !empty($params['filters'])) {
23
            $filters = preg_replace('/^&amp;/i', '', $params['filters'], 1);
24
            $filters = '?' . trim($filters);
25
        }
26
27
        // If `?unsort` is appended to the URL, then sorting is reverted
28
        // to 'none', aka. by 'entry-id'.
29
        if ($params['unsort']) {
30
            $section->setSortingField('id', false);
31
            $section->setSortingOrder('desc');
32
33
            redirect(Administration::instance()->getCurrentPageURL() . $filters);
34
        }
35
36
        // By default, sorting information are retrieved from
37
        // the file system and stored inside the `Configuration` object
38
        if (!$sort) {
39
            $sort = $section->getSortingField();
40
            $order = $section->getSortingOrder();
41
        } else {
42
            $sort = General::sanitize($sort);
43
            $field = (new FieldManager)->select()->field($sort)->execute()->next();
0 ignored issues
show
Bug introduced by
$sort of type string is incompatible with the type integer expected by parameter $field_id of FieldQuery::field(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

43
            $field = (new FieldManager)->select()->field(/** @scrutinizer ignore-type */ $sort)->execute()->next();
Loading history...
44
45
            // Ensure that this field is in fact sortable, otherwise
46
            // fallback to IDs
47
            if (!$field || !$field->isSortable()) {
0 ignored issues
show
introduced by
$field is of type Field, thus it always evaluated to true.
Loading history...
48
                $sort = $section->getDefaultSortingField();
49
            }
50
51
            // If the sort order or direction differs from what is saved,
52
            // update the config file and reload the page
53
            if ($sort != $section->getSortingField() || $order != $section->getSortingOrder()) {
54
                $section->setSortingField($sort, false);
55
                $section->setSortingOrder($order);
56
                redirect(Administration::instance()->getCurrentPageURL() . $filters);
57
            }
58
59
            // If the sort order and direction remains the same, reload the page
60
            if ($sort == $section->getSortingField() && $order == $section->getSortingOrder()) {
61
                redirect(Administration::instance()->getCurrentPageURL() . $filters);
62
            }
63
        }
64
    }
65
66
    /**
67
     * Append filtering interface
68
     */
69
    public function createFilteringInterface()
70
    {
71
        //Check if section has filtering enabled
72
        $context = $this->getContext();
73
        $handle = $context['section_handle'];
74
        $section_id = SectionManager::fetchIDFromHandle($handle);
75
        $section = (new SectionManager)->select()->section($section_id)->execute()->next();
76
        $filter = $section->get('filter');
77
        $count = (new EntryManager)->select()->count()->section($section_id)->execute()->variable(0);
78
79
        if ($filter !== 'no' && $count > 1) {
80
            $drawer = Widget::Drawer('filtering-' . $section_id, __('Filter Entries'), $this->createFilteringDrawer($section));
81
            $drawer->addClass('drawer-filtering');
82
            $this->insertDrawer($drawer);
83
        }
84
    }
85
86
    /**
87
     * Create filtering drawer
88
     */
89
    public function createFilteringDrawer($section)
90
    {
91
        $this->filteringForm = Widget::Form(null, 'get', 'filtering');
0 ignored issues
show
Bug Best Practice introduced by
The property filteringForm does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
92
        $this->createFilteringDuplicator($section);
93
94
        return $this->filteringForm;
95
    }
96
97
    public function createFilteringDuplicator($section)
98
    {
99
        $div = new XMLElement('div');
100
        $div->setAttribute('class', 'frame filters-duplicator');
101
        $div->setAttribute('data-interactive', 'data-interactive');
102
103
        $ol = new XMLElement('ol');
104
        $ol->setAttribute('data-add', __('Add filter'));
105
        $ol->setAttribute('data-remove', __('Clear filter'));
106
        $ol->setAttribute('data-empty', __('No filters applied yet.'));
107
108
        $this->createFieldFilters($ol, $section);
109
        $this->createSystemDateFilters($ol);
110
111
        $div->appendChild($ol);
112
        $this->filteringForm->appendChild($div);
113
    }
114
115
    private function createFieldFilters(&$wrapper, $section)
116
    {
117
        $filters = $_GET['filter'];
118
119
        foreach ($section->fetchFilterableFields() as $field) {
120
            if (!$field->canPublishFilter()) {
121
                continue;
122
            }
123
124
            $filter = $filters[$field->get('element_name')];
125
126
            // Filter data
127
            $data = array();
128
            $data['type'] = $field->get('element_name');
129
            $data['name'] = $field->get('label');
130
            $data['filter'] = $filter;
131
            $data['instance'] = 'unique';
132
            $data['search'] = $field->fetchSuggestionTypes();
133
            $data['operators'] = $field->fetchFilterableOperators();
134
            $data['comparisons'] = $this->createFilterComparisons($data);
135
            $data['query'] = $this->getFilterQuery($data);
136
            $data['field-id'] = $field->get('id');
137
138
            // Add existing filter
139
            if (isset($filter)) {
140
                $this->createFilter($wrapper, $data);
141
            }
142
143
            // Add filter template
144
            $data['instance'] = 'unique template';
145
            $data['query'] = '';
146
            $this->createFilter($wrapper, $data);
147
        }
148
    }
149
150
    private function createSystemDateFilters(&$wrapper)
151
    {
152
        $filters = $_GET['filter'];
153
        $dateField = new FieldDate;
154
155
        $fields = array(
156
            array(
157
                'type' => 'system:creation-date',
158
                'label' => __('System Creation Date')
159
            ),
160
            array(
161
                'type' => 'system:modification-date',
162
                'label' => __('System Modification Date')
163
            )
164
        );
165
166
        foreach ($fields as $field) {
167
            $filter = $filters[$field['type']];
168
169
            // Filter data
170
            $data = array();
171
            $data['type'] = $field['type'];
172
            $data['name'] = $field['label'];
173
            $data['filter'] = $filter;
174
            $data['instance'] = 'unique';
175
            $data['search'] = $dateField->fetchSuggestionTypes();
176
            $data['operators'] = $dateField->fetchFilterableOperators();
177
            $data['comparisons'] = $this->createFilterComparisons($data);
178
            $data['query'] = $this->getFilterQuery($data);
179
180
            // Add existing filter
181
            if (isset($filter)) {
182
                $this->createFilter($wrapper, $data);
183
            }
184
185
            // Add filter template
186
            $data['instance'] = 'unique template';
187
            $data['query'] = '';
188
            $this->createFilter($wrapper, $data);
189
        }
190
    }
191
192
    private function createFilter(&$wrapper, $data)
193
    {
194
        $li = new XMLElement('li');
195
        $li->setAttribute('class', $data['instance']);
196
        $li->setAttribute('data-type', $data['type']);
197
198
        // Header
199
        $li->appendChild(new XMLElement('header', $data['name'], array(
200
            'data-name' => $data['name']
201
        )));
202
203
        // Settings
204
        $div = new XMLElement('div', null, array('class' => 'two columns'));
205
206
        // Comparisons
207
        $label = Widget::Label();
208
        $label->setAttribute('class', 'column secondary');
209
210
        $select = Widget::Select($data['type'] . '-comparison', $data['comparisons'], array(
211
            'class' => 'comparison'
212
        ));
213
214
        $label->appendChild($select);
215
        $div->appendChild($label);
216
217
        // Query
218
        $label = Widget::Label();
219
        $label->setAttribute('class', 'column primary');
220
221
        $input = Widget::Input($data['type'], General::sanitize($data['query']), 'text', array(
222
            'placeholder' => __('Type and hit enter to apply filter…'),
223
            'autocomplete' => 'off'
224
        ));
225
        $input->setAttribute('class', 'filter');
226
        $label->appendChild($input);
227
228
        $this->createFilterSuggestions($label, $data);
229
230
        $div->appendChild($label);
231
        $li->appendChild($div);
232
        $wrapper->appendChild($li);
233
    }
234
235
    private function createFilterComparisons($data)
236
    {
237
        // Default comparison
238
        $comparisons = array();
239
240
        // Custom field comparisons
241
        foreach ($data['operators'] as $operator) {
242
243
            $filter = trim($operator['filter']);
244
245
            // Check selected state
246
            $selected = false;
247
248
            // Selected state : Comparison mode "between" (x to y)
249
            if ($operator['title'] === 'between' && preg_match('/^(-?(?:\d+(?:\.\d+)?|\.\d+)) to (-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $data['filter'] )) {
250
                $selected = true;
251
            // Selected state : Other comparison modes (except "is")
252
            } elseif ((!empty($filter) && strpos($data['filter'], $filter) === 0)) {
253
                $selected = true;
254
            }
255
256
            $comparisons[] = array(
257
                $operator['filter'],
258
                $selected,
259
                __($operator['title']),
260
                null,
261
                null,
262
                array('data-comparison' => $operator['title'])
263
            );
264
        }
265
266
        return $comparisons;
267
    }
268
269
    private function createFilterSuggestions(&$wrapper, $data)
270
    {
271
        $ul = new XMLElement('ul');
272
        $ul->setAttribute('class', 'suggestions');
273
        $ul->setAttribute('data-field-id', $data['field-id']);
274
        $ul->setAttribute('data-associated-ids', '0');
275
        $ul->setAttribute('data-search-types', implode($data['search'], ','));
0 ignored issues
show
Bug introduced by
',' of type string is incompatible with the type array expected by parameter $pieces of implode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

275
        $ul->setAttribute('data-search-types', implode($data['search'], /** @scrutinizer ignore-type */ ','));
Loading history...
276
277
        // Add help text for each filter operator
278
        foreach ($data['operators'] as $operator) {
279
            $this->createFilterHelp($ul, $operator);
280
        }
281
282
        $wrapper->appendChild($ul);
283
    }
284
285
    private function createFilterHelp(&$wrapper, $operator)
286
    {
287
        if (empty($operator['help'])) {
288
            return;
289
        }
290
291
        $li = new XMLElement('li', __('Comparison mode') . ': ' . $operator['help'], array(
292
            'class' => 'help',
293
            'data-comparison' => $operator['title']
294
        ));
295
296
        $wrapper->appendChild($li);
297
    }
298
299
    private function getFilterQuery($data)
300
    {
301
        $query = $data['filter'];
302
303
        foreach ($data['operators'] as $operator) {
304
            $filter = trim($operator['filter']);
305
306
            if (!empty($filter) && strpos($data['filter'], $filter) === 0) {
307
                $query = substr($data['filter'], strlen($operator['filter']));
308
            }
309
        }
310
311
        return (string)$query;
312
    }
313
314
    public function build(array $context = array())
315
    {
316
        $section_id = SectionManager::fetchIDFromHandle($context['section_handle']);
317
318
        if ($section_id) {
319
            $context['associations'] = array(
320
                'parent' => SectionManager::fetchParentAssociations($section_id),
321
                'child' => SectionManager::fetchChildAssociations($section_id)
322
            );
323
        }
324
325
        return parent::build($context);
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::build($context) targeting AdministrationPage::build() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
326
    }
327
328
    public function action()
329
    {
330
        $this->__switchboard('action');
331
    }
332
333
    public function __switchboard($type = 'view')
334
    {
335
        $function = ($type == 'action' ? '__action' : '__view') . ucfirst($this->_context['page']);
336
337
        if (!method_exists($this, $function)) {
338
            // If there is no action function, just return without doing anything
339
            if ($type == 'action') {
340
                return;
341
            }
342
343
            Administration::instance()->errorPageNotFound();
344
        }
345
346
        // Is this request allowed by server?
347
        if ($this->isRequestValid() === false) {
348
            $this->pageAlert(__('This request exceeds the maximum allowed request size of %s specified by your host.', array(
349
                    ini_get('post_max_size')
350
                )),
351
                Alert::ERROR
352
            );
353
        }
354
        $this->$function();
355
    }
356
357
    public function view()
358
    {
359
        $this->__switchboard();
360
    }
361
362
    public function __viewIndex()
363
    {
364
        if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
365
            Administration::instance()->throwCustomError(
366
                __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
367
                __('Unknown Section'),
368
                Page::HTTP_STATUS_NOT_FOUND
369
            );
370
        } elseif (!is_writable(CONFIG)) {
0 ignored issues
show
Bug introduced by
The constant CONFIG was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
371
            $this->pageAlert(__('The Symphony configuration file, %s, is not writable. The sort order cannot be modified.', array('<code>/manifest/config.php</code>')), Alert::NOTICE);
372
        }
373
374
        $section = (new SectionManager)->select()->section($section_id)->execute()->next();
375
376
        $this->setPageType('table');
377
        $this->setTitle(__('%1$s &ndash; %2$s', array(General::sanitize($section->get('name')), __('Symphony'))));
0 ignored issues
show
Bug introduced by
It seems like $section->get('name') can also be of type array; however, parameter $source of General::sanitize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

377
        $this->setTitle(__('%1$s &ndash; %2$s', array(General::sanitize(/** @scrutinizer ignore-type */ $section->get('name')), __('Symphony'))));
Loading history...
378
379
        $filter_querystring = $prepopulate_querystring = $where = $joins = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $where is dead and can be removed.
Loading history...
380
        $current_page = (isset($_REQUEST['pg']) && is_numeric($_REQUEST['pg']) ? max(1, intval($_REQUEST['pg'])) : 1);
381
382
        // get visible columns
383
        $visible_columns = $section->fetchVisibleColumns();
384
        // extract the needed schema
385
        $element_names = array_values(array_map(function ($field) {
386
            return $field->get('element_name');
387
        }, $visible_columns));
388
        // create the query
389
        $entryQuery = (new EntryManager)
390
            ->select($element_names)
391
            ->distinct()
392
            ->section($section_id)
393
            ->sort($section->getSortingField(), $section->getSortingOrder())
394
            ->paginate(
395
                $current_page,
396
                Symphony::Configuration()->get('pagination_maximum_rows', 'symphony')
397
            );
398
399
        if (isset($_REQUEST['filter'])) {
400
            // legacy implementation, convert single filter to an array
401
            // split string in the form ?filter=handle:value
402
            // @deprecated
403
            // This should be removed in Symphony 4.0.0
404
            if (!is_array($_REQUEST['filter'])) {
405
                list($field_handle, $filter_value) = explode(':', $_REQUEST['filter'], 2);
406
                $filters[$field_handle] = rawurldecode($filter_value);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$filters was never initialized. Although not strictly required by PHP, it is generally a good practice to add $filters = array(); before regardless.
Loading history...
407
            } else {
408
                $filters = $_REQUEST['filter'];
409
            }
410
411
            foreach ($filters as $handle => $value) {
412
                // Handle multiple values through filtering. RE: #2290
413
                if ((is_array($value) && empty($value)) || trim($value) == '') {
414
                    continue;
415
                }
416
417
                if (!is_array($value)) {
418
                    $filter_type = Datasource::determineFilterType($value);
419
                    $value = Datasource::splitFilter($filter_type, $value);
420
                } else {
421
                    $filter_type = Datasource::FILTER_OR;
422
                }
423
424
                // Handle date meta data #2003
425
                if (in_array($handle, array('system:creation-date', 'system:modification-date'))) {
426
                    $op = $filter_type === Datasource::FILTER_AND ? 'and' : 'or';
427
                    $entryQuery->filter($handle, $value, $op);
428
                // Handle normal fields
429
                } else {
430
                    $field = (new FieldManager)->select()->name($handle)->execute()->next();
431
                    // Use EQFA
432
                    if ($field && $field->getEntryQueryFieldAdapter()) {
433
                        $op = $filter_type === Datasource::FILTER_AND ? 'and' : 'or';
434
                        $entryQuery->filter($field, $value, $op);
435
                    // Compat layer with the old API
436
                    } elseif ($field) {
437
                        $joins = $where = '';
438
                        $field->buildDSRetrievalSQL($value, $joins, $where, ($filter_type == Datasource::FILTER_AND ? true : false));
0 ignored issues
show
Deprecated Code introduced by
The function Field::buildDSRetrievalSQL() has been deprecated: @since Symphony 3.0.0 Use EntryQueryFieldAdapter::filter() instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

438
                        /** @scrutinizer ignore-deprecated */ $field->buildDSRetrievalSQL($value, $joins, $where, ($filter_type == Datasource::FILTER_AND ? true : false));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
439
440
                        if ($joins) {
441
                            $joins = $entryQuery->replaceTablePrefix($joins);
442
                            $entryQuery->unsafe()->unsafeAppendSQLPart('join', $joins);
443
                        }
444
                        if ($where) {
445
                            $where = $entryQuery->replaceTablePrefix($where);
446
                            $wherePrefix = $entryQuery->containsSQLParts('where') ? '' : 'WHERE 1 = 1';
447
                            $entryQuery->unsafe()->unsafeAppendSQLPart('where', "$wherePrefix $where");
448
                        }
449
450
                        $value = implode(',', $value);
451
                        $encoded_value = rawurlencode($value);
452
                        $filter_querystring .= sprintf("filter[%s]=%s&amp;", $handle, $encoded_value);
453
454
                        // Some fields require that prepopulation be done via ID. RE: #2331
455
                        if (!is_numeric($value) && method_exists($field, 'fetchIDfromValue')) {
456
                            $encoded_value = $field->fetchIDfromValue($value);
457
                        }
458
                        $prepopulate_querystring .= sprintf("prepopulate[%d]=%s&amp;", $field_id, $encoded_value);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $field_id does not exist. Did you maybe mean $parent_section_field_id?
Loading history...
459
                    // Invalid, ignore.
460
                    } else {
461
                        unset($filters[$handle]);
462
                    }
463
                    unset($field);
464
                }
465
            }
466
467
            $filter_querystring = preg_replace("/&amp;$/", '', $filter_querystring);
468
            $prepopulate_querystring = preg_replace("/&amp;$/", '', $prepopulate_querystring);
469
        }
470
471
        Sortable::initialize($this, $entriesUnused, $sort, $order, array(
472
            'current-section' => $section,
473
            'filters' => ($filter_querystring ? "&amp;" . $filter_querystring : ''),
474
            'unsort' => isset($_REQUEST['unsort'])
475
        ));
476
477
        $this->Form->setAttribute('action', Administration::instance()->getCurrentPageURL(). '?pg=' . $current_page.($filter_querystring ? "&amp;" . $filter_querystring : ''));
478
479
        // Build filtering interface
480
        $this->createFilteringInterface();
481
482
        $subheading_buttons = array(
483
            Widget::Anchor(__('Create New'), Administration::instance()->getCurrentPageURL().'new/'.($prepopulate_querystring ? '?' . $prepopulate_querystring : ''), __('Create a new entry'), 'create button', null, array('accesskey' => 'c'))
484
        );
485
486
        // Only show the Edit Section button if the Author is a developer. #938 ^BA
487
        if (Symphony::Author()->isDeveloper()) {
488
            array_unshift($subheading_buttons, Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button'));
0 ignored issues
show
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
489
        }
490
491
        $this->appendSubheading(General::sanitize($section->get('name')), $subheading_buttons);
492
493
        $joins = $where = '';
494
        /**
495
         * Allows adjustments to be made to the SQL where and joins statements
496
         * before they are used to fetch the entries for the page
497
         *
498
         * @delegate AdjustPublishFiltering
499
         * @since Symphony 2.3.3
500
         * @param string $context
501
         * '/publish/'
502
         * @param integer $section_id
503
         *  The section id we are fetching
504
         * @param Section $section
505
         *  The section object
506
         *  @since Symphony 3.0.0
507
         * @param EntryQuery $query
508
         *  The query object
509
         *  @since Symphony 3.0.0
510
         * @param string $where
511
         *  The current where statement, or null if not set.
512
         *  @deprecated Symphony 3.0.0 use $query instead
513
         * @param string $joins
514
         *  @deprecated Symphony 3.0.0 use $query instead
515
         */
516
        Symphony::ExtensionManager()->notifyMembers(
517
            'AdjustPublishFiltering',
518
            '/publish/',
519
            [
520
                'section-id' => $section_id,
521
                'section' => $section,
522
                'query' => $entryQuery,
523
                'where' => &$where,
524
                'joins' => &$joins,
525
            ]
526
        );
527
        if ($joins) {
528
            $joins = $entryQuery->replaceTablePrefix($joins);
529
            $entryQuery->unsafe()->unsafeAppendSQLPart('join', $joins);
530
        }
531
        if ($where) {
532
            $where = $entryQuery->replaceTablePrefix($where);
533
            $entryQuery->unsafe()->unsafeAppendSQLPart('where', "WHERE 1=1 AND $where");
534
        }
535
536
        // Check that the filtered query fails that the filter is dropped and an
537
        // error is logged. #841 ^BA
538
        try {
539
            $pagination = $entryQuery->execute()->pagination();
540
        } catch (DatabaseException $ex) {
541
            $this->pageAlert(
542
                __('An error occurred while retrieving filtered entries. Showing all entries instead.'),
543
                Alert::ERROR
544
            );
545
            $filter_querystring = null;
546
            Symphony::Log()->pushToLog(
547
                sprintf(
548
                    '%s - %s%s%s',
549
                    $section->get('name') . ' Publish Index',
0 ignored issues
show
Bug introduced by
Are you sure $section->get('name') of type string|array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

549
                    /** @scrutinizer ignore-type */ $section->get('name') . ' Publish Index',
Loading history...
550
                    $ex->getMessage(),
551
                    ($ex->getFile() ? " in file " .  $ex->getFile() : null),
552
                    ($ex->getLine() ? " on line " . $ex->getLine() : null)
553
                ),
554
                E_NOTICE,
555
                true
556
            );
557
            $pagination = (new EntryManager)
558
                ->select($element_names)
559
                ->distinct()
560
                ->disableDefaultSort()
561
                ->section($section_id)
562
                ->paginate(
563
                    $current_page,
564
                    Symphony::Configuration()->get('pagination_maximum_rows', 'symphony')
565
                )
566
                ->execute()->pagination();
567
        }
568
        $entries = $pagination->rows();
569
570
        // Flag filtering
571
        if (isset($_REQUEST['filter'])) {
572
            $totalCount = (new EntryManager)
573
                ->select()
574
                ->count()
575
                ->section($section_id)
576
                ->execute()
577
                ->variable(0);
578
            $filter_text = __('%d of %d entries (filtered)', [$pagination->totalEntries(), $totalCount]);
579
        } else {
580
            $filter_text = __('%d entries', [$pagination->totalEntries()]);
581
        }
582
        $filter_stats = new XMLElement(
583
            'p',
584
            '<span>– ' . $filter_text . '</span>',
585
            ['class' => 'inactive']
586
        );
587
        $this->Breadcrumbs->appendChild($filter_stats);
588
589
        // Build table
590
        $columns = array();
591
592
        if (is_array($visible_columns) && !empty($visible_columns)) {
593
            foreach ($visible_columns as $column) {
594
                $columns[] = array(
595
                    'label' => $column->get('label'),
596
                    'sortable' => $column->isSortable(),
597
                    'handle' => $column->get('id'),
598
                    'attrs' => array(
599
                        'id' => 'field-' . $column->get('id'),
600
                        'class' => 'field-' . $column->get('type')
601
                    )
602
                );
603
            }
604
        } else {
605
            $columns[] = array(
606
                'label' => __('ID'),
607
                'sortable' => true,
608
                'handle' => 'id'
609
            );
610
        }
611
612
        $aTableHead = Sortable::buildTableHeaders($columns, $sort, $order, ($filter_querystring) ? "&amp;" . $filter_querystring : '');
613
614
        $child_sections = array();
615
        $associated_sections = $section->fetchChildAssociations(true);
616
617
        if (is_array($associated_sections) && !empty($associated_sections)) {
618
            foreach ($associated_sections as $key => $as) {
619
                $child_sections[$key] = (new SectionManager)
620
                    ->select()
621
                    ->section($as['child_section_id'])
622
                    ->execute()
623
                    ->next();
624
                $aTableHead[] = array($child_sections[$key]->get('name'), 'col');
625
            }
626
        }
627
628
        /**
629
         * Allows the creation of custom table columns for each entry. Called
630
         * after all the Section Visible columns have been added as well
631
         * as the Section Associations
632
         *
633
         * @delegate AddCustomPublishColumn
634
         * @since Symphony 2.2
635
         * @param string $context
636
         * '/publish/'
637
         * @param array $tableHead
638
         * An array of the current columns, passed by reference
639
         * @param integer $section_id
640
         * The current Section ID
641
         */
642
        Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumn', '/publish/', array('tableHead' => &$aTableHead, 'section_id' => $section->get('id')));
643
644
        // Table Body
645
        $aTableBody = array();
646
647
        if (empty($entries)) {
648
            $aTableBody = array(
649
                Widget::TableRow(array(Widget::TableData(__('None found.'), 'inactive', null, count($aTableHead))), 'odd')
650
            );
651
        } else {
652
            $field_pool = array();
653
654
            if (is_array($visible_columns) && !empty($visible_columns)) {
655
                foreach ($visible_columns as $column) {
656
                    $field_pool[$column->get('id')] = $column;
657
                }
658
            }
659
660
            $link_column = array_reverse($visible_columns);
661
            $link_column = end($link_column);
662
            reset($visible_columns);
663
664
            foreach ($entries as $entry) {
665
                $tableData = array();
666
667
                // Setup each cell
668
                if (!is_array($visible_columns) || empty($visible_columns)) {
669
                    $tableData[] = Widget::TableData(Widget::Anchor($entry->get('id'), Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/'));
670
                } else {
671
                    $link = Widget::Anchor(
672
                        '',
673
                        Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/'.($filter_querystring ? '?' . $prepopulate_querystring : ''),
674
                        $entry->get('id'),
675
                        'content'
676
                    );
677
678
                    foreach ($visible_columns as $position => $column) {
679
                        $data = $entry->getData($column->get('id'));
680
                        $field = $field_pool[$column->get('id')];
681
682
                        $value = $field->prepareTableValue($data, ($column == $link_column) ? $link : null, $entry->get('id'));
683
684
                        if (!is_object($value) && (strlen(trim($value)) == 0 || $value == __('None'))) {
685
                            $value = ($position == 0 ? $link->generate() : __('None'));
686
                        }
687
688
                        if ($value == __('None')) {
689
                            $tableData[] = Widget::TableData($value, 'inactive field-' . $column->get('type') . ' field-' . $column->get('id'));
690
                        } else {
691
                            $tableData[] = Widget::TableData($value, 'field-' . $column->get('type') . ' field-' . $column->get('id'));
692
                        }
693
694
                        unset($field);
695
                    }
696
                }
697
698
                if (is_array($child_sections) && !empty($child_sections)) {
699
                    foreach ($child_sections as $key => $as) {
700
                        $field = (new FieldManager)
701
                            ->select()
702
                            ->field($associated_sections[$key]['child_section_field_id'])
703
                            ->execute()
704
                            ->next();
705
                        $parent_section_field_id = (int)$associated_sections[$key]['parent_section_field_id'];
706
707
                        if (!is_null($parent_section_field_id)) {
708
                            $search_value = $field->fetchAssociatedEntrySearchValue(
709
                                $entry->getData($parent_section_field_id),
710
                                $parent_section_field_id,
711
                                $entry->get('id')
712
                            );
713
                        } else {
714
                            $search_value = $entry->get('id');
715
                        }
716
717
                        if (!is_array($search_value)) {
718
                            $associated_entry_count = $field->fetchAssociatedEntryCount($search_value);
719
720
                            $tableData[] = Widget::TableData(
721
                                Widget::Anchor(
722
                                    sprintf('%d &rarr;', max(0, intval($associated_entry_count))),
723
                                    sprintf(
724
                                        '%s/publish/%s/?filter[%s]=%s',
725
                                        SYMPHONY_URL,
726
                                        $as->get('handle'),
727
                                        $field->get('element_name'),
0 ignored issues
show
Bug introduced by
It seems like $field->get('element_name') can also be of type array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

727
                                        /** @scrutinizer ignore-type */ $field->get('element_name'),
Loading history...
728
                                        rawurlencode($search_value)
729
                                    ),
730
                                    $entry->get('id'),
731
                                    'content'
732
                                )
733
                            );
734
                        }
735
736
                        unset($field);
737
                    }
738
                }
739
740
                /**
741
                 * Allows Extensions to inject custom table data for each Entry
742
                 * into the Publish Index
743
                 *
744
                 * @delegate AddCustomPublishColumnData
745
                 * @since Symphony 2.2
746
                 * @param string $context
747
                 * '/publish/'
748
                 * @param array $tableData
749
                 *  An array of `Widget::TableData`, passed by reference
750
                 * @param integer $section_id
751
                 *  The current Section ID
752
                 * @param Entry $entry_id
753
                 *  The entry object, please note that this is by error and this will
754
                 *  be removed in Symphony 2.4. The entry object is available in
755
                 *  the 'entry' key as of Symphony 2.3.1.
756
                 * @param Entry $entry
757
                 *  The entry object for this row
758
                 */
759
                Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumnData', '/publish/', array(
760
                    'tableData' => &$tableData,
761
                    'section_id' => $section->get('id'),
762
                    'entry_id' => $entry,
763
                    'entry' => $entry
764
                ));
765
766
                $lastCol = $tableData[count($tableData) - 1];
767
                $lastCol->appendChild(Widget::Label(__('Select Entry %d', array($entry->get('id'))), null, 'accessible', null, array(
768
                    'for' => 'entry-' . $entry->get('id')
769
                )));
770
                $lastCol->appendChild(Widget::Input('items['.$entry->get('id').']', $entry->get('modification_date'), 'checkbox', array(
771
                    'id' => 'entry-' . $entry->get('id')
772
                )));
773
774
                // Add a row to the body array, assigning each cell to the row
775
                $aTableBody[] = Widget::TableRow($tableData, null, 'id-' . $entry->get('id'));
776
            }
777
        }
778
779
        $table = Widget::Table(
780
            Widget::TableHead($aTableHead),
781
            null,
782
            Widget::TableBody($aTableBody),
783
            'selectable',
784
            null,
785
            array('role' => 'directory', 'aria-labelledby' => 'symphony-subheading', 'data-interactive' => 'data-interactive')
786
        );
787
788
        $this->Form->appendChild($table);
789
790
        $tableActions = new XMLElement('div');
791
        $tableActions->setAttribute('class', 'actions');
792
793
        $options = array(
794
            array(null, false, __('With Selected...')),
795
            array('delete', false, __('Delete'), 'confirm', null, array(
796
                'data-message' => __('Are you sure you want to delete the selected entries?')
797
            ))
798
        );
799
800
        $toggable_fields = $section->fetchToggleableFields();
801
802
        if (is_array($toggable_fields) && !empty($toggable_fields)) {
803
            $index = 2;
804
805
            foreach ($toggable_fields as $field) {
806
                $toggle_states = $field->getToggleStates();
807
808
                if (is_array($toggle_states)) {
809
                    $options[$index] = array('label' => __('Set %s', array($field->get('label'))), 'options' => array());
810
811
                    foreach ($toggle_states as $value => $state) {
812
                        $options[$index]['options'][] = array('toggle-' . $field->get('id') . '-' . $value, false, $state);
813
                    }
814
                }
815
816
                $index++;
817
            }
818
        }
819
820
        /**
821
         * Allows an extension to modify the existing options for this page's
822
         * With Selected menu. If the `$options` parameter is an empty array,
823
         * the 'With Selected' menu will not be rendered.
824
         *
825
         * @delegate AddCustomActions
826
         * @since Symphony 2.3.2
827
         * @param string $context
828
         * '/publish/'
829
         * @param array $options
830
         *  An array of arrays, where each child array represents an option
831
         *  in the With Selected menu. Options should follow the same format
832
         *  expected by `Widget::__SelectBuildOption`. Passed by reference.
833
         */
834
        Symphony::ExtensionManager()->notifyMembers('AddCustomActions', '/publish/', array(
835
            'options' => &$options
836
        ));
837
838
        if (!empty($options)) {
839
            $tableActions->appendChild(Widget::Apply($options));
840
            $this->Form->appendChild($tableActions);
841
        }
842
843
        if ($pagination->totalPages() > 1) {
844
            $ul = new XMLElement('ul');
845
            $ul->setAttribute('class', 'page');
846
847
            // First
848
            $li = new XMLElement('li');
849
850
            if ($current_page > 1) {
851
                $li->appendChild(Widget::Anchor(__('First'), Administration::instance()->getCurrentPageURL(). '?pg=1'.($filter_querystring ? "&amp;" . $filter_querystring : '')));
852
            } else {
853
                $li->setValue(__('First'));
854
            }
855
856
            $ul->appendChild($li);
857
858
            // Previous
859
            $li = new XMLElement('li');
860
861
            if ($current_page > 1) {
862
                $li->appendChild(Widget::Anchor(__('&larr; Previous'), Administration::instance()->getCurrentPageURL(). '?pg=' . ($current_page - 1).($filter_querystring ? "&amp;" . $filter_querystring : '')));
863
            } else {
864
                $li->setValue(__('&larr; Previous'));
865
            }
866
867
            $ul->appendChild($li);
868
869
            // Summary
870
            $li = new XMLElement('li');
871
            $titleInfos = [
872
                $pagination->currentPage(),
873
                ($current_page != $pagination->totalPages())
874
                    ? $current_page * Symphony::Configuration()->get('pagination_maximum_rows', 'symphony')
875
                    : $pagination->totalEntries(),
876
                $pagination->totalEntries(),
877
            ];
878
            $li->setAttribute('title', __('Viewing %1$s - %2$s of %3$s entries', $titleInfos));
879
880
            $pgform = Widget::Form(Administration::instance()->getCurrentPageURL(), 'get', 'paginationform');
881
882
            $pgmax = max($current_page, $pagination->totalPages());
883
            $pgform->appendChild(Widget::Input('pg', null, 'text', array(
884
                'data-active' => __('Go to page …'),
885
                'data-inactive' => __('Page %1$s of %2$s', array((string)$current_page, $pgmax)),
886
                'data-max' => $pgmax
887
            )));
888
889
            $li->appendChild($pgform);
890
            $ul->appendChild($li);
891
892
            // Next
893
            $li = new XMLElement('li');
894
895
            if ($current_page < $pagination->totalPages()) {
896
                $li->appendChild(Widget::Anchor(__('Next &rarr;'), Administration::instance()->getCurrentPageURL(). '?pg=' . ($current_page + 1).($filter_querystring ? "&amp;" . $filter_querystring : '')));
897
            } else {
898
                $li->setValue(__('Next &rarr;'));
899
            }
900
901
            $ul->appendChild($li);
902
903
            // Last
904
            $li = new XMLElement('li');
905
906
            if ($current_page < $pagination->totalPages()) {
907
                $li->appendChild(
908
                    Widget::Anchor(
909
                        __('Last'),
910
                        Administration::instance()->getCurrentPageURL() .
911
                        '?pg=' .
912
                        $pagination->totalPages() .
913
                        ($filter_querystring ? "&amp;" . $filter_querystring : '')
914
                    )
915
                );
916
            } else {
917
                $li->setValue(__('Last'));
918
            }
919
920
            $ul->appendChild($li);
921
922
            $this->Contents->appendChild($ul);
923
        }
924
    }
925
926
    public function __actionIndex()
927
    {
928
        $checked = (is_array($_POST['items'])) ? array_keys($_POST['items']) : null;
929
930
        if (is_array($checked) && !empty($checked)) {
931
            /**
932
             * Extensions can listen for any custom actions that were added
933
             * through `AddCustomPreferenceFieldsets` or `AddCustomActions`
934
             * delegates.
935
             *
936
             * @delegate CustomActions
937
             * @since Symphony 2.3.2
938
             * @param string $context
939
             *  '/publish/'
940
             * @param array $checked
941
             *  An array of the selected rows. The value is usually the ID of the
942
             *  the associated object.
943
             */
944
            Symphony::ExtensionManager()->notifyMembers('CustomActions', '/publish/', array(
945
                'checked' => $checked
946
            ));
947
948
            switch ($_POST['with-selected']) {
949
                case 'delete':
950
                    /**
951
                     * Prior to deletion of entries. An array of Entry ID's is provided which
952
                     * can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete`
953
                     * in Symphony 2.3.
954
                     *
955
                     * @delegate EntryPreDelete
956
                     * @param string $context
957
                     * '/publish/'
958
                     * @param array $entry_id
959
                     *  An array of Entry ID's passed by reference
960
                     */
961
                    Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/', array('entry_id' => &$checked));
962
963
                    EntryManager::delete($checked);
964
965
                    /**
966
                     * After the deletion of entries, this delegate provides an array of Entry ID's
967
                     * that were deleted.
968
                     *
969
                     * @since Symphony 2.3
970
                     * @delegate EntryPostDelete
971
                     * @param string $context
972
                     * '/publish/'
973
                     * @param array $entry_id
974
                     *  An array of Entry ID's that were deleted.
975
                     */
976
                    Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/', array('entry_id' => $checked));
977
978
                    redirect(server_safe('REQUEST_URI'));
979
                    break;
980
                default:
981
                    list($option, $field_id, $value) = explode('-', $_POST['with-selected'], 3);
982
983
                    if ($option == 'toggle') {
984
                        $field = (new FieldManager)->select()->field($field_id)->execute()->next();
0 ignored issues
show
Bug introduced by
$field_id of type string is incompatible with the type integer expected by parameter $field_id of FieldQuery::field(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

984
                        $field = (new FieldManager)->select()->field(/** @scrutinizer ignore-type */ $field_id)->execute()->next();
Loading history...
985
                        $fields = array($field->get('element_name') => $value);
986
987
                        $section = (new SectionManager)
988
                            ->select()
989
                            ->section($field->get('parent_section'))
0 ignored issues
show
Bug introduced by
It seems like $field->get('parent_section') can also be of type array; however, parameter $section_id of SectionQuery::section() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

989
                            ->section(/** @scrutinizer ignore-type */ $field->get('parent_section'))
Loading history...
990
                            ->execute()
991
                            ->next();
992
993
                        foreach ($checked as $entry_id) {
994
                            $entry = (new EntryManager)->select()->entry($entry_id)->execute()->next();
995
                            $existing_data = $entry[0]->getData($field_id);
996
                            $entry[0]->setData($field_id, $field->toggleFieldData(is_array($existing_data) ? $existing_data : array(), $value, $entry_id));
997
998
                            /**
999
                             * Just prior to editing of an Entry
1000
                             *
1001
                             * @delegate EntryPreEdit
1002
                             * @param string $context
1003
                             * '/publish/edit/'
1004
                             * @param Section $section
1005
                             * @param Entry $entry
1006
                             * @param array $fields
1007
                             */
1008
                            Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/', array(
1009
                                'section' => $section,
1010
                                'entry' => &$entry[0],
1011
                                'fields' => $fields
1012
                            ));
1013
1014
                            $entry[0]->commit();
1015
1016
                            /**
1017
                             * Editing an entry. Entry object is provided.
1018
                             *
1019
                             * @delegate EntryPostEdit
1020
                             * @param string $context
1021
                             * '/publish/edit/'
1022
                             * @param Section $section
1023
                             * @param Entry $entry
1024
                             * @param array $fields
1025
                             */
1026
                            Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/', array(
1027
                                'section' => $section,
1028
                                'entry' => $entry[0],
1029
                                'fields' => $fields
1030
                            ));
1031
                        }
1032
1033
                        unset($field);
1034
                        redirect(server_safe('REQUEST_URI'));
1035
                    }
1036
            }
1037
        }
1038
    }
1039
1040
    public function __viewNew()
1041
    {
1042
        if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
1043
            Administration::instance()->throwCustomError(
1044
                __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
1045
                __('Unknown Section'),
1046
                Page::HTTP_STATUS_NOT_FOUND
1047
            );
1048
        }
1049
1050
        $section = (new SectionManager)->select()->section($section_id)->execute()->next();
1051
1052
        $this->setPageType('form');
1053
        $this->setTitle(__('%1$s &ndash; %2$s', array(General::sanitize($section->get('name')), __('Symphony'))));
0 ignored issues
show
Bug introduced by
It seems like $section->get('name') can also be of type array; however, parameter $source of General::sanitize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1053
        $this->setTitle(__('%1$s &ndash; %2$s', array(General::sanitize(/** @scrutinizer ignore-type */ $section->get('name')), __('Symphony'))));
Loading history...
1054
1055
        // Ensure errored entries still maintain any prepopulated values [#2211]
1056
        $this->Form->setAttribute('action', $this->Form->getAttribute('action') . $this->getPrepopulateString());
1057
        $this->Form->setAttribute('enctype', 'multipart/form-data');
1058
1059
        $sidebar_fields = $section->fetchFields(null, 'sidebar');
1060
        $main_fields = $section->fetchFields(null, 'main');
1061
1062
        if (!empty($sidebar_fields) && !empty($main_fields)) {
1063
            $this->Form->setAttribute('class', 'two columns');
1064
        } else {
1065
            $this->Form->setAttribute('class', 'columns');
1066
        }
1067
1068
        // Only show the Edit Section button if the Author is a developer. #938 ^BA
1069
        if (Symphony::Author()->isDeveloper()) {
1070
            $this->appendSubheading(__('Untitled'),
1071
                Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button')
0 ignored issues
show
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1072
            );
1073
        } else {
1074
            $this->appendSubheading(__('Untitled'));
1075
        }
1076
1077
        // Build filtered breadcrumb [#1378}
1078
        $this->insertBreadcrumbs(array(
1079
            Widget::Anchor(General::sanitize($section->get('name')), SYMPHONY_URL . '/publish/' . $this->_context['section_handle'] . '/' . $this->getFilterString()),
1080
        ));
1081
1082
        $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
0 ignored issues
show
Bug introduced by
It seems like Symphony::Configuration(..._upload_size', 'admin') can also be of type array; however, parameter $value of Widget::Input() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1082
        $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', /** @scrutinizer ignore-type */ Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
Loading history...
1083
1084
        // If there is post data floating around, due to errors, create an entry object
1085
        if (isset($_POST['fields'])) {
1086
            $entry = EntryManager::create();
1087
            $entry->set('section_id', $section_id);
1088
            $entry->setDataFromPost($_POST['fields'], $error, true);
1089
1090
            // Brand new entry, so need to create some various objects
1091
        } else {
1092
            $entry = EntryManager::create();
1093
            $entry->set('section_id', $section_id);
1094
        }
1095
1096
        // Check if there is a field to prepopulate
1097
        if (isset($_REQUEST['prepopulate'])) {
1098
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1099
                $this->Form->prependChild(Widget::Input(
1100
                    "prepopulate[{$field_id}]",
1101
                    rawurlencode($value),
1102
                    'hidden'
1103
                ));
1104
1105
                // The actual pre-populating should only happen if there is not existing fields post data
1106
                // and if the field allows it
1107
                $field = (new FieldManager)->select()->field($field_id);
1108
                if (!isset($_POST['fields']) && ($field = $field->execute()->next()) && $field->canPrePopulate()) {
1109
                    $entry->setData(
1110
                        $field->get('id'),
0 ignored issues
show
Bug introduced by
It seems like $field->get('id') can also be of type array; however, parameter $field_id of Entry::setData() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1110
                        /** @scrutinizer ignore-type */ $field->get('id'),
Loading history...
1111
                        $field->processRawFieldData($value, $error, $message, true)
1112
                    );
1113
                    unset($field);
1114
                }
1115
            }
1116
        }
1117
1118
        $primary = new XMLElement('fieldset');
1119
        $primary->setAttribute('class', 'primary column');
1120
1121
        if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) {
1122
            $message = __('Fields must be added to this section before an entry can be created.');
1123
1124
            if (Symphony::Author()->isDeveloper()) {
1125
                $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . $section->get('id') . '/" accesskey="c">'
0 ignored issues
show
Bug introduced by
Are you sure $section->get('id') of type string|array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1125
                $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . /** @scrutinizer ignore-type */ $section->get('id') . '/" accesskey="c">'
Loading history...
1126
                . __('Add fields')
1127
                . '</a>';
1128
            }
1129
1130
            $this->pageAlert($message, Alert::ERROR);
1131
        } else {
1132
            if (is_array($main_fields) && !empty($main_fields)) {
1133
                foreach ($main_fields as $field) {
1134
                    $primary->appendChild($this->__wrapFieldWithDiv($field, $entry));
1135
                }
1136
1137
                $this->Form->appendChild($primary);
1138
            }
1139
1140
            if (is_array($sidebar_fields) && !empty($sidebar_fields)) {
1141
                $sidebar = new XMLElement('fieldset');
1142
                $sidebar->setAttribute('class', 'secondary column');
1143
1144
                foreach ($sidebar_fields as $field) {
1145
                    $sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry));
1146
                }
1147
1148
                $this->Form->appendChild($sidebar);
1149
            }
1150
1151
            $div = new XMLElement('div');
1152
            $div->setAttribute('class', 'actions');
1153
            $div->appendChild(Widget::Input('action[save]', __('Create Entry'), 'submit', array('accesskey' => 's')));
1154
1155
            $this->Form->appendChild($div);
1156
1157
            // Create a Drawer for Associated Sections
1158
            $this->prepareAssociationsDrawer($section);
1159
        }
1160
    }
1161
1162
    public function __actionNew()
1163
    {
1164
        if (is_array($_POST['action']) && array_key_exists('save', $_POST['action'])) {
1165
            $section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle']);
1166
1167
            $section = (new SectionManager)->select()->section($section_id)->execute()->next();
1168
            if (!$section) {
0 ignored issues
show
introduced by
$section is of type Section, thus it always evaluated to true.
Loading history...
1169
                Administration::instance()->throwCustomError(
1170
                    __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
1171
                    __('Unknown Section'),
1172
                    Page::HTTP_STATUS_NOT_FOUND
1173
                );
1174
            }
1175
1176
            $entry = EntryManager::create();
1177
            $entry->set('author_id', Symphony::Author()->get('id'));
1178
            $entry->set('section_id', $section_id);
1179
            $entry->set('creation_date', DateTimeObj::get('c'));
1180
            $entry->set('modification_date', DateTimeObj::get('c'));
1181
1182
            $fields = $_POST['fields'];
1183
1184
            // Combine FILES and POST arrays, indexed by their custom field handles
1185
            if (isset($_FILES['fields'])) {
1186
                $filedata = General::processFilePostData($_FILES['fields']);
1187
1188
                foreach ($filedata as $handle => $data) {
1189
                    if (!isset($fields[$handle])) {
1190
                        $fields[$handle] = $data;
1191
                    } elseif (isset($data['error']) && $data['error'] == UPLOAD_ERR_NO_FILE) {
1192
                        $fields[$handle] = null;
1193
                    } else {
1194
                        foreach ($data as $ii => $d) {
1195
                            if (isset($d['error']) && $d['error'] == UPLOAD_ERR_NO_FILE) {
1196
                                $fields[$handle][$ii] = null;
1197
                            } elseif (is_array($d) && !empty($d)) {
1198
                                foreach ($d as $key => $val) {
1199
                                    $fields[$handle][$ii][$key] = $val;
1200
                                }
1201
                            }
1202
                        }
1203
                    }
1204
                }
1205
            }
1206
1207
            // Initial checks to see if the Entry is ok
1208
            if (Entry::__ENTRY_FIELD_ERROR__ == $entry->checkPostData($fields, $this->_errors)) {
1209
                $this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR);
1210
1211
                // Secondary checks, this will actually process the data and attempt to save
1212
            } elseif (Entry::__ENTRY_OK__ != $entry->setDataFromPost($fields, $errors)) {
1213
                foreach ($errors as $field_id => $message) {
1214
                    $this->pageAlert($message, Alert::ERROR);
1215
                }
1216
1217
                // Everything is awesome. Dance.
1218
            } else {
1219
                /**
1220
                 * Just prior to creation of an Entry
1221
                 *
1222
                 * @delegate EntryPreCreate
1223
                 * @param string $context
1224
                 * '/publish/new/'
1225
                 * @param Section $section
1226
                 * @param Entry $entry
1227
                 * @param array $fields
1228
                 */
1229
                Symphony::ExtensionManager()->notifyMembers('EntryPreCreate', '/publish/new/', array('section' => $section, 'entry' => &$entry, 'fields' => &$fields));
1230
1231
                $entry->set('modification_author_id', Symphony::Author()->get('id'));
1232
1233
                // Check to see if the dancing was premature
1234
                if (!$entry->commit()) {
1235
                    $this->pageAlert(null, Alert::ERROR);
1236
                } else {
1237
                    /**
1238
                     * Creation of an Entry. New Entry object is provided.
1239
                     *
1240
                     * @delegate EntryPostCreate
1241
                     * @param string $context
1242
                     * '/publish/new/'
1243
                     * @param Section $section
1244
                     * @param Entry $entry
1245
                     * @param array $fields
1246
                     */
1247
                    Symphony::ExtensionManager()->notifyMembers('EntryPostCreate', '/publish/new/', array('section' => $section, 'entry' => $entry, 'fields' => $fields));
1248
1249
                    $prepopulate_querystring = $this->getPrepopulateString();
1250
                    redirect(sprintf(
1251
                        '%s/publish/%s/edit/%d/created/%s',
1252
                        SYMPHONY_URL,
0 ignored issues
show
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1253
                        $this->_context['section_handle'],
1254
                        $entry->get('id'),
0 ignored issues
show
Bug introduced by
It seems like $entry->get('id') can also be of type array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1254
                        /** @scrutinizer ignore-type */ $entry->get('id'),
Loading history...
1255
                        (!empty($prepopulate_querystring) ? $prepopulate_querystring : null)
1256
                    ));
1257
                }
1258
            }
1259
        }
1260
    }
1261
1262
    public function __viewEdit()
1263
    {
1264
        if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
1265
            Administration::instance()->throwCustomError(
1266
                __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
1267
                __('Unknown Section'),
1268
                Page::HTTP_STATUS_NOT_FOUND
1269
            );
1270
        }
1271
1272
        $section = (new SectionManager)->select()->section($section_id)->execute()->next();
1273
        $entry_id = $this->_context['entry_id'];
1274
        $base = '/publish/' . $this->_context['section_handle'] . '/';
1275
        $new_link = $base . 'new/';
1276
        $filter_link = $base;
1277
        $canonical_link = $base . 'edit/' . $entry_id . '/';
1278
1279
        if ($entry_id < 1) {
1280
            Administration::instance()->throwCustomError(
1281
                __('Unknown Entry'),
1282
                __('The requested Entry id is not valid.'),
1283
                Page::HTTP_STATUS_NOT_FOUND
1284
            );
1285
        }
1286
1287
        $existingEntry = (new EntryManager)
1288
            ->select()
1289
            ->entry($entry_id)
1290
            ->section($section_id)
1291
            ->includeAllFields()
1292
            ->execute()
1293
            ->next();
1294
1295
        if (!$existingEntry) {
0 ignored issues
show
introduced by
$existingEntry is of type Entry, thus it always evaluated to true.
Loading history...
1296
            Administration::instance()->throwCustomError(
1297
                __('Unknown Entry'),
1298
                __('The Entry, %s, could not be found.', array($entry_id)),
1299
                Page::HTTP_STATUS_NOT_FOUND
1300
            );
1301
        }
1302
1303
        // If there is post data floating around, due to errors, create an entry object
1304
        if (isset($_POST['fields'])) {
1305
            $fields = $_POST['fields'];
1306
1307
            $entry = EntryManager::create();
1308
            $entry->set('id', $entry_id);
1309
            $entry->set('author_id', $existingEntry->get('author_id'));
1310
            $entry->set('modification_author_id', $existingEntry->get('modification_author_id'));
1311
            $entry->set('section_id', $existingEntry->get('section_id'));
1312
            $entry->set('creation_date', $existingEntry->get('creation_date'));
1313
            $entry->set('modification_date', $existingEntry->get('modification_date'));
1314
            $entry->setDataFromPost($fields, $errors, true);
1315
1316
            $timestamp = isset($_POST['action']['timestamp'])
1317
                ? $_POST['action']['timestamp']
1318
                : $entry->get('modification_date');
1319
1320
            // Editing an entry, so need to create some various objects
1321
        } else {
1322
            $entry = $existingEntry;
1323
            $fields = array();
1324
1325
            if (!$section) {
0 ignored issues
show
introduced by
$section is of type Section, thus it always evaluated to true.
Loading history...
1326
                $section = (new SectionManager)
1327
                    ->select()
1328
                    ->section($entry->get('section_id'))
1329
                    ->execute()
1330
                    ->next();
1331
            }
1332
1333
            $timestamp = $entry->get('modification_date');
1334
        }
1335
1336
        /**
1337
         * Just prior to rendering of an Entry edit form.
1338
         *
1339
         * @delegate EntryPreRender
1340
         * @param string $context
1341
         * '/publish/edit/'
1342
         * @param Section $section
1343
         * @param Entry $entry
1344
         * @param array $fields
1345
         */
1346
        Symphony::ExtensionManager()->notifyMembers('EntryPreRender', '/publish/edit/', array(
1347
            'section' => $section,
1348
            'entry' => &$entry,
1349
            'fields' => $fields
1350
        ));
1351
1352
        // Iterate over the `prepopulate` parameters to build a URL
1353
        // to remember this state for Create New, View all Entries and
1354
        // Breadcrumb links. If `prepopulate` doesn't exist, this will
1355
        // just use the standard pages (ie. no filtering)
1356
        if (isset($_REQUEST['prepopulate'])) {
1357
            $new_link .= $this->getPrepopulateString();
1358
            $filter_link .= $this->getFilterString();
1359
            $canonical_link .= $this->getPrepopulateString();
1360
        }
1361
1362
        if (isset($this->_context['flag'])) {
1363
            // These flags are only relevant if there are no errors
1364
            if (empty($this->_errors)) {
1365
                $time = Widget::Time();
1366
1367
                switch ($this->_context['flag']) {
1368
                    case 'saved':
1369
                        $message = __('Entry updated at %s.', array($time->generate()));
1370
                        break;
1371
                    case 'created':
1372
                        $message = __('Entry created at %s.', array($time->generate()));
1373
                }
1374
1375
                $this->pageAlert(
1376
                    $message
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $message does not seem to be defined for all execution paths leading up to this point.
Loading history...
1377
                    . ' <a href="' . SYMPHONY_URL . $new_link . '" accesskey="c">'
0 ignored issues
show
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1378
                    . __('Create another?')
1379
                    . '</a> <a href="' . SYMPHONY_URL . $filter_link . '" accesskey="a">'
1380
                    . __('View all Entries')
1381
                    . '</a>',
1382
                    Alert::SUCCESS
1383
                );
1384
            }
1385
        }
1386
1387
        // Determine the page title
1388
        $field_id = Symphony::Database()
1389
            ->select(['id'])
1390
            ->from('tbl_fields')
1391
            ->where(['parent_section' => $section->get('id')])
1392
            ->orderBy('sortorder')
1393
            ->limit(1)
1394
            ->execute()
1395
            ->variable('id');
1396
1397
        if (!is_null($field_id)) {
1398
            $field = (new FieldManager)->select()->field($field_id)->execute()->next();
1399
        }
1400
1401
        if ($field) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $field does not seem to be defined for all execution paths leading up to this point.
Loading history...
1402
            $title = $field->prepareReadableValue($existingEntry->getData($field->get('id')), $entry_id, true);
1403
        } else {
1404
            $title = '';
1405
        }
1406
1407
        if (!trim($title)) {
1408
            $title = __('Untitled');
1409
        }
1410
1411
        // Check if there is a field to prepopulate
1412
        if (isset($_REQUEST['prepopulate'])) {
1413
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1414
                $this->Form->prependChild(Widget::Input(
1415
                    "prepopulate[{$field_id}]",
1416
                    rawurlencode($value),
1417
                    'hidden'
1418
                ));
1419
            }
1420
        }
1421
1422
        $this->setPageType('form');
1423
        $this->Form->setAttribute('enctype', 'multipart/form-data');
1424
        $this->setTitle(__('%1$s &ndash; %2$s &ndash; %3$s', array($title, General::sanitize($section->get('name')), __('Symphony'))));
0 ignored issues
show
Bug introduced by
It seems like $section->get('name') can also be of type array; however, parameter $source of General::sanitize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1424
        $this->setTitle(__('%1$s &ndash; %2$s &ndash; %3$s', array($title, General::sanitize(/** @scrutinizer ignore-type */ $section->get('name')), __('Symphony'))));
Loading history...
1425
        $this->addElementToHead(new XMLElement('link', null, array(
1426
            'rel' => 'canonical',
1427
            'href' => SYMPHONY_URL . $canonical_link,
1428
        )));
1429
1430
        $sidebar_fields = $section->fetchFields(null, 'sidebar');
1431
        $main_fields = $section->fetchFields(null, 'main');
1432
1433
        if (!empty($sidebar_fields) && !empty($main_fields)) {
1434
            $this->Form->setAttribute('class', 'two columns');
1435
        } else {
1436
            $this->Form->setAttribute('class', 'columns');
1437
        }
1438
1439
        // Only show the Edit Section button if the Author is a developer. #938 ^BA
1440
        if (Symphony::Author()->isDeveloper()) {
1441
            $this->appendSubheading($title, Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button'));
1442
        } else {
1443
            $this->appendSubheading($title);
1444
        }
1445
1446
        $this->insertBreadcrumbs(array(
1447
            Widget::Anchor(General::sanitize($section->get('name')), SYMPHONY_URL . (isset($filter_link) ? $filter_link : $base)),
1448
        ));
1449
1450
        $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
0 ignored issues
show
Bug introduced by
It seems like Symphony::Configuration(..._upload_size', 'admin') can also be of type array; however, parameter $value of Widget::Input() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1450
        $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', /** @scrutinizer ignore-type */ Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
Loading history...
1451
1452
        $primary = new XMLElement('fieldset');
1453
        $primary->setAttribute('class', 'primary column');
1454
1455
        if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) {
1456
            $message = __('Fields must be added to this section before an entry can be created.');
1457
1458
            if (Symphony::Author()->isDeveloper()) {
1459
                $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . $section->get('id') . '/" accesskey="c">'
0 ignored issues
show
Bug introduced by
Are you sure $section->get('id') of type string|array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1459
                $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . /** @scrutinizer ignore-type */ $section->get('id') . '/" accesskey="c">'
Loading history...
1460
                . __('Add fields')
1461
                . '</a>';
1462
            }
1463
1464
            $this->pageAlert($message, Alert::ERROR);
1465
        } else {
1466
            if (is_array($main_fields) && !empty($main_fields)) {
1467
                foreach ($main_fields as $field) {
1468
                    $primary->appendChild($this->__wrapFieldWithDiv($field, $entry));
1469
                }
1470
1471
                $this->Form->appendChild($primary);
1472
            }
1473
1474
            if (is_array($sidebar_fields) && !empty($sidebar_fields)) {
1475
                $sidebar = new XMLElement('fieldset');
1476
                $sidebar->setAttribute('class', 'secondary column');
1477
1478
                foreach ($sidebar_fields as $field) {
1479
                    $sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry));
1480
                }
1481
1482
                $this->Form->appendChild($sidebar);
1483
            }
1484
1485
            $div = new XMLElement('div');
1486
            $div->setAttribute('class', 'actions');
1487
            $div->appendChild(Widget::Input('action[save]', __('Save Changes'), 'submit', array('accesskey' => 's')));
1488
1489
            $button = new XMLElement('button', __('Delete'));
1490
            $button->setAttributeArray(array('name' => 'action[delete]', 'class' => 'button confirm delete', 'title' => __('Delete this entry'), 'type' => 'submit', 'accesskey' => 'd', 'data-message' => __('Are you sure you want to delete this entry?')));
1491
            $div->appendChild($button);
1492
1493
            $div->appendChild(Widget::Input('action[timestamp]', $timestamp, 'hidden'));
1494
            $div->appendChild(Widget::Input('action[ignore-timestamp]', 'yes', 'checkbox', array('class' => 'irrelevant')));
1495
1496
            $this->Form->appendChild($div);
1497
1498
            // Create a Drawer for Associated Sections
1499
            $this->prepareAssociationsDrawer($section);
1500
        }
1501
    }
1502
1503
    public function __actionEdit()
1504
    {
1505
        $entry_id = $this->_context['entry_id'];
1506
1507
        if (is_array($_POST['action']) && array_key_exists('save', $_POST['action'])) {
1508
            $entry = (new EntryManager)->select()->entry($entry_id)->execute()->next();
1509
            if (!$entry) {
0 ignored issues
show
introduced by
$entry is of type Entry, thus it always evaluated to true.
Loading history...
1510
                Administration::instance()->throwCustomError(
1511
                    __('The Entry, %s, could not be found.', array($entry_id)),
1512
                    __('Unknown Entry'),
1513
                    Page::HTTP_STATUS_NOT_FOUND
1514
                );
1515
            }
1516
1517
            $section = (new SectionManager)
1518
                ->select()
1519
                ->section($entry->get('section_id'))
0 ignored issues
show
Bug introduced by
It seems like $entry->get('section_id') can also be of type array; however, parameter $section_id of SectionQuery::section() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1519
                ->section(/** @scrutinizer ignore-type */ $entry->get('section_id'))
Loading history...
1520
                ->execute()
1521
                ->next();
1522
1523
            $post = General::getPostData();
1524
            $fields = $post['fields'];
1525
1526
            $canProceed = $this->validateTimestamp($entry_id, true);
1527
1528
            // Timestamp validation
1529
            if (!$canProceed) {
1530
                $this->addTimestampValidationPageAlert($this->_errors['timestamp'], $entry, 'save');
1531
1532
                // Initial checks to see if the Entry is ok
1533
            } elseif (Entry::__ENTRY_FIELD_ERROR__ == $entry->checkPostData($fields, $this->_errors)) {
1534
                $this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR);
1535
1536
                // Secondary checks, this will actually process the data and attempt to save
1537
            } elseif (Entry::__ENTRY_OK__ != $entry->setDataFromPost($fields, $errors)) {
1538
                foreach ($errors as $field_id => $message) {
1539
                    $this->pageAlert($message, Alert::ERROR);
1540
                }
1541
1542
                // Everything is awesome. Dance.
1543
            } else {
1544
                /**
1545
                 * Just prior to editing of an Entry.
1546
                 *
1547
                 * @delegate EntryPreEdit
1548
                 * @param string $context
1549
                 * '/publish/edit/'
1550
                 * @param Section $section
1551
                 * @param Entry $entry
1552
                 * @param array $fields
1553
                 */
1554
                Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/', array('section' => $section, 'entry' => &$entry, 'fields' => $fields));
1555
1556
                $entry->set('modification_author_id', Symphony::Author()->get('id'));
1557
1558
                // Check to see if the dancing was premature
1559
                if (!$entry->commit()) {
1560
                    $this->pageAlert(null, Alert::ERROR);
1561
                } else {
1562
                    /**
1563
                     * Just after the editing of an Entry
1564
                     *
1565
                     * @delegate EntryPostEdit
1566
                     * @param string $context
1567
                     * '/publish/edit/'
1568
                     * @param Section $section
1569
                     * @param Entry $entry
1570
                     * @param array $fields
1571
                     */
1572
                    Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/', array('section' => $section, 'entry' => $entry, 'fields' => $fields));
1573
1574
                    redirect(sprintf(
1575
                        '%s/publish/%s/edit/%d/saved/%s',
1576
                        SYMPHONY_URL,
0 ignored issues
show
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1577
                        $this->_context['section_handle'],
1578
                        $entry->get('id'),
0 ignored issues
show
Bug introduced by
It seems like $entry->get('id') can also be of type array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1578
                        /** @scrutinizer ignore-type */ $entry->get('id'),
Loading history...
1579
                        $this->getPrepopulateString()
1580
                    ));
1581
                }
1582
            }
1583
        } elseif (is_array($_POST['action']) && array_key_exists('delete', $_POST['action']) && is_numeric($entry_id)) {
1584
            /**
1585
             * Prior to deletion of entries. An array of Entry ID's is provided which
1586
             * can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete`
1587
             * in Symphony 2.3.
1588
             *
1589
             * @delegate EntryPreDelete
1590
             * @param string $context
1591
             * '/publish/'
1592
             * @param array $entry_id
1593
             *    An array of Entry ID's passed by reference
1594
             */
1595
            $checked = array($entry_id);
1596
            Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/', array('entry_id' => &$checked));
1597
1598
            $canProceed = $this->validateTimestamp($entry_id);
1599
1600
            if ($canProceed) {
1601
                EntryManager::delete($checked);
1602
1603
                /**
1604
                 * After the deletion of entries, this delegate provides an array of Entry ID's
1605
                 * that were deleted.
1606
                 *
1607
                 * @since Symphony 2.3
1608
                 * @delegate EntryPostDelete
1609
                 * @param string $context
1610
                 * '/publish/'
1611
                 * @param array $entry_id
1612
                 *  An array of Entry ID's that were deleted.
1613
                 */
1614
                Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/', array('entry_id' => $checked));
1615
1616
                redirect(SYMPHONY_URL . '/publish/'.$this->_context['section_handle'].'/');
1617
            } else {
1618
                $ret = (new EntryManager)->select()->entry($entry_id)->execute()->next();
1619
                if (!empty($ret)) {
1620
                    $entry = $ret[0];
1621
                    $this->addTimestampValidationPageAlert($this->_errors['timestamp'], $entry, 'delete');
1622
                }
1623
            }
1624
        }
1625
    }
1626
1627
    /**
1628
     * Given a Field and Entry object, this function will wrap
1629
     * the Field's displayPublishPanel result with a div that
1630
     * contains some contextual information such as the Field ID,
1631
     * the Field handle and whether it is required or not.
1632
     *
1633
     * @param Field $field
1634
     * @param Entry $entry
1635
     * @return XMLElement
1636
     */
1637
    private function __wrapFieldWithDiv(Field $field, Entry $entry)
1638
    {
1639
        $is_hidden = $this->isFieldHidden($field);
1640
        $div = new XMLElement('div', null, array('id' => 'field-' . $field->get('id'), 'class' => 'field field-'.$field->handle().($field->get('required') == 'yes' ? ' required' : '').($is_hidden === true ? ' irrelevant' : '')));
0 ignored issues
show
Bug introduced by
Are you sure $field->get('id') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1640
        $div = new XMLElement('div', null, array('id' => 'field-' . /** @scrutinizer ignore-type */ $field->get('id'), 'class' => 'field field-'.$field->handle().($field->get('required') == 'yes' ? ' required' : '').($is_hidden === true ? ' irrelevant' : '')));
Loading history...
1641
1642
        $field->setAssociationContext($div);
1643
1644
        $field->displayPublishPanel(
1645
            $div, $entry->getData($field->get('id')),
0 ignored issues
show
Bug introduced by
It seems like $entry->getData($field->get('id')) can also be of type object; however, parameter $data of Field::displayPublishPanel() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1645
            $div, /** @scrutinizer ignore-type */ $entry->getData($field->get('id')),
Loading history...
Bug introduced by
It seems like $field->get('id') can also be of type array; however, parameter $field_id of Entry::getData() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1645
            $div, $entry->getData(/** @scrutinizer ignore-type */ $field->get('id')),
Loading history...
1646
            (isset($this->_errors[$field->get('id')]) ? $this->_errors[$field->get('id')] : null),
1647
            null, null, (is_numeric($entry->get('id')) ? $entry->get('id') : null)
0 ignored issues
show
Bug introduced by
It seems like is_numeric($entry->get('...entry->get('id') : null can also be of type array; however, parameter $entry_id of Field::displayPublishPanel() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1647
            null, null, /** @scrutinizer ignore-type */ (is_numeric($entry->get('id')) ? $entry->get('id') : null)
Loading history...
1648
        );
1649
1650
        /**
1651
         * Allows developers modify the field before it is rendered in the publish
1652
         * form. Passes the `Field` object, `Entry` object, the `XMLElement` div and
1653
         * any errors for the entire `Entry`. Only the `$div` element
1654
         * will be altered before appending to the page, the rest are read only.
1655
         *
1656
         * @since Symphony 2.5.0
1657
         * @delegate ModifyFieldPublishWidget
1658
         * @param string $context
1659
         * '/backend/'
1660
         * @param Field $field
1661
         * @param Entry $entry
1662
         * @param array $errors
1663
         * @param Widget $widget
1664
         */
1665
        Symphony::ExtensionManager()->notifyMembers('ModifyFieldPublishWidget', '/backend/', array(
1666
            'field' => $field,
1667
            'entry' => $entry,
1668
            'errors' => $this->_errors,
1669
            'widget' => &$div
1670
        ));
1671
1672
        return $div;
1673
    }
1674
1675
    /**
1676
     * Check whether the given `$field` will be hidden because it's been
1677
     * prepopulated.
1678
     *
1679
     * @param  Field  $field
1680
     * @return boolean
1681
     */
1682
    public function isFieldHidden(Field $field)
1683
    {
1684
        if ($field->get('hide_when_prepopulated') == 'yes') {
1685
            if (isset($_REQUEST['prepopulate'])) {
1686
                foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1687
                    if ($field_id == $field->get('id')) {
1688
                        return true;
1689
                    }
1690
                }
1691
            }
1692
        }
1693
1694
        return false;
1695
    }
1696
1697
    /**
1698
     * Prepare a Drawer to visualize section associations
1699
     *
1700
     * @param  Section $section The current Section object
1701
     * @throws InvalidArgumentException
1702
     * @throws Exception
1703
     */
1704
    private function prepareAssociationsDrawer($section)
1705
    {
1706
        $entry_id = (!is_null($this->_context['entry_id'])) ? $this->_context['entry_id'] : null;
1707
        $show_entries = Symphony::Configuration()->get('association_maximum_rows', 'symphony');
1708
1709
        if (is_null($entry_id) && !isset($_GET['prepopulate']) || is_null($show_entries) || $show_entries == 0) {
1710
            return;
1711
        }
1712
1713
        $parent_associations = SectionManager::fetchParentAssociations($section->get('id'), true);
0 ignored issues
show
Bug introduced by
$section->get('id') of type string|array is incompatible with the type integer expected by parameter $section_id of SectionManager::fetchParentAssociations(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1713
        $parent_associations = SectionManager::fetchParentAssociations(/** @scrutinizer ignore-type */ $section->get('id'), true);
Loading history...
1714
        $child_associations = SectionManager::fetchChildAssociations($section->get('id'), true);
0 ignored issues
show
Bug introduced by
$section->get('id') of type string|array is incompatible with the type integer expected by parameter $section_id of SectionManager::fetchChildAssociations(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1714
        $child_associations = SectionManager::fetchChildAssociations(/** @scrutinizer ignore-type */ $section->get('id'), true);
Loading history...
1715
        $content = null;
1716
        $drawer_position = 'vertical-right';
1717
1718
        /**
1719
         * Prepare Associations Drawer from an Extension
1720
         *
1721
         * @since Symphony 2.3.3
1722
         * @delegate PrepareAssociationsDrawer
1723
         * @param string $context
1724
         * '/publish/'
1725
         * @param integer $entry_id
1726
         *  The entry ID or null
1727
         * @param array $parent_associations
1728
         *  Array of Sections
1729
         * @param array $child_associations
1730
         *  Array of Sections
1731
         * @param string $drawer_position
1732
         *  The position of the Drawer, defaults to `vertical-right`. Available
1733
         *  values of `vertical-left, `vertical-right` and `horizontal`
1734
         */
1735
        Symphony::ExtensionManager()->notifyMembers('PrepareAssociationsDrawer', '/publish/', array(
1736
            'entry_id' => $entry_id,
1737
            'parent_associations' => &$parent_associations,
1738
            'child_associations' => &$child_associations,
1739
            'content' => &$content,
1740
            'drawer-position' => &$drawer_position
1741
        ));
1742
1743
        // If there are no associations, return now.
1744
        if (
1745
            (is_null($parent_associations) || empty($parent_associations))
1746
            &&
1747
            (is_null($child_associations) || empty($child_associations))
1748
        ) {
1749
            return;
1750
        }
1751
1752
        if (!($content instanceof XMLElement)) {
1753
            $content = new XMLElement('div', null, array('class' => 'content'));
1754
            $content->setSelfClosingTag(false);
1755
1756
            // Process Parent Associations
1757
            if (!is_null($parent_associations) && !empty($parent_associations)) {
1758
                $title = new XMLElement('h2', __('Linked to') . ':', array('class' => 'association-title'));
1759
                $content->appendChild($title);
1760
1761
                foreach ($parent_associations as $as) {
1762
                    if (empty($as['parent_section_field_id'])) {
1763
                        continue;
1764
                    }
1765
                    $field = (new FieldManager)->select()->field($as['parent_section_field_id'])->execute()->next();
1766
                    if ($field) {
1767
                        // Get the related section
1768
                        $parent_section = (new SectionManager)
1769
                            ->select()
1770
                            ->section($as['parent_section_id'])
1771
                            ->execute()
1772
                            ->next();
1773
1774
                        if (!($parent_section instanceof Section)) {
1775
                            continue;
1776
                        }
1777
1778
                        if (isset($_GET['prepopulate'])) {
1779
                            $prepopulate_field = key($_GET['prepopulate']);
1780
                        }
1781
1782
                        // get associated entries if entry exists,
1783
                        if ($entry_id) {
1784
                            $relation_field = (new FieldManager)
1785
                                ->select()
1786
                                ->field($as['child_section_field_id'])
1787
                                ->execute()
1788
                                ->next();
1789
                            $entry_ids = $relation_field->findParentRelatedEntries($as['parent_section_field_id'], $entry_id);
1790
1791
                            // get prepopulated entry otherwise
1792
                        } elseif (isset($_GET['prepopulate']) && is_array($_GET['prepopulate']) && isset($_GET['prepopulate'][$as['child_section_field_id']])) {
1793
                            $entry_ids = array(intval($_GET['prepopulate'][$as['child_section_field_id']]));
1794
                        } else {
1795
                            $entry_ids = array();
1796
                        }
1797
1798
                        // Use $schema for perf reasons
1799
                        $schema = [$field->get('element_name')];
1800
                        $entries = [];
1801
                        $pagination = null;
1802
                        if ((!empty($entry_ids) || isset($_GET['prepopulate'])) &&
1803
                            $field->get('id') === $prepopulate_field) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $prepopulate_field does not seem to be defined for all execution paths leading up to this point.
Loading history...
1804
                            $pagination = (new EntryManager)
1805
                                ->select($schema)
1806
                                ->distinct()
1807
                                ->section($parent_section->get('id'))
0 ignored issues
show
Bug introduced by
$parent_section->get('id') of type string|array is incompatible with the type integer expected by parameter $section_id of EntryQuery::section(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1807
                                ->section(/** @scrutinizer ignore-type */ $parent_section->get('id'))
Loading history...
1808
                                ->sort($parent_section->getSortingField(), $parent_section->getSortingOrder())
1809
                                ->paginate(
1810
                                    1,
1811
                                    $show_entries ?: Symphony::Configuration()->get('pagination_maximum_rows', 'symphony')
1812
                                );
1813
                            if (!empty($entry_ids)) {
1814
                                $pagination->entries($entry_ids);
1815
                            }
1816
                            $pagination = $pagination->execute()->pagination();
1817
                            $entries = $pagination->rows();
1818
                        }
1819
                        $has_entries = !empty($entries) && $pagination && $pagination->totalEntries() != 0;
1820
1821
                        // Create link
1822
                        $link = SYMPHONY_URL . '/publish/' . $as['handle'] . '/';
0 ignored issues
show
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1823
                        $aname = General::sanitize($as['name']);
1824
                        if ($has_entries) {
1825
                            $aname .= ' <span>(' . $pagination->totalEntries() . ')</span>';
1826
                        }
1827
                        $a = new XMLElement('a', $aname, array(
1828
                            'class' => 'association-section',
1829
                            'href' => $link,
1830
                            'title' => strip_tags($aname),
1831
                        ));
1832
1833
                        if (!$has_entries) {
1834
                            unset($field);
1835
                            continue;
1836
                        }
1837
1838
                        $element = new XMLElement('section', null, array('class' => 'association parent'));
1839
                        $header = new XMLElement('header');
1840
                        $header->appendChild(new XMLElement('p', $a->generate()));
1841
                        $element->appendChild($header);
1842
1843
                        $ul = new XMLElement('ul', null, array(
1844
                            'class' => 'association-links',
1845
                            'data-section-id' => $as['child_section_id'],
1846
                            'data-association-ids' => implode(', ', $entry_ids)
1847
                        ));
1848
1849
                        foreach ($entries as $e) {
1850
                            // let the field create the mark up
1851
                            $li = $field->prepareAssociationsDrawerXMLElement($e, $as);
1852
                            // add it to the unordered list
1853
                            $ul->appendChild($li);
1854
                        }
1855
1856
                        $element->appendChild($ul);
1857
                        $content->appendChild($element);
1858
                        unset($field);
1859
                    }
1860
                }
1861
            }
1862
1863
            // Process Child Associations
1864
            if (!is_null($child_associations) && !empty($child_associations)) {
0 ignored issues
show
introduced by
The condition is_null($child_associations) is always false.
Loading history...
1865
                $title = new XMLElement('h2', __('Links in') . ':', array('class' => 'association-title'));
1866
                $content->appendChild($title);
1867
1868
                foreach ($child_associations as $as) {
1869
                    // Get the related section
1870
                    $child_section = (new SectionManager)
1871
                        ->select()
1872
                        ->section($as['child_section_id'])
1873
                        ->execute()
1874
                        ->next();
1875
1876
                    if (!($child_section instanceof Section)) {
1877
                        continue;
1878
                    }
1879
1880
                    // Get the visible field instance (using the sorting field, this is more flexible than visibleColumns())
1881
                    // Get the link field instance
1882
                    $visible_field = current($child_section->fetchVisibleColumns());
1883
                    $relation_field = (new FieldManager)
1884
                        ->select()
1885
                        ->field($as['child_section_field_id'])
1886
                        ->execute()
1887
                        ->next();
1888
1889
                    $entry_ids = $relation_field->findRelatedEntries($entry_id, $as['parent_section_field_id']);
1890
1891
                    $schema = $visible_field
1892
                        ? [$visible_field->get('element_name')]
1893
                        : [$relation_field->get('element_name')];
1894
                    $entries = [];
1895
                    $pagination = null;
1896
                    if (!empty($entry_ids)) {
1897
                        $pagination = (new EntryManager)
1898
                            ->select($schema)
1899
                            ->entries($entry_ids)
1900
                            ->section($child_section->get('id'))
1901
                            ->sort($child_section->getSortingField(), $child_section->getSortingOrder())
1902
                            ->paginate(
1903
                                1,
1904
                                $show_entries ?: Symphony::Configuration()->get('pagination_maximum_rows', 'symphony')
1905
                            )
1906
                            ->execute()
1907
                            ->pagination();
1908
                        $entries = $pagination->rows();
1909
                    }
1910
                    $has_entries = !empty($entries) && $pagination && $pagination->totalEntries() != 0;
1911
1912
                    // Build the HTML of the relationship
1913
                    $element = new XMLElement('section', null, array('class' => 'association child'));
1914
                    $header = new XMLElement('header');
1915
1916
                    // Get the search value for filters and prepopulate
1917
                    $filter = '';
1918
                    $prepopulate = '';
1919
                    $entry = (new EntryManager)
1920
                        ->select($schema)
1921
                        ->section($section_id)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $section_id does not exist. Did you maybe mean $section?
Loading history...
1922
                        ->entry($entry_id)
1923
                        ->execute()
1924
                        ->next();
1925
                    if ($entry) {
1926
                        $search_value = $relation_field->fetchAssociatedEntrySearchValue(
1927
                            $entry->getData($as['parent_section_field_id']),
0 ignored issues
show
Bug introduced by
It seems like $entry->getData($as['parent_section_field_id']) can also be of type object; however, parameter $data of Field::fetchAssociatedEntrySearchValue() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1927
                            /** @scrutinizer ignore-type */ $entry->getData($as['parent_section_field_id']),
Loading history...
1928
                            $as['parent_section_field_id'],
1929
                            $entry_id
1930
                        );
1931
                        unset($entry);
1932
                        if (is_array($search_value) || !$search_value) {
1933
                            $search_value = $entry_id;
1934
                        }
1935
                        $filter = '?filter[' . $relation_field->get('element_name') . ']=' . $search_value;
0 ignored issues
show
Bug introduced by
Are you sure $relation_field->get('element_name') of type null|array|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1935
                        $filter = '?filter[' . /** @scrutinizer ignore-type */ $relation_field->get('element_name') . ']=' . $search_value;
Loading history...
1936
                        $prepopulate = '?prepopulate[' . $as['child_section_field_id'] . ']=' . $search_value;
1937
                    }
1938
1939
                    // Create link with filter or prepopulate
1940
                    $link = SYMPHONY_URL . '/publish/' . $as['handle'] . '/' . $filter;
1941
                    $aname = General::sanitize($as['name']);
1942
                    if ($has_entries) {
1943
                        $aname .= ' <span>(' . $pagination->totalEntries() . ')</span>';
1944
                    }
1945
                    $a = new XMLElement('a', $aname, array(
1946
                        'class' => 'association-section',
1947
                        'href' => $link,
1948
                        'title' => strip_tags($aname),
1949
                    ));
1950
1951
                    // Create new entries
1952
                    $create = new XMLElement('a', __('New'), array(
1953
                        'class' => 'button association-new',
1954
                        'href' => SYMPHONY_URL . '/publish/' . $as['handle'] . '/new/' . $prepopulate
1955
                    ));
1956
1957
                    // Display existing entries
1958
                    if ($has_entries) {
1959
                        $header->appendChild(new XMLElement('p', $a->generate()));
1960
1961
                        $ul = new XMLElement('ul', null, array(
1962
                            'class' => 'association-links',
1963
                            'data-section-id' => $as['child_section_id'],
1964
                            'data-association-ids' => implode(', ', $entry_ids)
1965
                        ));
1966
1967
                        foreach ($entries as $e) {
1968
                            // let the first visible field create the mark up
1969
                            if ($visible_field) {
1970
                                $li = $visible_field->prepareAssociationsDrawerXMLElement($e, $as, $prepopulate);
1971
                            }
1972
                            // or use the system:id if no visible field exists.
1973
                            else {
1974
                                $li = Field::createAssociationsDrawerXMLElement($e->get('id'), $e, $as, $prepopulate);
1975
                            }
1976
1977
                            // add it to the unordered list
1978
                            $ul->appendChild($li);
1979
                        }
1980
1981
                        $element->appendChild($ul);
1982
1983
                        // If we are only showing 'some' of the entries, then show this on the UI
1984
                        if ($pagination->totalEntries() > $show_entries) {
1985
                            $xmlPagination = new XMLElement('li', null, array(
1986
                                'class' => 'association-more',
1987
                                'data-current-page' => '1',
1988
                                'data-total-pages' => ceil($pagination->totalEntries() / $show_entries),
1989
                                'data-total-entries' => $pagination->totalEntries()
1990
                            ));
1991
                            $counts = new XMLElement('a', __('Show more entries'), array(
1992
                                'href' => $link
1993
                            ));
1994
1995
                            $xmlPagination->appendChild($counts);
1996
                            $ul->appendChild($xmlPagination);
1997
                        }
1998
1999
                        // No entries
2000
                    } else {
2001
                        $element->setAttribute('class', 'association child empty');
2002
                        $header->appendChild(new XMLElement('p', __('No links in %s', array($a->generate()))));
2003
                    }
2004
2005
                    $header->appendChild($create);
2006
                    $element->prependChild($header);
2007
                    $content->appendChild($element);
2008
                }
2009
            }
2010
        }
2011
2012
        $drawer = Widget::Drawer('section-associations', __('Show Associations'), $content);
2013
        $this->insertDrawer($drawer, $drawer_position, 'prepend');
2014
    }
2015
2016
    /**
2017
     * If this entry is being prepopulated, this function will return the prepopulated
2018
     * fields and values as a query string.
2019
     *
2020
     * @since Symphony 2.5.2
2021
     * @return string
2022
     */
2023
    public function getPrepopulateString()
2024
    {
2025
        $prepopulate_querystring = '';
2026
2027
        if (isset($_REQUEST['prepopulate']) && is_array($_REQUEST['prepopulate'])) {
2028
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
2029
                // Properly decode and re-encode value for output
2030
                $value = rawurlencode(rawurldecode($value));
2031
                $prepopulate_querystring .= sprintf("prepopulate[%s]=%s&", $field_id, $value);
2032
            }
2033
            $prepopulate_querystring = trim($prepopulate_querystring, '&');
2034
        }
2035
2036
        // This is to prevent the value being interpreted as an additional GET
2037
        // parameter. eg. prepopulate[cat]=Minx&June, would come through as:
2038
        // $_GET['cat'] = Minx
2039
        // $_GET['June'] = ''
2040
        $prepopulate_querystring = preg_replace("/&amp;$/", '', $prepopulate_querystring);
2041
2042
        return $prepopulate_querystring ? '?' . $prepopulate_querystring : null;
2043
    }
2044
2045
    /**
2046
     * If the entry is being prepopulated, we may want to filter other views by this entry's
2047
     * value. This function will create that filter query string.
2048
     *
2049
     * @since Symphony 2.5.2
2050
     * @return string
2051
     */
2052
    public function getFilterString()
2053
    {
2054
        $filter_querystring = '';
2055
2056
        if (isset($_REQUEST['prepopulate']) && is_array($_REQUEST['prepopulate'])) {
2057
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
2058
                $handle = FieldManager::fetchHandleFromID($field_id);
2059
                // Properly decode and re-encode value for output
2060
                $value = rawurlencode(rawurldecode($value));
2061
                $filter_querystring .= sprintf('filter[%s]=%s&', $handle, $value);
2062
            }
2063
            $filter_querystring = trim($filter_querystring, '&');
2064
        }
2065
2066
        // This is to prevent the value being interpreted as an additional GET
2067
        // parameter. eg. filter[cat]=Minx&June, would come through as:
2068
        // $_GET['cat'] = Minx
2069
        // $_GET['June'] = ''
2070
        $filter_querystring = preg_replace("/&amp;$/", '', $filter_querystring);
2071
2072
        return $filter_querystring ? '?' . $filter_querystring : null;
2073
    }
2074
2075
    /**
2076
     * Given $_POST values, this function will validate the current timestamp
2077
     * and set the proper error messages.
2078
     *
2079
     * @since Symphony 2.7.0
2080
     * @param int $entry_id
2081
     *  The entry id to validate
2082
     * @return boolean
2083
     *  true if the timestamp is valid
2084
     */
2085
    protected function validateTimestamp($entry_id, $checkMissing = false)
2086
    {
2087
        if (!isset($_POST['action']['ignore-timestamp'])) {
2088
            if ($checkMissing && !isset($_POST['action']['timestamp'])) {
2089
                if (isset($this->_errors) && is_array($this->_errors)) {
2090
                    $this->_errors['timestamp'] = __('The entry could not be saved due to conflicting changes');
2091
                }
2092
                return false;
2093
            } elseif (isset($_POST['action']['timestamp'])) {
2094
                $tv = new TimestampValidator('entries');
2095
                if (!$tv->check($entry_id, $_POST['action']['timestamp'])) {
2096
                    if (isset($this->_errors) && is_array($this->_errors)) {
2097
                        $this->_errors['timestamp'] = __('The entry could not be saved due to conflicting changes');
2098
                    }
2099
                    return false;
2100
                }
2101
            }
2102
        }
2103
        return true;
2104
    }
2105
}
2106