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
Push — master ( 2240c6...8ae5ca )
by Nicolas
04:17
created

contentBlueprintsDatasources   F

Complexity

Total Complexity 260

Size/Duplication

Total Lines 1672
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 1672
rs 0.6314
wmc 260

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 988 146

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
/**
4
 * @package content
5
 */
6
/**
7
 * The Datasource Editor page allows a developer to create new datasources
8
 * from the four Symphony types, Section, Authors, Navigation and Static XML
9
 */
10
11
class contentBlueprintsDatasources extends ResourcesPage
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]');
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', General::sanitize($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(
988
                $page['id'],
989
                in_array($page['id'], $selected),
990
                General::sanitize(PageManager::resolvePageTitle($page['id']))
991
            );
992
        }
993
994
        $label->appendChild(Widget::Select('fields[connections][]', $options, array('multiple' => 'multiple')));
995
        $div->appendChild($label);
996
997
        $fieldset->appendChild($div);
998
        $this->Form->appendChild($fieldset);
999
1000
1001
        // Call the provided datasources to let them inject their filters
1002
        // @todo Ideally when a new Datasource is chosen an AJAX request will fire
1003
        // to get the HTML from the extension. This is hardcoded for now into
1004
        // creating a 'big' page and then hiding the fields with JS
1005
        if (!empty($providers)) {
1006
            foreach ($providers as $providerClass => $provider) {
1007
                call_user_func_array(array($providerClass, 'buildEditor'), array($this->Form, &$this->_errors, $fields, $handle));
1008
            }
1009
        }
1010
1011
        $div = new XMLElement('div');
1012
        $div->setAttribute('class', 'actions');
1013
        $div->appendChild(Widget::Input('action[save]', ($isEditing ? __('Save Changes') : __('Create Data Source')), 'submit', array('accesskey' => 's')));
1014
1015
        if ($isEditing) {
1016
            $button = new XMLElement('button', __('Delete'));
1017
            $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?')));
1018
            $div->appendChild($button);
1019
        }
1020
1021
        $this->Form->appendChild($div);
1022
    }
1023
1024
    public function __viewInfo()
1025
    {
1026
        $this->setPageType('form');
1027
1028
        $datasource = DatasourceManager::create($this->_context[1], array(), false);
1029
        $about = General::array_map_recursive('stripslashes', $datasource->about());
1030
1031
        $this->setTitle(__('%1$s &ndash; %2$s &ndash; %3$s', array($about['name'], __('Data Source'), __('Symphony'))));
1032
        $this->appendSubheading((($this->_context[0] == 'info') ? $about['name'] : __('Untitled')));
1033
        $this->insertBreadcrumbs(array(
1034
            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...
1035
        ));
1036
        $this->Form->setAttribute('id', 'controller');
1037
1038
        $link = $about['author']['name'];
1039
1040
        if (isset($about['author']['website'])) {
1041
            $link = Widget::Anchor($about['author']['name'], General::validateURL($about['author']['website']));
1042
        } elseif (isset($about['author']['email'])) {
1043
            $link = Widget::Anchor($about['author']['name'], 'mailto:' . $about['author']['email']);
1044
        }
1045
1046
        foreach ($about as $key => $value) {
1047
            $fieldset = null;
1048
1049
            switch ($key) {
1050
                case 'author':
1051
                    if ($link) {
1052
                        $fieldset = new XMLElement('fieldset');
1053
                        $fieldset->appendChild(new XMLElement('legend', __('Author')));
1054
                        $fieldset->appendChild(new XMLElement('p', $link->generate(false)));
1055
                    }
1056
                    break;
1057
                case 'version':
1058
                    $fieldset = new XMLElement('fieldset');
1059
                    $fieldset->appendChild(new XMLElement('legend', __('Version')));
1060
                    $release_date = array_key_exists('release-date', $about) ? $about['release-date'] : filemtime(DatasourceManager::__getDriverPath($this->_context[1]));
1061
1062
                    if (preg_match('/^\d+(\.\d+)*$/', $value)) {
1063
                        $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...
1064
                    } else {
1065
                        $fieldset->appendChild(new XMLElement('p', __('Created by %1$s at %2$s', array($value, DateTimeObj::format($release_date, __SYM_DATE_FORMAT__)))));
1066
                    }
1067
                    break;
1068
                case 'description':
1069
                    $fieldset = new XMLElement('fieldset');
1070
                    $fieldset->appendChild(new XMLElement('legend', __('Description')));
1071
                    $fieldset->appendChild((is_object($about['description']) ? $about['description'] : new XMLElement('p', $about['description'])));
1072
                    break;
1073
                case 'example':
1074
                    if (is_callable(array($datasource, 'example'))) {
1075
                        $fieldset = new XMLElement('fieldset');
1076
                        $fieldset->appendChild(new XMLElement('legend', __('Example XML')));
1077
1078
                        $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

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

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

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