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.

contentPublish::prepareAssociationsDrawer()   F
last analyzed

Complexity

Conditions 44
Paths 14

Size

Total Lines 278
Code Lines 152

Duplication

Lines 0
Ratio 0 %

Importance

Changes 16
Bugs 0 Features 0
Metric Value
cc 44
eloc 152
c 16
b 0
f 0
nc 14
nop 1
dl 0
loc 278
rs 3.3333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
0 ignored issues
show
Coding Style introduced by
This class is not in CamelCase format.

Classes in PHP are usually named in CamelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. The whole name starts with a capital letter as well.

Thus the name database provider becomes DatabaseProvider.

Loading history...
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 (is_null($sort) && is_null($order)) {
39
            $sort = $section->getSortingField();
40
            $order = $section->getSortingOrder();
41
42
            // Set the sorting in the `EntryManager` for subsequent use
43
            EntryManager::setFetchSorting($sort, $order);
44
        } else {
45
            $sort = General::sanitize($sort);
46
47
            // Ensure that this field is infact sortable, otherwise
48
            // fallback to IDs
49
            if (($field = FieldManager::fetch($sort)) instanceof Field && !$field->isSortable()) {
0 ignored issues
show
Bug introduced by
$sort of type string is incompatible with the type array|integer expected by parameter $id of FieldManager::fetch(). ( Ignorable by Annotation )

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

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

277
        $ul->setAttribute('data-search-types', implode($data['search'], /** @scrutinizer ignore-type */ ','));
Loading history...
278
279
        // Add help text for each filter operator
280
        foreach ($data['operators'] as $operator) {
281
            $this->createFilterHelp($ul, $operator);
282
        }
283
284
        $wrapper->appendChild($ul);
285
    }
286
287
    private function createFilterHelp(&$wrapper, $operator)
288
    {
289
        if (empty($operator['help'])) {
290
            return;
291
        }
292
293
        $li = new XMLElement('li', __('Comparison mode') . ': ' . $operator['help'], array(
294
            'class' => 'help',
295
            'data-comparison' => $operator['title']
296
        ));
297
298
        $wrapper->appendChild($li);
299
    }
300
301
    private function getFilterQuery($data)
302
    {
303
        $query = $data['filter'];
304
305
        foreach ($data['operators'] as $operator) {
306
            $filter = trim($operator['filter']);
307
308
            if (!empty($filter) && strpos($data['filter'], $filter) === 0) {
309
                $query = substr($data['filter'], strlen($operator['filter']));
310
            }
311
        }
312
313
        return (string)$query;
314
    }
315
316
    public function build(array $context = array())
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$context" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$context"; expected 0 but found 1
Loading history...
317
    {
318
        $section_id = SectionManager::fetchIDFromHandle($context['section_handle']);
319
320
        if ($section_id) {
321
            $context['associations'] = array(
322
                'parent' => SectionManager::fetchParentAssociations($section_id),
323
                'child' => SectionManager::fetchChildAssociations($section_id)
324
            );
325
        }
326
327
        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...
328
    }
329
330
    public function action()
331
    {
332
        $this->__switchboard('action');
333
    }
334
335
    public function __switchboard($type = 'view')
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$type" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$type"; expected 0 but found 1
Loading history...
336
    {
337
        $function = ($type == 'action' ? '__action' : '__view') . ucfirst($this->_context['page']);
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
338
339
        if (!method_exists($this, $function)) {
340
            // If there is no action function, just return without doing anything
341
            if ($type == 'action') {
342
                return;
343
            }
344
345
            Administration::instance()->errorPageNotFound();
346
        }
347
348
        // Is this request allowed by server?
349
        if ($this->isRequestValid() === false) {
350
            $this->pageAlert(__('This request exceeds the maximum allowed request size of %s specified by your host.', array(
351
                    ini_get('post_max_size')
352
                )),
353
                Alert::ERROR
354
            );
355
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
356
        $this->$function();
357
    }
358
359
    public function view()
360
    {
361
        $this->__switchboard();
362
    }
363
364
    public function __viewIndex()
365
    {
366
        if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
367
            Administration::instance()->throwCustomError(
368
                __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
369
                __('Unknown Section'),
370
                Page::HTTP_STATUS_NOT_FOUND
371
            );
372
        } 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...
373
            $this->pageAlert(__('The Symphony configuration file, %s, is not writable. The sort order cannot be modified.', array('<code>/manifest/config.php</code>')), Alert::NOTICE);
374
        }
375
376
        $section = SectionManager::fetch($section_id);
377
378
        $this->setPageType('table');
379
        $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

379
        $this->setTitle(__('%1$s &ndash; %2$s', array(General::sanitize(/** @scrutinizer ignore-type */ $section->get('name')), __('Symphony'))));
Loading history...
380
381
        $filters = array();
382
        $filter_querystring = $prepopulate_querystring = $where = $joins = null;
383
        $current_page = (isset($_REQUEST['pg']) && is_numeric($_REQUEST['pg']) ? max(1, intval($_REQUEST['pg'])) : 1);
384
385
        if (isset($_REQUEST['filter'])) {
386
            // legacy implementation, convert single filter to an array
387
            // split string in the form ?filter=handle:value
388
            // @deprecated
389
            // This should be removed in Symphony 4.0.0
390
            if (!is_array($_REQUEST['filter'])) {
391
                list($field_handle, $filter_value) = explode(':', $_REQUEST['filter'], 2);
392
                $filters[$field_handle] = rawurldecode($filter_value);
393
            } else {
394
                $filters = $_REQUEST['filter'];
395
            }
396
397
            foreach ($filters as $handle => $value) {
398
                // Handle multiple values through filtering. RE: #2290
399
                if ((is_array($value) && empty($value)) || trim($value) == '') {
400
                    continue;
401
                }
402
403
                if (!is_array($value)) {
404
                    $filter_type = Datasource::determineFilterType($value);
405
                    $value = Datasource::splitFilter($filter_type, $value);
406
                } else {
407
                    $filter_type = Datasource::FILTER_OR;
408
                }
409
410
                // Handle date meta data #2003
411
                $handle = Symphony::Database()->cleanValue($handle);
412
                if (in_array($handle, array('system:creation-date', 'system:modification-date'))) {
413
                    $date_joins = '';
414
                    $date_where = '';
415
                    $date = new FieldDate();
416
                    $date->buildDSRetrievalSQL($value, $date_joins, $date_where, ($filter_type == Datasource::FILTER_AND ? true : false));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
417
418
                    // Replace the date field where with the `creation_date` or `modification_date`.
419
                    $date_where = preg_replace('/`t\d+`.date/', ($field_id !== 'system:modification-date') ? '`e`.creation_date_gmt' : '`e`.modification_date_gmt', $date_where);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $field_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
420
                    $where .= $date_where;
421
                } else {
422
                    // Handle normal fields
423
                    $field_id = FieldManager::fetchFieldIDFromElementName(
424
                        $handle,
425
                        $section->get('id')
0 ignored issues
show
Bug introduced by
$section->get('id') of type array|string is incompatible with the type integer expected by parameter $section_id of FieldManager::fetchFieldIDFromElementName(). ( Ignorable by Annotation )

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

425
                        /** @scrutinizer ignore-type */ $section->get('id')
Loading history...
426
                    );
427
428
                    $field = FieldManager::fetch($field_id);
0 ignored issues
show
Bug introduced by
It seems like $field_id can also be of type false; however, parameter $id of FieldManager::fetch() does only seem to accept array|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

428
                    $field = FieldManager::fetch(/** @scrutinizer ignore-type */ $field_id);
Loading history...
429
                    if ($field instanceof Field) {
430
                        $field->buildDSRetrievalSQL($value, $joins, $where, ($filter_type == Datasource::FILTER_AND ? true : false));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
431
432
                        $value = implode(',', $value);
433
                        $encoded_value = rawurlencode($value);
434
                        $filter_querystring .= sprintf("filter[%s]=%s&amp;", $handle, $encoded_value);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal filter[%s]=%s& does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
435
436
                        // Some fields require that prepopulation be done via ID. RE: #2331
437
                        if (!is_numeric($value) && method_exists($field, 'fetchIDfromValue')) {
438
                            $encoded_value = $field->fetchIDfromValue($value);
439
                        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
440
                        $prepopulate_querystring .= sprintf("prepopulate[%d]=%s&amp;", $field_id, $encoded_value);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal prepopulate[%d]=%s& does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
441
                    } else {
442
                        unset($filters[$handle]);
443
                    }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
444
                    unset($field);
445
                }
446
            }
447
448
            $filter_querystring = preg_replace("/&amp;$/", '', $filter_querystring);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /&$/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
449
            $prepopulate_querystring = preg_replace("/&amp;$/", '', $prepopulate_querystring);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /&$/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
450
        }
451
452
        Sortable::initialize($this, $entries, $sort, $order, array(
453
            'current-section' => $section,
454
            'filters' => ($filter_querystring ? "&amp;" . $filter_querystring : ''),
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style Comprehensibility introduced by
The string literal & does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
455
            'unsort' => isset($_REQUEST['unsort'])
456
        ));
457
458
        $this->Form->setAttribute('action', Administration::instance()->getCurrentPageURL(). '?pg=' . $current_page.($filter_querystring ? "&amp;" . $filter_querystring : ''));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style Comprehensibility introduced by
The string literal & does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
459
460
        // Build filtering interface
461
        $this->createFilteringInterface();
462
463
        $subheading_buttons = array(
464
            Widget::Anchor(__('Create New'), Administration::instance()->getCurrentPageURL().'new/'.($prepopulate_querystring ? '?' . $prepopulate_querystring : ''), __('Create a new entry'), 'create button', null, array('accesskey' => 'c'))
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
465
        );
466
467
        // Only show the Edit Section button if the Author is a developer. #938 ^BA
468
        if (Symphony::Author()->isDeveloper()) {
469
            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...
470
        }
471
472
        $this->appendSubheading(General::sanitize($section->get('name')), $subheading_buttons);
473
474
        /**
475
         * Allows adjustments to be made to the SQL where and joins statements
476
         * before they are used to fetch the entries for the page
477
         *
478
         * @delegate AdjustPublishFiltering
479
         * @since Symphony 2.3.3
480
         * @param string $context
481
         * '/publish/'
482
         * @param integer $section_id
483
         * An array of the current columns, passed by reference
484
         * @param string $where
485
         * The current where statement, or null if not set
486
         * @param string $joins
487
         */
488
        Symphony::ExtensionManager()->notifyMembers('AdjustPublishFiltering', '/publish/', array('section-id' => $section_id, 'where' => &$where, 'joins' => &$joins));
489
490
        // get visible columns
491
        $visible_columns = $section->fetchVisibleColumns();
492
        // extract the needed schema
493
        $element_names = array_values(array_map(function ($field) {
494
            return $field->get('element_name');
495
        }, $visible_columns));
496
497
        // Check that the filtered query fails that the filter is dropped and an
498
        // error is logged. #841 ^BA
499
        try {
500
            $entries = EntryManager::fetchByPage($current_page, $section_id, Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), $where, $joins, true, false, true, $element_names);
501
        } catch (DatabaseException $ex) {
502
            $this->pageAlert(__('An error occurred while retrieving filtered entries. Showing all entries instead.'), Alert::ERROR);
503
            $filter_querystring = null;
504
            Symphony::Log()->pushToLog(sprintf(
505
                    '%s - %s%s%s',
506
                    $section->get('name') . ' Publish Index',
0 ignored issues
show
Bug introduced by
Are you sure $section->get('name') of type array|string 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

506
                    /** @scrutinizer ignore-type */ $section->get('name') . ' Publish Index',
Loading history...
507
                    $ex->getMessage(),
508
                    ($ex->getFile() ? " in file " .  $ex->getFile() : null),
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal in file does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
509
                    ($ex->getLine() ? " on line " . $ex->getLine() : null)
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal on line does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
510
                ),
511
                E_NOTICE,
512
                true
513
            );
514
            $entries = EntryManager::fetchByPage($current_page, $section_id, Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), null, null, true, false, true, $element_names);
515
        }
516
517
        // Flag filtering
518
        if (isset($_REQUEST['filter'])) {
519
            $filter_stats = new XMLElement('p', '<span>– ' . __('%d of %d entries (filtered)', array($entries['total-entries'], EntryManager::fetchCount($section_id))) . '</span>', array('class' => 'inactive'));
520
        } else {
521
            $filter_stats = new XMLElement('p', '<span>– ' . __('%d entries', array($entries['total-entries'])) . '</span>', array('class' => 'inactive'));
522
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
523
        $this->Breadcrumbs->appendChild($filter_stats);
524
525
        // Build table
526
        $columns = array();
527
528
        if (is_array($visible_columns) && !empty($visible_columns)) {
529
            foreach ($visible_columns as $column) {
530
                $columns[] = array(
531
                    'label' => $column->get('label'),
532
                    'sortable' => $column->isSortable(),
533
                    'handle' => $column->get('id'),
534
                    'attrs' => array(
535
                        'id' => 'field-' . $column->get('id'),
536
                        'class' => 'field-' . $column->get('type')
537
                    )
538
                );
539
            }
540
        } else {
541
            $columns[] = array(
542
                'label' => __('ID'),
543
                'sortable' => true,
544
                'handle' => 'id'
545
            );
546
        }
547
548
        $aTableHead = Sortable::buildTableHeaders($columns, $sort, $order, ($filter_querystring) ? "&amp;" . $filter_querystring : '');
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal & does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
549
550
        $child_sections = array();
551
        $associated_sections = $section->fetchChildAssociations(true);
552
553
        if (is_array($associated_sections) && !empty($associated_sections)) {
554
            foreach ($associated_sections as $key => $as) {
555
                $child_sections[$key] = SectionManager::fetch($as['child_section_id']);
556
                $aTableHead[] = array($child_sections[$key]->get('name'), 'col');
557
            }
558
        }
559
560
        /**
561
         * Allows the creation of custom table columns for each entry. Called
562
         * after all the Section Visible columns have been added as well
563
         * as the Section Associations
564
         *
565
         * @delegate AddCustomPublishColumn
566
         * @since Symphony 2.2
567
         * @param string $context
568
         * '/publish/'
569
         * @param array $tableHead
570
         * An array of the current columns, passed by reference
571
         * @param integer $section_id
572
         * The current Section ID
573
         */
574
        Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumn', '/publish/', array('tableHead' => &$aTableHead, 'section_id' => $section->get('id')));
575
576
        // Table Body
577
        $aTableBody = array();
578
579
        if (!is_array($entries['records']) || empty($entries['records'])) {
580
            $aTableBody = array(
581
                Widget::TableRow(array(Widget::TableData(__('None found.'), 'inactive', null, count($aTableHead))), 'odd')
582
            );
583
        } else {
584
            $field_pool = array();
585
586
            if (is_array($visible_columns) && !empty($visible_columns)) {
587
                foreach ($visible_columns as $column) {
588
                    $field_pool[$column->get('id')] = $column;
589
                }
590
            }
591
592
            $link_column = array_reverse($visible_columns);
593
            $link_column = end($link_column);
594
            reset($visible_columns);
595
596
            foreach ($entries['records'] as $entry) {
597
                $tableData = array();
598
599
                // Setup each cell
600
                if (!is_array($visible_columns) || empty($visible_columns)) {
601
                    $tableData[] = Widget::TableData(Widget::Anchor($entry->get('id'), Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/'));
602
                } else {
603
                    $link = Widget::Anchor(
604
                        '',
605
                        Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/'.($filter_querystring ? '?' . $prepopulate_querystring : ''),
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
606
                        $entry->get('id'),
607
                        'content'
608
                    );
609
610
                    foreach ($visible_columns as $position => $column) {
611
                        $data = $entry->getData($column->get('id'));
612
                        $field = $field_pool[$column->get('id')];
613
614
                        $value = $field->prepareTableValue($data, ($column == $link_column) ? $link : null, $entry->get('id'));
615
616
                        if (!is_object($value) && (strlen(trim($value)) == 0 || $value == __('None'))) {
617
                            $value = ($position == 0 ? $link->generate() : __('None'));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
618
                        }
619
620
                        if ($value == __('None')) {
621
                            $tableData[] = Widget::TableData($value, 'inactive field-' . $column->get('type') . ' field-' . $column->get('id'));
622
                        } else {
623
                            $tableData[] = Widget::TableData($value, 'field-' . $column->get('type') . ' field-' . $column->get('id'));
624
                        }
625
626
                        unset($field);
627
                    }
628
                }
629
630
                if (is_array($child_sections) && !empty($child_sections)) {
631
                    foreach ($child_sections as $key => $as) {
632
                        $field = FieldManager::fetch((int)$associated_sections[$key]['child_section_field_id']);
633
                        $parent_section_field_id = (int)$associated_sections[$key]['parent_section_field_id'];
634
635
                        if (!is_null($parent_section_field_id)) {
636
                            $search_value = $field->fetchAssociatedEntrySearchValue(
637
                                $entry->getData($parent_section_field_id),
638
                                $parent_section_field_id,
639
                                $entry->get('id')
640
                            );
641
                        } else {
642
                            $search_value = $entry->get('id');
643
                        }
644
645
                        if (!is_array($search_value)) {
646
                            $associated_entry_count = $field->fetchAssociatedEntryCount($search_value);
647
648
                            $tableData[] = Widget::TableData(
649
                                Widget::Anchor(
650
                                    sprintf('%d &rarr;', max(0, intval($associated_entry_count))),
651
                                    sprintf(
652
                                        '%s/publish/%s/?filter[%s]=%s',
653
                                        SYMPHONY_URL,
654
                                        $as->get('handle'),
655
                                        $field->get('element_name'),
656
                                        rawurlencode($search_value)
657
                                    ),
658
                                    $entry->get('id'),
659
                                    'content'
660
                                )
661
                            );
662
                        }
663
664
                        unset($field);
665
                    }
666
                }
667
668
                /**
669
                 * Allows Extensions to inject custom table data for each Entry
670
                 * into the Publish Index
671
                 *
672
                 * @delegate AddCustomPublishColumnData
673
                 * @since Symphony 2.2
674
                 * @param string $context
675
                 * '/publish/'
676
                 * @param array $tableData
677
                 *  An array of `Widget::TableData`, passed by reference
678
                 * @param integer $section_id
679
                 *  The current Section ID
680
                 * @param Entry $entry_id
681
                 *  The entry object, please note that this is by error and this will
682
                 *  be removed in Symphony 2.4. The entry object is available in
683
                 *  the 'entry' key as of Symphony 2.3.1.
684
                 * @param Entry $entry
685
                 *  The entry object for this row
686
                 */
687
                Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumnData', '/publish/', array(
688
                    'tableData' => &$tableData,
689
                    'section_id' => $section->get('id'),
690
                    'entry_id' => $entry,
691
                    'entry' => $entry
692
                ));
693
694
                $lastCol = $tableData[count($tableData) - 1];
695
                $lastCol->appendChild(Widget::Label(__('Select Entry %d', array($entry->get('id'))), null, 'accessible', null, array(
696
                    'for' => 'entry-' . $entry->get('id')
697
                )));
698
                $lastCol->appendChild(Widget::Input('items['.$entry->get('id').']', $entry->get('modification_date'), 'checkbox', array(
699
                    'id' => 'entry-' . $entry->get('id')
700
                )));
701
702
                // Add a row to the body array, assigning each cell to the row
703
                $aTableBody[] = Widget::TableRow($tableData, null, 'id-' . $entry->get('id'));
704
            }
705
        }
706
707
        $table = Widget::Table(
708
            Widget::TableHead($aTableHead),
709
            null,
710
            Widget::TableBody($aTableBody),
711
            'selectable',
712
            null,
713
            array('role' => 'directory', 'aria-labelledby' => 'symphony-subheading', 'data-interactive' => 'data-interactive')
714
        );
715
716
        $this->Form->appendChild($table);
717
718
        $tableActions = new XMLElement('div');
719
        $tableActions->setAttribute('class', 'actions');
720
721
        $options = array(
722
            array(null, false, __('With Selected...')),
723
            array('delete', false, __('Delete'), 'confirm', null, array(
724
                'data-message' => __('Are you sure you want to delete the selected entries?')
725
            ))
726
        );
727
728
        $toggable_fields = $section->fetchToggleableFields();
729
730
        if (is_array($toggable_fields) && !empty($toggable_fields)) {
731
            $index = 2;
732
733
            foreach ($toggable_fields as $field) {
734
                $toggle_states = $field->getToggleStates();
735
736
                if (is_array($toggle_states)) {
737
                    $options[$index] = array('label' => __('Set %s', array($field->get('label'))), 'options' => array());
738
739
                    foreach ($toggle_states as $value => $state) {
740
                        $options[$index]['options'][] = array('toggle-' . $field->get('id') . '-' . $value, false, $state);
741
                    }
742
                }
743
744
                $index++;
745
            }
746
        }
747
748
        /**
749
         * Allows an extension to modify the existing options for this page's
750
         * With Selected menu. If the `$options` parameter is an empty array,
751
         * the 'With Selected' menu will not be rendered.
752
         *
753
         * @delegate AddCustomActions
754
         * @since Symphony 2.3.2
755
         * @param string $context
756
         * '/publish/'
757
         * @param array $options
758
         *  An array of arrays, where each child array represents an option
759
         *  in the With Selected menu. Options should follow the same format
760
         *  expected by `Widget::__SelectBuildOption`. Passed by reference.
761
         */
762
        Symphony::ExtensionManager()->notifyMembers('AddCustomActions', '/publish/', array(
763
            'options' => &$options
764
        ));
765
766
        if (!empty($options)) {
767
            $tableActions->appendChild(Widget::Apply($options));
768
            $this->Form->appendChild($tableActions);
769
        }
770
771
        if ($entries['total-pages'] > 1) {
772
            $ul = new XMLElement('ul');
773
            $ul->setAttribute('class', 'page');
774
775
            // First
776
            $li = new XMLElement('li');
777
778
            if ($current_page > 1) {
779
                $li->appendChild(Widget::Anchor(__('First'), Administration::instance()->getCurrentPageURL(). '?pg=1'.($filter_querystring ? "&amp;" . $filter_querystring : '')));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style Comprehensibility introduced by
The string literal & does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
780
            } else {
781
                $li->setValue(__('First'));
782
            }
783
784
            $ul->appendChild($li);
785
786
            // Previous
787
            $li = new XMLElement('li');
788
789
            if ($current_page > 1) {
790
                $li->appendChild(Widget::Anchor(__('&larr; Previous'), Administration::instance()->getCurrentPageURL(). '?pg=' . ($current_page - 1).($filter_querystring ? "&amp;" . $filter_querystring : '')));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style Comprehensibility introduced by
The string literal & does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
791
            } else {
792
                $li->setValue(__('&larr; Previous'));
793
            }
794
795
            $ul->appendChild($li);
796
797
            // Summary
798
            $li = new XMLElement('li');
799
800
            $li->setAttribute('title', __('Viewing %1$s - %2$s of %3$s entries', array(
801
                $entries['start'],
802
                ($current_page != $entries['total-pages']) ? $current_page * Symphony::Configuration()->get('pagination_maximum_rows', 'symphony') : $entries['total-entries'],
803
                $entries['total-entries']
804
            )));
805
806
            $pgform = Widget::Form(Administration::instance()->getCurrentPageURL(), 'get', 'paginationform');
807
808
            $pgmax = max($current_page, $entries['total-pages']);
809
            $pgform->appendChild(Widget::Input('pg', null, 'text', array(
810
                'data-active' => __('Go to page …'),
811
                'data-inactive' => __('Page %1$s of %2$s', array((string)$current_page, $pgmax)),
812
                'data-max' => $pgmax
813
            )));
814
815
            $li->appendChild($pgform);
816
            $ul->appendChild($li);
817
818
            // Next
819
            $li = new XMLElement('li');
820
821
            if ($current_page < $entries['total-pages']) {
822
                $li->appendChild(Widget::Anchor(__('Next &rarr;'), Administration::instance()->getCurrentPageURL(). '?pg=' . ($current_page + 1).($filter_querystring ? "&amp;" . $filter_querystring : '')));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style Comprehensibility introduced by
The string literal & does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
823
            } else {
824
                $li->setValue(__('Next &rarr;'));
825
            }
826
827
            $ul->appendChild($li);
828
829
            // Last
830
            $li = new XMLElement('li');
831
832
            if ($current_page < $entries['total-pages']) {
833
                $li->appendChild(Widget::Anchor(__('Last'), Administration::instance()->getCurrentPageURL(). '?pg=' . $entries['total-pages'].($filter_querystring ? "&amp;" . $filter_querystring : '')));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style Comprehensibility introduced by
The string literal & does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
834
            } else {
835
                $li->setValue(__('Last'));
836
            }
837
838
            $ul->appendChild($li);
839
840
            $this->Contents->appendChild($ul);
841
        }
842
    }
843
844
    public function __actionIndex()
845
    {
846
        $checked = (is_array($_POST['items'])) ? array_keys($_POST['items']) : null;
847
848
        if (is_array($checked) && !empty($checked)) {
849
            /**
850
             * Extensions can listen for any custom actions that were added
851
             * through `AddCustomPreferenceFieldsets` or `AddCustomActions`
852
             * delegates.
853
             *
854
             * @delegate CustomActions
855
             * @since Symphony 2.3.2
856
             * @param string $context
857
             *  '/publish/'
858
             * @param array $checked
859
             *  An array of the selected rows. The value is usually the ID of the
860
             *  the associated object.
861
             */
862
            Symphony::ExtensionManager()->notifyMembers('CustomActions', '/publish/', array(
863
                'checked' => $checked
864
            ));
865
866
            switch ($_POST['with-selected']) {
867
                case 'delete':
868
                    /**
869
                     * Prior to deletion of entries. An array of Entry ID's is provided which
870
                     * can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete`
871
                     * in Symphony 2.3.
872
                     *
873
                     * @delegate EntryPreDelete
874
                     * @param string $context
875
                     * '/publish/'
876
                     * @param array $entry_id
877
                     *  An array of Entry ID's passed by reference
878
                     */
879
                    Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/', array('entry_id' => &$checked));
880
881
                    EntryManager::delete($checked);
882
883
                    /**
884
                     * After the deletion of entries, this delegate provides an array of Entry ID's
885
                     * that were deleted.
886
                     *
887
                     * @since Symphony 2.3
888
                     * @delegate EntryPostDelete
889
                     * @param string $context
890
                     * '/publish/'
891
                     * @param array $entry_id
892
                     *  An array of Entry ID's that were deleted.
893
                     */
894
                    Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/', array('entry_id' => $checked));
895
896
                    redirect(server_safe('REQUEST_URI'));
897
                    break;
898
                default:
899
                    list($option, $field_id, $value) = explode('-', $_POST['with-selected'], 3);
900
901
                    if ($option == 'toggle') {
902
                        $field = FieldManager::fetch($field_id);
0 ignored issues
show
Bug introduced by
$field_id of type string is incompatible with the type array|integer expected by parameter $id of FieldManager::fetch(). ( Ignorable by Annotation )

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

902
                        $field = FieldManager::fetch(/** @scrutinizer ignore-type */ $field_id);
Loading history...
903
                        $fields = array($field->get('element_name') => $value);
904
905
                        $section = SectionManager::fetch($field->get('parent_section'));
906
907
                        foreach ($checked as $entry_id) {
908
                            $entry = EntryManager::fetch($entry_id);
909
                            $existing_data = $entry[0]->getData($field_id);
910
                            $entry[0]->setData($field_id, $field->toggleFieldData(is_array($existing_data) ? $existing_data : array(), $value, $entry_id));
911
912
                            /**
913
                             * Just prior to editing of an Entry
914
                             *
915
                             * @delegate EntryPreEdit
916
                             * @param string $context
917
                             * '/publish/edit/'
918
                             * @param Section $section
919
                             * @param Entry $entry
920
                             * @param array $fields
921
                             */
922
                            Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/', array(
923
                                'section' => $section,
924
                                'entry' => &$entry[0],
925
                                'fields' => $fields
926
                            ));
927
928
                            $entry[0]->commit();
929
930
                            /**
931
                             * Editing an entry. Entry object is provided.
932
                             *
933
                             * @delegate EntryPostEdit
934
                             * @param string $context
935
                             * '/publish/edit/'
936
                             * @param Section $section
937
                             * @param Entry $entry
938
                             * @param array $fields
939
                             */
940
                            Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/', array(
941
                                'section' => $section,
942
                                'entry' => $entry[0],
943
                                'fields' => $fields
944
                            ));
945
                        }
946
947
                        unset($field);
948
                        redirect(server_safe('REQUEST_URI'));
949
                    }
950
            }
951
        }
952
    }
953
954
    public function __viewNew()
955
    {
956
        if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
957
            Administration::instance()->throwCustomError(
958
                __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
959
                __('Unknown Section'),
960
                Page::HTTP_STATUS_NOT_FOUND
961
            );
962
        }
963
964
        $section = SectionManager::fetch($section_id);
965
966
        $this->setPageType('form');
967
        $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

967
        $this->setTitle(__('%1$s &ndash; %2$s', array(General::sanitize(/** @scrutinizer ignore-type */ $section->get('name')), __('Symphony'))));
Loading history...
968
969
        // Ensure errored entries still maintain any prepopulated values [#2211]
970
        $this->Form->setAttribute('action', $this->Form->getAttribute('action') . $this->getPrepopulateString());
971
        $this->Form->setAttribute('enctype', 'multipart/form-data');
972
973
        $sidebar_fields = $section->fetchFields(null, 'sidebar');
974
        $main_fields = $section->fetchFields(null, 'main');
975
976
        if (!empty($sidebar_fields) && !empty($main_fields)) {
977
            $this->Form->setAttribute('class', 'two columns');
978
        } else {
979
            $this->Form->setAttribute('class', 'columns');
980
        }
981
982
        // Only show the Edit Section button if the Author is a developer. #938 ^BA
983
        if (Symphony::Author()->isDeveloper()) {
984
            $this->appendSubheading(__('Untitled'),
985
                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...
986
            );
987
        } else {
988
            $this->appendSubheading(__('Untitled'));
989
        }
990
991
        // Build filtered breadcrumb [#1378}
992
        $this->insertBreadcrumbs(array(
993
            Widget::Anchor(General::sanitize($section->get('name')), SYMPHONY_URL . '/publish/' . $this->_context['section_handle'] . '/' . $this->getFilterString()),
994
        ));
995
996
        $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

996
        $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', /** @scrutinizer ignore-type */ Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
Loading history...
997
998
        // If there is post data floating around, due to errors, create an entry object
999
        if (isset($_POST['fields'])) {
1000
            $entry = EntryManager::create();
1001
            $entry->set('section_id', $section_id);
1002
            $entry->setDataFromPost($_POST['fields'], $error, true);
1003
1004
            // Brand new entry, so need to create some various objects
1005
        } else {
1006
            $entry = EntryManager::create();
1007
            $entry->set('section_id', $section_id);
1008
        }
1009
1010
        // Check if there is a field to prepopulate
1011
        if (isset($_REQUEST['prepopulate'])) {
1012
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1013
                $this->Form->prependChild(Widget::Input(
1014
                    "prepopulate[{$field_id}]",
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $field_id instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1015
                    rawurlencode($value),
1016
                    'hidden'
1017
                ));
1018
1019
                // The actual pre-populating should only happen if there is not existing fields post data
1020
                // and if the field allows it
1021
                if (!isset($_POST['fields']) && ($field = FieldManager::fetch($field_id)) && $field->canPrePopulate()) {
1022
                    $entry->setData(
1023
                        $field->get('id'),
1024
                        $field->processRawFieldData($value, $error, $message, true)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $error does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $message seems to be never defined.
Loading history...
1025
                    );
1026
                    unset($field);
1027
                }
1028
            }
1029
        }
1030
1031
        $primary = new XMLElement('fieldset');
1032
        $primary->setAttribute('class', 'primary column');
1033
1034
        if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) {
1035
            $message = __('Fields must be added to this section before an entry can be created.');
1036
1037
            if (Symphony::Author()->isDeveloper()) {
1038
                $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 array|string 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

1038
                $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . /** @scrutinizer ignore-type */ $section->get('id') . '/" accesskey="c">'
Loading history...
1039
                . __('Add fields')
1040
                . '</a>';
1041
            }
1042
1043
            $this->pageAlert($message, Alert::ERROR);
1044
        } else {
1045
            if (is_array($main_fields) && !empty($main_fields)) {
1046
                foreach ($main_fields as $field) {
1047
                    $primary->appendChild($this->__wrapFieldWithDiv($field, $entry));
1048
                }
1049
1050
                $this->Form->appendChild($primary);
1051
            }
1052
1053
            if (is_array($sidebar_fields) && !empty($sidebar_fields)) {
1054
                $sidebar = new XMLElement('fieldset');
1055
                $sidebar->setAttribute('class', 'secondary column');
1056
1057
                foreach ($sidebar_fields as $field) {
1058
                    $sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry));
1059
                }
1060
1061
                $this->Form->appendChild($sidebar);
1062
            }
1063
1064
            $div = new XMLElement('div');
1065
            $div->setAttribute('class', 'actions');
1066
            $div->appendChild(Widget::Input('action[save]', __('Create Entry'), 'submit', array('accesskey' => 's')));
1067
1068
            $this->Form->appendChild($div);
1069
1070
            // Create a Drawer for Associated Sections
1071
            $this->prepareAssociationsDrawer($section);
0 ignored issues
show
Bug introduced by
It seems like $section can also be of type array; however, parameter $section of contentPublish::prepareAssociationsDrawer() does only seem to accept Section, 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

1071
            $this->prepareAssociationsDrawer(/** @scrutinizer ignore-type */ $section);
Loading history...
1072
        }
1073
    }
1074
1075
    public function __actionNew()
1076
    {
1077
        if (is_array($_POST['action']) && (array_key_exists('save', $_POST['action']) || array_key_exists('done', $_POST['action']))) {
1078
            $section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle']);
1079
1080
            if (!$section = SectionManager::fetch($section_id)) {
1081
                Administration::instance()->throwCustomError(
1082
                    __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
1083
                    __('Unknown Section'),
1084
                    Page::HTTP_STATUS_NOT_FOUND
1085
                );
1086
            }
1087
1088
            $entry = EntryManager::create();
1089
            $entry->set('author_id', Symphony::Author()->get('id'));
1090
            $entry->set('section_id', $section_id);
1091
            $entry->set('creation_date', DateTimeObj::get('c'));
1092
            $entry->set('modification_date', DateTimeObj::get('c'));
1093
1094
            $fields = $_POST['fields'];
1095
1096
            // Combine FILES and POST arrays, indexed by their custom field handles
1097
            if (isset($_FILES['fields'])) {
1098
                $filedata = General::processFilePostData($_FILES['fields']);
1099
1100
                foreach ($filedata as $handle => $data) {
1101
                    if (!isset($fields[$handle])) {
1102
                        $fields[$handle] = $data;
1103
                    } elseif (isset($data['error']) && $data['error'] == UPLOAD_ERR_NO_FILE) {
1104
                        $fields[$handle] = null;
1105
                    } else {
1106
                        foreach ($data as $ii => $d) {
1107
                            if (isset($d['error']) && $d['error'] == UPLOAD_ERR_NO_FILE) {
1108
                                $fields[$handle][$ii] = null;
1109
                            } elseif (is_array($d) && !empty($d)) {
1110
                                foreach ($d as $key => $val) {
1111
                                    $fields[$handle][$ii][$key] = $val;
1112
                                }
1113
                            }
1114
                        }
1115
                    }
1116
                }
1117
            }
1118
1119
            // Initial checks to see if the Entry is ok
1120
            if (Entry::__ENTRY_FIELD_ERROR__ == $entry->checkPostData($fields, $this->_errors)) {
1121
                $this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR);
1122
1123
                // Secondary checks, this will actually process the data and attempt to save
1124
            } elseif (Entry::__ENTRY_OK__ != $entry->setDataFromPost($fields, $errors)) {
1125
                foreach ($errors as $field_id => $message) {
1126
                    $this->pageAlert($message, Alert::ERROR);
1127
                }
1128
1129
                // Everything is awesome. Dance.
1130
            } else {
1131
                /**
1132
                 * Just prior to creation of an Entry
1133
                 *
1134
                 * @delegate EntryPreCreate
1135
                 * @param string $context
1136
                 * '/publish/new/'
1137
                 * @param Section $section
1138
                 * @param Entry $entry
1139
                 * @param array $fields
1140
                 */
1141
                Symphony::ExtensionManager()->notifyMembers('EntryPreCreate', '/publish/new/', array('section' => $section, 'entry' => &$entry, 'fields' => &$fields));
1142
1143
                $entry->set('modification_author_id', Symphony::Author()->get('id'));
1144
1145
                // Check to see if the dancing was premature
1146
                if (!$entry->commit()) {
1147
                    $this->pageAlert(null, Alert::ERROR);
1148
                } else {
1149
                    /**
1150
                     * Creation of an Entry. New Entry object is provided.
1151
                     *
1152
                     * @delegate EntryPostCreate
1153
                     * @param string $context
1154
                     * '/publish/new/'
1155
                     * @param Section $section
1156
                     * @param Entry $entry
1157
                     * @param array $fields
1158
                     */
1159
                    Symphony::ExtensionManager()->notifyMembers('EntryPostCreate', '/publish/new/', array('section' => $section, 'entry' => $entry, 'fields' => $fields));
1160
1161
                    $prepopulate_querystring = $this->getPrepopulateString();
1162
                    redirect(sprintf(
1163
                        '%s/publish/%s/edit/%d/created/%s',
1164
                        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...
1165
                        $this->_context['section_handle'],
1166
                        $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

1166
                        /** @scrutinizer ignore-type */ $entry->get('id'),
Loading history...
1167
                        (!empty($prepopulate_querystring) ? $prepopulate_querystring : null)
1168
                    ));
1169
                }
1170
            }
1171
        }
1172
    }
1173
1174
    public function __viewEdit()
1175
    {
1176
        if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
1177
            Administration::instance()->throwCustomError(
1178
                __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
1179
                __('Unknown Section'),
1180
                Page::HTTP_STATUS_NOT_FOUND
1181
            );
1182
        }
1183
1184
        $section = SectionManager::fetch($section_id);
1185
        $entry_id = intval($this->_context['entry_id']);
1186
        $base = '/publish/'.$this->_context['section_handle'] . '/';
1187
        $new_link = $base . 'new/';
1188
        $filter_link = $base;
1189
        $canonical_link = $base . 'edit/' . $entry_id . '/';
1190
1191
        EntryManager::setFetchSorting('id', 'DESC');
0 ignored issues
show
Bug introduced by
'id' of type string is incompatible with the type integer expected by parameter $field_id of EntryManager::setFetchSorting(). ( Ignorable by Annotation )

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

1191
        EntryManager::setFetchSorting(/** @scrutinizer ignore-type */ 'id', 'DESC');
Loading history...
1192
1193
        $existingEntry = EntryManager::fetch($entry_id);
1194
        if (empty($existingEntry)) {
1195
            Administration::instance()->throwCustomError(
1196
                __('Unknown Entry'),
1197
                __('The Entry, %s, could not be found.', array($entry_id)),
1198
                Page::HTTP_STATUS_NOT_FOUND
1199
            );
1200
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1201
        $existingEntry = $existingEntry[0];
1202
1203
        // If the entry does not belong in the context's section
1204
        if ($section_id != $existingEntry->get('section_id')) {
1205
            Administration::instance()->throwCustomError(
1206
                __('Wrong section'),
1207
                __('The Entry, %s, does not belong in section %s', array($entry_id, $section_id)),
1208
                Page::HTTP_STATUS_BAD_REQUEST
1209
            );
1210
        }
1211
1212
        // If there is post data floating around, due to errors, create an entry object
1213
        if (isset($_POST['fields'])) {
1214
            $fields = $_POST['fields'];
1215
1216
            $entry = EntryManager::create();
1217
            $entry->set('id', $entry_id);
1218
            $entry->set('author_id', $existingEntry->get('author_id'));
1219
            $entry->set('modification_author_id', $existingEntry->get('modification_author_id'));
1220
            $entry->set('section_id', $existingEntry->get('section_id'));
1221
            $entry->set('creation_date', $existingEntry->get('creation_date'));
1222
            $entry->set('modification_date', $existingEntry->get('modification_date'));
1223
            $entry->setDataFromPost($fields, $errors, true);
1224
1225
            $timestamp = isset($_POST['action']['timestamp'])
1226
                ? $_POST['action']['timestamp']
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement must be declared on a single line
Loading history...
1227
                : $entry->get('modification_date');
1228
1229
            // Editing an entry, so need to create some various objects
1230
        } else {
1231
            $entry = $existingEntry;
1232
            $fields = array();
1233
1234
            if (!$section) {
1235
                $section = SectionManager::fetch($entry->get('section_id'));
1236
            }
1237
1238
            $timestamp = $entry->get('modification_date');
1239
        }
1240
1241
        /**
1242
         * Just prior to rendering of an Entry edit form.
1243
         *
1244
         * @delegate EntryPreRender
1245
         * @param string $context
1246
         * '/publish/edit/'
1247
         * @param Section $section
1248
         * @param Entry $entry
1249
         * @param array $fields
1250
         */
1251
        Symphony::ExtensionManager()->notifyMembers('EntryPreRender', '/publish/edit/', array(
1252
            'section' => $section,
1253
            'entry' => &$entry,
1254
            'fields' => $fields
1255
        ));
1256
1257
        // Iterate over the `prepopulate` parameters to build a URL
1258
        // to remember this state for Create New, View all Entries and
1259
        // Breadcrumb links. If `prepopulate` doesn't exist, this will
1260
        // just use the standard pages (ie. no filtering)
1261
        if (isset($_REQUEST['prepopulate'])) {
1262
            $new_link .= $this->getPrepopulateString();
1263
            $filter_link .= $this->getFilterString();
1264
            $canonical_link .= $this->getPrepopulateString();
1265
        }
1266
1267
        if (isset($this->_context['flag'])) {
1268
            // These flags are only relevant if there are no errors
1269
            if (empty($this->_errors)) {
1270
                $time = Widget::Time();
1271
1272
                switch ($this->_context['flag']) {
1273
                    case 'saved':
1274
                        $message = __('Entry updated at %s.', array($time->generate()));
1275
                        break;
1276
                    case 'created':
1277
                        $message = __('Entry created at %s.', array($time->generate()));
1278
                }
1279
1280
                $this->pageAlert(
1281
                    $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...
1282
                    . ' <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...
1283
                    . __('Create another?')
1284
                    . '</a> <a href="' . SYMPHONY_URL . $filter_link . '" accesskey="a">'
1285
                    . __('View all Entries')
1286
                    . '</a>',
1287
                    Alert::SUCCESS
1288
                );
1289
            }
1290
        }
1291
1292
        // Determine the page title
1293
        $field_id = Symphony::Database()->fetchVar('id', 0, sprintf("
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal \n SELECT `id... BY `sortorder` LIMIT 1 does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1294
            SELECT `id`
1295
            FROM `tbl_fields`
1296
            WHERE `parent_section` = %d
1297
            ORDER BY `sortorder` LIMIT 1",
1298
            $section->get('id')
0 ignored issues
show
Bug introduced by
It seems like $section->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

1298
            /** @scrutinizer ignore-type */ $section->get('id')
Loading history...
1299
        ));
1300
        if (!is_null($field_id)) {
1301
            $field = FieldManager::fetch($field_id);
0 ignored issues
show
Bug introduced by
$field_id of type string is incompatible with the type array|integer expected by parameter $id of FieldManager::fetch(). ( Ignorable by Annotation )

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

1301
            $field = FieldManager::fetch(/** @scrutinizer ignore-type */ $field_id);
Loading history...
1302
        }
1303
1304
        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...
1305
            $title = $field->prepareReadableValue($existingEntry->getData($field->get('id')), $entry_id, true);
1306
        } else {
1307
            $title = '';
1308
        }
1309
1310
        if (trim($title) == '') {
1311
            $title = __('Untitled');
1312
        }
1313
1314
        // Check if there is a field to prepopulate
1315
        if (isset($_REQUEST['prepopulate'])) {
1316
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1317
                $this->Form->prependChild(Widget::Input(
1318
                    "prepopulate[{$field_id}]",
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $field_id instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1319
                    rawurlencode($value),
1320
                    'hidden'
1321
                ));
1322
            }
1323
        }
1324
1325
        $this->setPageType('form');
1326
        $this->Form->setAttribute('enctype', 'multipart/form-data');
1327
        $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

1327
        $this->setTitle(__('%1$s &ndash; %2$s &ndash; %3$s', array($title, General::sanitize(/** @scrutinizer ignore-type */ $section->get('name')), __('Symphony'))));
Loading history...
1328
        $this->addElementToHead(new XMLElement('link', null, array(
1329
            'rel' => 'canonical',
1330
            'href' => SYMPHONY_URL . $canonical_link,
1331
        )));
1332
1333
        $sidebar_fields = $section->fetchFields(null, 'sidebar');
1334
        $main_fields = $section->fetchFields(null, 'main');
1335
1336
        if (!empty($sidebar_fields) && !empty($main_fields)) {
1337
            $this->Form->setAttribute('class', 'two columns');
1338
        } else {
1339
            $this->Form->setAttribute('class', 'columns');
1340
        }
1341
1342
        // Only show the Edit Section button if the Author is a developer. #938 ^BA
1343
        if (Symphony::Author()->isDeveloper()) {
1344
            $this->appendSubheading($title, Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button'));
1345
        } else {
1346
            $this->appendSubheading($title);
1347
        }
1348
1349
        $this->insertBreadcrumbs(array(
1350
            Widget::Anchor(General::sanitize($section->get('name')), SYMPHONY_URL . (isset($filter_link) ? $filter_link : $base)),
1351
        ));
1352
1353
        $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

1353
        $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', /** @scrutinizer ignore-type */ Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
Loading history...
1354
1355
        $primary = new XMLElement('fieldset');
1356
        $primary->setAttribute('class', 'primary column');
1357
1358
        if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) {
1359
            $message = __('Fields must be added to this section before an entry can be created.');
1360
1361
            if (Symphony::Author()->isDeveloper()) {
1362
                $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 array|string 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

1362
                $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . /** @scrutinizer ignore-type */ $section->get('id') . '/" accesskey="c">'
Loading history...
1363
                . __('Add fields')
1364
                . '</a>';
1365
            }
1366
1367
            $this->pageAlert($message, Alert::ERROR);
1368
        } else {
1369
            if (is_array($main_fields) && !empty($main_fields)) {
1370
                foreach ($main_fields as $field) {
1371
                    $primary->appendChild($this->__wrapFieldWithDiv($field, $entry));
1372
                }
1373
1374
                $this->Form->appendChild($primary);
1375
            }
1376
1377
            if (is_array($sidebar_fields) && !empty($sidebar_fields)) {
1378
                $sidebar = new XMLElement('fieldset');
1379
                $sidebar->setAttribute('class', 'secondary column');
1380
1381
                foreach ($sidebar_fields as $field) {
1382
                    $sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry));
1383
                }
1384
1385
                $this->Form->appendChild($sidebar);
1386
            }
1387
1388
            $div = new XMLElement('div');
1389
            $div->setAttribute('class', 'actions');
1390
            $div->appendChild(Widget::Input('action[save]', __('Save Changes'), 'submit', array('accesskey' => 's')));
1391
1392
            $button = new XMLElement('button', __('Delete'));
1393
            $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?')));
1394
            $div->appendChild($button);
1395
1396
            $div->appendChild(Widget::Input('action[timestamp]', $timestamp, 'hidden'));
1397
            $div->appendChild(Widget::Input('action[ignore-timestamp]', 'yes', 'checkbox', array('class' => 'irrelevant')));
1398
1399
            $this->Form->appendChild($div);
1400
1401
            // Create a Drawer for Associated Sections
1402
            $this->prepareAssociationsDrawer($section);
0 ignored issues
show
Bug introduced by
It seems like $section can also be of type array; however, parameter $section of contentPublish::prepareAssociationsDrawer() does only seem to accept Section, 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

1402
            $this->prepareAssociationsDrawer(/** @scrutinizer ignore-type */ $section);
Loading history...
1403
        }
1404
    }
1405
1406
    public function __actionEdit()
1407
    {
1408
        $entry_id = intval($this->_context['entry_id']);
1409
1410
        if (is_array($_POST['action']) && (array_key_exists('save', $_POST['action']) || array_key_exists('done', $_POST['action']))) {
1411
            $ret = EntryManager::fetch($entry_id);
1412
            if (empty($ret)) {
1413
                Administration::instance()->throwCustomError(
1414
                    __('The Entry, %s, could not be found.', array($entry_id)),
1415
                    __('Unknown Entry'),
1416
                    Page::HTTP_STATUS_NOT_FOUND
1417
                );
1418
            }
1419
1420
            $entry = $ret[0];
1421
1422
            $section = SectionManager::fetch($entry->get('section_id'));
1423
1424
            $post = General::getPostData();
1425
            $fields = $post['fields'];
1426
1427
            $canProceed = $this->validateTimestamp($entry_id, true);
1428
1429
            // Timestamp validation
1430
            if (!$canProceed) {
1431
                $this->addTimestampValidationPageAlert($this->_errors['timestamp'], $entry, 'save');
1432
1433
                // Initial checks to see if the Entry is ok
1434
            } elseif (Entry::__ENTRY_FIELD_ERROR__ == $entry->checkPostData($fields, $this->_errors)) {
1435
                $this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR);
1436
1437
                // Secondary checks, this will actually process the data and attempt to save
1438
            } elseif (Entry::__ENTRY_OK__ != $entry->setDataFromPost($fields, $errors)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $errors seems to be never defined.
Loading history...
1439
                foreach ($errors as $field_id => $message) {
1440
                    $this->pageAlert($message, Alert::ERROR);
1441
                }
1442
1443
                // Everything is awesome. Dance.
1444
            } else {
1445
                /**
1446
                 * Just prior to editing of an Entry.
1447
                 *
1448
                 * @delegate EntryPreEdit
1449
                 * @param string $context
1450
                 * '/publish/edit/'
1451
                 * @param Section $section
1452
                 * @param Entry $entry
1453
                 * @param array $fields
1454
                 */
1455
                Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/', array('section' => $section, 'entry' => &$entry, 'fields' => $fields));
1456
1457
                $entry->set('modification_author_id', Symphony::Author()->get('id'));
1458
1459
                // Check to see if the dancing was premature
1460
                if (!$entry->commit()) {
1461
                    $this->pageAlert(null, Alert::ERROR);
1462
                } else {
1463
                    /**
1464
                     * Just after the editing of an Entry
1465
                     *
1466
                     * @delegate EntryPostEdit
1467
                     * @param string $context
1468
                     * '/publish/edit/'
1469
                     * @param Section $section
1470
                     * @param Entry $entry
1471
                     * @param array $fields
1472
                     */
1473
                    Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/', array('section' => $section, 'entry' => $entry, 'fields' => $fields));
1474
1475
                    redirect(sprintf(
1476
                        '%s/publish/%s/edit/%d/saved/%s',
1477
                        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...
1478
                        $this->_context['section_handle'],
1479
                        $entry->get('id'),
1480
                        $this->getPrepopulateString()
1481
                    ));
1482
                }
1483
            }
1484
        } elseif (is_array($_POST['action']) && array_key_exists('delete', $_POST['action']) && is_numeric($entry_id)) {
1485
            /**
1486
             * Prior to deletion of entries. An array of Entry ID's is provided which
1487
             * can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete`
1488
             * in Symphony 2.3.
1489
             *
1490
             * @delegate EntryPreDelete
1491
             * @param string $context
1492
             * '/publish/'
1493
             * @param array $entry_id
1494
             *    An array of Entry ID's passed by reference
1495
             */
1496
            $checked = array($entry_id);
1497
            Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/', array('entry_id' => &$checked));
1498
1499
            $canProceed = $this->validateTimestamp($entry_id);
1500
1501
            if ($canProceed) {
1502
                EntryManager::delete($checked);
1503
1504
                /**
1505
                 * After the deletion of entries, this delegate provides an array of Entry ID's
1506
                 * that were deleted.
1507
                 *
1508
                 * @since Symphony 2.3
1509
                 * @delegate EntryPostDelete
1510
                 * @param string $context
1511
                 * '/publish/'
1512
                 * @param array $entry_id
1513
                 *  An array of Entry ID's that were deleted.
1514
                 */
1515
                Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/', array('entry_id' => $checked));
1516
1517
                redirect(SYMPHONY_URL . '/publish/'.$this->_context['section_handle'].'/');
1518
            } else {
1519
                $ret = EntryManager::fetch($entry_id);
1520
                if (!empty($ret)) {
1521
                    $entry = $ret[0];
1522
                    $this->addTimestampValidationPageAlert($this->_errors['timestamp'], $entry, 'delete');
1523
                }
1524
            }
1525
        }
1526
    }
1527
1528
    /**
1529
     * Given a Field and Entry object, this function will wrap
1530
     * the Field's displayPublishPanel result with a div that
1531
     * contains some contextual information such as the Field ID,
1532
     * the Field handle and whether it is required or not.
1533
     *
1534
     * @param Field $field
1535
     * @param Entry $entry
1536
     * @return XMLElement
1537
     */
1538
    private function __wrapFieldWithDiv(Field $field, Entry $entry)
1539
    {
1540
        $is_hidden = $this->isFieldHidden($field);
1541
        $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
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Bug introduced by
Are you sure $field->get('id') of type array|mixed|null 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

1541
        $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...
1542
1543
        $field->setAssociationContext($div);
1544
1545
        $field->displayPublishPanel(
1546
            $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

1546
            $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

1546
            $div, $entry->getData(/** @scrutinizer ignore-type */ $field->get('id')),
Loading history...
1547
            (isset($this->_errors[$field->get('id')]) ? $this->_errors[$field->get('id')] : null),
1548
            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

1548
            null, null, /** @scrutinizer ignore-type */ (is_numeric($entry->get('id')) ? $entry->get('id') : null)
Loading history...
1549
        );
1550
1551
        /**
1552
         * Allows developers modify the field before it is rendered in the publish
1553
         * form. Passes the `Field` object, `Entry` object, the `XMLElement` div and
1554
         * any errors for the entire `Entry`. Only the `$div` element
1555
         * will be altered before appending to the page, the rest are read only.
1556
         *
1557
         * @since Symphony 2.5.0
1558
         * @delegate ModifyFieldPublishWidget
1559
         * @param string $context
1560
         * '/backend/'
1561
         * @param Field $field
1562
         * @param Entry $entry
1563
         * @param array $errors
1564
         * @param Widget $widget
1565
         */
1566
        Symphony::ExtensionManager()->notifyMembers('ModifyFieldPublishWidget', '/backend/', array(
1567
            'field' => $field,
1568
            'entry' => $entry,
1569
            'errors' => $this->_errors,
1570
            'widget' => &$div
1571
        ));
1572
1573
        return $div;
1574
    }
1575
1576
    /**
1577
     * Check whether the given `$field` will be hidden because it's been
1578
     * prepopulated.
1579
     *
1580
     * @param  Field  $field
1581
     * @return boolean
1582
     */
1583
    public function isFieldHidden(Field $field)
1584
    {
1585
        if ($field->get('hide_when_prepopulated') == 'yes') {
1586
            if (isset($_REQUEST['prepopulate'])) {
1587
                foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1588
                    if ($field_id == $field->get('id')) {
1589
                        return true;
1590
                    }
1591
                }
1592
            }
1593
        }
1594
1595
        return false;
1596
    }
1597
1598
    /**
1599
     * Prepare a Drawer to visualize section associations
1600
     *
1601
     * @param  Section $section The current Section object
1602
     * @throws InvalidArgumentException
1603
     * @throws Exception
1604
     */
1605
    private function prepareAssociationsDrawer($section)
1606
    {
1607
        $entry_id = (!is_null($this->_context['entry_id'])) ? $this->_context['entry_id'] : null;
1608
        $show_entries = Symphony::Configuration()->get('association_maximum_rows', 'symphony');
1609
1610
        if (is_null($entry_id) && !isset($_GET['prepopulate']) || is_null($show_entries) || $show_entries == 0) {
1611
            return;
1612
        }
1613
1614
        $parent_associations = SectionManager::fetchParentAssociations($section->get('id'), true);
0 ignored issues
show
Bug introduced by
$section->get('id') of type array|string 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

1614
        $parent_associations = SectionManager::fetchParentAssociations(/** @scrutinizer ignore-type */ $section->get('id'), true);
Loading history...
1615
        $child_associations = SectionManager::fetchChildAssociations($section->get('id'), true);
0 ignored issues
show
Bug introduced by
$section->get('id') of type array|string 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

1615
        $child_associations = SectionManager::fetchChildAssociations(/** @scrutinizer ignore-type */ $section->get('id'), true);
Loading history...
1616
        $content = null;
1617
        $drawer_position = 'vertical-right';
1618
1619
        /**
1620
         * Prepare Associations Drawer from an Extension
1621
         *
1622
         * @since Symphony 2.3.3
1623
         * @delegate PrepareAssociationsDrawer
1624
         * @param string $context
1625
         * '/publish/'
1626
         * @param integer $entry_id
1627
         *  The entry ID or null
1628
         * @param array $parent_associations
1629
         *  Array of Sections
1630
         * @param array $child_associations
1631
         *  Array of Sections
1632
         * @param string $drawer_position
1633
         *  The position of the Drawer, defaults to `vertical-right`. Available
1634
         *  values of `vertical-left, `vertical-right` and `horizontal`
1635
         */
1636
        Symphony::ExtensionManager()->notifyMembers('PrepareAssociationsDrawer', '/publish/', array(
1637
            'entry_id' => $entry_id,
1638
            'parent_associations' => &$parent_associations,
1639
            'child_associations' => &$child_associations,
1640
            'content' => &$content,
1641
            'drawer-position' => &$drawer_position
1642
        ));
1643
1644
        // If there are no associations, return now.
1645
        if (
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
1646
            (is_null($parent_associations) || empty($parent_associations))
1647
            &&
1648
            (is_null($child_associations) || empty($child_associations))
1649
        ) {
1650
            return;
1651
        }
1652
1653
        if (!($content instanceof XMLElement)) {
1654
            $content = new XMLElement('div', null, array('class' => 'content'));
1655
            $content->setSelfClosingTag(false);
1656
1657
            // backup global sorting
1658
            $sorting = EntryManager::getFetchSorting();
1659
1660
            // Process Parent Associations
1661
            if (!is_null($parent_associations) && !empty($parent_associations)) {
1662
                $title = new XMLElement('h2', __('Linked to') . ':', array('class' => 'association-title'));
1663
                $content->appendChild($title);
1664
1665
                foreach ($parent_associations as $as) {
1666
                    if (empty($as['parent_section_field_id'])) {
1667
                        continue;
1668
                    }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1669
                    if ($field = FieldManager::fetch($as['parent_section_field_id'])) {
1670
                        // Get the related section
1671
                        $parent_section = SectionManager::fetch($as['parent_section_id']);
1672
1673
                        if (!($parent_section instanceof Section)) {
1674
                            continue;
1675
                        }
1676
1677
                        // set global sorting for associated section
1678
                        EntryManager::setFetchSorting(
1679
                            $parent_section->getSortingField(),
0 ignored issues
show
Bug introduced by
$parent_section->getSortingField() of type string is incompatible with the type integer expected by parameter $field_id of EntryManager::setFetchSorting(). ( Ignorable by Annotation )

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

1679
                            /** @scrutinizer ignore-type */ $parent_section->getSortingField(),
Loading history...
1680
                            $parent_section->getSortingOrder()
1681
                        );
1682
1683
                        if (isset($_GET['prepopulate'])) {
1684
                            $prepopulate_field = key($_GET['prepopulate']);
1685
                        }
1686
1687
                        // get associated entries if entry exists,
1688
                        if ($entry_id) {
1689
                            $relation_field = FieldManager::fetch($as['child_section_field_id']);
1690
                            $entry_ids = $relation_field->findParentRelatedEntries($as['parent_section_field_id'], $entry_id);
1691
1692
                            // get prepopulated entry otherwise
1693
                        } elseif (isset($_GET['prepopulate']) && is_array($_GET['prepopulate']) && isset($_GET['prepopulate'][$as['child_section_field_id']])) {
1694
                            $entry_ids = array(intval($_GET['prepopulate'][$as['child_section_field_id']]));
1695
                        } else {
1696
                            $entry_ids = array();
1697
                        }
1698
1699
                        // Use $schema for perf reasons
1700
                        $schema = array($field->get('element_name'));
1701
                        $where = (!empty($entry_ids)) ? sprintf(' AND `e`.`id` IN (%s)', implode(', ', $entry_ids)) : null;
1702
                        $entries = (!empty($entry_ids) || isset($_GET['prepopulate']) && $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...
1703
                            ? EntryManager::fetchByPage(1, $as['parent_section_id'], $show_entries, $where, null, false, false, true, $schema)
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement must be declared on a single line
Loading history...
1704
                            : array();
1705
                        $has_entries = !empty($entries) && $entries['total-entries'] != 0;
1706
1707
                        // Create link
1708
                        $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...
1709
                        $aname = General::sanitize($as['name']);
1710
                        if ($has_entries) {
1711
                            $aname .= ' <span>(' . $entries['total-entries'] . ')</span>';
1712
                        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1713
                        $a = new XMLElement('a', $aname, array(
1714
                            'class' => 'association-section',
1715
                            'href' => $link,
1716
                            'title' => strip_tags($aname),
1717
                        ));
1718
1719
                        if (!$has_entries) {
1720
                            unset($field);
1721
                            continue;
1722
                        }
1723
1724
                        $element = new XMLElement('section', null, array('class' => 'association parent'));
1725
                        $header = new XMLElement('header');
1726
                        $header->appendChild(new XMLElement('p', $a->generate()));
1727
                        $element->appendChild($header);
1728
1729
                        $ul = new XMLElement('ul', null, array(
1730
                            'class' => 'association-links',
1731
                            'data-section-id' => $as['child_section_id'],
1732
                            'data-association-ids' => implode(', ', $entry_ids)
1733
                        ));
1734
1735
                        foreach ($entries['records'] as $e) {
1736
                            // let the field create the mark up
1737
                            $li = $field->prepareAssociationsDrawerXMLElement($e, $as);
1738
                            // add it to the unordered list
1739
                            $ul->appendChild($li);
1740
                        }
1741
1742
                        $element->appendChild($ul);
1743
                        $content->appendChild($element);
1744
                        unset($field);
1745
                    }
1746
                }
1747
            }
1748
1749
            // Process Child Associations
1750
            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...
1751
                $title = new XMLElement('h2', __('Links in') . ':', array('class' => 'association-title'));
1752
                $content->appendChild($title);
1753
1754
                foreach ($child_associations as $as) {
1755
                    // Get the related section
1756
                    $child_section = SectionManager::fetch($as['child_section_id']);
1757
1758
                    if (!($child_section instanceof Section)) {
1759
                        continue;
1760
                    }
1761
1762
                    // set global sorting for associated section
1763
                    EntryManager::setFetchSorting(
1764
                        $child_section->getSortingField(),
1765
                        $child_section->getSortingOrder()
1766
                    );
1767
1768
                    // Get the visible field instance (using the sorting field, this is more flexible than visibleColumns())
1769
                    // Get the link field instance
1770
                    $visible_field   = current($child_section->fetchVisibleColumns());
1771
                    $relation_field  = FieldManager::fetch($as['child_section_field_id']);
1772
1773
                    $entry_ids = $relation_field->findRelatedEntries($entry_id, $as['parent_section_field_id']);
1774
1775
                    $schema = $visible_field ? array($visible_field->get('element_name')) : array();
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
1776
                    $where = sprintf(' AND `e`.`id` IN (%s)', implode(', ', $entry_ids));
1777
1778
                    $entries = (!empty($entry_ids)) ? EntryManager::fetchByPage(1, $as['child_section_id'], $show_entries, $where, null, false, false, true, $schema) : array();
1779
                    $has_entries = !empty($entries) && $entries['total-entries'] != 0;
1780
1781
                    // Build the HTML of the relationship
1782
                    $element = new XMLElement('section', null, array('class' => 'association child'));
1783
                    $header = new XMLElement('header');
1784
1785
                    // Get the search value for filters and prepopulate
1786
                    $filter = '';
1787
                    $prepopulate = '';
1788
                    $entry = current(EntryManager::fetch($entry_id));
1789
                    if ($entry) {
1790
                        $search_value = $relation_field->fetchAssociatedEntrySearchValue(
1791
                            $entry->getData($as['parent_section_field_id']),
1792
                            $as['parent_section_field_id'],
1793
                            $entry_id
1794
                        );
1795
                        if (is_array($search_value)) {
1796
                            $search_value = $entry_id;
1797
                        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1798
                        $filter = '?filter[' . $relation_field->get('element_name') . ']=' . $search_value;
1799
                        $prepopulate = '?prepopulate[' . $as['child_section_field_id'] . ']=' . $search_value;
1800
                    }
1801
1802
                    // Create link with filter or prepopulate
1803
                    $link = SYMPHONY_URL . '/publish/' . $as['handle'] . '/' . $filter;
1804
                    $aname = General::sanitize($as['name']);
1805
                    if ($has_entries) {
1806
                        $aname .= ' <span>(' . $entries['total-entries'] . ')</span>';
1807
                    }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1808
                    $a = new XMLElement('a', $aname, array(
1809
                        'class' => 'association-section',
1810
                        'href' => $link,
1811
                        'title' => strip_tags($aname),
1812
                    ));
1813
1814
                    // Create new entries
1815
                    $create = new XMLElement('a', __('New'), array(
1816
                        'class' => 'button association-new',
1817
                        'href' => SYMPHONY_URL . '/publish/' . $as['handle'] . '/new/' . $prepopulate
1818
                    ));
1819
1820
                    // Display existing entries
1821
                    if ($has_entries) {
1822
                        $header->appendChild(new XMLElement('p', $a->generate()));
1823
1824
                        $ul = new XMLElement('ul', null, array(
1825
                            'class' => 'association-links',
1826
                            'data-section-id' => $as['child_section_id'],
1827
                            'data-association-ids' => implode(', ', $entry_ids)
1828
                        ));
1829
1830
                        foreach ($entries['records'] as $key => $e) {
1831
                            // let the first visible field create the mark up
1832
                            if ($visible_field) {
1833
                                $li = $visible_field->prepareAssociationsDrawerXMLElement($e, $as, $prepopulate);
1834
                            }
1835
                            // or use the system:id if no visible field exists.
1836
                            else {
1837
                                $li = Field::createAssociationsDrawerXMLElement($e->get('id'), $e, $as, $prepopulate);
1838
                            }
1839
1840
                            // add it to the unordered list
1841
                            $ul->appendChild($li);
1842
                        }
1843
1844
                        $element->appendChild($ul);
1845
1846
                        // If we are only showing 'some' of the entries, then show this on the UI
1847
                        if ($entries['total-entries'] > $show_entries) {
1848
                            $pagination = new XMLElement('li', null, array(
1849
                                'class' => 'association-more',
1850
                                'data-current-page' => '1',
1851
                                'data-total-pages' => ceil($entries['total-entries'] / $show_entries),
1852
                                'data-total-entries' => $entries['total-entries']
1853
                            ));
1854
                            $counts = new XMLElement('a', __('Show more entries'), array(
1855
                                'href' => $link
1856
                            ));
1857
1858
                            $pagination->appendChild($counts);
1859
                            $ul->appendChild($pagination);
1860
                        }
1861
1862
                        // No entries
1863
                    } else {
1864
                        $element->setAttribute('class', 'association child empty');
1865
                        $header->appendChild(new XMLElement('p', __('No links in %s', array($a->generate()))));
1866
                    }
1867
1868
                    $header->appendChild($create);
1869
                    $element->prependChild($header);
1870
                    $content->appendChild($element);
1871
                }
1872
            }
1873
1874
            // reset global sorting
1875
            EntryManager::setFetchSorting(
1876
                $sorting->field,
1877
                $sorting->direction
1878
            );
1879
        }
1880
1881
        $drawer = Widget::Drawer('section-associations', __('Show Associations'), $content);
1882
        $this->insertDrawer($drawer, $drawer_position, 'prepend');
1883
    }
1884
1885
    /**
1886
     * If this entry is being prepopulated, this function will return the prepopulated
1887
     * fields and values as a query string.
1888
     *
1889
     * @since Symphony 2.5.2
1890
     * @return string
1891
     */
1892
    public function getPrepopulateString()
1893
    {
1894
        $prepopulate_querystring = '';
1895
1896
        if (isset($_REQUEST['prepopulate']) && is_array($_REQUEST['prepopulate'])) {
1897
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1898
                // Properly decode and re-encode value for output
1899
                $value = rawurlencode(rawurldecode($value));
1900
                $prepopulate_querystring .= sprintf("prepopulate[%s]=%s&", $field_id, $value);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal prepopulate[%s]=%s& does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1901
            }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1902
            $prepopulate_querystring = trim($prepopulate_querystring, '&');
1903
        }
1904
1905
        // This is to prevent the value being interpreted as an additional GET
1906
        // parameter. eg. prepopulate[cat]=Minx&June, would come through as:
1907
        // $_GET['cat'] = Minx
1908
        // $_GET['June'] = ''
1909
        $prepopulate_querystring = preg_replace("/&amp;$/", '', $prepopulate_querystring);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /&$/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1910
1911
        return $prepopulate_querystring ? '?' . $prepopulate_querystring : null;
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
1912
    }
1913
1914
    /**
1915
     * If the entry is being prepopulated, we may want to filter other views by this entry's
1916
     * value. This function will create that filter query string.
1917
     *
1918
     * @since Symphony 2.5.2
1919
     * @return string
1920
     */
1921
    public function getFilterString()
1922
    {
1923
        $filter_querystring = '';
1924
1925
        if (isset($_REQUEST['prepopulate']) && is_array($_REQUEST['prepopulate'])) {
1926
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1927
                $handle = FieldManager::fetchHandleFromID($field_id);
1928
                // Properly decode and re-encode value for output
1929
                $value = rawurlencode(rawurldecode($value));
1930
                $filter_querystring .= sprintf('filter[%s]=%s&', $handle, $value);
1931
            }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1932
            $filter_querystring = trim($filter_querystring, '&');
1933
        }
1934
1935
        // This is to prevent the value being interpreted as an additional GET
1936
        // parameter. eg. filter[cat]=Minx&June, would come through as:
1937
        // $_GET['cat'] = Minx
1938
        // $_GET['June'] = ''
1939
        $filter_querystring = preg_replace("/&amp;$/", '', $filter_querystring);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /&$/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1940
1941
        return $filter_querystring ? '?' . $filter_querystring : null;
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
1942
    }
1943
1944
    /**
1945
     * Given $_POST values, this function will validate the current timestamp
1946
     * and set the proper error messages.
1947
     *
1948
     * @since Symphony 2.7.0
1949
     * @param int $entry_id
1950
     *  The entry id to validate
1951
     * @return boolean
1952
     *  true if the timestamp is valid
1953
     */
1954
    protected function validateTimestamp($entry_id, $checkMissing = false)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$checkMissing" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$checkMissing"; expected 0 but found 1
Loading history...
1955
    {
1956
        if (!isset($_POST['action']['ignore-timestamp'])) {
1957
            if ($checkMissing && !isset($_POST['action']['timestamp'])) {
1958
                if (isset($this->_errors) && is_array($this->_errors)) {
1959
                    $this->_errors['timestamp'] = __('The entry could not be saved due to conflicting changes');
1960
                }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1961
                return false;
1962
            } elseif (isset($_POST['action']['timestamp'])) {
1963
                $tv = new TimestampValidator('entries');
1964
                if (!$tv->check($entry_id, $_POST['action']['timestamp'])) {
1965
                    if (isset($this->_errors) && is_array($this->_errors)) {
1966
                        $this->_errors['timestamp'] = __('The entry could not be saved due to conflicting changes');
1967
                    }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1968
                    return false;
1969
                }
1970
            }
1971
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1972
        return true;
1973
    }
1974
}
1975