Passed
Branch develop (9d5aa6)
by Nikolay
04:57
created

Elements::addMenuItemsFromExternalModules()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 26
rs 9.0444
c 0
b 0
f 0
cc 6
nc 5
nop 0
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright © 2017-2023 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\AdminCabinet\Library;
21
22
use MikoPBX\AdminCabinet\Controllers\AsteriskManagersController;
23
use MikoPBX\AdminCabinet\Controllers\CallDetailRecordsController;
24
use MikoPBX\AdminCabinet\Controllers\CallQueuesController;
25
use MikoPBX\AdminCabinet\Controllers\ConferenceRoomsController;
26
use MikoPBX\AdminCabinet\Controllers\ConsoleController;
27
use MikoPBX\AdminCabinet\Controllers\CustomFilesController;
28
use MikoPBX\AdminCabinet\Controllers\DialplanApplicationsController;
29
use MikoPBX\AdminCabinet\Controllers\ExtensionsController;
30
use MikoPBX\AdminCabinet\Controllers\Fail2BanController;
31
use MikoPBX\AdminCabinet\Controllers\FirewallController;
32
use MikoPBX\AdminCabinet\Controllers\GeneralSettingsController;
33
use MikoPBX\AdminCabinet\Controllers\IncomingRoutesController;
34
use MikoPBX\AdminCabinet\Controllers\IvrMenuController;
35
use MikoPBX\AdminCabinet\Controllers\MailSettingsController;
36
use MikoPBX\AdminCabinet\Controllers\NetworkController;
37
use MikoPBX\AdminCabinet\Controllers\OutboundRoutesController;
38
use MikoPBX\AdminCabinet\Controllers\OutOffWorkTimeController;
39
use MikoPBX\AdminCabinet\Controllers\PbxExtensionModulesController;
40
use MikoPBX\AdminCabinet\Controllers\ProvidersController;
41
use MikoPBX\AdminCabinet\Controllers\RestartController;
42
use MikoPBX\AdminCabinet\Controllers\SoundFilesController;
43
use MikoPBX\AdminCabinet\Controllers\SystemDiagnosticController;
44
use MikoPBX\AdminCabinet\Controllers\TimeSettingsController;
45
use MikoPBX\AdminCabinet\Controllers\UpdateController;
46
use MikoPBX\AdminCabinet\Providers\SecurityPluginProvider;
47
use MikoPBX\Common\Models\PbxExtensionModules;
48
use MikoPBX\Common\Models\PbxSettings;
49
use MikoPBX\Common\Providers\PBXConfModulesProvider;
50
use MikoPBX\Core\System\Util;
51
use MikoPBX\Modules\Config\WebUIConfigInterface;
52
use Phalcon\Di\Injectable;
53
use Phalcon\Text;
54
55
/**
56
 * Elements
57
 * Helps to build UI elements for the application
58
 *
59
 * @property \MikoPBX\Common\Providers\TranslationProvider translation
60
 *
61
 */
62
class Elements extends Injectable
63
{
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' => '',
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
                    DialplanApplicationsController::class => [
155
                        'caption' => 'mm_DialplanApplications',
156
                        'iconclass' => 'php',
157
                        'action' => 'index',
158
                        'param' => '',
159
                        'style' => '',
160
                    ],
161
                    PbxExtensionModulesController::class => [
162
                        'caption' => 'mm_ModuleManager',
163
                        'iconclass' => 'puzzle piece',
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
274
        ];
275
276
    // Array of controllers that are hidden in menu in the Docker installation
277
    private array $_hiddenInDocker = [
278
        RestartController::class,
279
        UpdateController::class
280
    ];
281
282
    /**
283
     * Generates the HTML code for the header menu by iterating through the items and checking if they are allowed
284
     * to be displayed by the current user based on their role.
285
     *
286
     * @return void
287
     */
288
    public function getMenu(): void
289
    {
290
        $resultHtml = '';
291
        $this->addMenuItemSSHMenu();
292
        $this->addMenuItemsFromExternalModules();
293
294
        foreach ($this->_headerMenu as $group => $groupparams) {
295
            $addToHTML = false;
296
            $groupHtml = '';
297
            if (array_key_exists('submenu', $groupparams)) {
298
                $groupHtml .= '<div class="item">';
299
                $groupHtml .= '<div class="header">';
300
                if (array_key_exists('iconclass', $groupparams) && !empty($groupparams['iconclass'])) {
301
                    $groupHtml .= "<i class='{$groupparams['iconclass']} icon'></i>";
302
                }
303
                $groupHtml .= $this->translation->_($groupparams['caption']) . '</div>';
304
                $groupHtml .= "<div class='menu' data-group='{$group}'>";
305
                foreach ($groupparams['submenu'] as $controller => $option) {
306
                    if ($this->ifItPossibleToShowThisElement($controller, $option['action'])) {
307
                        $link = $this->getLinkToControllerAction($controller,  $option['action'], $option['param']);
308
                        $caption = $this->translation->_($option['caption']);
309
                        $groupHtml .= "<a class='item {$option['style']}' href='{$link}'";
310
                        if (isset($option['data-value'])) {
311
                            $groupHtml .= " data-value='{$option['data-value']}'";
312
                        }
313
                        $groupHtml .= ">
314
                    		<i class='{$option['iconclass']} icon'></i>{$caption}
315
                    	 </a>";
316
317
                        $addToHTML = true;
318
                    }
319
                }
320
                $groupHtml .= '</div>';
321
                $groupHtml .= '</div>';
322
            } elseif ($this->ifItPossibleToShowThisElement($group, $groupparams['action'] ?? 'index')) {
323
                    $link = $this->getLinkToControllerAction($group,  $groupparams['action'], $groupparams['param']);
324
                    $caption = $this->translation->_($groupparams['caption']);
325
                    $groupHtml .= "<a class='item {$groupparams['style']}' href='{$link}'>
326
                    	    <i class='{$groupparams['iconclass']} icon'></i>{$caption}
327
                        </a>";
328
                    $addToHTML = true;
329
            }
330
            if ($addToHTML) {
331
                $resultHtml .= $groupHtml;
332
            }
333
        }
334
        echo $resultHtml;
335
    }
336
337
    /**
338
     * Returns icon html by controller full class name
339
     *
340
     * @param $controllerClass
341
     *
342
     * @return string
343
     */
344
    public function getIconByController($controllerClass): string
345
    {
346
        $result = '';
347
        foreach ($this->_headerMenu as $index => $group) {
348
            if ($index === $controllerClass
349
                && array_key_exists('iconclass', $group[$controllerClass])
350
                && !empty($group[$controllerClass]['iconclass'])
351
            ) {
352
                $result = "<i class='{$group[$controllerClass]['iconclass']} icon'></i>";
353
                break;
354
            }
355
            if (array_key_exists('submenu', $group)) {
356
                foreach ($group['submenu'] as $index2 => $submenu) {
357
                    if ($index2 === $controllerClass
358
                        && !empty($submenu['iconclass'])) {
359
                        $result = "<i class='{$submenu['iconclass']} icon'></i>";
360
                        break;
361
                    }
362
                }
363
            }
364
        }
365
366
        if (empty($result)) {
367
            $result = "<i class='puzzle icon'></i>";
368
        }
369
        return $result;
370
    }
371
372
    /**
373
     * Returns an array with the allowed menu groups based on the current user's permissions.
374
     *
375
     * @return array An array of the allowed menu groups where the key is the group name and the value is its caption.
376
     */
377
    public function getMenuGroups(): array
378
    {
379
        $result = [];
380
        foreach ($this->_headerMenu as $group => $groupparams) {
381
            if (array_key_exists('submenu', $groupparams)) {
382
                $result[$group] = $this->translation->_($groupparams['caption']);
383
            }
384
        }
385
386
        return $result;
387
    }
388
389
    /**
390
     * Adds menu items from enabled external modules to the header menu.
391
     *
392
     * @return void
393
     */
394
    private function addMenuItemsFromExternalModules(): void
395
    {
396
        $modules = PbxExtensionModules::getEnabledModulesArray();
397
        foreach ($modules as $module) {
398
            $moduleUniqId = $module['uniqid'];
399
            $moduleMainController = "Modules\\{$moduleUniqId}\\App\\Controllers\\{$moduleUniqId}Controller";
400
            if (!class_exists($moduleMainController) || !method_exists($moduleMainController, 'indexAction')) {
401
                continue;
402
            }
403
            $menuSettingsKey = "AdditionalMenuItem{$moduleUniqId}";
404
            $menuSettings = PbxSettings::findFirstByKey($menuSettingsKey);
405
            if ($menuSettings !== null) {
406
                $menuItem = json_decode($menuSettings->value, true);
407
                if ($menuItem['showAtSidebar']) {
408
                    $this->_headerMenu[$menuItem['group']]['submenu'][$moduleMainController] = [
409
                        'caption' => $menuItem['caption'],
410
                        'iconclass' => $menuItem['iconClass'],
411
                        'action' => 'index',
412
                        'param' => '',
413
                        'style' => '',
414
                    ];
415
                }
416
            }
417
        }
418
419
        PBXConfModulesProvider::hookModulesMethod(WebUIConfigInterface::ON_BEFORE_HEADER_MENU_SHOW, [&$this->_headerMenu]);
420
    }
421
422
    /**
423
     * Modifies SSH console menu item
424
     *
425
     * @return void
426
     */
427
    private function addMenuItemSSHMenu(): void
428
    {
429
        if (PbxSettings::getValueByKey('SSHDisablePasswordLogins') !== '1') {
430
            $sshPort = PbxSettings::getValueByKey('SSHPort');
431
            $this->_headerMenu['maintenance']['submenu'][ConsoleController::class]['data-value'] = "root@{$_SERVER['SERVER_ADDR']}:$sshPort";
432
        } else {
433
            unset ($this->_headerMenu['maintenance']['submenu'][ConsoleController::class]);
434
        }
435
    }
436
437
    /**
438
     * Get the link to a controller action.
439
     *
440
     * @param string $controller The controller namespace.
441
     * @param string $action The action name.
442
     * @param string $param The parameter value.
443
     *
444
     * @return string The generated link.
445
     */
446
    private function getLinkToControllerAction(string $controller, string $action, string $param): string
447
    {
448
        $controllerParts = explode('\\', $controller);
449
        $controllerName = end($controllerParts);
450
        // Remove the "Controller" suffix if present
451
        $controllerName = str_replace("Controller", "", $controllerName);
452
453
        // Convert the controller name to a dash-separated format
454
        $controllerName = Text::uncamelize($controllerName, '-');
455
456
        if ($controllerParts[0]==='Module'){
457
            // Convert the module name to a dash-separated format
458
            $moduleName = Text::uncamelize($controllerParts[1], '-');
459
            $url = $this->url->get("$moduleName/$controllerName/$action/$param");
460
        } else {
461
            $url = $this->url->get("$controllerName/$action/$param");
462
        }
463
464
        return $url;
465
    }
466
467
    /**
468
     * Checks if it's possible to show a specific element based on the controller and action.
469
     *
470
     * @param string $controller The name of the controller.
471
     * @param string $action The name of the action.
472
     * @return bool True if the element can be shown, false otherwise.
473
     */
474
    private function ifItPossibleToShowThisElement(string $controller, string $action):bool
475
    {
476
        // Check if the application is running in a Docker environment and if the controller is in the hidden list.
477
        // If so, return false as the element should not be shown.
478
        if (Util::isDocker() and in_array($controller, $this->_hiddenInDocker)){
479
           return false;
480
        }
481
482
        // If the application is not running in Docker or the controller is not in the hidden list,
483
        // use the SecurityPluginProvider to check if the element can be shown.
484
        return $this->di->get(SecurityPluginProvider::SERVICE_NAME, [$controller, $action]);
485
    }
486
487
}
488