Passed
Push — develop ( bef097...351c48 )
by Nikolay
12:23
created

PbxExtensionUtils::getModuleDir()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
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\Modules;
21
22
23
use MikoPBX\Common\Models\ModelsBase;
24
use MikoPBX\Common\Models\PbxExtensionModules;
25
use MikoPBX\Core\System\Processes;
26
use MikoPBX\Core\System\Util;
27
use Phalcon\Di;
28
use Phalcon\Mvc\Application;
29
30
use Phalcon\Mvc\Router;
31
use Phalcon\Text;
32
use Throwable;
33
34
use function MikoPBX\Common\Config\appPath;
35
36
/**
37
 * Utility class for managing extension modules.
38
 *
39
 * @package MikoPBX\Modules
40
 */
41
class PbxExtensionUtils
42
{
43
    /**
44
     * Checks if a module is enabled by UniqueID.
45
     *
46
     * @param string $moduleUniqueID The UniqueID of the module.
47
     * @return bool True if the module is enabled, false otherwise.
48
     */
49
    public static function isEnabled(string $moduleUniqueID): bool
50
    {
51
        $parameters = [
52
            'conditions'=>'uniqid = :uniqid:',
53
            'bind'=>[
54
                'uniqid' => $moduleUniqueID,
55
            ],
56
            'cache' => [
57
                'key'=>ModelsBase::makeCacheKey(PbxExtensionModules::class, 'isEnabled'.$moduleUniqueID),
58
                'lifetime' => 3600,
59
            ]
60
        ];
61
62
        $result = PbxExtensionModules::findFirst($parameters);
63
64
        return ($result !== null && $result->disabled !== '1');
65
    }
66
67
    /**
68
     * Creates symbolic links for JS, CSS, and IMG assets of a module.
69
     *
70
     * @param string $moduleUniqueID The UniqueID of the module.
71
     * @return void
72
     */
73
    public static function createAssetsSymlinks(string $moduleUniqueID): void
74
    {
75
        $moduleDir = self::getModuleDir($moduleUniqueID);
76
77
        // Create symlinks for IMG
78
        $moduleImageDir      = "{$moduleDir}/public/assets/img";
79
        $imgCacheDir         = appPath('sites/admin-cabinet/assets/img/cache');
80
        $moduleImageCacheDir = "{$imgCacheDir}/{$moduleUniqueID}";
81
        if (file_exists($moduleImageCacheDir)) {
82
            unlink($moduleImageCacheDir);
83
        }
84
        if (file_exists($moduleImageDir)) {
85
            symlink($moduleImageDir, $moduleImageCacheDir);
86
        }
87
88
        // Create symlinks for CSS
89
        $moduleCSSDir      = "{$moduleDir}/public/assets/css";
90
        $cssCacheDir       = appPath('sites/admin-cabinet/assets/css/cache');
91
        $moduleCSSCacheDir = "{$cssCacheDir}/{$moduleUniqueID}";
92
        if (file_exists($moduleCSSCacheDir)) {
93
            unlink($moduleCSSCacheDir);
94
        }
95
        if (file_exists($moduleCSSDir)) {
96
            symlink($moduleCSSDir, $moduleCSSCacheDir);
97
        }
98
99
        // Create symlinks for JS
100
        $moduleJSDir      = "{$moduleDir}/public/assets/js";
101
        $jsCacheDir       = appPath('sites/admin-cabinet/assets/js/cache');
102
        $moduleJSCacheDir = "{$jsCacheDir}/{$moduleUniqueID}";
103
        if (file_exists($moduleJSCacheDir)) {
104
            unlink($moduleJSCacheDir);
105
        }
106
        if (file_exists($moduleJSDir)) {
107
            symlink($moduleJSDir, $moduleJSCacheDir);
108
        }
109
    }
110
111
    /**
112
     * Retrieves the directory path of a module by UniqueID.
113
     *
114
     * @param string $moduleUniqueID The UniqueID of the module.
115
     * @return string The directory path of the module.
116
     */
117
    public static function getModuleDir(string $moduleUniqueID): string
118
    {
119
        $di = Di::getDefault();
120
        if ($di === null) {
121
            return "/tmp/{$moduleUniqueID}";
122
        }
123
        $config     = $di->getShared('config');
124
        $modulesDir = $config->path('core.modulesDir');
125
126
        return "{$modulesDir}/{$moduleUniqueID}";
127
    }
128
129
    /**
130
     * Creates symbolic links for agi-bin files of a module.
131
     *
132
     * @param string $moduleUniqueID The UniqueID of the module.
133
     * @return void
134
     */
135
    public static function createAgiBinSymlinks(string $moduleUniqueID): void
136
    {
137
        $moduleDir = self::getModuleDir($moduleUniqueID);
138
139
        $di = Di::getDefault();
140
        if ($di === null) {
141
            return;
142
        }
143
        $config = $di->getShared('config');
144
145
        // Create symlinks to AGI-BIN
146
        $agiBinDir       = $config->path('asterisk.astagidir');
147
        $moduleAgiBinDir = "{$moduleDir}/agi-bin";
148
        $files           = glob("$moduleAgiBinDir/*.{php}", GLOB_BRACE);
149
        foreach ($files as $file) {
150
            $newFilename = $agiBinDir . '/' . basename($file);
151
            Util::createUpdateSymlink($file, $newFilename);
152
        }
153
154
        $pathChmod = Util::which('chmod');
155
        Processes::mwExec("{$pathChmod} +x {$agiBinDir}/*");
156
    }
157
158
    /**
159
     * Creates symbolic links for view templates of a module.
160
     *
161
     * @param string $moduleUniqueID The UniqueID of the module.
162
     * @return void
163
     */
164
    public static function createViewSymlinks(string $moduleUniqueID): void
165
    {
166
        $moduleDir = self::getModuleDir($moduleUniqueID);
167
168
        $di = Di::getDefault();
169
        if ($di === null) {
170
            return;
171
        }
172
        $moduleViewDir      = "{$moduleDir}/App/Views";
173
        $viewCacheDir       = appPath('src/AdminCabinet/Views/Modules');
174
        $moduleViewCacheDir = "{$viewCacheDir}/{$moduleUniqueID}";
175
        if (file_exists($moduleViewCacheDir)) {
176
            unlink($moduleViewCacheDir);
177
        }
178
        if (file_exists($moduleViewDir)) {
179
            symlink($moduleViewDir, $moduleViewCacheDir);
180
        }
181
    }
182
183
    /**
184
     * Disables incompatible modules.
185
     *
186
     * @return void
187
     */
188
    public static function disableOldModules(): void
189
    {
190
        $parameters = [
191
            'conditions' => 'disabled=0',
192
        ];
193
        $modules    = PbxExtensionModules::find($parameters)->toArray();
194
        foreach ($modules as $module) {
195
            $needDisable = false;
196
            $moduleDir   = PbxExtensionUtils::getModuleDir($module['uniqid']);
197
198
            // Check if module.json file exists
199
            $moduleJson  = "{$moduleDir}/module.json";
200
            if ( ! file_exists($moduleJson)) {
201
                $needDisable = true;
202
            }
203
            $jsonString            = file_get_contents($moduleJson);
204
            $jsonModuleDescription = json_decode($jsonString, true);
205
            $minPBXVersion         = $jsonModuleDescription['min_pbx_version'] ?? '1.0.0';
206
207
            // Check if module version is lower than the minimum supported version
208
            if (version_compare($minPBXVersion, ModelsBase::MIN_MODULE_MODEL_VER, '<')) {
209
                $needDisable = true;
210
            }
211
            if ($needDisable) {
212
                try {
213
                    $moduleStateProcessor = new PbxExtensionState($module['uniqid']);
214
                    $moduleStateProcessor->disableModule();
215
                } catch (Throwable $exception) {
216
                    Util::sysLogMsg(__CLASS__, "Can not disable module {$module['uniqid']} Message: {$exception}", LOG_ERR);
217
                } finally {
218
                    // Update module status to disabled if it was not already disabled
219
                    $currentModule           = PbxExtensionModules::findFirstByUniqid($module['uniqid']);
220
                    if ($currentModule->disabled==='0'){
221
                        $currentModule->disabled = '1';
222
                        $currentModule->update();
223
                    }
224
                }
225
            }
226
        }
227
    }
228
229
    /**
230
     * Registers enabled modules with App/Module.php file as external modules for the application.
231
     *
232
     * @param Application $application The application instance.
233
     * @return void
234
     */
235
    public static function registerEnabledModulesInApp(Application &$application){
236
        $parameters = [
237
            'conditions' => 'disabled=0',
238
        ];
239
        $modules    = PbxExtensionModules::find($parameters)->toArray();
240
        foreach ($modules as $module) {
241
            $moduleUniqueId = $module['uniqid'];
242
            $moduleDir = PbxExtensionUtils::getModuleDir($moduleUniqueId);
243
            $unCamelizedModuleName = Text::uncamelize($moduleUniqueId,'-');
244
            $moduleAppClass = "{$moduleDir}/App/Module.php";
245
            if (file_exists($moduleAppClass)) {
246
                $application->registerModules([
247
                    $unCamelizedModuleName => [
248
                        "className" => "Modules\\{$moduleUniqueId}\\App\\Module",
249
                        "path"      => $moduleAppClass,
250
                    ],
251
                ],true);
252
            }
253
        }
254
    }
255
256
    /**
257
     * Registers enabled modules with App/Module.php file as external module for routes
258
     *
259
     * @param Router $router
260
     * @return void
261
     */
262
    public static function registerEnabledModulesInRouter(Router &$router){
263
        $parameters = [
264
            'conditions' => 'disabled=0',
265
        ];
266
        $modules    = PbxExtensionModules::find($parameters)->toArray();
267
        foreach ($modules as $module) {
268
            $moduleUniqueId = $module['uniqid'];
269
            $moduleDir = PbxExtensionUtils::getModuleDir($moduleUniqueId);
270
            $unCamelizedModuleName = Text::uncamelize($moduleUniqueId,'-');
271
            $moduleAppClass = "{$moduleDir}/App/Module.php";
272
            if (file_exists($moduleAppClass)) {
273
                $router->add("/{$unCamelizedModuleName}/:controller/:action/:params", [
274
                    'module'     => $unCamelizedModuleName,
275
                    'controller' => 1,
276
                    'action'     => 2,
277
                    'params'     => 3,
278
                    'namespace' => "Modules\\{$moduleUniqueId}\\App\\Controllers"
279
                ]);
280
            }
281
        }
282
    }
283
284
}