Completed
Push — master ( da2115...dd0feb )
by Andreas
18:06
created

midcom_admin_help_help::get_documentation_dir()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 10
c 0
b 0
f 0
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
     * @param string $component Component name
45
     * @return string Component documentation directory path
46
     */
47 6
    private static function get_documentation_dir($component)
48
    {
49 6
        if (!midcom::get()->componentloader->is_installed($component)) {
50
            throw new midcom_error("Failed to generate documentation path for component {$component} as it is not installed.");
51
        }
52 6
        return midcom::get()->componentloader->path_to_snippetpath($component) . '/documentation/';
53
    }
54
55 6
    public static function generate_file_path($help_id, $component, $language = null)
56
    {
57 6
        if ($language === null) {
58 6
            $language = midcom::get()->i18n->get_current_language();
59
        }
60
61 6
        $file = self::get_documentation_dir($component) . "{$help_id}.{$language}.txt";
62 6
        if (!file_exists($file)) {
63 6
            if ($language != midcom::get()->config->get('i18n_fallback_language')) {
64
                // Try MidCOM's default fallback language
65 1
                return self::generate_file_path($help_id, $component, midcom::get()->config->get('i18n_fallback_language'));
66
            }
67 6
            return null;
68
        }
69
70 3
        return $file;
71
    }
72
73 2
    private function get_help_title($help_id, $component)
74
    {
75 2
        if ($path = self::generate_file_path($help_id, $component)) {
76 2
            $file_contents = file($path);
77 2
            if (trim($file_contents[0])) {
78 2
                return trim($file_contents[0]);
79
            }
80
        }
81
82
        return midcom::get()->i18n->get_string("help_" . $help_id, 'midcom.admin.help');
83
    }
84
85
    /**
86
     * Load the file from the component's documentation directory.
87
     */
88 5
    private function _load_file($help_id, $component)
89
    {
90
        // Try loading the file
91 5
        $file = self::generate_file_path($help_id, $component);
92 5
        if (!$file) {
93 4
            return false;
94
        }
95
96
        // Load the contents
97 1
        $help_contents = file_get_contents($file);
98
99
        // Replace static URLs (URLs for screenshots etc)
100 1
        return str_replace('MIDCOM_STATIC_URL', MIDCOM_STATIC_URL, $help_contents);
101
    }
102
103
    /**
104
     * Load a help file and markdownize it
105
     */
106 5
    public function get_help_contents($help_id, $component)
107
    {
108 5
        $text = $this->_load_file($help_id, $component);
109 5
        if (!$text) {
110 4
            return false;
111
        }
112 1
        return MarkdownExtra::defaultTransform($text);
113
    }
114
115 3
    public function list_files($component, $with_index = false)
116
    {
117 3
        $files = $this->_list_physical_files($component);
118 3
        $files = $this->_add_virtual_files($files, $component);
119
120 3
        ksort($files);
121
        // prepend 'index' URL if required
122 3
        if ($with_index) {
123 1
            $files = array_merge([
124
                'index' => [
125 1
                    'path' => '/',
126 1
                    'subject' => $this->_l10n->get('help_index'),
127 1
                    'lang' => 'en',
128
                ]],
129
                $files
130
            );
131
        }
132 3
        return $files;
133
    }
134
135 3
    private function _add_virtual_files($files, $component)
136
    {
137
        // Schemas
138 3
        $this->_request_data['mgdschemas'] = midcom::get()->dbclassloader->get_component_classes($component);
139 3
        if (!empty($this->_request_data['mgdschemas'])) {
140 1
            $files['mgdschemas'] = [
141 1
                'path' => '/mgdschemas',
142 1
                'subject' => $this->_l10n->get('help_mgdschemas'),
143 1
                'lang' => 'en',
144
            ];
145
        }
146
147
        // URL Methods
148 3
        $this->_request_data['urlmethods'] = $this->read_url_methods($component);
149 3
        if (!empty($this->_request_data['urlmethods'])) {
150 2
            $files['urlmethods'] = [
151 2
                'path' => '/urlmethods',
152 2
                'subject' => $this->_l10n->get('help_urlmethods'),
153 2
                'lang' => 'en',
154
            ];
155
        }
156
157
        // Break if dealing with MidCOM Core docs
158 3
        if ($component == 'midcom') {
159
            ksort($files);
160
            return $files;
161
        }
162
163
        // handlers
164 3
        $this->_request_data['request_switch_info'] = $this->read_component_handlers($component);
165 3
        if (!empty($this->_request_data['request_switch_info'])) {
166 2
            $files['handlers'] = [
167 2
                'path' => '/handlers',
168 2
                'subject' => $this->_l10n->get('help_handlers'),
169 2
                'lang' => 'en',
170
            ];
171
        }
172
173 3
        return $files;
174
    }
175
176 3
    private function _list_physical_files($component)
177
    {
178 3
        $component_dir = self::get_documentation_dir($component);
179 3
        if (!is_dir($component_dir)) {
180 1
            return [];
181
        }
182
183 2
        $files = [];
184 2
        $pattern = $component_dir . '*.{' . midcom::get()->i18n->get_current_language() . ',' . midcom::get()->config->get('i18n_fallback_language') . '}.txt';
185
186 2
        foreach (glob($pattern, GLOB_NOSORT|GLOB_BRACE) as $path) {
187 2
            $entry = basename($path);
188 2
            if (    substr($entry, 0, 5) == 'index'
189 2
                 || substr($entry, 0, 7) == 'handler'
190 2
                 || substr($entry, 0, 9) == 'urlmethod') {
191
                // Ignore dotfiles, handlers & index.lang.txt
192 1
                continue;
193
            }
194
195 2
            $filename_parts = explode('.', $entry);
196
197 2
            $files[$filename_parts[0]] = [
198 2
                'path' => $path,
199 2
                'subject' => $this->get_help_title($filename_parts[0], $component),
200 2
                'lang' => $filename_parts[1],
201
            ];
202
        }
203
204 2
        return $files;
205
    }
206
207 3
    private function read_component_handlers($component)
208
    {
209 3
        $data = [];
210
211 3
        $handler = midcom::get()->componentloader->get_interface_class($component);
212 3
        $viewer = $handler->get_viewer(new midcom_db_topic);
213 3
        $routes = $viewer->get_router()->getRouteCollection()->all();
214 3
        foreach ($routes as $request_handler_id => $route) {
215 2
            $details = [];
216
217
            // Build the dynamic_loadable URI, starting from topic path
218 2
            $details['route'] = str_replace(midcom_connection::get_url('prefix') . '/', '', midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX));
219
220
            // Add fixed arguments
221 2
            $details['route'] .= preg_replace('/args_(\d+)/', 'args[\1]', $route->getPath());
222 2
            list ($details['controller'], $details['action']) = explode('::', $route->getDefault('_controller'), 2);
223
224 2
            if (self::generate_file_path('handlers_' . $request_handler_id, $component)) {
225 1
                $details['info'] = $this->get_help_contents('handlers_' . $request_handler_id, $component);
226 1
                $details['handler_help_url'] = 'handlers_' . $request_handler_id;
227
            }
228 2
            $data[$request_handler_id] = $details;
229
        }
230
231 3
        return $data;
232
    }
233
234 3
    private function read_url_methods($component)
235
    {
236 3
        $data = [];
237
238 3
        $exec_path = midcom::get()->componentloader->path_to_snippetpath($component) . '/exec/';
239 3
        if (   !is_dir($exec_path)
240 3
            || !is_readable($exec_path)) {
241
            // Directory not accessible, skip loading it
242 1
            return $data;
243
        }
244
245 2
        foreach (glob($exec_path . '/*.php', GLOB_NOSORT) as $path) {
246 2
            $file = basename($path);
247 2
            $data[$file] = [];
248
249 2
            $info_id = "urlmethod_" . str_replace('.php', '', $file);
250
251 2
            $data[$file]['url'] = '/midcom-exec-' . $component . '/' . $file;
252 2
            $data[$file]['description'] = $this->get_help_contents($info_id, $component);
253
254 2
            if (self::generate_file_path($info_id, $component)) {
255 2
                $data[$file]['handler_help_url'] = $info_id;
256
            }
257
        }
258
259 2
        return $data;
260
    }
261
262
    private function read_schema_properties()
263
    {
264
        foreach (array_keys($this->_request_data['mgdschemas']) as $mgdschema_class) {
265
            $mrp = new midgard_reflection_property($mgdschema_class);
266
            $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

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