Passed
Push — develop ( 48ed7a...f087ba )
by Nikolay
05:31
created

PbxExtensionSetupBase   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 460
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 48
eloc 173
c 4
b 0
f 0
dl 0
loc 460
rs 8.5599

15 Methods

Rating   Name   Duplication   Size   Complexity  
A installModule() 0 23 5
A activateLicense() 0 3 1
A uninstallModule() 0 18 5
A createSettingsTableByModelsAnnotations() 0 21 3
A unInstallFiles() 0 36 5
A addToSidebar() 0 20 2
A registerNewModule() 0 24 3
A __construct() 0 32 5
A installDB() 0 3 1
A installFiles() 0 16 2
A fixFilesRights() 0 12 2
A getMessages() 0 3 1
A unregisterModule() 0 9 3
A unInstallDB() 0 3 1
B locString() 0 35 9

How to fix   Complexity   

Complex Class

Complex classes like PbxExtensionSetupBase 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 PbxExtensionSetupBase, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Copyright © MIKO LLC - All Rights Reserved
4
 * Unauthorized copying of this file, via any medium is strictly prohibited
5
 * Proprietary and confidential
6
 * Written by Alexey Portnov, 12 2019
7
 */
8
9
namespace MikoPBX\Modules\Setup;
10
11
use MikoPBX\Core\Config\RegisterDIServices;
12
use MikoPBX\Core\System\Upgrade\UpdateDatabase;
13
use MikoPBX\Modules\PbxExtensionUtils;
14
use MikoPBX\Common\Models\{PbxExtensionModules, PbxSettings};
15
use MikoPBX\Core\System\Util;
16
use Phalcon\Di\Injectable;
17
use Phalcon\Text;
18
19
use function MikoPBX\Common\Config\appPath;
20
21
/**
22
 * Class PbxExtensionSetupBase
23
 * Общие для всех модулей методы
24
 * Подключается при установке, удалении модуля
25
 */
26
abstract class PbxExtensionSetupBase extends Injectable implements PbxExtensionSetupInterface
27
{
28
    /**
29
     * Trial product version identify number from module.json
30
     *
31
     * @var int
32
     */
33
    public $lic_product_id;
34
    /**
35
     * License feature identify number from module.json
36
     *
37
     * @var int
38
     */
39
    public $lic_feature_id;
40
    /**
41
     * Module unique identify  from module.json
42
     *
43
     * @var string
44
     */
45
    protected string $moduleUniqueID;
46
    /**
47
     * Module version from module.json
48
     *
49
     * @var string
50
     */
51
    protected $version;
52
    /**
53
     * Minimal require version PBX
54
     *
55
     * @var string
56
     */
57
    protected $min_pbx_version;
58
    /**
59
     * Module developer name
60
     *
61
     * @var string
62
     */
63
    protected $developer;
64
    /**
65
     * Module developer's email from module.json
66
     *
67
     * @var string
68
     */
69
    protected $support_email;
70
    /**
71
     * PBX general database
72
     *
73
     * @var \Phalcon\Db\Adapter\Pdo\Sqlite
74
     */
75
    protected $db;
76
77
78
    /**
79
     * Folder with module files
80
     *
81
     * @var string
82
     */
83
    protected string $moduleDir;
84
85
    /**
86
     * Phalcon config service
87
     *
88
     * @var \Phalcon\Config
89
     */
90
    protected $config;
91
92
    /**
93
     * License worker
94
     *
95
     * @var \MikoPBX\Service\License
96
     */
97
    protected $license;
98
99
    /**
100
     * Error and verbose messages
101
     *
102
     * @var array
103
     */
104
    protected array $messages;
105
106
    /**
107
     * PbxExtensionBase constructor.
108
     *
109
     * @param string $moduleUniqueID
110
     */
111
    public function __construct(string $moduleUniqueID)
112
    {
113
        $this->moduleUniqueID = $moduleUniqueID;
114
        $this->messages = [];
115
        $this->db      = $this->di->getShared('db');
116
        $this->config  = $this->di->getShared('config');
117
        $this->license =  $this->di->getShared('license');
118
        $this->moduleDir = $this->config->path('core.modulesDir') . '/' . $this->moduleUniqueID;
119
        $settings_file = "{$this->moduleDir}/module.json";
120
        if (file_exists($settings_file)) {
121
            $module_settings = json_decode(file_get_contents($settings_file), true);
122
            if ($module_settings) {
123
                $this->version         = $module_settings['version'];
124
                $this->min_pbx_version = $module_settings['min_pbx_version'];
125
                $this->developer       = $module_settings['developer'];
126
                $this->support_email   = $module_settings['support_email'];
127
                if (array_key_exists('lic_product_id', $module_settings)) {
128
                    $this->lic_product_id = $module_settings['lic_product_id'];
129
                } else {
130
                    $this->lic_product_id = 0;
131
                }
132
                if (array_key_exists('lic_feature_id', $module_settings)) {
133
                    $this->lic_feature_id = $module_settings['lic_feature_id'];
134
                } else {
135
                    $this->lic_feature_id = 0;
136
                }
137
            } else {
138
                $this->messages[] = 'Error on decode module.json';
139
            }
140
        }
141
142
        $this->messages  = [];
143
144
145
    }
146
147
    /**
148
     * Последовательный вызов процедур установки модуля расширения
149
     * с текстового результата установки
150
     *
151
     * @return bool - результат установки
152
     */
153
    public function installModule(): bool
154
    {
155
        $result = true;
156
        try {
157
            if ( ! $this->activateLicense()) {
158
                $this->messages[] = 'License activate error';
159
                $result           = false;
160
            }
161
            if ( ! $this->installFiles()) {
162
                $this->messages[] = ' installFiles error';
163
                $result           = false;
164
            }
165
            if ( ! $this->installDB()) {
166
                $this->messages[] = ' installDB error';
167
                $result           = false;
168
            }
169
            $this->fixFilesRights();
170
        } catch (\Exception $exception) {
171
            $result         = false;
172
            $this->messages[] = $exception->getMessage();
173
        }
174
175
        return $result;
176
    }
177
178
    /**
179
     * Выполняет активацию триалов, проверку лицензионного клчюча
180
     *
181
     * @return bool результат активации лицензии
182
     */
183
    public function activateLicense(): bool
184
    {
185
        return true;
186
    }
187
188
    /**
189
     * Copies files, creates folders and symlinks for module
190
     *
191
     * @return bool setup result
192
     */
193
    public function installFiles(): bool
194
    {
195
        // Create cache links for JS, CSS, IMG folders
196
        PbxExtensionUtils::createAssetsSymlinks($this->moduleUniqueID);
197
198
        // Create cache links for agi-bin scripts
199
        PbxExtensionUtils::createAgiBinSymlinks($this->moduleUniqueID);
200
201
        // Restore database settings
202
        $modulesDir          = $this->config->path('core.modulesDir');
203
        $backupPath = "{$modulesDir}/Backup/{$this->moduleUniqueID}";
204
        if (is_dir($backupPath)) {
205
            $cpPath = Util::which('cp');
206
            Util::mwExec("{$cpPath} -r {$backupPath}/db/* {$this->moduleDir}/db/");
207
        }
208
        return true;
209
    }
210
211
    /**
212
     * Setup ownerships and folder rights
213
     *
214
     * @return bool
215
     */
216
    public function fixFilesRights(): bool
217
    {
218
        // Add regular www rights
219
        Util::addRegularWWWRights($this->moduleDir);
220
221
        // Add executable right to module's binary
222
        $binDir = $this->moduleDir.'/bin';
223
        if (is_dir($binDir)){
224
            Util::addExecutableRights($binDir);
225
        }
226
227
        return true;
228
    }
229
230
    /**
231
     * Создает структуру для хранения настроек модуля в своей модели
232
     * и заполняет настройки по-умолчанию если таблицы не было в системе
233
     * см (unInstallDB)
234
     *
235
     * Регистрирует модуль в PbxExtensionModules
236
     *
237
     * @return bool результат установки
238
     */
239
    public function installDB(): bool
240
    {
241
        return true;
242
    }
243
244
    /**
245
     * Последовательный вызов процедур установки модуля расширения
246
     * с результата удаления
247
     *
248
     * @param $keepSettings bool сохранять настройки модуля при удалении
249
     *
250
     * @return bool результат удаления
251
     */
252
    public function uninstallModule(bool $keepSettings = false): bool
253
    {
254
        $result = true;
255
        try {
256
            if ( ! $this->unInstallDB($keepSettings)) {
257
                $this->messages[] = ' unInstallDB error';
258
                $result           = false;
259
            }
260
            if ($result && ! $this->unInstallFiles($keepSettings)) {
261
                $this->messages[] = ' unInstallFiles error';
262
                $result           = false;
263
            }
264
        } catch (\Exception $exception) {
265
            $result         = false;
266
            $this->messages[] = $exception->getMessage();
267
        }
268
269
        return $result;
270
    }
271
272
    /**
273
     * Удаляет запись о модуле из PbxExtensionModules
274
     * Удаляет свою модель
275
     *
276
     * @param  $keepSettings bool оставляет таблицу с данными своей модели
277
     *
278
     * @return bool результат очистки
279
     */
280
    public function unInstallDB(bool $keepSettings = false): bool
281
    {
282
        return $this->unregisterModule();
283
    }
284
285
    /**
286
     * Удаляет запись о модуле из PbxExtensionModules
287
     *
288
     * @return bool результат очистки
289
     */
290
    public function unregisterModule(): bool
291
    {
292
        $result = true;
293
        $module = PbxExtensionModules::findFirstByUniqid($this->moduleUniqueID);
294
        if ($module) {
295
            $result = $result && $module->delete();
296
        }
297
298
        return $result;
299
    }
300
301
    /**
302
     * Выполняет удаление своих файлов с остановной процессов
303
     * при необходимости
304
     *
305
     * @param $keepSettings bool сохранять настройки
306
     *
307
     * @return bool результат удаления
308
     */
309
    public function unInstallFiles(bool $keepSettings = false):bool
310
    {
311
        $cpPath = Util::which('cp');
312
        $rmPath = Util::which('rm');
313
        $modulesDir          = $this->config->path('core.modulesDir');
314
        $backupPath = "{$modulesDir}/Backup/{$this->moduleUniqueID}";
315
        Util::mwExec("{$rmPath} -rf {$backupPath}");
316
        if ($keepSettings) {
317
            Util::mwMkdir($backupPath);
318
            Util::mwExec("{$cpPath} -r {$this->moduleDir}/db {$backupPath}/");
319
        }
320
        Util::mwExec("{$rmPath} -rf {$this->moduleDir}");
321
322
        // Remove assets
323
        // IMG
324
        $imgCacheDir = appPath('sites/admin-cabinet/assets/img/cache');
325
        $moduleImageCacheDir = "{$imgCacheDir}/{$this->moduleUniqueID}";
326
        if (file_exists($moduleImageCacheDir)){
327
            unlink($moduleImageCacheDir);
328
        }
329
330
        // CSS
331
        $cssCacheDir = appPath('sites/admin-cabinet/assets/css/cache');
332
        $moduleCSSCacheDir = "{$cssCacheDir}/{$this->moduleUniqueID}";
333
        if (file_exists($moduleCSSCacheDir)){
334
            unlink($moduleCSSCacheDir);
335
        }
336
337
        // JS
338
        $jsCacheDir = appPath('sites/admin-cabinet/assets/js/cache');
339
        $moduleJSCacheDir = "{$jsCacheDir}/{$this->moduleUniqueID}";
340
        if (file_exists($moduleJSCacheDir)){
341
            unlink($moduleJSCacheDir);
342
        }
343
344
        return true;
345
    }
346
347
    /**
348
     * Returns error messages
349
     *
350
     * @return array
351
     */
352
    public function getMessages(): array
353
    {
354
        return $this->messages;
355
    }
356
357
    /**
358
     * Выполняет регистрацию модуля в таблице PbxExtensionModules
359
     *
360
     * @return bool
361
     */
362
    public function registerNewModule(): bool
363
    {
364
        // Проверим версию АТС и Модуля на совместимость
365
        $currentVersionPBX = PbxSettings::getValueByKey('PBXVersion');
366
        $currentVersionPBX = str_replace('-dev', '', $currentVersionPBX);
367
        if (version_compare($currentVersionPBX, $this->min_pbx_version) < 0) {
368
            $this->messages[] = "Module depends minimum PBX ver $this->min_pbx_version";
369
370
            return false;
371
        }
372
373
        $module = PbxExtensionModules::findFirstByUniqid($this->moduleUniqueID);
374
        if ( ! $module) {
375
            $module           = new PbxExtensionModules();
376
            $module->name     = $this->locString("Breadcrumb{$this->moduleUniqueID}");
377
            $module->disabled = '1';
0 ignored issues
show
Documentation Bug introduced by
It seems like '1' of type string is incompatible with the declared type integer|null of property $disabled.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
378
        }
379
        $module->uniqid        = $this->moduleUniqueID;
380
        $module->developer     = $this->developer;
381
        $module->version       = $this->version;
382
        $module->description   = $this->locString("SubHeader{$this->moduleUniqueID}");
383
        $module->support_email = $this->support_email;
384
385
        return $module->save();
386
    }
387
388
    /**
389
     * Возвращает перевод идентификатора на язык установленный в настройках PBX
390
     *
391
     * @param $stringId string  идентификатор фразы
392
     *
393
     * @return string  перевод
394
     */
395
    public function locString(string $stringId): string
396
    {
397
        $language             = substr(PbxSettings::getValueByKey('WebAdminLanguage'), 0, 2);
398
        $translates           = [];
399
        $extensionsTranslates = [[]];
400
        $results              = glob($this->moduleDir . '/{Messages}/en.php', GLOB_BRACE);
401
        foreach ($results as $path) {
402
            $langArr = require $path;
403
            if (is_array($langArr)) {
404
                $extensionsTranslates[] = $langArr;
405
            }
406
        }
407
        if ($extensionsTranslates !== [[]]) {
408
            $translates = array_merge($translates, ...$extensionsTranslates);
409
        }
410
        if ($language !== 'en') {
411
            $additionalTranslates = [[]];
412
            $results              = glob($this->moduleDir . "/{Messages}/{$language}.php", GLOB_BRACE);
413
            foreach ($results as $path) {
414
                $langArr = require $path;
415
                if (is_array($langArr)) {
416
                    $additionalTranslates[] = $langArr;
417
                }
418
            }
419
            if ($additionalTranslates !== [[]]) {
420
                $translates = array_merge($translates, ...$additionalTranslates);
421
            }
422
        }
423
424
        // Return a translation object
425
        if (array_key_exists($stringId, $translates)) {
426
            return $translates[$stringId];
427
        }
428
429
        return $stringId;
430
    }
431
432
    /**
433
     * Обходит файлы с описанием моделей и создает таблицы в базе данных
434
     *
435
     * @return bool
436
     */
437
    public function createSettingsTableByModelsAnnotations(): bool
438
    {
439
440
        // Add new connection for this module after add new Models folder
441
        RegisterDIServices::recreateModulesDBConnections();
442
443
        $results = glob($this->moduleDir . '/Models/*.php', GLOB_NOSORT);
444
        $dbUpgrade = new UpdateDatabase();
445
        foreach ($results as $file) {
446
            $className        = pathinfo($file)['filename'];
447
            $moduleModelClass = "\\Modules\\{$this->moduleUniqueID}\\Models\\{$className}";
448
            $upgradeResult = $dbUpgrade->createUpdateDbTableByAnnotations($moduleModelClass);
449
            if (!$upgradeResult){
450
                return false;
451
            }
452
453
        }
454
        // Update database connections after upgrade their structure
455
        RegisterDIServices::recreateModulesDBConnections();
456
457
        return true;
458
    }
459
460
461
    /**
462
     * Добавляет модуль в боковое меню
463
     *
464
     * @return bool
465
     */
466
    public function addToSidebar(): bool
467
    {
468
        $menuSettingsKey           = "AdditionalMenuItem{$this->moduleUniqueID}";
469
        $unCamelizedControllerName = Text::uncamelize($this->moduleUniqueID, '-');
470
        $menuSettings              = PbxSettings::findFirstByKey($menuSettingsKey);
471
        if ($menuSettings === null) {
472
            $menuSettings      = new PbxSettings();
473
            $menuSettings->key = $menuSettingsKey;
474
        }
475
        $value               = [
476
            'uniqid'        => $this->moduleUniqueID,
477
            'href'          => "/admin-cabinet/$unCamelizedControllerName",
478
            'group'         => 'maintenance',
479
            'iconClass'     => 'puzzle',
480
            'caption'       => "Breadcrumb$this->moduleUniqueID",
481
            'showAtSidebar' => true,
482
        ];
483
        $menuSettings->value = json_encode($value);
484
485
        return $menuSettings->save();
486
    }
487
}