Passed
Push — master ( 9df40e...8d71ac )
by Andreas
23:46
created

midcom_admin_help_help   F

Complexity

Total Complexity 63

Size/Duplication

Total Lines 444
Duplicated Lines 0 %

Test Coverage

Coverage 93.33%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 226
c 1
b 0
f 0
dl 0
loc 444
ccs 224
cts 240
cp 0.9333
rs 3.36
wmc 63

22 Methods

Rating   Name   Duplication   Size   Complexity  
A _add_virtual_files() 0 39 5
A _list_components() 0 13 3
A _handler_help() 0 19 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 8 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 13 2
A _handler_component() 0 9 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 _on_initialize() 0 10 1
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 3
    public function _on_initialize()
32
    {
33 3
        midcom::get()->auth->require_valid_user();
34
35 3
        midcom::get()->skip_page_style = true;
36
        // doing this here as this component most probably will not be called by itself.
37 3
        midcom::get()->style->prepend_component_styledir('midcom.admin.help');
38
39 3
        $this->add_stylesheet(MIDCOM_STATIC_URL . '/midcom.admin.help/style-editor.css');
40 3
        midcom::get()->head->add_jsfile(MIDCOM_STATIC_URL . '/midcom.admin.help/twisty.js');
41 3
    }
42
43
    /**
44
     * Get component's documentation directory path
45
     */
46 8
    private static function get_documentation_dir(string $component) : string
47
    {
48 8
        return midcom::get()->componentloader->path_to_snippetpath($component) . '/documentation/';
49
    }
50
51 8
    public static function generate_file_path(string $help_id, string $component, string $language = null) : ?string
52
    {
53 8
        if ($language === null) {
54 8
            $language = midcom::get()->i18n->get_current_language();
55
        }
56
57 8
        $file = self::get_documentation_dir($component) . "{$help_id}.{$language}.txt";
58 8
        if (!file_exists($file)) {
59 8
            if ($language != midcom::get()->config->get('i18n_fallback_language')) {
60
                // Try MidCOM's default fallback language
61 1
                return self::generate_file_path($help_id, $component, midcom::get()->config->get('i18n_fallback_language'));
62
            }
63 8
            return null;
64
        }
65
66 5
        return $file;
67
    }
68
69 4
    private function get_help_title(string $help_id, string $component) : string
70
    {
71 4
        if ($path = self::generate_file_path($help_id, $component)) {
72 4
            $file_contents = file($path);
73 4
            if (trim($file_contents[0])) {
74 4
                return trim($file_contents[0]);
75
            }
76
        }
77
78 1
        return $this->_l10n->get("help_" . $help_id);
79
    }
80
81
    /**
82
     * Load the file from the component's documentation directory.
83
     */
84 7
    private function _load_file(string $help_id, string $component) : ?string
85
    {
86
        // Try loading the file
87 7
        $file = self::generate_file_path($help_id, $component);
88 7
        if (!$file) {
89 6
            return null;
90
        }
91
92
        // Load the contents
93 1
        $help_contents = file_get_contents($file);
94
95
        // Replace static URLs (URLs for screenshots etc)
96 1
        return str_replace('MIDCOM_STATIC_URL', MIDCOM_STATIC_URL, $help_contents);
97
    }
98
99
    /**
100
     * Load a help file and markdownize it
101
     */
102 7
    public function get_help_contents(string $help_id, string $component) : ?string
103
    {
104 7
        $text = $this->_load_file($help_id, $component);
105 7
        if (!$text) {
106 6
            return null;
107
        }
108 1
        return MarkdownExtra::defaultTransform($text);
109
    }
110
111 5
    public function list_files(string $component, bool $with_index = false) : array
112
    {
113 5
        $files = $this->_list_physical_files($component);
114 5
        $files = $this->_add_virtual_files($files, $component);
115
116 5
        ksort($files);
117
        // prepend 'index' URL if required
118 5
        if ($with_index) {
119 1
            $files = array_merge([
120
                'index' => [
121 1
                    'path' => '/',
122 1
                    'subject' => $this->_l10n->get('help_index'),
123 1
                    'lang' => 'en',
124
                ]],
125 1
                $files
126
            );
127
        }
128 5
        return $files;
129
    }
130
131 5
    private function _add_virtual_files(array $files, string $component) : array
132
    {
133
        // Schemas
134 5
        $this->_request_data['mgdschemas'] = midcom::get()->dbclassloader->get_component_classes($component);
135 5
        if (!empty($this->_request_data['mgdschemas'])) {
136 3
            $files['mgdschemas'] = [
137 3
                'path' => '/mgdschemas',
138 3
                'subject' => $this->_l10n->get('help_mgdschemas'),
139 3
                'lang' => 'en',
140
            ];
141
        }
142
143
        // URL Methods
144 5
        $this->_request_data['urlmethods'] = $this->read_url_methods($component);
145 5
        if (!empty($this->_request_data['urlmethods'])) {
146 4
            $files['urlmethods'] = [
147 4
                'path' => '/urlmethods',
148 4
                'subject' => $this->_l10n->get('help_urlmethods'),
149 4
                'lang' => 'en',
150
            ];
151
        }
152
153
        // Break if dealing with MidCOM Core docs
154 5
        if ($component == 'midcom') {
155
            ksort($files);
156
            return $files;
157
        }
158
159
        // handlers
160 5
        $this->_request_data['request_switch_info'] = $this->read_component_handlers($component);
161 5
        if (!empty($this->_request_data['request_switch_info'])) {
162 4
            $files['handlers'] = [
163 4
                'path' => '/handlers',
164 4
                'subject' => $this->_l10n->get('help_handlers'),
165 4
                'lang' => 'en',
166
            ];
167
        }
168
169 5
        return $files;
170
    }
171
172 5
    private function _list_physical_files(string $component) : array
173
    {
174 5
        $component_dir = self::get_documentation_dir($component);
175 5
        if (!is_dir($component_dir)) {
176 1
            return [];
177
        }
178
179 4
        $files = [];
180 4
        $pattern = $component_dir . '*.{' . $this->_i18n->get_current_language() . ',' . midcom::get()->config->get('i18n_fallback_language') . '}.txt';
181
182 4
        foreach (glob($pattern, GLOB_NOSORT|GLOB_BRACE) as $path) {
183 4
            $entry = basename($path);
184 4
            if (   str_starts_with($entry, 'index')
185 4
                || str_starts_with($entry, 'handler')
186 4
                || str_starts_with($entry, 'urlmethod')) {
187
                // Ignore dotfiles, handlers & index.lang.txt
188 1
                continue;
189
            }
190
191 4
            $filename_parts = explode('.', $entry);
192
193 4
            $files[$filename_parts[0]] = [
194 4
                'path' => $path,
195 4
                'subject' => $this->get_help_title($filename_parts[0], $component),
196 4
                'lang' => $filename_parts[1],
197
            ];
198
        }
199
200 4
        return $files;
201
    }
202
203 5
    private function read_component_handlers(string $component) : array
204
    {
205 5
        $data = [];
206
207 5
        $handler = midcom::get()->componentloader->get_interface_class($component);
208 5
        $viewer = $handler->get_viewer(new midcom_db_topic);
209 5
        $routes = $viewer->get_router()->getRouteCollection()->all();
210 5
        foreach ($routes as $request_handler_id => $route) {
211 4
            $details = [];
212
213
            // Build the dynamic_loadable URI, starting from topic path
214 4
            $details['route'] = str_replace(midcom_connection::get_url('prefix') . '/', '', midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX));
215
216
            // Add fixed arguments
217 4
            $details['route'] .= preg_replace('/args_(\d+)/', 'args[\1]', $route->getPath());
218 4
            list ($details['controller'], $details['action']) = explode('::', $route->getDefault('_controller'), 2);
0 ignored issues
show
Bug introduced by
It seems like $route->getDefault('_controller') can also be of type null; however, parameter $string of explode() 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

218
            list ($details['controller'], $details['action']) = explode('::', /** @scrutinizer ignore-type */ $route->getDefault('_controller'), 2);
Loading history...
219
220 4
            if (self::generate_file_path('handlers_' . $request_handler_id, $component)) {
221 1
                $details['info'] = $this->get_help_contents('handlers_' . $request_handler_id, $component);
222 1
                $details['handler_help_url'] = 'handlers_' . $request_handler_id;
223
            }
224 4
            $data[$request_handler_id] = $details;
225
        }
226
227 5
        return $data;
228
    }
229
230 5
    private function read_url_methods(string $component) : array
231
    {
232 5
        $data = [];
233
234 5
        $exec_path = midcom::get()->componentloader->path_to_snippetpath($component) . '/exec/';
235 5
        if (   !is_dir($exec_path)
236 5
            || !is_readable($exec_path)) {
237
            // Directory not accessible, skip loading it
238 1
            return $data;
239
        }
240
241 4
        foreach (glob($exec_path . '/*.php', GLOB_NOSORT) as $path) {
242 4
            $file = basename($path);
243 4
            $data[$file] = [];
244
245 4
            $info_id = "urlmethod_" . str_replace('.php', '', $file);
246
247 4
            $data[$file]['url'] = '/midcom-exec-' . $component . '/' . $file;
248 4
            $data[$file]['description'] = $this->get_help_contents($info_id, $component);
249
250 4
            if (self::generate_file_path($info_id, $component)) {
251
                $data[$file]['handler_help_url'] = $info_id;
252
            }
253
        }
254
255 4
        return $data;
256
    }
257
258 1
    private function read_schema_properties()
259
    {
260 1
        foreach (array_keys($this->_request_data['mgdschemas']) as $mgdschema_class) {
261 1
            $mrp = new midgard_reflection_property($mgdschema_class);
262 1
            $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

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