Passed
Branch develop (c87c46)
by Nikolay
05:17
created

Elements::addMenuItemSSHMenu()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
/*
4
 * MikoPBX - free phone system for small business
5
 * Copyright © 2017-2023 Alexey Portnov and Nikolay Beketov
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License along with this program.
18
 * If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
namespace MikoPBX\AdminCabinet\Library;
22
23
use MikoPBX\AdminCabinet\Controllers\AsteriskManagersController;
24
use MikoPBX\AdminCabinet\Controllers\CallDetailRecordsController;
25
use MikoPBX\AdminCabinet\Controllers\CallQueuesController;
26
use MikoPBX\AdminCabinet\Controllers\ConferenceRoomsController;
27
use MikoPBX\AdminCabinet\Controllers\ConsoleController;
28
use MikoPBX\AdminCabinet\Controllers\CustomFilesController;
29
use MikoPBX\AdminCabinet\Controllers\DialplanApplicationsController;
30
use MikoPBX\AdminCabinet\Controllers\ExtensionsController;
31
use MikoPBX\AdminCabinet\Controllers\Fail2BanController;
32
use MikoPBX\AdminCabinet\Controllers\FirewallController;
33
use MikoPBX\AdminCabinet\Controllers\GeneralSettingsController;
34
use MikoPBX\AdminCabinet\Controllers\IncomingRoutesController;
35
use MikoPBX\AdminCabinet\Controllers\IvrMenuController;
36
use MikoPBX\AdminCabinet\Controllers\MailSettingsController;
37
use MikoPBX\AdminCabinet\Controllers\NetworkController;
38
use MikoPBX\AdminCabinet\Controllers\OutboundRoutesController;
39
use MikoPBX\AdminCabinet\Controllers\OutOffWorkTimeController;
40
use MikoPBX\AdminCabinet\Controllers\PbxExtensionModulesController;
41
use MikoPBX\AdminCabinet\Controllers\ProvidersController;
42
use MikoPBX\AdminCabinet\Controllers\RestartController;
43
use MikoPBX\AdminCabinet\Controllers\SoundFilesController;
44
use MikoPBX\AdminCabinet\Controllers\SystemDiagnosticController;
45
use MikoPBX\AdminCabinet\Controllers\TimeSettingsController;
46
use MikoPBX\AdminCabinet\Controllers\UpdateController;
47
use MikoPBX\AdminCabinet\Providers\SecurityPluginProvider;
48
use MikoPBX\Common\Models\PbxExtensionModules;
49
use MikoPBX\Common\Models\PbxSettings;
50
use MikoPBX\Common\Providers\PBXConfModulesProvider;
51
use MikoPBX\Core\System\Util;
52
use MikoPBX\Modules\Config\WebUIConfigInterface;
53
use Phalcon\Di\Injectable;
54
use MikoPBX\Common\Library\Text;
55
56
/**
57
 * Elements
58
 * Helps to build UI elements for the application
59
 *
60
 * @property \MikoPBX\Common\Providers\TranslationProvider translation
61
 *
62
 */
63
class Elements extends Injectable
64
{
65
    private array $_headerMenu
66
        = [
67
            'setup' => [
68
                'caption' => 'mm_Setup',
69
                'iconclass' => '',
70
                'submenu' => [
71
                    ExtensionsController::class => [
72
                        'caption' => 'mm_Extensions',
73
                        'iconclass' => 'user outline',
74
                        'action' => 'index',
75
                        'param' => '#reset-cache',
76
                        'style' => '',
77
                    ],
78
                    CallQueuesController::class => [
79
                        'caption' => 'mm_CallQueues',
80
                        'iconclass' => 'users',
81
                        'action' => 'index',
82
                        'param' => '',
83
                        'style' => '',
84
                    ],
85
                    IvrMenuController::class => [
86
                        'caption' => 'mm_IvrMenu',
87
                        'iconclass' => 'sitemap',
88
                        'action' => 'index',
89
                        'param' => '',
90
                        'style' => '',
91
                    ],
92
                    ConferenceRoomsController::class => [
93
                        'caption' => 'mm_ConferenceRooms',
94
                        'iconclass' => 'phone volume',
95
                        'action' => 'index',
96
                        'param' => '',
97
                        'style' => '',
98
                    ],
99
                    SoundFilesController::class => [
100
                        'caption' => 'mm_SoundFiles',
101
                        'iconclass' => 'sound',
102
                        'action' => 'index',
103
                        'param' => '',
104
                        'style' => '',
105
                    ],
106
                    CallDetailRecordsController::class => [
107
                        'caption' => 'mm_CallDetailRecords',
108
                        'iconclass' => 'list ul',
109
                        'action' => 'index',
110
                        'param' => '',
111
                        'style' => '',
112
                    ],
113
                ],
114
            ],
115
            'routing' => [
116
                'caption' => 'mm_Routing',
117
                'iconclass' => '',
118
                'submenu' => [
119
                    ProvidersController::class => [
120
                        'caption' => 'mm_Providers',
121
                        'iconclass' => 'server',
122
                        'action' => 'index',
123
                        'param' => '',
124
                        'style' => '',
125
                    ],
126
                    IncomingRoutesController::class => [
127
                        'caption' => 'mm_IncomingRoutes',
128
                        'iconclass' => 'map signs',
129
                        'action' => 'index',
130
                        'param' => '',
131
                        'style' => '',
132
                    ],
133
                    OutboundRoutesController::class => [
134
                        'caption' => 'mm_OutboundRoutes',
135
                        'iconclass' => 'random',
136
                        'action' => 'index',
137
                        'param' => '',
138
                        'style' => '',
139
                    ],
140
                    OutOffWorkTimeController::class => [
141
                        'caption' => 'mm_OutOffWorkTime',
142
                        'iconclass' => 'calendar times outline',
143
                        'action' => 'index',
144
                        'param' => '',
145
                        'style' => '',
146
                    ],
147
                ],
148
            ],
149
150
            'modules' => [
151
                'caption' => 'mm_PbxExtensionModules',
152
                'iconclass' => '',
153
                'submenu' => [
154
                    PbxExtensionModulesController::class => [
155
                        'caption' => 'mm_ModuleManager',
156
                        'iconclass' => 'store',
157
                        'action' => 'index',
158
                        'param' => '',
159
                        'style' => '',
160
                    ],
161
                    DialplanApplicationsController::class => [
162
                        'caption' => 'mm_DialplanApplications',
163
                        'iconclass' => 'php',
164
                        'action' => 'index',
165
                        'param' => '',
166
                        'style' => '',
167
                    ],
168
                ],
169
            ],
170
171
            'maintenance' => [
172
                'caption' => 'mm_Maintenance',
173
                'iconclass' => '',
174
                'submenu' => [
175
                    UpdateController::class => [
176
                        'caption' => 'mm_Update',
177
                        'iconclass' => 'sync',
178
                        'action' => 'index',
179
                        'param' => '',
180
                        'style' => '',
181
                    ],
182
                    SystemDiagnosticController::class => [
183
                        'caption' => 'mm_SystemDiagnostic',
184
                        'iconclass' => 'stethoscope',
185
                        'action' => 'index',
186
                        'param' => '',
187
                        'style' => '',
188
                    ],
189
                    ConsoleController::class => [
190
                        'caption' => 'mm_SSHConsole',
191
                        'iconclass' => 'terminal',
192
                        'action' => 'index',
193
                        'param' => '',
194
                        'style' => '',
195
                    ],
196
                    RestartController::class => [
197
                        'caption' => 'mm_Restart',
198
                        'iconclass' => 'power off',
199
                        'action' => 'manage',
200
                        'param' => '',
201
                        'style' => '',
202
                    ],
203
                ],
204
            ],
205
            'networkSettings' => [
206
                'caption' => 'mm_NetworkSettings',
207
                'iconclass' => '',
208
                'submenu' => [
209
                    NetworkController::class => [
210
                        'caption' => 'mm_Network',
211
                        'iconclass' => 'globe',
212
                        'action' => 'modify',
213
                        'param' => '',
214
                        'style' => '',
215
                    ],
216
                    FirewallController::class => [
217
                        'caption' => 'mm_Firewall',
218
                        'iconclass' => 'shield alternate',
219
                        'action' => 'index',
220
                        'param' => '',
221
                        'style' => '',
222
                    ],
223
                    Fail2BanController::class => [
224
                        'caption' => 'mm_Fail2Ban',
225
                        'iconclass' => 'user secret',
226
                        'action' => 'index',
227
                        'param' => '',
228
                        'style' => '',
229
                    ],
230
                ],
231
            ],
232
            'server' => [
233
                'caption' => 'mm_System',
234
                'iconclass' => '',
235
                'submenu' => [
236
                    GeneralSettingsController::class => [
237
                        'caption' => 'mm_GeneralSettings',
238
                        'iconclass' => 'cogs',
239
                        'action' => 'modify',
240
                        'param' => '',
241
                        'style' => '',
242
                    ],
243
                    TimeSettingsController::class => [
244
                        'caption' => 'mm_TimeSettings',
245
                        'iconclass' => 'time',
246
                        'action' => 'modify',
247
                        'param' => '',
248
                        'style' => '',
249
                    ],
250
                    MailSettingsController::class => [
251
                        'caption' => 'mm_MailSettings',
252
                        'iconclass' => 'envelope outline',
253
                        'action' => 'modify',
254
                        'param' => '',
255
                        'style' => '',
256
                    ],
257
                    AsteriskManagersController::class => [
258
                        'caption' => 'mm_AsteriskManagers',
259
                        'iconclass' => 'asterisk',
260
                        'action' => 'index',
261
                        'param' => '',
262
                        'style' => '',
263
                    ],
264
                    CustomFilesController::class => [
265
                        'caption' => 'mm_CustomFiles',
266
                        'iconclass' => 'linux',
267
                        'action' => 'index',
268
                        'param' => '',
269
                        'style' => '',
270
                    ],
271
                ],
272
            ],
273
            'integrations' => [
274
                'caption' => 'mm_Integrations',
275
                'iconclass' => 'plug',
276
                'submenu' => [],
277
            ]
278
279
        ];
280
281
    // Array of controllers that are hidden in menu in the Docker installation
282
    private array $_hiddenInDocker = [
283
        RestartController::class,
284
        UpdateController::class
285
    ];
286
287
    /**
288
     * Generates the HTML code for the header menu by iterating through the items and checking if they are allowed
289
     * to be displayed by the current user based on their role.
290
     *
291
     * @return void
292
     */
293
    public function getMenu(): void
294
    {
295
        $resultHtml = '';
296
        $this->addMenuItemSSHMenu();
297
        $this->addMenuItemsFromExternalModules();
298
299
        foreach ($this->_headerMenu as $group => $groupparams) {
300
            $addToHTML = false;
301
            $groupHtml = '';
302
            if (array_key_exists('submenu', $groupparams)) {
303
                $groupHtml .= '<div class="item">';
304
                $groupHtml .= '<div class="header">';
305
                if (array_key_exists('iconclass', $groupparams) && !empty($groupparams['iconclass'])) {
306
                    $groupHtml .= "<i class='{$groupparams['iconclass']} icon'></i>";
307
                }
308
                $groupHtml .= $this->translation->_($groupparams['caption']) . '</div>';
309
                $groupHtml .= "<div class='menu' data-group='$group'>";
310
                foreach ($groupparams['submenu'] as $controller => $option) {
311
                    if ($this->ifItPossibleToShowThisElement($controller, $option['action'])) {
312
                        $link = $this->getLinkToControllerAction($controller, $option['action'], $option['param']);
313
                        $caption = $this->translation->_($option['caption']);
314
                        $groupHtml .= "<a class='item {$option['style']}' href='$link'";
315
                        if (isset($option['data-value'])) {
316
                            $groupHtml .= " data-value='{$option['data-value']}'";
317
                        }
318
                        $groupHtml .= ">
319
                    		<i class='{$option['iconclass']} icon'></i>{$caption}
320
                    	 </a>";
321
322
                        $addToHTML = true;
323
                    }
324
                }
325
                $groupHtml .= '</div>';
326
                $groupHtml .= '</div>';
327
            } elseif ($this->ifItPossibleToShowThisElement($group, $groupparams['action'] ?? 'index')) {
328
                    $link = $this->getLinkToControllerAction($group, $groupparams['action'], $groupparams['param']);
329
                    $caption = $this->translation->_($groupparams['caption']);
330
                    $groupHtml .= "<a class='item {$groupparams['style']}' href='$link'>
331
                    	    <i class='{$groupparams['iconclass']} icon'></i>{$caption}
332
                        </a>";
333
                    $addToHTML = true;
334
            }
335
            if ($addToHTML) {
336
                $resultHtml .= $groupHtml;
337
            }
338
        }
339
        echo $resultHtml;
340
    }
341
342
    /**
343
     * Returns icon html by controller full class name
344
     *
345
     * @param $controllerClass
346
     *
347
     * @return string
348
     */
349
    public function getIconByController($controllerClass): string
350
    {
351
        $result = '';
352
        foreach ($this->_headerMenu as $index => $group) {
353
            if (
354
                $index === $controllerClass
355
                && array_key_exists('iconclass', $group[$controllerClass])
356
                && !empty($group[$controllerClass]['iconclass'])
357
            ) {
358
                $result = "<i class='{$group[$controllerClass]['iconclass']} icon'></i>";
359
                break;
360
            }
361
            if (array_key_exists('submenu', $group)) {
362
                foreach ($group['submenu'] as $index2 => $submenu) {
363
                    if (
364
                        $index2 === $controllerClass
365
                        && !empty($submenu['iconclass'])
366
                    ) {
367
                        $result = "<i class='{$submenu['iconclass']} icon'></i>";
368
                        break;
369
                    }
370
                }
371
            }
372
        }
373
374
        if (empty($result)) {
375
            $result = "<i class='puzzle icon'></i>";
376
        }
377
        return $result;
378
    }
379
380
    /**
381
     * Returns an array with the allowed menu groups based on the current user's permissions.
382
     *
383
     * @return array An array of the allowed menu groups where the key is the group name and the value is its caption.
384
     */
385
    public function getMenuGroups(): array
386
    {
387
        $result = [];
388
        foreach ($this->_headerMenu as $group => $groupparams) {
389
            if (array_key_exists('submenu', $groupparams)) {
390
                $result[$group] = $this->translation->_($groupparams['caption']);
391
            }
392
        }
393
394
        return $result;
395
    }
396
397
    /**
398
     * Adds menu items from enabled external modules to the header menu.
399
     *
400
     * @return void
401
     */
402
    private function addMenuItemsFromExternalModules(): void
403
    {
404
        $modules = PbxExtensionModules::getEnabledModulesArray();
405
        foreach ($modules as $module) {
406
            $moduleUniqId = $module['uniqid'];
407
            $moduleMainController = "Modules\\$moduleUniqId\\App\\Controllers\\{$moduleUniqId}Controller";
408
            if (!class_exists($moduleMainController) || !method_exists($moduleMainController, 'indexAction')) {
409
                continue;
410
            }
411
            $menuSettingsKey = "AdditionalMenuItem$moduleUniqId";
412
            $menuSettings = PbxSettings::findFirstByKey($menuSettingsKey);
413
            if ($menuSettings !== null) {
414
                $menuItem = json_decode($menuSettings->value, true);
415
                if ($menuItem['showAtSidebar']) {
416
                    $this->_headerMenu[$menuItem['group']]['submenu'][$moduleMainController] = [
417
                        'caption' => $menuItem['caption'],
418
                        'iconclass' => $menuItem['iconClass'],
419
                        'action' => 'index',
420
                        'param' => '',
421
                        'style' => '',
422
                    ];
423
                }
424
            }
425
        }
426
427
        PBXConfModulesProvider::hookModulesMethod(
428
            WebUIConfigInterface::ON_BEFORE_HEADER_MENU_SHOW,
429
            [&$this->_headerMenu]
430
        );
431
    }
432
433
    /**
434
     * Modifies SSH console menu item
435
     *
436
     * @return void
437
     */
438
    private function addMenuItemSSHMenu(): void
439
    {
440
        if (PbxSettings::getValueByKey(PbxSettings::SSH_DISABLE_SSH_PASSWORD) !== '1') {
441
            $sshPort = PbxSettings::getValueByKey(PbxSettings::SSH_PORT);
442
            $this->_headerMenu['maintenance']['submenu'][ConsoleController::class]['data-value']
443
                = "root@{$_SERVER['SERVER_ADDR']}:$sshPort";
444
        } else {
445
            unset($this->_headerMenu['maintenance']['submenu'][ConsoleController::class]);
446
        }
447
    }
448
449
    /**
450
     * Get the link to a controller action.
451
     *
452
     * @param string $controller The controller namespace.
453
     * @param string $action The action name.
454
     * @param string $param The parameter value.
455
     *
456
     * @return string The generated link.
457
     */
458
    private function getLinkToControllerAction(string $controller, string $action, string $param): string
459
    {
460
        $controllerParts = explode('\\', $controller);
461
        $controllerName = end($controllerParts);
462
        // Remove the "Controller" suffix if present
463
        $controllerName = str_replace("Controller", "", $controllerName);
464
465
        // Convert the controller name to a dash-separated format
466
        $controllerName = Text::uncamelize($controllerName, '-');
467
468
        if ($controllerParts[0] === 'Module') {
469
            // Convert the module name to a dash-separated format
470
            $moduleName = Text::uncamelize($controllerParts[1], '-');
471
            $url = $this->url->get("$moduleName/$controllerName/$action/$param");
472
        } else {
473
            $url = $this->url->get("$controllerName/$action/$param");
474
        }
475
476
        return $url;
477
    }
478
479
    /**
480
     * Checks if it's possible to show a specific element based on the controller and action.
481
     *
482
     * @param string $controller The name of the controller.
483
     * @param string $action The name of the action.
484
     * @return bool True if the element can be shown, false otherwise.
485
     */
486
    private function ifItPossibleToShowThisElement(string $controller, string $action): bool
487
    {
488
        // Check if the application is running in a Docker environment and if the controller is in the hidden list.
489
        // If so, return false as the element should not be shown.
490
        if (Util::isDocker() and in_array($controller, $this->_hiddenInDocker)) {
491
            return false;
492
        }
493
494
        // If the application is not running in Docker or the controller is not in the hidden list,
495
        // use the SecurityPluginProvider to check if the element can be shown.
496
        return $this->di->get(SecurityPluginProvider::SERVICE_NAME, [$controller, $action]);
497
    }
498
}
499