Completed
Push — master ( 640b0d...80f9cb )
by Andreas
20:40
created

midcom_admin_help_help   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 456
Duplicated Lines 0 %

Test Coverage

Coverage 43.09%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 231
c 1
b 0
f 0
dl 0
loc 456
ccs 106
cts 246
cp 0.4309
rs 3.28
wmc 64

22 Methods

Rating   Name   Duplication   Size   Complexity  
A _on_initialize() 0 8 1
A _add_virtual_files() 0 39 5
A _list_components() 0 15 3
A _handler_help() 0 21 2
A get_documentation_dir() 0 3 1
A _show_component() 0 8 1
A read_component_handlers() 0 25 3
A read_url_methods() 0 26 5
A _prepare_breadcrumb() 0 16 4
A _handler_welcome() 0 10 1
A get_help_contents() 0 7 2
B read_schema_properties() 0 28 6
A _load_file() 0 13 2
A _load_component_data() 0 16 3
A _handler_component() 0 11 1
A _list_physical_files() 0 29 6
A list_files() 0 18 2
A _show_welcome() 0 17 3
A get_help_title() 0 10 3
A _show_help() 0 25 5
A generate_file_path() 0 16 4
A _get_property_data() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like midcom_admin_help_help 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 midcom_admin_help_help, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package midcom.admin.help
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 Michelf\MarkdownExtra;
10
use midgard\portable\storage\connection;
11
12
/**
13
 * Online help display
14
 *
15
 * @package midcom.admin.help
16
 */
17
class midcom_admin_help_help extends midcom_baseclasses_components_plugin
18
{
19
    private $mgdtypes = [
20
        MGD_TYPE_STRING => "string",
21
        MGD_TYPE_INT => "integer",
22
        MGD_TYPE_UINT => "unsigned integer",
23
        MGD_TYPE_FLOAT => "float",
24
        MGD_TYPE_BOOLEAN => "boolean",
25
        MGD_TYPE_TIMESTAMP => "datetime",
26
        MGD_TYPE_LONGTEXT => "longtext",
27
        MGD_TYPE_GUID => "guid",
28
        MGD_TYPE_NONE => 'none'
29
    ];
30
31
    public function _on_initialize()
32
    {
33
        midcom::get()->skip_page_style = true;
34
        // doing this here as this component most probably will not be called by itself.
35
        midcom::get()->style->prepend_component_styledir('midcom.admin.help');
36
37
        $this->add_stylesheet(MIDCOM_STATIC_URL . '/midcom.admin.help/style-editor.css');
38
        midcom::get()->head->add_jsfile(MIDCOM_STATIC_URL . '/midcom.admin.help/twisty.js');
39
    }
40
41
    /**
42
     * Get component's documentation directory path
43
     */
44 6
    private static function get_documentation_dir(string $component) : string
45
    {
46 6
        return midcom::get()->componentloader->path_to_snippetpath($component) . '/documentation/';
47
    }
48
49 6
    public static function generate_file_path(string $help_id, string $component, $language = null) : ?string
50
    {
51 6
        if ($language === null) {
52 6
            $language = midcom::get()->i18n->get_current_language();
53
        }
54
55 6
        $file = self::get_documentation_dir($component) . "{$help_id}.{$language}.txt";
56 6
        if (!file_exists($file)) {
57 6
            if ($language != midcom::get()->config->get('i18n_fallback_language')) {
58
                // Try MidCOM's default fallback language
59 1
                return self::generate_file_path($help_id, $component, midcom::get()->config->get('i18n_fallback_language'));
60
            }
61 6
            return null;
62
        }
63
64 3
        return $file;
65
    }
66
67 2
    private function get_help_title($help_id, string $component) : string
68
    {
69 2
        if ($path = self::generate_file_path($help_id, $component)) {
70 2
            $file_contents = file($path);
71 2
            if (trim($file_contents[0])) {
72 2
                return trim($file_contents[0]);
73
            }
74
        }
75
76
        return midcom::get()->i18n->get_string("help_" . $help_id, 'midcom.admin.help');
77
    }
78
79
    /**
80
     * Load the file from the component's documentation directory.
81
     */
82 5
    private function _load_file($help_id, string $component) : ?string
83
    {
84
        // Try loading the file
85 5
        $file = self::generate_file_path($help_id, $component);
86 5
        if (!$file) {
87 4
            return null;
88
        }
89
90
        // Load the contents
91 1
        $help_contents = file_get_contents($file);
92
93
        // Replace static URLs (URLs for screenshots etc)
94 1
        return str_replace('MIDCOM_STATIC_URL', MIDCOM_STATIC_URL, $help_contents);
95
    }
96
97
    /**
98
     * Load a help file and markdownize it
99
     */
100 5
    public function get_help_contents($help_id, $component) : ?string
101
    {
102 5
        $text = $this->_load_file($help_id, $component);
103 5
        if (!$text) {
104 4
            return null;
105
        }
106 1
        return MarkdownExtra::defaultTransform($text);
107
    }
108
109 3
    public function list_files(string $component, bool $with_index = false) : array
110
    {
111 3
        $files = $this->_list_physical_files($component);
112 3
        $files = $this->_add_virtual_files($files, $component);
113
114 3
        ksort($files);
115
        // prepend 'index' URL if required
116 3
        if ($with_index) {
117 1
            $files = array_merge([
118
                'index' => [
119 1
                    'path' => '/',
120 1
                    'subject' => $this->_l10n->get('help_index'),
121 1
                    'lang' => 'en',
122
                ]],
123 1
                $files
124
            );
125
        }
126 3
        return $files;
127
    }
128
129 3
    private function _add_virtual_files(array $files, string $component) : array
130
    {
131
        // Schemas
132 3
        $this->_request_data['mgdschemas'] = midcom::get()->dbclassloader->get_component_classes($component);
133 3
        if (!empty($this->_request_data['mgdschemas'])) {
134 1
            $files['mgdschemas'] = [
135 1
                'path' => '/mgdschemas',
136 1
                'subject' => $this->_l10n->get('help_mgdschemas'),
137 1
                'lang' => 'en',
138
            ];
139
        }
140
141
        // URL Methods
142 3
        $this->_request_data['urlmethods'] = $this->read_url_methods($component);
143 3
        if (!empty($this->_request_data['urlmethods'])) {
144 2
            $files['urlmethods'] = [
145 2
                'path' => '/urlmethods',
146 2
                'subject' => $this->_l10n->get('help_urlmethods'),
147 2
                'lang' => 'en',
148
            ];
149
        }
150
151
        // Break if dealing with MidCOM Core docs
152 3
        if ($component == 'midcom') {
153
            ksort($files);
154
            return $files;
155
        }
156
157
        // handlers
158 3
        $this->_request_data['request_switch_info'] = $this->read_component_handlers($component);
159 3
        if (!empty($this->_request_data['request_switch_info'])) {
160 2
            $files['handlers'] = [
161 2
                'path' => '/handlers',
162 2
                'subject' => $this->_l10n->get('help_handlers'),
163 2
                'lang' => 'en',
164
            ];
165
        }
166
167 3
        return $files;
168
    }
169
170 3
    private function _list_physical_files(string $component) : array
171
    {
172 3
        $component_dir = self::get_documentation_dir($component);
173 3
        if (!is_dir($component_dir)) {
174 1
            return [];
175
        }
176
177 2
        $files = [];
178 2
        $pattern = $component_dir . '*.{' . midcom::get()->i18n->get_current_language() . ',' . midcom::get()->config->get('i18n_fallback_language') . '}.txt';
179
180 2
        foreach (glob($pattern, GLOB_NOSORT|GLOB_BRACE) as $path) {
181 2
            $entry = basename($path);
182 2
            if (   str_starts_with($entry, 'index')
183 2
                || str_starts_with($entry, 'handler')
184 2
                || str_starts_with($entry, 'urlmethod')) {
185
                // Ignore dotfiles, handlers & index.lang.txt
186 1
                continue;
187
            }
188
189 2
            $filename_parts = explode('.', $entry);
190
191 2
            $files[$filename_parts[0]] = [
192 2
                'path' => $path,
193 2
                'subject' => $this->get_help_title($filename_parts[0], $component),
194 2
                'lang' => $filename_parts[1],
195
            ];
196
        }
197
198 2
        return $files;
199
    }
200
201 3
    private function read_component_handlers(string $component) : array
202
    {
203 3
        $data = [];
204
205 3
        $handler = midcom::get()->componentloader->get_interface_class($component);
206 3
        $viewer = $handler->get_viewer(new midcom_db_topic);
207 3
        $routes = $viewer->get_router()->getRouteCollection()->all();
208 3
        foreach ($routes as $request_handler_id => $route) {
209 2
            $details = [];
210
211
            // Build the dynamic_loadable URI, starting from topic path
212 2
            $details['route'] = str_replace(midcom_connection::get_url('prefix') . '/', '', midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX));
213
214
            // Add fixed arguments
215 2
            $details['route'] .= preg_replace('/args_(\d+)/', 'args[\1]', $route->getPath());
216 2
            list ($details['controller'], $details['action']) = explode('::', $route->getDefault('_controller'), 2);
217
218 2
            if (self::generate_file_path('handlers_' . $request_handler_id, $component)) {
219 1
                $details['info'] = $this->get_help_contents('handlers_' . $request_handler_id, $component);
220 1
                $details['handler_help_url'] = 'handlers_' . $request_handler_id;
221
            }
222 2
            $data[$request_handler_id] = $details;
223
        }
224
225 3
        return $data;
226
    }
227
228 3
    private function read_url_methods(string $component) : array
229
    {
230 3
        $data = [];
231
232 3
        $exec_path = midcom::get()->componentloader->path_to_snippetpath($component) . '/exec/';
233 3
        if (   !is_dir($exec_path)
234 3
            || !is_readable($exec_path)) {
235
            // Directory not accessible, skip loading it
236 1
            return $data;
237
        }
238
239 2
        foreach (glob($exec_path . '/*.php', GLOB_NOSORT) as $path) {
240 2
            $file = basename($path);
241 2
            $data[$file] = [];
242
243 2
            $info_id = "urlmethod_" . str_replace('.php', '', $file);
244
245 2
            $data[$file]['url'] = '/midcom-exec-' . $component . '/' . $file;
246 2
            $data[$file]['description'] = $this->get_help_contents($info_id, $component);
247
248 2
            if (self::generate_file_path($info_id, $component)) {
249
                $data[$file]['handler_help_url'] = $info_id;
250
            }
251
        }
252
253 2
        return $data;
254
    }
255
256
    private function read_schema_properties()
257
    {
258
        foreach (array_keys($this->_request_data['mgdschemas']) as $mgdschema_class) {
259
            $mrp = new midgard_reflection_property($mgdschema_class);
260
            $class_props = connection::get_em()->getClassMetadata($mgdschema_class)->get_schema_properties();
0 ignored issues
show
introduced by
The method get_schema_properties() does not exist on Doctrine\ORM\Mapping\ClassMetadata. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

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

260
            $class_props = connection::get_em()->getClassMetadata($mgdschema_class)->/** @scrutinizer ignore-call */ get_schema_properties();
Loading history...
261
262
            unset($class_props['metadata']);
263
            $default_properties = [];
264
            $additional_properties = [];
265
266
            foreach ($class_props as $prop) {
267
                switch ($prop) {
268
                    case 'action':
269
                        // Midgard-internal properties, skip
270
                        break;
271
                    case 'guid':
272
                    case 'id':
273
                        $default_properties[$prop] = $this->_get_property_data($mrp, $prop);
274
                        break;
275
                    default:
276
                        $additional_properties[$prop] = $this->_get_property_data($mrp, $prop);
277
                        break;
278
                }
279
            }
280
            ksort($default_properties);
281
            ksort($additional_properties);
282
283
            $this->_request_data['properties'][$mgdschema_class] = array_merge($default_properties, $additional_properties);
284
        }
285
    }
286
287
    private function _get_property_data(midgard_reflection_property $mrp, string $prop) : array
288
    {
289
        return [
290
            'value' => $mrp->description($prop),
291
            'link' => $mrp->is_link($prop),
292
            'link_name' => $mrp->get_link_name($prop),
293
            'link_target' => $mrp->get_link_target($prop),
294
            'midgard_type' => $this->mgdtypes[$mrp->get_midgard_type($prop)]
295
        ];
296
    }
297
298
    private function _load_component_data(string $name) : array
299
    {
300
        $component_array = [];
301
        $component_array['name'] = $name;
302
        $component_array['title'] = '';
303
        if (midcom::get()->i18n->get_l10n($name)->string_exists($name)) {
304
            $component_array['title'] = midcom::get()->i18n->get_string($name, $name);
305
        }
306
        $component_array['icon'] = midcom::get()->componentloader->get_component_icon($name);
307
308
        if (midcom::get()->componentloader->is_installed($name)) {
309
            $manifest = midcom::get()->componentloader->get_manifest($name);
310
            $component_array['purecode'] = $manifest->purecode;
311
            $component_array['description'] = $manifest->description;
312
        }
313
        return $component_array;
314
    }
315
316
    private function _list_components()
317
    {
318
        $this->_request_data['components'] = [];
319
        $this->_request_data['libraries'] = [];
320
321
        foreach (midcom::get()->componentloader->get_manifests() as $name => $manifest) {
322
            $type = $manifest->purecode ? 'libraries' : 'components';
323
324
            $component_array = $this->_load_component_data($name);
325
326
            $this->_request_data[$type][$name] = $component_array;
327
        }
328
329
        asort($this->_request_data['components']);
330
        asort($this->_request_data['libraries']);
331
    }
332
333
    private function _prepare_breadcrumb($handler_id)
334
    {
335
        $this->add_breadcrumb($this->router->generate('welcome'), $this->_l10n->get('midcom.admin.help'));
336
337
        if (in_array($handler_id, ['help', 'component'])) {
338
            $this->add_breadcrumb(
339
                $this->router->generate('component', ['component' => $this->_request_data['component']]),
340
                sprintf($this->_l10n->get('help for %s'), midcom::get()->i18n->get_string($this->_request_data['component'], $this->_request_data['component']))
341
            );
342
        }
343
344
        if ($handler_id == 'help') {
345
            if (in_array($this->_request_data['help_id'], ['handlers', 'urlmethods', 'mgdschemas'])) {
346
                $this->add_breadcrumb("", $this->_l10n->get($this->_request_data['help_id']));
347
            } else {
348
                $this->add_breadcrumb("", $this->get_help_title($this->_request_data['help_id'], $this->_request_data['component']));
349
            }
350
        }
351
    }
352
353
    public function _handler_welcome(string $handler_id, array &$data)
354
    {
355
        midcom::get()->auth->require_valid_user();
356
357
        $data['view_title'] = $this->_l10n->get($this->_component);
358
        midcom::get()->head->set_pagetitle($data['view_title']);
359
360
        $this->_list_components();
361
362
        $this->_prepare_breadcrumb($handler_id);
363
    }
364
365
    /**
366
     * Shows the help system main screen
367
     *
368
     * @param mixed $handler_id The ID of the handler.
369
     * @param array $data The local request data.
370
     */
371
    public function _show_welcome($handler_id, array &$data)
372
    {
373
        midcom_show_style('midcom_admin_help_header');
374
        midcom_show_style('midcom_admin_help_about');
375
        $list_types = ['components', 'libraries'];
376
377
        foreach ($list_types as $list_type) {
378
            $data['list_type'] = $list_type;
379
            midcom_show_style('midcom_admin_help_list_header');
380
            foreach ($data[$list_type] as $component_data) {
381
                $data['component_data'] = $component_data;
382
                midcom_show_style('midcom_admin_help_list_item');
383
            }
384
            midcom_show_style('midcom_admin_help_list_footer');
385
        }
386
387
        midcom_show_style('midcom_admin_help_footer');
388
    }
389
390
    public function _handler_component(string $handler_id, string $component, array &$data)
391
    {
392
        midcom::get()->auth->require_valid_user();
393
394
        $data['component'] = $component;
395
        $data['view_title'] = sprintf($this->_l10n->get('help for %s'), $this->_i18n->get_string($data['component'], $data['component']));
396
        midcom::get()->head->set_pagetitle($data['view_title']);
397
398
        $data['help_files'] = $this->list_files($data['component']);
399
        $data['html'] = $this->get_help_contents('index', $data['component']);
400
        $this->_prepare_breadcrumb($handler_id);
401
    }
402
403
    /**
404
     * Shows the component help ToC.
405
     *
406
     * @param mixed $handler_id The ID of the handler.
407
     * @param array $data The local request data.
408
     */
409
    public function _show_component($handler_id, array &$data)
410
    {
411
        midcom_show_style('midcom_admin_help_header');
412
413
        midcom_show_style('midcom_admin_help_show');
414
        midcom_show_style('midcom_admin_help_component');
415
416
        midcom_show_style('midcom_admin_help_footer');
417
    }
418
419
    public function _handler_help(string $handler_id, string $component, string $help_id, array &$data)
420
    {
421
        midcom::get()->auth->require_valid_user();
422
423
        $data['help_id'] = $help_id;
424
        $data['component'] = $component;
425
        $data['help_files'] = $this->list_files($data['component']);
426
427
        if ($data['help_id'] == 'mgdschemas') {
428
            $this->read_schema_properties();
429
        }
430
        $data['html'] = $this->get_help_contents($data['help_id'], $data['component']);
431
432
        // Table of contents navi
433
        $data['view_title'] = sprintf(
434
            $this->_l10n->get('help for %s in %s'),
435
            $this->get_help_title($data['help_id'], $data['component']),
436
            $this->_i18n->get_string($data['component'], $data['component'])
437
        );
438
        midcom::get()->head->set_pagetitle($data['view_title']);
439
        $this->_prepare_breadcrumb($handler_id);
440
    }
441
442
    /**
443
     * Shows the help page.
444
     *
445
     * @param mixed $handler_id The ID of the handler.
446
     * @param array $data The local request data.
447
     */
448
    public function _show_help($handler_id, array &$data)
449
    {
450
        midcom_show_style('midcom_admin_help_header');
451
        switch ($this->_request_data['help_id']) {
452
            case 'handlers':
453
                midcom_show_style('midcom_admin_help_handlers');
454
                break;
455
            case 'mgdschemas':
456
                midcom_show_style('midcom_admin_help_show');
457
                midcom_show_style('midcom_admin_help_mgdschemas');
458
                break;
459
            case 'urlmethods':
460
                midcom_show_style('midcom_admin_help_show');
461
                midcom_show_style('midcom_admin_help_urlmethods');
462
                break;
463
            default:
464
                midcom_show_style('midcom_admin_help_show');
465
466
                if (!$this->_request_data['html']) {
467
                    $this->_request_data['html'] = $this->get_help_contents('notfound', 'midcom.admin.help');
468
                    midcom_show_style('midcom_admin_help_show');
469
                    midcom_show_style('midcom_admin_help_component');
470
                }
471
        }
472
        midcom_show_style('midcom_admin_help_footer');
473
    }
474
}
475