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.

contentBlueprintsDatasources   F
last analyzed

Complexity

Total Complexity 271

Size/Duplication

Total Lines 1666
Duplicated Lines 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 1007
c 7
b 0
f 0
dl 0
loc 1666
rs 1.572
wmc 271

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __viewNew() 0 3 1
A __viewEdit() 0 3 1
A __actionNew() 0 4 2
A injectFilters() 0 20 6
A injectAboutInformation() 0 12 6
C __viewInfo() 0 88 17
A __isValidPageString() 0 3 1
A __injectIncludedElements() 0 8 3
B __injectVarList() 0 23 7
A __appendAuthorFilter() 0 23 2
A setContext() 0 4 1
A __viewIndex() 0 6 1
A __isValidURL() 0 25 5
F __formAction() 0 362 55
A __actionEdit() 0 33 5
A __actionIndex() 0 3 1
F __form() 0 978 157

How to fix   Complexity   

Complex Class

Complex classes like contentBlueprintsDatasources often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use contentBlueprintsDatasources, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @package content
5
 */
6
/**
7
 * The Datasource Editor page allows a developer to create new datasources
8
 * from the four Symphony types, Section, Authors, Navigation and Static XML
9
 */
10
11
class contentBlueprintsDatasources extends ResourcesPage
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...
12
{
13
    public $_errors = array();
14
15
    public function __viewIndex($resource_type)
16
    {
17
        parent::__viewIndex(ResourceManager::RESOURCE_TYPE_DS);
18
19
        $this->setTitle(__('%1$s &ndash; %2$s', array(__('Data Sources'), __('Symphony'))));
20
        $this->appendSubheading(__('Data Sources'), Widget::Anchor(__('Create New'), Administration::instance()->getCurrentPageURL().'new/', __('Create a new data source'), 'create button', null, array('accesskey' => 'c')));
21
    }
22
23
    // Both the Edit and New pages need the same form
24
    public function __viewNew()
25
    {
26
        $this->__form();
27
    }
28
29
    public function __viewEdit()
30
    {
31
        $this->__form();
32
    }
33
34
    public function __form()
35
    {
36
        $formHasErrors = (is_array($this->_errors) && !empty($this->_errors));
37
38
        if ($formHasErrors) {
39
            $this->pageAlert(
40
                __('An error occurred while processing this form. See below for details.'),
41
                Alert::ERROR
42
            );
43
44
            // These alerts are only valid if the form doesn't have errors
45
        } elseif (isset($this->_context[2])) {
46
            $time = Widget::Time();
47
48
            switch ($this->_context[2]) {
49
                case 'saved':
50
                    $message = __('Data Source updated at %s.', array($time->generate()));
51
                    break;
52
                case 'created':
53
                    $message = __('Data Source created at %s.', array($time->generate()));
54
            }
55
56
            $this->pageAlert(
57
                $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...
58
                . ' <a href="' . SYMPHONY_URL . '/blueprints/datasources/new/" 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...
59
                . __('Create another?')
60
                . '</a> <a href="' . SYMPHONY_URL . '/blueprints/datasources/" accesskey="a">'
61
                . __('View all Data Sources')
62
                . '</a>',
63
                Alert::SUCCESS
64
            );
65
        }
66
67
        $providers = Symphony::ExtensionManager()->getProvidersOf(iProvider::DATASOURCE);
68
        $canonical_link = null;
69
        $isEditing = false;
70
        $about = $handle = null;
71
        $fields = array(
72
            'name' => null,
73
            'source' => null,
74
            'filter'=> null,
75
            'required_url_param' => null,
76
            'negate_url_param' => null,
77
            'param' => null,
78
        );
79
80
        if (isset($_POST['fields'])) {
81
            $fields = $_POST['fields'];
82
83
            if (
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
84
                !in_array($fields['source'], array('authors', 'navigation', 'static_xml'))
85
                && !empty($fields['filter']) && is_array($fields['filter'])
86
            ) {
87
                $filters = array();
88
                foreach ($fields['filter'] as $f) {
89
                    foreach ($f as $key => $val) {
90
                        $filters[$key] = $val;
91
                    }
92
                }
93
94
                $fields['filter'][$fields['source']] = $filters;
95
            }
96
97
            if (!isset($fields['xml_elements']) || !is_array($fields['xml_elements'])) {
98
                $fields['xml_elements'] = array();
99
            }
100
101
            if ($this->_context[0] == 'edit') {
102
                $isEditing = true;
103
            }
104
        } elseif ($this->_context[0] == 'edit') {
105
            $isEditing = true;
106
            $handle = $this->_context[1];
107
            $existing = DatasourceManager::create($handle, array(), false);
108
            $order = isset($existing->dsParamORDER) ? stripslashes($existing->dsParamORDER) : 'asc';
109
            $canonical_link = '/blueprints/datasources/edit/' . $handle . '/';
110
111
            if (!$existing->allowEditorToParse()) {
112
                redirect(SYMPHONY_URL . '/blueprints/datasources/info/' . $handle . '/');
113
            }
114
115
            $about = General::array_map_recursive('stripslashes', $existing->about());
116
            $fields['name'] = $about['name'];
117
118
            $fields['order'] = ($order == 'rand') ? 'random' : $order;
119
            $fields['param'] = isset($existing->dsParamPARAMOUTPUT) ? array_map('stripslashes', $existing->dsParamPARAMOUTPUT) : null;
120
            $fields['required_url_param'] = isset($existing->dsParamREQUIREDPARAM) ? stripslashes(trim($existing->dsParamREQUIREDPARAM)) : null;
121
            $fields['negate_url_param'] = isset($existing->dsParamNEGATEPARAM) ? stripslashes(trim($existing->dsParamNEGATEPARAM)) : null;
122
123
            if (isset($existing->dsParamINCLUDEDELEMENTS) && is_array($existing->dsParamINCLUDEDELEMENTS)) {
124
                $fields['xml_elements'] = array_map('stripslashes', $existing->dsParamINCLUDEDELEMENTS);
125
            } else {
126
                $fields['xml_elements'] = array();
127
            }
128
129
            $fields['sort'] = isset($existing->dsParamSORT) ? stripslashes($existing->dsParamSORT) : null;
130
            $fields['paginate_results'] = isset($existing->dsParamPAGINATERESULTS) ? stripslashes($existing->dsParamPAGINATERESULTS) : 'yes';
131
            $fields['page_number'] = isset($existing->dsParamSTARTPAGE) ? stripslashes($existing->dsParamSTARTPAGE) : '1';
132
            $fields['group'] = isset($existing->dsParamGROUP) ? stripslashes($existing->dsParamGROUP) : null;
0 ignored issues
show
Bug introduced by
The property dsParamGROUP does not exist on Datasource. Did you mean dsParamSORT?
Loading history...
133
            $fields['html_encode'] = isset($existing->dsParamHTMLENCODE) ? stripslashes($existing->dsParamHTMLENCODE) : 'no';
134
            $fields['associated_entry_counts'] = isset($existing->dsParamASSOCIATEDENTRYCOUNTS) ? stripslashes($existing->dsParamASSOCIATEDENTRYCOUNTS) : 'no';
135
            $fields['redirect_on_empty'] = isset($existing->dsParamREDIRECTONEMPTY) ? stripslashes($existing->dsParamREDIRECTONEMPTY) : 'no';
136
            $fields['redirect_on_forbidden'] = isset($existing->dsParamREDIRECTONFORBIDDEN) ? stripslashes($existing->dsParamREDIRECTONFORBIDDEN) : 'no';
137
            $fields['redirect_on_required'] = isset($existing->dsParamREDIRECTONREQUIRED) ? stripslashes($existing->dsParamREDIRECTONREQUIRED) : 'no';
138
139
            if (!isset($existing->dsParamFILTERS) || !is_array($existing->dsParamFILTERS)) {
140
                $existing->dsParamFILTERS = array();
0 ignored issues
show
Bug Best Practice introduced by
The property dsParamFILTERS does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
141
            }
142
143
            if (!empty($existing->dsParamFILTERS)) {
144
                $existing->dsParamFILTERS = array_map('stripslashes', $existing->dsParamFILTERS);
145
            }
146
147
            $fields['source'] = stripslashes($existing->getSource());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $existing->getSource() targeting Datasource::getSource() 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...
148
149
            $provided = false;
150
151
            if (!empty($providers)) {
152
                foreach ($providers as $providerClass => $provider) {
153
                    if ($fields['source'] == call_user_func(array($providerClass, 'getClass'))) {
154
                        $fields = array_merge($fields, $existing->settings());
0 ignored issues
show
Bug introduced by
The method settings() does not exist on Datasource. ( Ignorable by Annotation )

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

154
                        $fields = array_merge($fields, $existing->/** @scrutinizer ignore-call */ settings());

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

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

Loading history...
155
                        $provided = true;
156
                        break;
157
                    }
158
                }
159
            }
160
161
            if ($provided === false) {
162
                switch ($fields['source']) {
163
                    case 'authors':
164
                        $fields['filter']['author'] = $existing->dsParamFILTERS;
165
                        break;
166
                    case 'navigation':
167
                        $fields['filter']['navigation'] = $existing->dsParamFILTERS;
168
                        break;
169
                    case 'static_xml':
170
                        // Symphony 2.3+
171
                        if (isset($existing->dsParamSTATIC)) {
172
                            $fields['static_xml'] = stripslashes(trim($existing->dsParamSTATIC));
173
174
                            // Handle Symphony 2.2.2 to 2.3 DS's
175
                            // This is deprecated and will be removed in Symphony 3.0.0
176
                        } elseif (isset($existing->dsSTATIC)) {
177
                            $fields['static_xml'] = stripslashes(trim($existing->dsSTATIC));
178
179
                            // Handle pre Symphony 2.2.1 Static DS's
180
                            // This is deprecated and will be removed in Symphony 3.0.0
181
                        } else {
182
                            $fields['static_xml'] = trim($existing->grab());
0 ignored issues
show
Deprecated Code introduced by
The function Datasource::grab() has been deprecated: This function has been renamed to `execute` as of Symphony 2.3.1, please use `execute()` instead. This function will be removed in Symphony 3.0 ( Ignorable by Annotation )

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

182
                            $fields['static_xml'] = trim(/** @scrutinizer ignore-deprecated */ $existing->grab());

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

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

Loading history...
Bug introduced by
$existing->grab() of type XMLElement is incompatible with the type string expected by parameter $str of trim(). ( Ignorable by Annotation )

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

182
                            $fields['static_xml'] = trim(/** @scrutinizer ignore-type */ $existing->grab());
Loading history...
183
                        }
184
                        break;
185
                    default:
186
                        $fields['filter'][$fields['source']] = $existing->dsParamFILTERS;
187
                        $fields['max_records'] = stripslashes($existing->dsParamLIMIT);
188
                        break;
189
                }
190
            }
191
        } else {
192
            $fields['max_records'] = '20';
193
            $fields['page_number'] = '1';
194
            $fields['order'] = 'desc';
195
        }
196
197
        $name = null;
198
        // Handle name on edited changes, or from reading an edited datasource
199
        if (isset($about['name'])) {
200
            $name = $about['name'];
201
        } elseif (isset($fields['name'])) {
202
            $name = $fields['name'];
203
        }
204
205
        $this->setPageType('form');
206
        $this->setTitle(__(($isEditing ? '%1$s &ndash; %2$s &ndash; %3$s' : '%2$s &ndash; %3$s'), array($name, __('Data Sources'), __('Symphony'))));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
207
        if ($canonical_link) {
208
            $this->addElementToHead(new XMLElement('link', null, array(
209
                'rel' => 'canonical',
210
                'href' => SYMPHONY_URL . $canonical_link,
211
            )));
212
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
213
        $this->appendSubheading(($isEditing ? $name : __('Untitled')));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
214
        $this->insertBreadcrumbs(array(
215
            Widget::Anchor(__('Data Sources'), SYMPHONY_URL . '/blueprints/datasources/'),
216
        ));
217
218
        // Sources
219
        $sources = new XMLElement('div', null, array('class' => 'apply actions'));
220
        $div = new XMLElement('div');
221
        $label = Widget::Label(__('Source'), null, 'apply-label-left');
222
        $sources->appendChild($label);
223
        $sources->appendChild($div);
224
225
        $sections = SectionManager::fetch(null, 'ASC', 'name');
226
227
        if (!is_array($sections)) {
228
            $sections = array();
229
        }
230
231
        $field_groups = array();
232
233
        foreach ($sections as $section) {
234
            $field_groups[$section->get('id')] = array('fields' => $section->fetchFields(), 'section' => $section);
235
        }
236
237
        $options = array(
238
            array('label' => __('System'), 'data-label' => 'system', 'options' => array(
239
                    array('authors', ($fields['source'] == 'authors'), __('Authors'), null, null, array('data-context' => 'authors')),
240
                    array('navigation', ($fields['source'] == 'navigation'), __('Navigation'), null, null, array('data-context' => 'navigation')),
241
            )),
242
            array('label' => __('Custom XML'), 'data-label' => 'custom-xml', 'options' => array(
243
                    array('static_xml', ($fields['source'] == 'static_xml'), __('Static XML'), null, null, array('data-context' => 'static-xml')),
244
            )),
245
        );
246
247
        // Loop over the datasource providers
248
        if (!empty($providers)) {
249
            $p = array('label' => __('From extensions'), 'data-label' => 'from_extensions', 'options' => array());
250
251
            foreach ($providers as $providerClass => $provider) {
252
                $p['options'][] = array(
253
                    $providerClass, ($fields['source'] == $providerClass), $provider, null, null, array('data-context' => Lang::createHandle($provider))
254
                );
255
            }
256
257
            $options[] = $p;
258
        }
259
260
        // Add Sections
261
        if (is_array($sections) && !empty($sections)) {
262
            array_unshift($options, array('label' => __('Sections'), 'data-label' => 'sections', 'options' => array()));
263
264
            foreach ($sections as $s) {
265
                $options[0]['options'][] = array($s->get('id'), ($fields['source'] == $s->get('id')), General::sanitize($s->get('name')));
266
            }
267
        }
268
269
        $div->appendChild(Widget::Select('source', $options, array('id' => 'ds-context')));
270
        $this->Context->prependChild($sources);
271
272
        $this->Form->appendChild(
273
            Widget::Input('fields[source]', null, 'hidden', array('id' => 'ds-source'))
274
        );
275
276
        // Name
277
        $fieldset = new XMLElement('fieldset');
278
        $fieldset->setAttribute('class', 'settings');
279
        $fieldset->appendChild(new XMLElement('legend', __('Essentials')));
280
281
        $group = new XMLElement('div');
282
283
        $label = Widget::Label(__('Name'));
284
        $label->appendChild(Widget::Input('fields[name]', General::sanitize($fields['name'])));
285
286
        if (isset($this->_errors['name'])) {
287
            $group->appendChild(Widget::Error($label, $this->_errors['name']));
288
        } else {
289
            $group->appendChild($label);
290
        }
291
292
        $fieldset->appendChild($group);
293
        $this->Form->appendChild($fieldset);
294
295
        // Conditions
296
        $fieldset = new XMLElement('fieldset');
297
        $this->setContext($fieldset, array('sections', 'system'));
298
        $fieldset->appendChild(new XMLElement('legend', __('Execution Conditions')));
299
        $p = new XMLElement('p', __('Leaving these fields empty will always execute the data source.'));
300
        $p->setAttribute('class', 'help');
301
        $fieldset->appendChild($p);
302
303
        $group = new XMLElement('div');
304
        $group->setAttribute('class', 'two columns');
305
306
        $label = Widget::Label(__('Required Parameter'));
307
        $label->setAttribute('class', 'column ds-param');
308
        $label->appendChild(new XMLElement('i', __('Optional')));
309
        $input = Widget::Input('fields[required_url_param]', General::sanitize(trim($fields['required_url_param'])), 'text', array(
310
            'placeholder' => __('$param'),
311
            'data-search-types' => 'parameters',
312
            'data-trigger' => '$'
313
        ));
314
        $label->appendChild($input);
315
        $group->appendChild($label);
316
317
        $label = Widget::Label(__('Forbidden Parameter'));
318
        $label->setAttribute('class', 'column ds-param');
319
        $label->appendChild(new XMLElement('i', __('Optional')));
320
        $input = Widget::Input('fields[negate_url_param]', General::sanitize(trim($fields['negate_url_param'])), 'text', array(
321
            'placeholder' => __('$param'),
322
            'data-search-types' => 'parameters',
323
            'data-trigger' => '$'
324
        ));
325
        $label->appendChild($input);
326
        $group->appendChild($label);
327
328
        $fieldset->appendChild($group);
329
330
        $this->Form->appendChild($fieldset);
331
332
        $fieldset = new XMLElement('fieldset');
333
        $this->setContext($fieldset, array('sections', 'system'));
334
        $fieldset->appendChild(new XMLElement('legend', __('Error Conditions')));
335
        $p = new XMLElement('p', __('Meeting one of these conditions will cause a <code>404 Not Found</code> response.'));
336
        $p->setAttribute('class', 'help');
337
        $fieldset->appendChild($p);
338
        $group = new XMLElement('div');
339
340
        $label = Widget::Checkbox('fields[redirect_on_required]', isset($fields['redirect_on_required']) ? $fields['redirect_on_required'] : null, __('The required parameter is missing'));
341
        $group->appendChild($label);
342
343
        $label = Widget::Checkbox('fields[redirect_on_forbidden]', isset($fields['redirect_on_forbidden']) ? $fields['redirect_on_forbidden'] : null, __('The forbidden parameter is present'));
344
        $group->appendChild($label);
345
346
        $label = Widget::Checkbox('fields[redirect_on_empty]', isset($fields['redirect_on_empty']) ? $fields['redirect_on_empty'] : null, __('No results are found'));
347
        $group->appendChild($label);
348
349
        $fieldset->appendChild($group);
350
351
        $this->Form->appendChild($fieldset);
352
353
        // Filters
354
        $fieldset = new XMLElement('fieldset');
355
        $this->setContext($fieldset, array('sections', 'system'));
356
        $fieldset->appendChild(new XMLElement('legend', __('Filters')));
357
        $p = new XMLElement('p',
358
            __('Use %s syntax to filter by page parameters. A default value can be set using %s.', array(
359
                '<code>{' . __('$param') . '}</code>',
360
                '<code>{' . __('$param:default') . '}</code>'
361
            ))
362
        );
363
        $p->setAttribute('class', 'help');
364
        $fieldset->appendChild($p);
365
366
        foreach ($field_groups as $section_id => $section_data) {
367
            $div = new XMLElement('div');
368
            $div->setAttribute('class', 'contextual frame filters-duplicator');
369
            $div->setAttribute('data-context', 'section-' . $section_id);
370
            $div->setAttribute('data-interactive', 'data-interactive');
371
372
            $ol = new XMLElement('ol');
373
            $ol->setAttribute('class', 'suggestable');
374
            $ol->setAttribute('data-interactive', 'data-interactive');
375
            $ol->setAttribute('data-add', __('Add filter'));
376
            $ol->setAttribute('data-remove', __('Remove filter'));
377
378
            // Add system:id filter
379
            if (
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
380
                isset($fields['filter'][$section_id]['system:id'])
381
                || isset($fields['filter'][$section_id]['id'])
382
            ) {
383
                $id = isset($fields['filter'][$section_id]['system:id'])
384
                    ? $fields['filter'][$section_id]['system:id']
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement must be declared on a single line
Loading history...
385
                    : $fields['filter'][$section_id]['id'];
386
387
                $li = new XMLElement('li');
388
                $li->setAttribute('class', 'unique');
389
                $li->setAttribute('data-type', 'system:id');
390
                $li->appendChild(new XMLElement('header', '<h4>' . __('System ID') . '</h4>'));
391
                $label = Widget::Label(__('Value'));
392
                $input = Widget::Input('fields[filter]['.$section_id.'][system:id]', General::sanitize($id));
393
                $input->setAttribute('data-search-types', 'parameters');
394
                $input->setAttribute('data-trigger', '{$');
395
                $label->appendChild($input);
396
                $li->appendChild($label);
397
                $ol->appendChild($li);
398
            }
399
400
            $li = new XMLElement('li');
401
            $li->setAttribute('class', 'unique template');
402
            $li->setAttribute('data-type', 'system:id');
403
            $li->appendChild(new XMLElement('header', '<h4>' . __('System ID') . '</h4>'));
404
            $label = Widget::Label(__('Value'));
405
            $input = Widget::Input('fields[filter]['.$section_id.'][system:id]');
406
            $input->setAttribute('data-search-types', 'parameters');
407
            $input->setAttribute('data-trigger', '{$');
408
            $label->appendChild($input);
409
            $li->appendChild($label);
410
            $ol->appendChild($li);
411
412
            // Add system:date filter
413
            if (
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
414
                isset($fields['filter'][$section_id]['system:creation-date'])
415
                || isset($fields['filter'][$section_id]['system:date'])
416
            ) {
417
                $creation_date = isset($fields['filter'][$section_id]['system:creation-date'])
418
                    ? $fields['filter'][$section_id]['system:creation-date']
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement must be declared on a single line
Loading history...
419
                    : $fields['filter'][$section_id]['system:date'];
420
421
                $li = new XMLElement('li');
422
                $li->setAttribute('class', 'unique');
423
                $li->setAttribute('data-type', 'system:creation-date');
424
                $li->appendChild(new XMLElement('header', '<h4>' . __('System Creation Date') . '</h4>'));
425
                $label = Widget::Label(__('Value'));
426
                $input = Widget::Input('fields[filter]['.$section_id.'][system:creation-date]', General::sanitize($creation_date));
427
                $input->setAttribute('data-search-types', 'parameters');
428
                $input->setAttribute('data-trigger', '{$');
429
                $label->appendChild($input);
430
                $li->appendChild($label);
431
                $ol->appendChild($li);
432
            }
433
434
            $li = new XMLElement('li');
435
            $li->setAttribute('class', 'unique template');
436
            $li->setAttribute('data-type', 'system:creation-date');
437
            $li->appendChild(new XMLElement('header', '<h4>' . __('System Creation Date') . '</h4>'));
438
            $label = Widget::Label(__('Value'));
439
            $input = Widget::Input('fields[filter]['.$section_id.'][system:creation-date]');
440
            $input->setAttribute('data-search-types', 'parameters');
441
            $input->setAttribute('data-trigger', '{$');
442
            $label->appendChild($input);
443
            $li->appendChild($label);
444
            $ol->appendChild($li);
445
446
            if (isset($fields['filter'][$section_id]['system:modification-date'])) {
447
                $li = new XMLElement('li');
448
                $li->setAttribute('class', 'unique');
449
                $li->setAttribute('data-type', 'system:modification-date');
450
                $li->appendChild(new XMLElement('header', '<h4>' . __('System Modification Date') . '</h4>'));
451
                $label = Widget::Label(__('Value'));
452
                $input = Widget::Input('fields[filter]['.$section_id.'][system:modification-date]', General::sanitize($fields['filter'][$section_id]['system:modification-date']));
453
                $input->setAttribute('data-search-types', 'parameters');
454
                $input->setAttribute('data-trigger', '{$');
455
                $label->appendChild($input);
456
                $li->appendChild($label);
457
                $ol->appendChild($li);
458
            }
459
460
            $li = new XMLElement('li');
461
            $li->setAttribute('class', 'unique template');
462
            $li->setAttribute('data-type', 'system:modification-date');
463
            $li->appendChild(new XMLElement('header', '<h4>' . __('System Modification Date') . '</h4>'));
464
            $label = Widget::Label(__('Value'));
465
            $input = Widget::Input('fields[filter]['.$section_id.'][system:modification-date]');
466
            $input->setAttribute('data-search-types', 'parameters');
467
            $input->setAttribute('data-trigger', '{$');
468
            $label->appendChild($input);
469
            $li->appendChild($label);
470
            $ol->appendChild($li);
471
472
            if (is_array($section_data['fields']) && !empty($section_data['fields'])) {
473
                foreach ($section_data['fields'] as $field) {
474
                    if (!$field->canFilter()) {
475
                        continue;
476
                    }
477
478
                    if (isset($fields['filter'][$section_id], $fields['filter'][$section_id][$field->get('id')])) {
479
                        $wrapper = new XMLElement('li');
480
                        $wrapper->setAttribute('class', 'unique');
481
                        $wrapper->setAttribute('data-type', $field->get('element_name'));
482
                        $errors = isset($this->_errors[$field->get('id')])
483
                            ? $this->_errors[$field->get('id')]
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement must be declared on a single line
Loading history...
484
                            : array();
485
486
                        $field->displayDatasourceFilterPanel($wrapper, $fields['filter'][$section_id][$field->get('id')], $errors, $section_id);
487
                        $ol->appendChild($wrapper);
488
                    }
489
490
                    $wrapper = new XMLElement('li');
491
                    $wrapper->setAttribute('class', 'unique template');
492
                    $wrapper->setAttribute('data-type', $field->get('element_name'));
493
                    $field->displayDatasourceFilterPanel($wrapper, null, null, $section_id);
494
                    $ol->appendChild($wrapper);
495
                }
496
            }
497
498
            $div->appendChild($ol);
499
500
            $fieldset->appendChild($div);
501
        }
502
503
        $div = new XMLElement('div');
504
        $div->setAttribute('class', 'contextual frame filters-duplicator');
505
        $div->setAttribute('data-context', 'authors');
506
        $div->setAttribute('data-interactive', 'data-interactive');
507
508
        $ol = new XMLElement('ol');
509
        $ol->setAttribute('class', 'suggestable');
510
        $ol->setAttribute('data-interactive', 'data-interactive');
511
        $ol->setAttribute('data-add', __('Add filter'));
512
        $ol->setAttribute('data-remove', __('Remove filter'));
513
514
        $this->__appendAuthorFilter($ol, __('ID'), 'id', isset($fields['filter']['author']['id']) ? $fields['filter']['author']['id'] : null, !isset($fields['filter']['author']['id']));
515
        $this->__appendAuthorFilter($ol, __('Username'), 'username', isset($fields['filter']['author']['username']) ? $fields['filter']['author']['username'] : null, !isset($fields['filter']['author']['username']));
516
        $this->__appendAuthorFilter($ol, __('First Name'), 'first_name', isset($fields['filter']['author']['first_name']) ? $fields['filter']['author']['first_name'] : null, !isset($fields['filter']['author']['first_name']));
517
        $this->__appendAuthorFilter($ol, __('Last Name'), 'last_name', isset($fields['filter']['author']['last_name']) ? $fields['filter']['author']['last_name'] : null, !isset($fields['filter']['author']['last_name']));
518
        $this->__appendAuthorFilter($ol, __('Email'), 'email', isset($fields['filter']['author']['email']) ? $fields['filter']['author']['email'] : null, !isset($fields['filter']['author']['email']));
519
        $this->__appendAuthorFilter($ol, __('User Type'), 'user_type', isset($fields['filter']['author']['user_type']) ? $fields['filter']['author']['user_type'] : null, !isset($fields['filter']['author']['user_type']));
520
521
        $div->appendChild($ol);
522
523
        $fieldset->appendChild($div);
524
525
        $div = new XMLElement('div');
526
        $div->setAttribute('class', 'contextual frame filters-duplicator');
527
        $div->setAttribute('data-context', 'navigation');
528
        $div->setAttribute('data-interactive', 'data-interactive');
529
530
        $ol = new XMLElement('ol');
531
        $ol->setAttribute('class', 'suggestable');
532
        $ol->setAttribute('data-interactive', 'data-interactive');
533
        $ol->setAttribute('data-add', __('Add filter'));
534
        $ol->setAttribute('data-remove', __('Remove filter'));
535
536
        $ul = new XMLElement('ul');
537
        $ul->setAttribute('class', 'tags');
538
        $ul->setAttribute('data-interactive', 'data-interactive');
539
540
        $pages = PageManager::fetch(false, array('*'), array(), 'title ASC');
541
542
        foreach ($pages as $page) {
543
            $ul->appendChild(new XMLElement('li', preg_replace('/\/{2,}/i', '/', '/' . $page['path'] . '/' . $page['handle'])));
544
        }
545
546
        if (isset($fields['filter']['navigation']['parent'])) {
547
            $li = new XMLElement('li');
548
            $li->setAttribute('class', 'unique');
549
            $li->setAttribute('data-type', 'parent');
550
            $li->appendChild(new XMLElement('header', '<h4>' . __('Parent Page') . '</h4>'));
551
            $label = Widget::Label(__('Value'));
552
            $label->appendChild(Widget::Input('fields[filter][navigation][parent]', General::sanitize($fields['filter']['navigation']['parent'])));
553
            $li->appendChild($label);
554
            $li->appendChild($ul);
555
            $ol->appendChild($li);
556
        }
557
558
        $li = new XMLElement('li');
559
        $li->setAttribute('class', 'unique template');
560
        $li->setAttribute('data-type', 'parent');
561
        $li->appendChild(new XMLElement('header', '<h4>' . __('Parent Page') . '</h4>'));
562
        $label = Widget::Label(__('Value'));
563
        $label->appendChild(Widget::Input('fields[filter][navigation][parent]'));
564
        $li->appendChild($label);
565
        $li->appendChild($ul);
566
        $ol->appendChild($li);
567
568
        $ul = new XMLElement('ul');
569
        $ul->setAttribute('class', 'tags');
570
        $ul->setAttribute('data-interactive', 'data-interactive');
571
572
        if ($types = PageManager::fetchAvailablePageTypes()) {
573
            foreach ($types as $type) {
574
                $ul->appendChild(new XMLElement('li', General::sanitize($type)));
575
            }
576
        }
577
578
        if (isset($fields['filter']['navigation']['type'])) {
579
            $li = new XMLElement('li');
580
            $li->setAttribute('class', 'unique');
581
            $li->setAttribute('data-type', 'type');
582
            $li->appendChild(new XMLElement('header', '<h4>' . __('Page Type') . '</h4>'));
583
            $label = Widget::Label(__('Value'));
584
            $label->appendChild(Widget::Input('fields[filter][navigation][type]', General::sanitize($fields['filter']['navigation']['type'])));
585
            $li->appendChild($label);
586
            $li->appendChild($ul);
587
            $ol->appendChild($li);
588
        }
589
590
        $li = new XMLElement('li');
591
        $li->setAttribute('class', 'unique template');
592
        $li->appendChild(new XMLElement('header', '<h4>' . __('Page Type') . '</h4>'));
593
        $li->setAttribute('data-type', 'type');
594
        $label = Widget::Label(__('Value'));
595
        $label->appendChild(Widget::Input('fields[filter][navigation][type]'));
596
        $li->appendChild($label);
597
        $li->appendChild($ul);
598
        $ol->appendChild($li);
599
600
        $div->appendChild($ol);
601
602
        $fieldset->appendChild($div);
603
        $this->Form->appendChild($fieldset);
604
605
        // Sorting
606
        $fieldset = new XMLElement('fieldset');
607
        $this->setContext($fieldset, array('sections', 'system'));
608
        $fieldset->appendChild(new XMLElement('legend', __('Sorting')));
609
610
        $p = new XMLElement('p',
611
            __('Use %s syntax to order by page parameters.', array(
612
                '<code>{' . __('$param') . '}</code>'
613
            ))
614
        );
615
        $p->setAttribute('class', 'help');
616
        $fieldset->appendChild($p);
617
618
        $div = new XMLElement('div');
619
620
        $label = Widget::Label(__('Sort By'));
621
622
        $options = array(
623
            array('label' => __('Authors'), 'data-label' => 'authors', 'options' => array(
624
                    array('id', ($fields['source'] == 'authors' && $fields['sort'] == 'id'), __('Author ID')),
625
                    array('username', ($fields['source'] == 'authors' && $fields['sort'] == 'username'), __('Username')),
626
                    array('first-name', ($fields['source'] == 'authors' && $fields['sort'] == 'first-name'), __('First Name')),
627
                    array('last-name', ($fields['source'] == 'authors' && $fields['sort'] == 'last-name'), __('Last Name')),
628
                    array('email', ($fields['source'] == 'authors' && $fields['sort'] == 'email'), __('Email')),
629
                    array('status', ($fields['source'] == 'authors' && $fields['sort'] == 'status'), __('Status')),
630
                )
631
            ),
632
633
            array('label' => __('Navigation'), 'data-label' => 'navigation', 'options' => array(
634
                    array('id', ($fields['source'] == 'navigation' && $fields['sort'] == 'id'), __('Page ID')),
635
                    array('handle', ($fields['source'] == 'navigation' && $fields['sort'] == 'handle'), __('Handle')),
636
                    array('sortorder', ($fields['source'] == 'navigation' && $fields['sort'] == 'sortorder'), __('Sort Order')),
637
                )
638
            ),
639
        );
640
641
        foreach ($field_groups as $section_id => $section_data) {
642
            $optgroup = array('label' => General::sanitize($section_data['section']->get('name')), 'data-label' => 'section-' . $section_data['section']->get('id'), 'options' => array(
643
                array('system:id', ($fields['source'] == $section_id && $fields['sort'] == 'system:id'), __('System ID')),
644
                array('system:creation-date', ($fields['source'] == $section_id && ($fields['sort'] == 'system:creation-date' || $fields['sort'] == 'system:date')), __('System Creation Date')),
645
                array('system:modification-date', ($fields['source'] == $section_id && $fields['sort'] == 'system:modification-date'), __('System Modification Date')),
646
            ));
647
648
            if (is_array($section_data['fields']) && !empty($section_data['fields'])) {
649
                foreach ($section_data['fields'] as $input) {
650
                    if (!$input->isSortable()) {
651
                        continue;
652
                    }
653
654
                    $optgroup['options'][] = array(
655
                        $input->get('element_name'),
656
                        ($fields['source'] == $section_id && $input->get('element_name') == $fields['sort']),
657
                        $input->get('label')
658
                    );
659
                }
660
            }
661
662
            $options[] = $optgroup;
663
        }
664
665
        $label->appendChild(Widget::Select('fields[sort]', $options));
666
        $div->appendChild($label);
667
668
        $label = Widget::Label(__('Sort Order'));
669
        $label->setAttribute('class', 'ds-param');
670
671
        $input = Widget::Input('fields[order]', General::sanitize(trim($fields['order'])), 'text', array(
672
            'placeholder' => __('{$param}'),
673
            'data-search-types' => 'parameters',
674
            'data-trigger' => '{$'
675
        ));
676
        $label->appendChild($input);
677
        $div->appendChild($label);
678
679
        $orders = new XMLElement('ul');
680
        $orders->setAttribute('class', 'tags singular');
681
        $orders->setAttribute('data-interactive', 'data-interactive');
682
        $orders->appendChild(new XMLElement('li', 'asc'));
683
        $orders->appendChild(new XMLElement('li', 'desc'));
684
        $orders->appendChild(new XMLElement('li', 'random'));
685
        $div->appendChild($orders);
686
687
        $fieldset->appendChild($div);
688
        $this->Form->appendChild($fieldset);
689
690
        // Grouping
691
        $fieldset = new XMLElement('fieldset');
692
        $this->setContext($fieldset, array('sections', 'authors'));
693
        $fieldset->appendChild(new XMLElement('legend', __('Grouping')));
694
695
        $label = Widget::Label(__('Group By'));
696
        $options = array(
697
            array('', null, __('None')),
698
        );
699
700
        foreach ($field_groups as $section_id => $section_data) {
701
            $optgroup = array('label' => $section_data['section']->get('name'), 'data-label' => 'section-' . $section_data['section']->get('id'), 'options' => array());
702
703
            if (is_array($section_data['fields']) && !empty($section_data['fields'])) {
704
                foreach ($section_data['fields'] as $input) {
705
                    if (!$input->allowDatasourceOutputGrouping()) {
706
                        continue;
707
                    }
708
709
                    $optgroup['options'][] = array($input->get('id'), ($fields['source'] == $section_id && $fields['group'] == $input->get('id')), $input->get('label'));
710
                }
711
            }
712
713
            $options[] = $optgroup;
714
        }
715
716
        $label->appendChild(Widget::Select('fields[group]', $options));
717
        $fieldset->appendChild($label);
718
719
        $this->Form->appendChild($fieldset);
720
721
        // Pagination
722
        $fieldset = new XMLElement('fieldset');
723
        $this->setContext($fieldset, array('sections'));
724
        $fieldset->appendChild(new XMLElement('legend', __('Pagination')));
725
726
        $p = new XMLElement('p',
727
            __('Use %s syntax to limit by page parameters.', array(
728
                '<code>{' . __('$param') . '}</code>'
729
            ))
730
        );
731
        $p->setAttribute('class', 'help');
732
        $fieldset->appendChild($p);
733
734
        $group = new XMLElement('div');
735
        $group->setAttribute('class', 'two columns pagination');
736
737
        $label = Widget::Label(__('Entries per Page'));
738
        $label->setAttribute('class', 'column ds-param');
739
        $input = Widget::Input('fields[max_records]', isset($fields['max_records']) ? General::sanitize(trim($fields['max_records'])) : '10', 'text', array(
740
            'placeholder' => __('{$param}'),
741
            'data-search-types' => 'parameters',
742
            'data-trigger' => '{$'
743
        ));
744
        $label->appendChild($input);
745
        if (isset($this->_errors['max_records'])) {
746
            $group->appendChild(Widget::Error($label, $this->_errors['max_records']));
747
        } else {
748
            $group->appendChild($label);
749
        }
750
751
        $label = Widget::Label(__('Page Number'));
752
        $label->setAttribute('class', 'column ds-param');
753
        $input = Widget::Input('fields[page_number]', General::sanitize(trim($fields['page_number'])), 'text', array(
754
            'placeholder' => __('{$param}'),
755
            'data-search-types' => 'parameters',
756
            'data-trigger' => '{$'
757
        ));
758
        $label->appendChild($input);
759
        if (isset($this->_errors['page_number'])) {
760
            $group->appendChild(Widget::Error($label, $this->_errors['page_number']));
761
        } else {
762
            $group->appendChild($label);
763
        }
764
765
        $fieldset->appendChild($group);
766
767
        $label = Widget::Checkbox('fields[paginate_results]', isset($fields['paginate_results']) ? $fields['paginate_results'] : null, __('Enable pagination'));
768
        $fieldset->appendChild($label);
769
        $this->Form->appendChild($fieldset);
770
771
        // Content
772
        $fieldset = new XMLElement('fieldset');
773
        $this->setContext($fieldset, array('sections', 'authors'));
774
        $fieldset->appendChild(new XMLElement('legend', __('Content')));
775
776
        // XML
777
        $group = new XMLElement('div', null, array('class' => 'two columns'));
778
779
        $label = Widget::Label(__('Included Elements'));
780
        $label->setAttribute('class', 'column');
781
782
        $options = array(
783
            array('label' => __('Authors'), 'data-label' => 'authors', 'options' => array(
784
                    array('username', ($fields['source'] == 'authors' && in_array('username', $fields['xml_elements'])), 'username'),
785
                    array('name', ($fields['source'] == 'authors' && in_array('name', $fields['xml_elements'])), 'name'),
786
                    array('email', ($fields['source'] == 'authors' && in_array('email', $fields['xml_elements'])), 'email'),
787
                    array('author-token', ($fields['source'] == 'authors' && in_array('author-token', $fields['xml_elements'])), 'author-token'),
788
                    array('default-area', ($fields['source'] == 'authors' && in_array('default-area', $fields['xml_elements'])), 'default-area'),
789
            )),
790
        );
791
792
        foreach ($field_groups as $section_id => $section_data) {
793
            $optgroup = array(
794
                'label' => General::sanitize($section_data['section']->get('name')),
795
                'data-label' => 'section-' . $section_data['section']->get('id'),
796
                'options' => array(
797
                    array(
798
                        'system:pagination',
799
                        ($fields['source'] == $section_id && in_array('system:pagination', $fields['xml_elements'])),
800
                        'system: pagination'
801
                    ),
802
                    array(
803
                        'system:date',
804
                        ($fields['source'] == $section_id && in_array('system:date', $fields['xml_elements'])),
805
                        'system: date'
806
                    )
807
                )
808
            );
809
810
            if (is_array($section_data['fields']) && !empty($section_data['fields'])) {
811
                foreach ($section_data['fields'] as $field) {
812
                    $elements = $field->fetchIncludableElements();
813
814
                    if (is_array($elements) && !empty($elements)) {
815
                        foreach ($elements as $name) {
816
                            $selected = false;
817
818
                            if ($fields['source'] == $section_id && in_array($name, $fields['xml_elements'])) {
819
                                $selected = true;
820
                            }
821
822
                            $optgroup['options'][] = array($name, $selected, $name);
823
                        }
824
                    }
825
                }
826
            }
827
828
            $options[] = $optgroup;
829
        }
830
831
        $label->appendChild(Widget::Select('fields[xml_elements][]', $options, array('multiple' => 'multiple')));
832
        $group->appendChild($label);
833
834
        // Support multiple parameters
835
        if (!isset($fields['param'])) {
836
            $fields['param'] = array();
837
        } elseif (!is_array($fields['param'])) {
838
            $fields['param'] = array($fields['param']);
839
        }
840
841
        $label = Widget::Label(__('Parameters'));
842
        $label->setAttribute('class', 'column');
843
        $prefix = '$ds-' . (isset($this->_context[1]) ? Lang::createHandle($fields['name']) : __('untitled')) . '.';
844
845
        $options = array(
846
            array('label' => __('Authors'), 'data-label' => 'authors', 'options' => array())
847
        );
848
849
        foreach (array('id', 'username', 'name', 'email', 'user_type') as $p) {
850
            $options[0]['options'][] = array(
851
                $p,
852
                ($fields['source'] == 'authors' && in_array($p, $fields['param'])),
853
                $prefix . $p,
854
                null,
855
                null,
856
                array(
857
                    'data-handle' => $p
858
                )
859
            );
860
        }
861
862
        foreach ($field_groups as $section_id => $section_data) {
863
            $optgroup = array('label' => $section_data['section']->get('name'), 'data-label' => 'section-' . $section_data['section']->get('id'), 'options' => array());
864
865
            foreach (array('id', 'creation-date', 'modification-date', 'author') as $p) {
866
                $option = array(
867
                    'system:' . $p,
868
                    ($fields['source'] == $section_id && in_array('system:' . $p, $fields['param'])),
869
                    $prefix . 'system-' . $p,
870
                    null,
871
                    null,
872
                    array(
873
                        'data-handle' => 'system-' . $p
874
                    )
875
                );
876
877
                // Handle 'system:date' as an output paramater (backwards compatibility)
878
                if ($p === 'creation-date') {
879
                    if ($fields['source'] == $section_id && in_array('system:date', $fields['param'])) {
880
                        $option[1] = true;
881
                    }
882
                }
883
884
                $optgroup['options'][] = $option;
885
            }
886
887
            if (is_array($section_data['fields']) && !empty($section_data['fields'])) {
888
                foreach ($section_data['fields'] as $input) {
889
                    if (!$input->allowDatasourceParamOutput()) {
890
                        continue;
891
                    }
892
893
                    $optgroup['options'][] = array(
894
                        $input->get('element_name'),
895
                        ($fields['source'] == $section_id && in_array($input->get('element_name'), $fields['param'])),
896
                        $prefix . $input->get('element_name'),
897
                        null,
898
                        null,
899
                        array(
900
                            'data-handle' => $input->get('element_name')
901
                        )
902
                    );
903
                }
904
            }
905
906
            $options[] = $optgroup;
907
        }
908
909
        $label->appendChild(Widget::Select('fields[param][]', $options, array('multiple' => 'multiple')));
910
        $group->appendChild($label);
911
912
        $fieldset->appendChild($group);
913
914
        // Associations
915
        $label = Widget::Checkbox('fields[associated_entry_counts]', isset($fields['associated_entry_counts']) ? $fields['associated_entry_counts'] : null, __('Include a count of entries in associated sections'));
916
        $this->setContext($label, array('sections'));
917
        $fieldset->appendChild($label);
918
919
        // Encoding
920
        $label = Widget::Checkbox('fields[html_encode]', isset($fields['html_encode']) ? $fields['html_encode'] : null, __('HTML-encode text'));
921
        $this->setContext($label, array('sections'));
922
        $fieldset->appendChild($label);
923
924
        $this->Form->appendChild($fieldset);
925
926
        // Static XML
927
        if (!isset($fields['static_xml'])) {
928
            $fields['static_xml'] = null;
929
        }
930
931
        $fieldset = new XMLElement('fieldset');
932
        $this->setContext($fieldset, array('static-xml'));
933
        $fieldset->appendChild(new XMLElement('legend', __('Static XML')));
934
        $p = new XMLElement('p', __('Enter valid XML, exclude XML declaration'));
935
        $p->setAttribute('class', 'help');
936
        $fieldset->appendChild($p);
937
938
        $label = Widget::Label();
939
        $static_xml = htmlspecialchars(
940
            $fields['static_xml'],
941
            ENT_XML1|ENT_COMPAT,
942
            'UTF-8'
943
        );
944
        $label->appendChild(Widget::Textarea('fields[static_xml]', 12, 50, $static_xml, array('class' => 'code', 'placeholder' => '<static>content</static>')));
945
946
        if (isset($this->_errors['static_xml'])) {
947
            $fieldset->appendChild(Widget::Error($label, $this->_errors['static_xml']));
948
        } else {
949
            $fieldset->appendChild($label);
950
        }
951
952
        $this->Form->appendChild($fieldset);
953
954
        // Connections
955
        $fieldset = new XMLElement('fieldset');
956
        $fieldset->setAttribute('class', 'settings');
957
        $fieldset->appendChild(new XMLElement('legend', __('Attach to Pages')));
958
        $p = new XMLElement('p', __('The data will only be available on the selected pages.'));
959
        $p->setAttribute('class', 'help');
960
        $fieldset->appendChild($p);
961
962
        $div = new XMLElement('div');
963
        $label = Widget::Label(__('Pages'));
964
965
        $pages = PageManager::fetch();
966
        $ds_handle = str_replace('-', '_', Lang::createHandle($fields['name']));
967
        $connections = ResourceManager::getAttachedPages(ResourceManager::RESOURCE_TYPE_DS, $ds_handle);
968
        $selected = array();
969
970
        foreach ($connections as $connection) {
971
            $selected[] = $connection['id'];
972
        }
973
974
        $options = array();
975
976
        foreach ($pages as $page) {
977
            $options[] = array(
978
                $page['id'],
979
                in_array($page['id'], $selected),
980
                General::sanitize(PageManager::resolvePageTitle($page['id']))
981
            );
982
        }
983
984
        $label->appendChild(Widget::Select('fields[connections][]', $options, array('multiple' => 'multiple')));
985
        $div->appendChild($label);
986
987
        $fieldset->appendChild($div);
988
        $this->Form->appendChild($fieldset);
989
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
990
991
        // Call the provided datasources to let them inject their filters
992
        // @todo Ideally when a new Datasource is chosen an AJAX request will fire
993
        // to get the HTML from the extension. This is hardcoded for now into
994
        // creating a 'big' page and then hiding the fields with JS
995
        if (!empty($providers)) {
996
            foreach ($providers as $providerClass => $provider) {
997
                call_user_func_array(array($providerClass, 'buildEditor'), array($this->Form, &$this->_errors, $fields, $handle));
998
            }
999
        }
1000
1001
        $div = new XMLElement('div');
1002
        $div->setAttribute('class', 'actions');
1003
        $div->appendChild(Widget::Input('action[save]', ($isEditing ? __('Save Changes') : __('Create Data Source')), 'submit', array('accesskey' => 's')));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
1004
1005
        if ($isEditing) {
1006
            $button = new XMLElement('button', __('Delete'));
1007
            $button->setAttributeArray(array('name' => 'action[delete]', 'class' => 'button confirm delete', 'title' => __('Delete this data source'), 'type' => 'submit', 'accesskey' => 'd', 'data-message' => __('Are you sure you want to delete this data source?')));
1008
            $div->appendChild($button);
1009
        }
1010
1011
        $this->Form->appendChild($div);
1012
    }
1013
1014
    public function __viewInfo()
1015
    {
1016
        $this->setPageType('form');
1017
1018
        $datasource = DatasourceManager::create($this->_context[1], array(), false);
1019
        $about = General::array_map_recursive('stripslashes', $datasource->about());
1020
1021
        $this->setTitle(__('%1$s &ndash; %2$s &ndash; %3$s', array($about['name'], __('Data Source'), __('Symphony'))));
1022
        $this->appendSubheading((($this->_context[0] == 'info') ? $about['name'] : __('Untitled')));
1023
        $this->insertBreadcrumbs(array(
1024
            Widget::Anchor(__('Data Sources'), SYMPHONY_URL . '/blueprints/datasources/'),
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...
1025
        ));
1026
        $this->Form->setAttribute('id', 'controller');
1027
1028
        $link = $about['author']['name'];
1029
1030
        if (isset($about['author']['website'])) {
1031
            $link = Widget::Anchor($about['author']['name'], General::validateURL($about['author']['website']));
1032
        } elseif (isset($about['author']['email'])) {
1033
            $link = Widget::Anchor($about['author']['name'], 'mailto:' . $about['author']['email']);
1034
        }
1035
1036
        foreach ($about as $key => $value) {
1037
            $fieldset = null;
1038
1039
            switch ($key) {
1040
                case 'author':
1041
                    if ($link) {
1042
                        $fieldset = new XMLElement('fieldset');
1043
                        $fieldset->appendChild(new XMLElement('legend', __('Author')));
1044
                        $fieldset->appendChild(new XMLElement('p', $link->generate(false)));
1045
                    }
1046
                    break;
1047
                case 'version':
1048
                    $fieldset = new XMLElement('fieldset');
1049
                    $fieldset->appendChild(new XMLElement('legend', __('Version')));
1050
                    $release_date = array_key_exists('release-date', $about) ? $about['release-date'] : filemtime(DatasourceManager::__getDriverPath($this->_context[1]));
1051
1052
                    if (preg_match('/^\d+(\.\d+)*$/', $value)) {
1053
                        $fieldset->appendChild(new XMLElement('p', __('%1$s released on %2$s', array($value, DateTimeObj::format($release_date, __SYM_DATE_FORMAT__)))));
0 ignored issues
show
Bug introduced by
The constant __SYM_DATE_FORMAT__ was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1054
                    } else {
1055
                        $fieldset->appendChild(new XMLElement('p', __('Created by %1$s at %2$s', array($value, DateTimeObj::format($release_date, __SYM_DATE_FORMAT__)))));
1056
                    }
1057
                    break;
1058
                case 'description':
1059
                    $fieldset = new XMLElement('fieldset');
1060
                    $fieldset->appendChild(new XMLElement('legend', __('Description')));
1061
                    $fieldset->appendChild((is_object($about['description']) ? $about['description'] : new XMLElement('p', $about['description'])));
1062
                    break;
1063
                case 'example':
1064
                    if (is_callable(array($datasource, 'example'))) {
1065
                        $fieldset = new XMLElement('fieldset');
1066
                        $fieldset->appendChild(new XMLElement('legend', __('Example XML')));
1067
1068
                        $example = $datasource->example();
0 ignored issues
show
Bug introduced by
The method example() does not exist on Datasource. ( Ignorable by Annotation )

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

1068
                        /** @scrutinizer ignore-call */ 
1069
                        $example = $datasource->example();

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

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

Loading history...
1069
1070
                        if (is_object($example)) {
1071
                            $fieldset->appendChild($example);
1072
                        } else {
1073
                            $p = new XMLElement('p');
1074
                            $p->appendChild(new XMLElement('pre', '<code>' . str_replace('<', '&lt;', $example) . '</code>'));
1075
                            $fieldset->appendChild($p);
1076
                        }
1077
                    }
1078
                    break;
1079
            }
1080
1081
            if ($fieldset) {
1082
                $fieldset->setAttribute('class', 'settings');
1083
                $this->Form->appendChild($fieldset);
1084
            }
1085
        }
1086
1087
        // Display source
1088
        $file = DatasourceManager::__getClassPath($this->_context[1]) . '/data.' . $this->_context[1] . '.php';
1089
1090
        if (file_exists($file)) {
1091
            $fieldset = new XMLElement('fieldset');
1092
            $fieldset->setAttribute('class', 'settings');
1093
            $fieldset->appendChild(new XMLElement('legend', __('Source')));
1094
1095
            $source = file_get_contents($file);
1096
            $code = new XMLElement('code', htmlspecialchars($source));
1097
            $pre = new XMLElement('pre');
1098
            $pre->appendChild($code);
1099
1100
            $fieldset->appendChild($pre);
1101
            $this->Form->appendChild($fieldset);
1102
        }
1103
    }
1104
1105
    public function __actionIndex($resource_type)
1106
    {
1107
        return parent::__actionIndex(ResourceManager::RESOURCE_TYPE_DS);
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::__actionIndex(Re...ager::RESOURCE_TYPE_DS) targeting ResourcesPage::__actionIndex() 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...
1108
    }
1109
1110
    public function __actionEdit()
1111
    {
1112
        if (array_key_exists('save', $_POST['action'])) {
1113
            return $this->__formAction();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->__formAction() targeting contentBlueprintsDatasources::__formAction() 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...
1114
        } elseif (array_key_exists('delete', $_POST['action'])) {
1115
            /**
1116
             * Prior to deleting the Datasource file. Target file path is provided.
1117
             *
1118
             * @delegate DatasourcePreDelete
1119
             * @since Symphony 2.2
1120
             * @param string $context
1121
             * '/blueprints/datasources/'
1122
             * @param string $file
1123
             *  The path to the Datasource file
1124
             */
1125
            Symphony::ExtensionManager()->notifyMembers('DatasourcePreDelete', '/blueprints/datasources/', array(
1126
                'file' => DATASOURCES . "/data." . $this->_context[1] . ".php")
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /data. 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...
Coding Style Comprehensibility introduced by
The string literal .php 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...
Bug introduced by
The constant DATASOURCES was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1127
            );
1128
1129
            if (!General::deleteFile(DATASOURCES . '/data.' . $this->_context[1] . '.php')) {
1130
                $this->pageAlert(
1131
                    __('Failed to delete %s.', array('<code>' . $this->_context[1] . '</code>'))
1132
                    . ' ' . __('Please check permissions on %s.', array('<code>/workspace/data-sources</code>')),
1133
                    Alert::ERROR
1134
                );
1135
            } else {
1136
                $pages = ResourceManager::getAttachedPages(ResourceManager::RESOURCE_TYPE_DS, $this->_context[1]);
1137
1138
                foreach ($pages as $page) {
1139
                    ResourceManager::detach(ResourceManager::RESOURCE_TYPE_DS, $this->_context[1], $page['id']);
1140
                }
1141
1142
                redirect(SYMPHONY_URL . '/blueprints/datasources/');
1143
            }
1144
        }
1145
    }
1146
1147
    public function __actionNew()
1148
    {
1149
        if (array_key_exists('save', $_POST['action'])) {
1150
            return $this->__formAction();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->__formAction() targeting contentBlueprintsDatasources::__formAction() 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...
1151
        }
1152
    }
1153
1154
    public function __formAction()
1155
    {
1156
        $fields = $_POST['fields'];
1157
        $this->_errors = array();
1158
        $providers = Symphony::ExtensionManager()->getProvidersOf(iProvider::DATASOURCE);
1159
        $providerClass = null;
1160
1161
        if (trim($fields['name']) == '') {
1162
            $this->_errors['name'] = __('This is a required field');
1163
        } elseif (strpos($fields['name'], '\\') !== false) {
1164
            $this->_errors['name'] = __('This field contains invalid characters') . ' (\\)';
1165
        } elseif (!preg_match('/^[a-z]/i', $fields['name'])) {
1166
            $this->_errors['name'] = __('The name of the data source must begin with a letter.');
1167
        }
1168
1169
        if ($fields['source'] == 'static_xml') {
1170
            if (trim($fields['static_xml']) == '') {
1171
                $this->_errors['static_xml'] = __('This is a required field');
1172
            } else {
1173
                $xml_errors = null;
1174
1175
                General::validateXML($fields['static_xml'], $xml_errors, false, new XsltProcess());
1176
1177
                if (!empty($xml_errors)) {
1178
                    $this->_errors['static_xml'] = __('XML is invalid.');
1179
                }
1180
            }
1181
        } elseif (is_numeric($fields['source'])) {
1182
            if (strlen(trim($fields['max_records'])) == 0 || (is_numeric($fields['max_records']) && $fields['max_records'] < 1)) {
1183
                if ($fields['paginate_results'] === 'yes') {
1184
                    $this->_errors['max_records'] = __('A result limit must be set');
1185
                }
1186
            } elseif (!self::__isValidPageString($fields['max_records'])) {
1187
                $this->_errors['max_records'] = __('Must be a valid number or parameter');
1188
            }
1189
1190
            if (strlen(trim($fields['page_number'])) == 0 || (is_numeric($fields['page_number']) && $fields['page_number'] < 1)) {
1191
                if ($fields['paginate_results'] === 'yes') {
1192
                    $this->_errors['page_number'] = __('A page number must be set');
1193
                }
1194
            } elseif (!self::__isValidPageString($fields['page_number'])) {
1195
                $this->_errors['page_number'] = __('Must be a valid number or parameter');
1196
            }
1197
1198
            // See if a Provided Datasource is saved
1199
        } elseif (!empty($providers)) {
1200
            foreach ($providers as $providerClass => $provider) {
1201
                if ($fields['source'] == call_user_func(array($providerClass, 'getSource'))) {
1202
                    call_user_func_array(array($providerClass, 'validate'), array(&$fields, &$this->_errors));
1203
                    break;
1204
                }
1205
1206
                unset($providerClass);
1207
            }
1208
        }
1209
1210
        $classname = Lang::createHandle($fields['name'], 255, '_', false, true, array('@^[^a-z\d]+@i' => '', '/[^\w\-\.]/i' => ''));
1211
        // XML wants dashes
1212
        $rootelement = str_replace('_', '-', $classname);
1213
        // PHP wants underscores
1214
        $classname = str_replace('-', '_', $classname);
1215
1216
        // Check to make sure the classname is not empty after handlisation.
1217
        if (empty($classname) && !isset($this->_errors['name'])) {
1218
            $this->_errors['name'] = __('Please ensure name contains at least one Latin-based character.', array($classname));
1219
        }
1220
1221
        $file = DATASOURCES . '/data.' . $classname . '.php';
0 ignored issues
show
Bug introduced by
The constant DATASOURCES was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1222
1223
        $isDuplicate = false;
1224
        $queueForDeletion = null;
1225
1226
        if ($this->_context[0] == 'new' && is_file($file)) {
1227
            $isDuplicate = true;
1228
        } elseif ($this->_context[0] == 'edit') {
1229
            $existing_handle = $this->_context[1];
1230
1231
            if ($classname != $existing_handle && is_file($file)) {
1232
                $isDuplicate = true;
1233
            } elseif ($classname != $existing_handle) {
1234
                $queueForDeletion = DATASOURCES . '/data.' . $existing_handle . '.php';
1235
            }
1236
        }
1237
1238
        // Duplicate
1239
        if ($isDuplicate) {
1240
            $this->_errors['name'] = __('A Data source with the name %s already exists', array('<code>' . $classname . '</code>'));
1241
        }
1242
1243
        if (empty($this->_errors)) {
1244
            $filters = array();
1245
            $elements = null;
1246
            $source = $fields['source'];
1247
            $params = array(
1248
                'rootelement' => $rootelement
1249
            );
1250
1251
            $about = array(
1252
                'name' => $fields['name'],
1253
                'version' => 'Symphony ' . Symphony::Configuration()->get('version', 'symphony'),
1254
                'release date' => DateTimeObj::getGMT('c'),
1255
                'author name' => Symphony::Author()->getFullName(),
1256
                'author website' => URL,
0 ignored issues
show
Bug introduced by
The constant URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1257
                'author email' => Symphony::Author()->get('email')
1258
            );
1259
1260
            // If there is a provider, get their template
1261
            if ($providerClass) {
1262
                $dsShell = file_get_contents(call_user_func(array($providerClass, 'getTemplate')));
1263
            } else {
1264
                $dsShell = file_get_contents($this->getTemplate('blueprints.datasource'));
0 ignored issues
show
Bug introduced by
It seems like $this->getTemplate('blueprints.datasource') can also be of type false; however, parameter $filename of file_get_contents() 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

1264
                $dsShell = file_get_contents(/** @scrutinizer ignore-type */ $this->getTemplate('blueprints.datasource'));
Loading history...
1265
            }
1266
1267
            // Author metadata
1268
            self::injectAboutInformation($dsShell, $about);
1269
1270
            // Do dependencies, the template file must have <!-- CLASS NAME -->
1271
            $dsShell = str_replace('<!-- CLASS NAME -->', $classname, $dsShell);
1272
1273
            // If there is a provider, let them do the prepartion work
1274
            if ($providerClass) {
1275
                $dsShell = call_user_func(array($providerClass, 'prepare'), $fields, $params, $dsShell);
1276
            } else {
1277
                switch ($source) {
1278
                    case 'authors':
1279
                        $extends = 'AuthorDatasource';
1280
                        if (isset($fields['filter']['author'])) {
1281
                            $filters = $fields['filter']['author'];
1282
                        }
1283
1284
                        $elements = $fields['xml_elements'];
1285
1286
                        $params['order'] = $fields['order'];
1287
                        $params['redirectonempty'] = $fields['redirect_on_empty'];
1288
                        $params['redirectonforbidden'] = $fields['redirect_on_forbidden'];
1289
                        $params['redirectonrequired'] = $fields['redirect_on_required'];
1290
                        $params['requiredparam'] = trim($fields['required_url_param']);
1291
                        $params['negateparam'] = trim($fields['negate_url_param']);
1292
                        $params['paramoutput'] = $fields['param'];
1293
                        $params['sort'] = $fields['sort'];
1294
1295
                        break;
1296
                    case 'navigation':
1297
                        $extends = 'NavigationDatasource';
1298
                        if (isset($fields['filter']['navigation'])) {
1299
                            $filters = $fields['filter']['navigation'];
1300
                        }
1301
1302
                        $params['order'] = $fields['order'];
1303
                        $params['redirectonempty'] = $fields['redirect_on_empty'];
1304
                        $params['redirectonforbidden'] = $fields['redirect_on_forbidden'];
1305
                        $params['redirectonrequired'] = $fields['redirect_on_required'];
1306
                        $params['requiredparam'] = trim($fields['required_url_param']);
1307
                        $params['negateparam'] = trim($fields['negate_url_param']);
1308
                        $params['sort'] = $fields['sort'];
1309
1310
                        break;
1311
                    case 'static_xml':
1312
                        $extends = 'StaticXMLDatasource';
1313
                        $fields['static_xml'] = trim($fields['static_xml']);
1314
1315
                        if (preg_match('/^<\?xml/i', $fields['static_xml']) == true) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('/^<\?xml/i', $fields['static_xml']) of type integer to the boolean true. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
1316
                            // Need to remove any XML declaration
1317
                            $fields['static_xml'] = preg_replace('/^<\?xml[^>]+>/i', null, $fields['static_xml']);
1318
                        }
1319
1320
                        $params['static'] = sprintf(
1321
                            '%s',
1322
                            trim($fields['static_xml'])
1323
                        );
1324
                        break;
1325
                    default:
1326
                        $extends = 'SectionDatasource';
1327
                        $elements = $fields['xml_elements'];
1328
1329
                        if (is_array($fields['filter']) && !empty($fields['filter'])) {
1330
                            $filters = array();
1331
1332
                            foreach ($fields['filter'] as $f) {
1333
                                foreach ($f as $key => $val) {
1334
                                    $filters[$key] = $val;
1335
                                }
1336
                            }
1337
                        }
1338
1339
                        $params['order'] = $fields['order'];
1340
                        $params['group'] = $fields['group'];
1341
                        $params['paginateresults'] = $fields['paginate_results'];
1342
                        $params['limit'] = $fields['max_records'];
1343
                        $params['startpage'] = $fields['page_number'];
1344
                        $params['redirectonempty'] = $fields['redirect_on_empty'];
1345
                        $params['redirectonforbidden'] = $fields['redirect_on_forbidden'];
1346
                        $params['redirectonrequired'] = $fields['redirect_on_required'];
1347
                        $params['requiredparam'] = trim($fields['required_url_param']);
1348
                        $params['negateparam'] = trim($fields['negate_url_param']);
1349
                        $params['paramoutput'] = $fields['param'];
1350
                        $params['sort'] = $fields['sort'];
1351
                        $params['htmlencode'] = $fields['html_encode'];
1352
                        $params['associatedentrycounts'] = $fields['associated_entry_counts'];
1353
1354
                        break;
1355
                }
1356
1357
                $this->__injectVarList($dsShell, $params);
1358
                $this->__injectIncludedElements($dsShell, $elements);
1359
                self::injectFilters($dsShell, $filters);
1360
1361
                if (preg_match_all('@(\$ds-[0-9a-z_\.\-]+)@i', $dsShell, $matches)) {
1362
                    $dependencies = General::array_remove_duplicates($matches[1]);
1363
                    $dependencies = array_map('addslashes', $dependencies);
1364
                    $dsShell = str_replace('<!-- DS DEPENDENCY LIST -->', "'" . implode("', '", $dependencies) . "'", $dsShell);
1365
                }
1366
1367
                $dsShell = str_replace('<!-- CLASS EXTENDS -->', $extends, $dsShell);
1368
                $dsShell = str_replace('<!-- SOURCE -->', addslashes($source), $dsShell);
1369
            }
1370
1371
            if ($this->_context[0] == 'new') {
1372
                /**
1373
                 * Prior to creating the Datasource, the file path where it will be written to
1374
                 * is provided and well as the contents of that file.
1375
                 *
1376
                 * @delegate DatasourcePreCreate
1377
                 * @since Symphony 2.2
1378
                 * @param string $context
1379
                 * '/blueprints/datasources/'
1380
                 * @param string $file
1381
                 *  The path to the Datasource file
1382
                 * @param string $contents
1383
                 *  The contents for this Datasource as a string passed by reference
1384
                 * @param array $params
1385
                 *  An array of all the `$dsParam*` values
1386
                 * @param array $elements
1387
                 *  An array of all the elements included in this datasource
1388
                 * @param array $filters
1389
                 *  An associative array of all the filters for this datasource with the key
1390
                 *  being the `field_id` and the value the filter.
1391
                 * @param array $dependencies
1392
                 *  An array of dependencies that this datasource has
1393
                 * @param string $source
1394
                 *  The source of the datasource's data
1395
                 */
1396
                Symphony::ExtensionManager()->notifyMembers('DatasourcePreCreate', '/blueprints/datasources/', array(
1397
                    'file' => $file,
1398
                    'contents' => &$dsShell,
1399
                    'params' => $params,
1400
                    'elements' => $elements,
1401
                    'filters' => $filters,
1402
                    'dependencies' => $dependencies,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dependencies does not seem to be defined for all execution paths leading up to this point.
Loading history...
1403
                    'source' => $source
1404
                ));
1405
            } else {
1406
                /**
1407
                 * Prior to editing a Datasource, the file path where it will be written to
1408
                 * is provided and well as the contents of that file.
1409
                 *
1410
                 * @delegate DatasourcePreEdit
1411
                 * @since Symphony 2.2
1412
                 * @param string $context
1413
                 * '/blueprints/datasources/'
1414
                 * @param string $file
1415
                 *  The path to the Datasource file
1416
                 * @param string $contents
1417
                 *  The contents for this Datasource as a string passed by reference
1418
                 * @param array $params
1419
                 *  An array of all the `$dsParam*` values
1420
                 * @param array $elements
1421
                 *  An array of all the elements included in this datasource
1422
                 * @param array $filters
1423
                 *  An associative array of all the filters for this datasource with the key
1424
                 *  being the `field_id` and the value the filter.
1425
                 * @param array $dependencies
1426
                 *  An array of dependencies that this datasource has
1427
                 * @param string $source
1428
                 *  The source of the datasource's data
1429
                 */
1430
                Symphony::ExtensionManager()->notifyMembers('DatasourcePreEdit', '/blueprints/datasources/', array(
1431
                    'file' => $file,
1432
                    'contents' => &$dsShell,
1433
                    'params' => $params,
1434
                    'elements' => $elements,
1435
                    'filters' => $filters,
1436
                    'dependencies' => $dependencies,
1437
                    'source' => $source
1438
                ));
1439
            }
1440
1441
            // Remove left over placeholders
1442
            $dsShell = preg_replace(array('/<!--[\w ]++-->/', '/(\t+[\r\n]){2,}/', '/(\r\n){2,}/'), '$1', $dsShell);
1443
1444
            // Write the file
1445
            if (!General::writeFile($file, $dsShell, Symphony::Configuration()->get('write_mode', 'file'), 'w', true)) {
0 ignored issues
show
Bug introduced by
It seems like Symphony::Configuration(...t('write_mode', 'file') can also be of type array; however, parameter $perm of General::writeFile() does only seem to accept integer|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

1445
            if (!General::writeFile($file, $dsShell, /** @scrutinizer ignore-type */ Symphony::Configuration()->get('write_mode', 'file'), 'w', true)) {
Loading history...
1446
                $this->pageAlert(
1447
                    __('Failed to write Data source to disk.')
1448
                    . ' ' . __('Please check permissions on %s.', array('<code>/workspace/data-sources</code>')),
1449
                    Alert::ERROR
1450
                );
1451
1452
                // Write successful
1453
            } else {
1454
                if (function_exists('opcache_invalidate')) {
1455
                    @opcache_invalidate($file, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for opcache_invalidate(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1455
                    /** @scrutinizer ignore-unhandled */ @opcache_invalidate($file, true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1456
                }
1457
1458
                // Attach this datasources to pages
1459
                $connections = $fields['connections'];
1460
                ResourceManager::setPages(ResourceManager::RESOURCE_TYPE_DS, is_null($existing_handle) ? $classname : $existing_handle, $connections);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $existing_handle does not seem to be defined for all execution paths leading up to this point.
Loading history...
1461
1462
                // If the datasource has been updated and the name changed, then adjust all the existing pages that have the old datasource name
1463
                if ($queueForDeletion) {
1464
                    General::deleteFile($queueForDeletion);
1465
1466
                    // Update pages that use this DS
1467
                    $pages = PageManager::fetch(false, array('data_sources', 'id'), array("
1468
                        `data_sources` REGEXP '[[:<:]]" . $existing_handle . "[[:>:]]'
1469
                    "));
1470
1471
                    if (is_array($pages) && !empty($pages)) {
1472
                        foreach ($pages as $page) {
1473
                            $page['data_sources'] = preg_replace('/\b'.$existing_handle.'\b/i', $classname, $page['data_sources']);
1474
1475
                            PageManager::edit($page['id'], $page);
1476
                        }
1477
                    }
1478
                }
1479
1480
                if ($this->_context[0] == 'new') {
1481
                    /**
1482
                     * After creating the Datasource, the path to the Datasource file is provided
1483
                     *
1484
                     * @delegate DatasourcePostCreate
1485
                     * @since Symphony 2.2
1486
                     * @param string $context
1487
                     * '/blueprints/datasources/'
1488
                     * @param string $file
1489
                     *  The path to the Datasource file
1490
                     */
1491
                    Symphony::ExtensionManager()->notifyMembers('DatasourcePostCreate', '/blueprints/datasources/', array(
1492
                        'file' => $file
1493
                    ));
1494
                } else {
1495
                    /**
1496
                     * After editing the Datasource, the path to the Datasource file is provided
1497
                     *
1498
                     * @delegate DatasourcePostEdit
1499
                     * @since Symphony 2.2
1500
                     * @param string $context
1501
                     * '/blueprints/datasources/'
1502
                     * @param string $file
1503
                     *  The path to the Datasource file
1504
                     * @param string $previous_file
1505
                     *  The path of the previous Datasource file in the case where a Datasource may
1506
                     *  have been renamed. To get the handle from this value, see
1507
                     *  `DatasourceManager::__getHandleFromFilename`
1508
                     */
1509
                    Symphony::ExtensionManager()->notifyMembers('DatasourcePostEdit', '/blueprints/datasources/', array(
1510
                        'file' => $file,
1511
                        'previous_file' => ($queueForDeletion) ? $queueForDeletion : null
1512
                    ));
1513
                }
1514
1515
                redirect(SYMPHONY_URL . '/blueprints/datasources/edit/'.$classname.'/'.($this->_context[0] == 'new' ? 'created' : 'saved') . '/');
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Bug introduced by
The constant SYMPHONY_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1516
            }
1517
        }
1518
    }
1519
1520
    public static function injectFilters(&$shell, array $filters)
1521
    {
1522
        if (!is_array($filters) || empty($filters)) {
0 ignored issues
show
introduced by
The condition is_array($filters) is always true.
Loading history...
1523
            return;
1524
        }
1525
1526
        $placeholder = '<!-- FILTERS -->';
1527
        $string = 'public $dsParamFILTERS = array(' . PHP_EOL;
1528
1529
        foreach ($filters as $key => $val) {
1530
            if (trim($val) == '' || !is_string($val)) {
1531
                continue;
1532
            }
1533
1534
            $string .= "        '" . addslashes($key) . "' => '" . addslashes($val) . "'," . PHP_EOL;
1535
        }
1536
1537
        $string .= "    );" . PHP_EOL . "        " . $placeholder;
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...
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...
1538
1539
        $shell = str_replace($placeholder, trim($string), $shell);
1540
    }
1541
1542
    public static function injectAboutInformation(&$shell, array $details)
1543
    {
1544
        if (!is_array($details) || empty($details)) {
0 ignored issues
show
introduced by
The condition is_array($details) is always true.
Loading history...
1545
            return;
1546
        }
1547
1548
        foreach ($details as $key => $val) {
1549
            if (!is_string($key) || !is_string($val)) {
1550
                continue;
1551
            }
1552
1553
            $shell = str_replace('<!-- ' . strtoupper(addslashes($key)) . ' -->', addslashes($val), $shell);
1554
        }
1555
    }
1556
1557
    public function __injectIncludedElements(&$shell, $elements)
1558
    {
1559
        if (!is_array($elements) || empty($elements)) {
1560
            return;
1561
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1562
        $elements = array_map('addslashes', $elements);
1563
        $placeholder = '<!-- INCLUDED ELEMENTS -->';
1564
        $shell = str_replace($placeholder, "public \$dsParamINCLUDEDELEMENTS = array(" . PHP_EOL . "        '" . implode("'," . PHP_EOL . "        '", $elements) . "'" . PHP_EOL . '    );' . PHP_EOL . "    " . $placeholder, $shell);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal public \$dsParamINCLUDEDELEMENTS = array( 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...
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...
1565
    }
1566
1567
    public function __injectVarList(&$shell, $vars)
1568
    {
1569
        if (!is_array($vars) || empty($vars)) {
1570
            return;
1571
        }
1572
1573
        $var_list = null;
1574
1575
        foreach ($vars as $key => $val) {
1576
            if (!is_string($key)) {
1577
                continue;
1578
            }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
1579
            if (is_array($val)) {
1580
                $val = array_map('addslashes', $val);
1581
                $val = "array(" . PHP_EOL . "        '" . implode("'," . PHP_EOL . "        '", $val) . "'" . PHP_EOL . '    );';
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal array( 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...
1582
                $var_list .= '    public $dsParam' . strtoupper(addslashes($key)) . ' = ' . $val . PHP_EOL;
1583
            } elseif (trim($val) !== '') {
1584
                $var_list .= '    public $dsParam' . strtoupper(addslashes($key)) . " = '" . addslashes($val) . "';" . PHP_EOL;
1585
            }
1586
        }
1587
1588
        $placeholder = '<!-- VAR LIST -->';
1589
        $shell = str_replace($placeholder, trim($var_list) . PHP_EOL . "    " . $placeholder, $shell);
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...
1590
    }
1591
1592
    public function __appendAuthorFilter(&$wrapper, $h4_label, $name, $value = null, $templateOnly = true)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$value" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$value"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$templateOnly" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$templateOnly"; expected 0 but found 1
Loading history...
1593
    {
1594
        if (!$templateOnly) {
1595
            $li = new XMLElement('li');
1596
            $li->setAttribute('class', 'unique');
1597
            $li->setAttribute('data-type', $name);
1598
            $li->appendChild(new XMLElement('header', '<h4>' . $h4_label . '</h4>'));
1599
            $label = Widget::Label(__('Value'));
1600
            $label->appendChild(Widget::Input('fields[filter][author]['.$name.']', General::sanitize($value)));
1601
            $li->appendChild($label);
1602
1603
            $wrapper->appendChild($li);
1604
        }
1605
1606
        $li = new XMLElement('li');
1607
        $li->setAttribute('class', 'unique template');
1608
        $li->setAttribute('data-type', $name);
1609
        $li->appendChild(new XMLElement('header', '<h4>' . $h4_label . '</h4>'));
1610
        $label = Widget::Label(__('Value'));
1611
        $label->appendChild(Widget::Input('fields[filter][author]['.$name.']'));
1612
        $li->appendChild($label);
1613
1614
        $wrapper->appendChild($li);
1615
    }
1616
1617
    private static function __isValidPageString($string)
1618
    {
1619
        return (bool)preg_match('/^\{\$[\w-]+(.[\w]+(-[\w]+)?){0,1}\}|[\d]+$/', $string);
1620
    }
1621
1622
    /**
1623
     * Given a `$url` and `$timeout`, this function will use the `Gateway`
1624
     * class to determine that it is a valid URL and returns successfully
1625
     * before the `$timeout`. If it does not, an error message will be
1626
     * returned, otherwise true.
1627
     *
1628
     * @since Symphony 2.3
1629
     * @param string $url
1630
     * @param integer $timeout
1631
     *  If not provided, this will default to 6 seconds
1632
     * @param string $error
1633
     *  If this function returns false, this variable will be populated with the
1634
     *  error message.
1635
     * @return array|boolean
1636
     *  Returns an array with the 'data' if it is a valid URL, otherwise a string
1637
     *  containing an error message.
1638
     */
1639
    public static function __isValidURL($url, $timeout = 6, &$error)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$timeout" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$timeout"; expected 0 but found 1
Loading history...
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1640
    {
1641
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
1642
            $error = __('Invalid URL');
1643
            return false;
1644
        }
1645
1646
        // Check that URL was provided
1647
        $gateway = new Gateway;
1648
        $gateway->init($url);
1649
        $gateway->setopt('TIMEOUT', $timeout);
1650
        $data = $gateway->exec();
1651
1652
        $info = $gateway->getInfoLast();
1653
1654
        // 28 is CURLE_OPERATION_TIMEDOUT
1655
        if ($info['curl_error'] == 28) {
1656
            $error = __('Request timed out. %d second limit reached.', array($timeout));
1657
            return false;
1658
        } elseif ($data === false || $info['http_code'] != 200) {
1659
            $error = __('Failed to load URL, status code %d was returned.', array($info['http_code']));
1660
            return false;
1661
        }
1662
1663
        return array('data' => $data);
1664
    }
1665
1666
    /**
1667
     * Set Data Source context
1668
     *
1669
     * @since Symphony 2.3.3
1670
     * @param XMLElement $element
1671
     * @param array $context
1672
     */
1673
    public function setContext(&$element, $context)
1674
    {
1675
        $element->setAttribute('class', 'settings contextual');
1676
        $element->setAttribute('data-context', implode(' ', (array)$context));
1677
    }
1678
}
1679