Passed
Push — master ( b7f732...42e110 )
by Andreas
18:16
created

midcom_admin_help_help::get_help_title()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

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

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

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