Passed
Push — master ( d995fb...48f5cf )
by Andreas
28:37
created

load_controller()   B

Complexity

Conditions 11
Paths 60

Size

Total Lines 58
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 11.0908

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 35
c 1
b 0
f 0
nc 60
nop 0
dl 0
loc 58
ccs 30
cts 33
cp 0.9091
crap 11.0908
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @package midgard.admin.asgard
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
use midcom\datamanager\schemadb;
10
use midcom\datamanager\datamanager;
11
use Symfony\Component\HttpFoundation\Request;
12
use midcom\datamanager\controller;
13
use midcom\datamanager\validation\phpValidator;
14
15
/**
16
 * Component configuration handler
17
 *
18
 * @package midgard.admin.asgard
19
 */
20
class midgard_admin_asgard_handler_component_configuration extends midcom_baseclasses_components_handler
21
{
22
    use midgard_admin_asgard_handler;
23
24
    private $_controller;
25
26 3
    public function _on_initialize()
27
    {
28 3
        $this->add_stylesheet(MIDCOM_STATIC_URL . '/midgard.admin.asgard/libconfig.css');
29 3
    }
30
31 2
    private function _prepare_toolbar(bool $is_view)
32
    {
33 2
        $view_url = $this->router->generate('components_configuration', ['component' => $this->_request_data['name']]);
34 2
        $edit_url = $this->router->generate('components_configuration_edit', ['component' => $this->_request_data['name']]);
35
        $buttons = [[
36 2
            MIDCOM_TOOLBAR_URL => $view_url,
37 2
            MIDCOM_TOOLBAR_LABEL => $this->_l10n_midcom->get('view'),
38 2
            MIDCOM_TOOLBAR_GLYPHICON => 'eye',
39 2
            MIDCOM_TOOLBAR_ENABLED => !$is_view
40
        ], [
41 2
            MIDCOM_TOOLBAR_URL => $edit_url,
42 2
            MIDCOM_TOOLBAR_LABEL => $this->_l10n_midcom->get('edit'),
43 2
            MIDCOM_TOOLBAR_GLYPHICON => 'pencil',
44 2
            MIDCOM_TOOLBAR_ENABLED => $is_view
45
        ]];
46 2
        $this->_request_data['asgard_toolbar']->add_items($buttons);
47 2
    }
48
49
    /**
50
     * Set the breadcrumb data
51
     */
52 2
    private function _prepare_breadcrumbs()
53
    {
54 2
        $this->add_breadcrumb($this->router->generate('welcome'), $this->_l10n->get($this->_component));
55 2
        $this->add_breadcrumb($this->router->generate('components'), $this->_l10n->get('components'));
56
57 2
        $this->add_breadcrumb(
58 2
            $this->router->generate('components_component', ['component' => $this->_request_data['name']]),
59 2
            $this->_i18n->get_string($this->_request_data['name'], $this->_request_data['name'])
60
        );
61 2
        $this->add_breadcrumb(
62 2
            $this->router->generate('components_configuration', ['component' => $this->_request_data['name']]),
63 2
            $this->_l10n_midcom->get('component configuration')
64
        );
65 2
    }
66
67 3
    private function _load_configs(string $component, midcom_db_topic $topic = null) : midcom_helper_configuration
68
    {
69 3
        $config = midcom_baseclasses_components_configuration::get($component, 'config');
70
71 3
        if ($topic) {
72 1
            $topic_config = new midcom_helper_configuration($topic, $component);
73 1
            $config->store($topic_config->_local, false);
74
        }
75
76 3
        return $config;
77
    }
78
79 2
    private function load_controller() : controller
80
    {
81
        // Load SchemaDb
82 2
        $schemadb_config_path = midcom::get()->componentloader->path_to_snippetpath($this->_request_data['name']) . '/config/config_schemadb.inc';
83 2
        $schemaname = 'default';
84
85 2
        if (file_exists($schemadb_config_path)) {
86
            $schemadb = schemadb::from_path('file:/' . str_replace('.', '/', $this->_request_data['name']) . '/config/config_schemadb.inc');
87
            if ($schemadb->has('config')) {
88
                $schemaname = 'config';
89
            }
90
            // TODO: Log error on deprecated config schema?
91
        } else {
92
            // Create dummy schema. Naughty component would not provide config schema.
93 2
            $schemadb = new schemadb(['default' => [
94 2
                'description' => 'configuration for ' . $this->_request_data['name'],
95
                'fields'      => []
96
            ]]);
97
        }
98 2
        $schema = $schemadb->get($schemaname);
99 2
        $schema->set('l10n_db', $this->_request_data['name']);
100 2
        $fields = $schema->get('fields');
101
102 2
        foreach ($this->_request_data['config']->_global as $key => $value) {
103
            // try to sniff what fields are missing in schema
104 2
            if (!array_key_exists($key, $fields)) {
105 2
                $fields[$key] = $this->_detect_schema($key, $value);
106
            }
107
108 2
            if (   !isset($this->_request_data['config']->_local[$key])
109 2
                || $this->_request_data['config']->_local[$key] == $this->_request_data['config']->_global[$key]) {
110
                // No local configuration setting, note to user that this is the global value
111 2
                $fields[$key]['title'] = $schema->get_l10n()->get($fields[$key]['title']);
112 2
                $fields[$key]['title'] .= " <span class=\"global\">(" . $this->_l10n->get('global value') .")</span>";
113
            }
114
        }
115
116
        // Prepare defaults
117 2
        $config = array_intersect_key($this->_request_data['config']->get_all(), $fields);
118 2
        foreach ($config as $key => $value) {
119 2
            if (is_array($value)) {
120 2
                $fields[$key]['default'] = var_export($value, true);
121
            } else {
122 2
                if ($fields[$key]['widget'] == 'checkbox') {
123 2
                    $value = (boolean) $value;
124
                }
125 2
                $fields[$key]['default'] = $value;
126
            }
127
        }
128 2
        $schema->set('fields', $fields);
129 2
        $validation = $schema->get('validation') ?: [];
130 2
        $validation[] = [
131 2
            'callback' => [$this, 'check_config'],
132
        ];
133 2
        $schema->set('validation', $validation);
134
135 2
        $dm = new datamanager($schemadb);
136 2
        return $dm->get_controller();
137
    }
138
139 1
    public function _handler_view(string $component, array &$data)
140
    {
141 1
        $data['name'] = $component;
142 1
        $data['config'] = $this->_load_configs($data['name']);
143
144 1
        $data['view_title'] = sprintf($this->_l10n->get('configuration for %s'), $data['name']);
145 1
        $this->_prepare_toolbar(true);
146 1
        $this->_prepare_breadcrumbs();
147 1
        return $this->get_response();
148
    }
149
150
    /**
151
     * @param array $data Data passed to the show method
152
     */
153 1
    public function _show_view(string $handler_id, array &$data)
154
    {
155 1
        midcom_show_style('midgard_admin_asgard_component_configuration_header');
156
157 1
        foreach ($data['config']->_global as $key => $value) {
158 1
            $data['key'] = $this->_i18n->get_string($key, $data['name']);
159 1
            $data['global'] = $this->render($value);
160
161 1
            if (isset($data['config']->_local[$key])) {
162
                $data['local'] = $this->render($data['config']->_local[$key]);
163
            } else {
164 1
                $data['local'] = $this->render(null);
165
            }
166
167 1
            midcom_show_style('midgard_admin_asgard_component_configuration_item');
168
        }
169 1
        midcom_show_style('midgard_admin_asgard_component_configuration_footer');
170 1
    }
171
172 1
    private function render($value)
173
    {
174 1
        $type = gettype($value);
175
176 1
        switch ($type) {
177 1
            case 'boolean':
178 1
                $result = '<i class="fa fa-' . ($value === true ? 'check' : 'times') . '"></i>';
179 1
                break;
180 1
            case 'array':
181 1
                $content = '<ul>';
182 1
                foreach ($value as $key => $val) {
183 1
                    $content .= "<li>{$key} => " . $this->render($val) . ",</li>\n";
184
                }
185 1
                $content .= '</ul>';
186 1
                $result = "<ul>\n<li>array</li>\n<li>(\n{$content}\n)</li>\n</ul>\n";
187 1
                break;
188 1
            case 'object':
189
                $result = '<strong>Object</strong>';
190
                break;
191 1
            case 'NULL':
192 1
                $result = '<strong>N/A</strong>';
193 1
                break;
194
            default:
195 1
                $result = $value;
196
        }
197
198 1
        return $result;
199
    }
200
201
    /**
202
     * Ensure the configuration is valid
203
     *
204
     * @throws midcom_error
205
     */
206
    public function check_config(array $values)
207
    {
208
        $current = $this->_request_data['config']->get_all();
209
        $result = [];
210
        foreach ($values as $key => $newval) {
211
            if ($newval === '' || !isset($current[$key])) {
212
                continue;
213
            }
214
            $val = $current[$key];
215
216
            if (is_array($val)) {
217
                $code = "<?php\n\$data = array({$newval}\n);\n?>";
218
                if ($error = phpValidator::lint($code)) {
219
                    $result[$key] = $error;
220
                }
221
            }
222
        }
223
        if (empty($result)) {
224
            return true;
225
        }
226
        return $result;
227
    }
228
229
    private function convert_to_config(array $values) : array
230
    {
231
        $config_array = [];
232
233
        foreach ($this->_request_data['config']->get_all() as $key => $val) {
234
            if (!isset($values[$key])) {
235
                continue;
236
            }
237
            $newval = $values[$key];
238
239
            if ($newval === '') {
240
                continue;
241
            }
242
243
            if (is_array($val)) {
244
                //try make sure entries have the same format before deciding if there was a change
245
                eval("\$newval = $newval;");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
246
            }
247
248
            if ($newval != $val) {
249
                $config_array[$key] = $newval;
250
            }
251
        }
252
253
        return $config_array;
254
    }
255
256 2
    public function _handler_edit(Request $request, string $handler_id, array &$data, string $component, string $folder = null)
257
    {
258 2
        $data['name'] = $component;
259 2
        if ($handler_id == 'components_configuration_edit_folder') {
260 1
            $data['folder'] = new midcom_db_topic($folder);
261 1
            if ($data['folder']->component != $data['name']) {
262
                throw new midcom_error_notfound("Folder {$folder} not found for configuration.");
263
            }
264
265 1
            $data['folder']->require_do('midgard:update');
266
267 1
            $data['config'] = $this->_load_configs($data['name'], $data['folder']);
268
        } else {
269 1
            $data['config'] = $this->_load_configs($data['name']);
270
        }
271
272 2
        $this->_controller = $this->load_controller();
273
274 2
        switch ($this->_controller->handle($request)) {
275 2
            case 'save':
276
                if (!$this->save_configuration($data)) {
277
                    midcom::get()->uimessages->add(
278
                        $this->_l10n_midcom->get('component configuration'),
279
                        sprintf($this->_l10n->get('configuration save failed: %s'), midcom_connection::get_error_string()),
280
                        'error'
281
                    );
282
                    // back to edit
283
                    break;
284
                }
285
                midcom::get()->uimessages->add(
286
                    $this->_l10n_midcom->get('component configuration'),
287
                    $this->_l10n->get('configuration saved successfully')
288
                );
289
290
                // FALL-THROUGH (i.e. relocate to view)
291
292 2
            case 'cancel':
293
                if ($handler_id == 'components_configuration_edit_folder') {
294
                    return new midcom_response_relocate($this->router->generate('object_view', ['guid' => $data['folder']->guid]));
295
                }
296
                return new midcom_response_relocate($this->router->generate('components_configuration', ['component' => $data['name']]));
297
        }
298
299 2
        $data['controller'] = $this->_controller;
300
301 2
        if ($handler_id == 'components_configuration_edit_folder') {
302 1
            midgard_admin_asgard_plugin::bind_to_object($data['folder'], $handler_id, $data);
303 1
            $data['view_title'] = sprintf($this->_l10n->get('edit configuration for %s folder %s'), $data['name'], $data['folder']->extra);
304
        } else {
305 1
            $this->_prepare_toolbar(false);
306 1
            $data['view_title'] = sprintf($this->_l10n->get('edit configuration for %s'), $data['name']);
307 1
            $this->_prepare_breadcrumbs();
308 1
            $this->add_breadcrumb(
309 1
                $this->router->generate('components_configuration_edit', ['component' => $this->_request_data['name']]),
310 1
                $this->_l10n_midcom->get('edit')
311
            );
312
        }
313
314 2
        return $this->get_response('midgard_admin_asgard_component_configuration_edit');
315
    }
316
317
    private function save_configuration(array $data) : bool
318
    {
319
        $values = $this->convert_to_config($this->_controller->get_datamanager()->get_content_raw());
320
321
        if ($data['handler_id'] == 'components_configuration_edit_folder') {
322
            // Editing folder configuration
323
            return $this->save_topic($data['folder'], $values);
324
        }
325
        return $this->save_snippet($values);
326
    }
327
328
329
    /**
330
     * Save configuration values to a topic as "serialized" array
331
     */
332
    private function save_snippet(array $values) : bool
333
    {
334
        $config = var_export($values, true);
335
        // Remove opening and closing array( ) lines, because that's the way midcom likes it
336
        $config = preg_replace('/^.*?\n/', '', $config);
337
        $config = preg_replace('/(\n.*?|\))$/', '', $config);
338
339
        $basedir = midcom::get()->config->get('midcom_sgconfig_basedir');
340
        $sg_snippetdir = new midcom_db_snippetdir();
341
        if (!$sg_snippetdir->get_by_path($basedir)) {
0 ignored issues
show
Bug introduced by
It seems like $basedir can also be of type null; however, parameter $path of midcom_core_dbaobject::get_by_path() 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

341
        if (!$sg_snippetdir->get_by_path(/** @scrutinizer ignore-type */ $basedir)) {
Loading history...
342
            // Create config snippetdir
343
            $sg_snippetdir = new midcom_db_snippetdir();
344
            $sg_snippetdir->name = $basedir;
345
            // remove leading slash from name
346
            $sg_snippetdir->name = preg_replace("/^\//", "", $sg_snippetdir->name);
347
            if (!$sg_snippetdir->create()) {
348
                throw new midcom_error("Failed to create snippetdir {$basedir}: " . midcom_connection::get_error_string());
349
            }
350
        }
351
352
        $lib_snippetdir = new midcom_db_snippetdir();
353
        if (!$lib_snippetdir->get_by_path("{$basedir}/{$this->_request_data['name']}")) {
354
            $lib_snippetdir = new midcom_db_snippetdir();
355
            $lib_snippetdir->up = $sg_snippetdir->id;
356
            $lib_snippetdir->name = $this->_request_data['name'];
357
            if (!$lib_snippetdir->create()) {
358
                throw new midcom_error("Failed to create snippetdir {$basedir}/{$lib_snippetdir->name}: " . midcom_connection::get_error_string());
359
            }
360
        }
361
362
        $snippet = new midcom_db_snippet();
363
        if (!$snippet->get_by_path("{$basedir}/{$this->_request_data['name']}/config")) {
364
            $sn = new midcom_db_snippet();
365
            $sn->snippetdir = $lib_snippetdir->id;
366
            $sn->name = 'config';
367
            $sn->code = $config;
368
            return $sn->create();
369
        }
370
371
        $snippet->code = $config;
372
        return $snippet->update();
373
    }
374
375
    /**
376
     * Save configuration values to a topic as parameters
377
     */
378
    private function save_topic(midcom_db_topic $topic, array $config) : bool
379
    {
380
        $success = true;
381
        foreach ($this->_request_data['config']->_global as $global_key => $global_value) {
382
            if (   isset($config[$global_key])
383
                && $config[$global_key] != $global_value) {
384
                continue;
385
                // Skip the ones we will set next
386
            }
387
388
            // Clear unset params
389
            if ($topic->get_parameter($this->_request_data['name'], $global_key)) {
390
                $success = $topic->delete_parameter($this->_request_data['name'], $global_key) && $success;
391
            }
392
        }
393
394
        foreach ($config as $key => $value) {
395
            if (   is_array($value)
396
                || is_object($value)) {
397
                /**
398
                 * See http://trac.midgard-project.org/ticket/1442
399
                 $topic->set_parameter($this->_request_data['name'], var_export($value, true));
400
                 */
401
                continue;
402
            }
403
404
            if ($value === false) {
405
                $value = '0';
406
            }
407
            $success = $topic->set_parameter($this->_request_data['name'], $key, $value) && $success;
408
        }
409
        return $success;
410
    }
411
412 2
    private function _detect_schema(string $key, $value) : array
413
    {
414
        $result = [
415 2
            'title'       => $key,
416 2
            'type'        => 'text',
417 2
            'widget'      => 'text',
418
        ];
419
420 2
        $type = gettype($value);
421 2
        switch ($type) {
422 2
            case "boolean":
423 2
                $result['type'] = 'boolean';
424 2
                $result['widget'] = 'checkbox';
425 2
                break;
426 2
            case "array":
427 2
                $result['widget'] = 'textarea';
428
429 2
                if (isset($this->_request_data['folder'])) {
430
                    // Complex Array fields should be readonly for topics as we cannot store and read them properly with parameters
431 1
                    $result['readonly'] = true;
432
                }
433
434 2
                break;
435
            default:
436 2
                if (preg_match("/\n/", $value)) {
437
                    $result['widget'] = 'textarea';
438
                }
439
        }
440
441 2
        return $result;
442
    }
443
}
444