GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#2843)
by Brendan
04:11
created

contentBlueprintsDatasources   F

Complexity

Total Complexity 260

Size/Duplication

Total Lines 1670
Duplicated Lines 19.28 %

Coupling/Cohesion

Components 0
Dependencies 18

Importance

Changes 0
Metric Value
dl 322
loc 1670
rs 0.6314
c 0
b 0
f 0
wmc 260
lcom 0
cbo 18

17 Methods

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

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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

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

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

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

1447
            if (!General::writeFile($file, $dsShell, /** @scrutinizer ignore-type */ Symphony::Configuration()->get('write_mode', 'file'), 'w', true)) {
Loading history...
1448
                $this->pageAlert(
1449
                    __('Failed to write Data source to disk.')
1450
                    . ' ' . __('Please check permissions on %s.', array('<code>/workspace/data-sources</code>')),
1451
                    Alert::ERROR
1452
                );
1453
1454
                // Write successful
1455
            } else {
1456
                if (function_exists('opcache_invalidate')) {
1457
                    opcache_invalidate($file, true);
1458
                }
1459
1460
                // Attach this datasources to pages
1461
                $connections = $fields['connections'];
1462
                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...
1463
1464
                // If the datasource has been updated and the name changed, then adjust all the existing pages that have the old datasource name
1465
                if ($queueForDeletion) {
1466
                    General::deleteFile($queueForDeletion);
1467
1468
                    // Update pages that use this DS
1469
                    $pages = PageManager::fetch(false, array('data_sources', 'id'), array("
1470
                        `data_sources` REGEXP '[[:<:]]" . $existing_handle . "[[:>:]]'
1471
                    "));
1472
1473
                    if (is_array($pages) && !empty($pages)) {
1474
                        foreach ($pages as $page) {
1475
                            $page['data_sources'] = preg_replace('/\b'.$existing_handle.'\b/i', $classname, $page['data_sources']);
1476
1477
                            PageManager::edit($page['id'], $page);
1478
                        }
1479
                    }
1480
                }
1481
1482
                if ($this->_context[0] == 'new') {
1483
                    /**
1484
                     * After creating the Datasource, the path to the Datasource file is provided
1485
                     *
1486
                     * @delegate DatasourcePostCreate
1487
                     * @since Symphony 2.2
1488
                     * @param string $context
1489
                     * '/blueprints/datasources/'
1490
                     * @param string $file
1491
                     *  The path to the Datasource file
1492
                     */
1493
                    Symphony::ExtensionManager()->notifyMembers('DatasourcePostCreate', '/blueprints/datasources/', array(
1494
                        'file' => $file
1495
                    ));
1496
                } else {
1497
                    /**
1498
                     * After editing the Datasource, the path to the Datasource file is provided
1499
                     *
1500
                     * @delegate DatasourcePostEdit
1501
                     * @since Symphony 2.2
1502
                     * @param string $context
1503
                     * '/blueprints/datasources/'
1504
                     * @param string $file
1505
                     *  The path to the Datasource file
1506
                     * @param string $previous_file
1507
                     *  The path of the previous Datasource file in the case where a Datasource may
1508
                     *  have been renamed. To get the handle from this value, see
1509
                     *  `DatasourceManager::__getHandleFromFilename`
1510
                     */
1511
                    Symphony::ExtensionManager()->notifyMembers('DatasourcePostEdit', '/blueprints/datasources/', array(
1512
                        'file' => $file,
1513
                        'previous_file' => ($queueForDeletion) ? $queueForDeletion : null
1514
                    ));
1515
                }
1516
1517
                redirect(SYMPHONY_URL . '/blueprints/datasources/edit/'.$classname.'/'.($this->_context[0] == 'new' ? 'created' : 'saved') . '/');
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...
1518
            }
1519
        }
1520
    }
1521
1522
    public static function injectFilters(&$shell, array $filters)
1523
    {
1524
        if (!is_array($filters) || empty($filters)) {
0 ignored issues
show
introduced by
The condition is_array($filters) is always true.
Loading history...
1525
            return;
1526
        }
1527
1528
        $placeholder = '<!-- FILTERS -->';
1529
        $string = 'public $dsParamFILTERS = array(' . PHP_EOL;
1530
1531
        foreach ($filters as $key => $val) {
1532
            if (trim($val) == '' || !is_string($val)) {
1533
                continue;
1534
            }
1535
1536
            $string .= "        '" . addslashes($key) . "' => '" . addslashes($val) . "'," . PHP_EOL;
1537
        }
1538
1539
        $string .= "    );" . PHP_EOL . "        " . $placeholder;
1540
1541
        $shell = str_replace($placeholder, trim($string), $shell);
1542
    }
1543
1544
    public static function injectAboutInformation(&$shell, array $details)
1545
    {
1546
        if (!is_array($details) || empty($details)) {
0 ignored issues
show
introduced by
The condition is_array($details) is always true.
Loading history...
1547
            return;
1548
        }
1549
1550
        foreach ($details as $key => $val) {
1551
            if (!is_string($key) || !is_string($val)) {
1552
                continue;
1553
            }
1554
1555
            $shell = str_replace('<!-- ' . strtoupper(addslashes($key)) . ' -->', addslashes($val), $shell);
1556
        }
1557
    }
1558
1559
    public function __injectIncludedElements(&$shell, $elements)
1560
    {
1561
        if (!is_array($elements) || empty($elements)) {
1562
            return;
1563
        }
1564
        $elements = array_map('addslashes', $elements);
1565
        $placeholder = '<!-- INCLUDED ELEMENTS -->';
1566
        $shell = str_replace($placeholder, "public \$dsParamINCLUDEDELEMENTS = array(" . PHP_EOL . "        '" . implode("'," . PHP_EOL . "        '", $elements) . "'" . PHP_EOL . '    );' . PHP_EOL . "    " . $placeholder, $shell);
1567
    }
1568
1569
    public function __injectVarList(&$shell, $vars)
1570
    {
1571
        if (!is_array($vars) || empty($vars)) {
1572
            return;
1573
        }
1574
1575
        $var_list = null;
1576
1577
        foreach ($vars as $key => $val) {
1578
            if (!is_string($key)) {
1579
                continue;
1580
            }
1581
            if (is_array($val)) {
1582
                $val = array_map('addslashes', $val);
1583
                $val = "array(" . PHP_EOL . "        '" . implode("'," . PHP_EOL . "        '", $val) . "'" . PHP_EOL . '        );';
1584
                $var_list .= '    public $dsParam' . strtoupper(addslashes($key)) . ' = ' . $val . PHP_EOL;
1585
            } elseif (trim($val) !== '') {
1586
                $var_list .= '    public $dsParam' . strtoupper(addslashes($key)) . " = '" . addslashes($val) . "';" . PHP_EOL;
1587
            }
1588
        }
1589
1590
        $placeholder = '<!-- VAR LIST -->';
1591
        $shell = str_replace($placeholder, trim($var_list) . PHP_EOL . "    " . $placeholder, $shell);
1592
    }
1593
1594
    public function __appendAuthorFilter(&$wrapper, $h4_label, $name, $value = null, $templateOnly = true)
1595
    {
1596
        if (!$templateOnly) {
1597
            $li = new XMLElement('li');
1598
            $li->setAttribute('class', 'unique');
1599
            $li->setAttribute('data-type', $name);
1600
            $li->appendChild(new XMLElement('header', '<h4>' . $h4_label . '</h4>'));
1601
            $label = Widget::Label(__('Value'));
1602
            $label->appendChild(Widget::Input('fields[filter][author]['.$name.']', General::sanitize($value)));
1603
            $li->appendChild($label);
1604
1605
            $wrapper->appendChild($li);
1606
        }
1607
1608
        $li = new XMLElement('li');
1609
        $li->setAttribute('class', 'unique template');
1610
        $li->setAttribute('data-type', $name);
1611
        $li->appendChild(new XMLElement('header', '<h4>' . $h4_label . '</h4>'));
1612
        $label = Widget::Label(__('Value'));
1613
        $label->appendChild(Widget::Input('fields[filter][author]['.$name.']'));
1614
        $li->appendChild($label);
1615
1616
        $wrapper->appendChild($li);
1617
    }
1618
1619
    private static function __isValidPageString($string)
1620
    {
1621
        return (bool)preg_match('/^\{\$[\w-]+(.[\w]+(-[\w]+)?){0,1}\}|[\d]+$/', $string);
1622
    }
1623
1624
    /**
1625
     * Given a `$url` and `$timeout`, this function will use the `Gateway`
1626
     * class to determine that it is a valid URL and returns successfully
1627
     * before the `$timeout`. If it does not, an error message will be
1628
     * returned, otherwise true.
1629
     *
1630
     * @since Symphony 2.3
1631
     * @param string $url
1632
     * @param integer $timeout
1633
     *  If not provided, this will default to 6 seconds
1634
     * @param string $error
1635
     *  If this function returns false, this variable will be populated with the
1636
     *  error message.
1637
     * @return array|boolean
1638
     *  Returns an array with the 'data' if it is a valid URL, otherwise a string
1639
     *  containing an error message.
1640
     */
1641
    public static function __isValidURL($url, $timeout = 6, &$error)
1642
    {
1643
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
1644
            $error = __('Invalid URL');
1645
            return false;
1646
        }
1647
1648
        // Check that URL was provided
1649
        $gateway = new Gateway;
1650
        $gateway->init($url);
1651
        $gateway->setopt('TIMEOUT', $timeout);
1652
        $data = $gateway->exec();
1653
1654
        $info = $gateway->getInfoLast();
1655
1656
        // 28 is CURLE_OPERATION_TIMEDOUT
1657
        if ($info['curl_error'] == 28) {
1658
            $error = __('Request timed out. %d second limit reached.', array($timeout));
1659
            return false;
1660
        } elseif ($data === false || $info['http_code'] != 200) {
1661
            $error = __('Failed to load URL, status code %d was returned.', array($info['http_code']));
1662
            return false;
1663
        }
1664
1665
        return array('data' => $data);
1666
    }
1667
1668
    /**
1669
     * Set Data Source context
1670
     *
1671
     * @since Symphony 2.3.3
1672
     * @param XMLElement $element
1673
     * @param array $context
1674
     */
1675
    public function setContext(&$element, $context)
1676
    {
1677
        $element->setAttribute('class', 'settings contextual');
1678
        $element->setAttribute('data-context', implode(' ', (array)$context));
1679
    }
1680
}
1681