Passed
Push — develop ( 4df8ba...2db068 )
by Nikolay
04:59
created

BaseController::getVersionsHash()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright (C) 2017-2020 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\Controllers;
21
22
use Exception;
23
use GuzzleHttp;
24
use MikoPBX\Common\Providers\SessionProvider;
25
use MikoPBX\Common\Models\{PbxExtensionModules, PbxSettings};
26
use MikoPBX\Common\Providers\ManagedCacheProvider;
27
use MikoPBX\Core\System\Util;
28
use Phalcon\Cache\Adapter\Redis;
29
use Phalcon\Http\ResponseInterface;
30
use Phalcon\Mvc\{Controller, View};
31
use Phalcon\Tag;
32
use Phalcon\Text;
33
use Sentry\SentrySdk;
34
35
/**
36
 * @property \Phalcon\Session\Manager session
37
 * @property \MikoPBX\Common\Providers\TranslationProvider translation
38
 * @property string language
39
 * @property bool showModuleStatusToggle if false it hides status toggle on current UI page
40
 * @property \MikoPBX\AdminCabinet\Library\Elements elements
41
 * @property \Phalcon\Flash\Session flash
42
 * @property \Phalcon\Tag tag
43
 * @property \Phalcon\Config\Adapter\Json config
44
 * @property \Phalcon\Logger loggerAuth
45
 */
46
class BaseController extends Controller
47
{
48
    protected string $actionName;
49
    protected string $controllerName;
50
    protected string $controllerNameUnCamelized;
51
52
    public const WIKI_LINKS = '/var/etc/wiki-links-LANG.json';
53
54
    /**
55
     * Initializes base class
56
     */
57
    public function initialize(): void
58
    {
59
        $this->actionName = $this->dispatcher->getActionName();
60
        $this->controllerName = Text::camelize($this->dispatcher->getControllerName(), '_');
61
        $this->controllerNameUnCamelized = Text::uncamelize($this->controllerName, '-');
62
63
        if ($this->request->isAjax() === false) {
64
            $this->prepareView();
65
        }
66
    }
67
68
    /**
69
     * Customization of links to the wiki documentation for modules.
70
     * @param array $links
71
     * @return void
72
     */
73
    private function customModuleWikiLinks(array $links): void
74
    {
75
        $this->view->urlToWiki = $links[$this->language][$this->view->urlToWiki] ?? $this->view->urlToWiki;
76
        $this->view->urlToSupport = $links[$this->language][$this->view->urlToSupport] ?? $this->view->urlToSupport;
77
    }
78
79
    /**
80
     * Customization of links to the wiki documentation.
81
     * @return void
82
     */
83
    private function customWikiLinks(): void
84
    {
85
        if (!$this->session->has(SessionController::SESSION_ID)) {
86
            return;
87
        }
88
        /** @var Redis $cache */
89
        $cache = $this->di->getShared(ManagedCacheProvider::SERVICE_NAME);
90
        $links = $cache->get('WIKI_LINKS');
91
92
        if ($links === null) {
93
            $ttl = 86400;
94
            $client = new GuzzleHttp\Client();
95
            $url = 'https://raw.githubusercontent.com/mikopbx/Core/master/src/Common/WikiLinks/' . $this->language . '.json';
96
            try {
97
                $res = $client->request('GET', $url, ['timeout', 1, 'connect_timeout' => 1, 'read_timeout' => 1]);
98
            } catch (Exception $e) {
99
                $res = null;
100
                $ttl = 3600;
101
                if ($e->getCode() !== 404) {
102
                    Util::sysLogMsg('BaseController', 'Error access to raw.githubusercontent.com');
103
                }
104
            }
105
            $links = null;
106
            if ($res && $res->getStatusCode() === 200) {
107
                try {
108
                    $links = json_decode($res->getBody(), true, 512, JSON_THROW_ON_ERROR);
109
                } catch (Exception $e) {
110
                    $ttl = 3600;
111
                }
112
            }
113
            if (!is_array($links)) {
114
                $links = [];
115
            }
116
            $cache->set('WIKI_LINKS', $links, $ttl);
117
        }
118
119
        $filename = str_replace('LANG', $this->language, self::WIKI_LINKS);
120
        if (file_exists($filename)) {
121
            try {
122
                $local_links = json_decode(file_get_contents($filename), true, 512, JSON_THROW_ON_ERROR);
123
                $links = $local_links;
124
            } catch (\Exception $e) {
125
                Util::sysLogMsg('BaseController', $e->getMessage());
126
            }
127
        }
128
        $this->view->urlToWiki = $links[$this->view->urlToWiki] ?? $this->view->urlToWiki;
129
        $this->view->urlToSupport = $links[$this->view->urlToSupport] ?? $this->view->urlToSupport;
130
    }
131
132
    /**
133
     * Prepares some environments to every controller and view
134
     *
135
     */
136
    protected function prepareView(): void
137
    {
138
        date_default_timezone_set(PbxSettings::getValueByKey('PBXTimezone'));
139
        $this->view->PBXVersion = PbxSettings::getValueByKey('PBXVersion');
140
        $this->view->MetaTegHeadDescription=$this->translation->_('MetategHeadDescription');
141
        if ($this->session->has(SessionController::SESSION_ID)) {
142
            $this->view->PBXLicense = PbxSettings::getValueByKey('PBXLicense');
143
        } else {
144
            $this->view->PBXLicense = '';
145
        }
146
147
        $this->view->WebAdminLanguage = PbxSettings::getValueByKey('WebAdminLanguage');
148
        $this->view->AvailableLanguages = json_encode($this->elements->getAvailableWebAdminLanguages());
149
        $this->view->submitMode = $this->session->get('SubmitMode')??'SaveSettings';
150
151
        // Allow anonymous statistics collection for JS code
152
        if (PbxSettings::getValueByKey('SendMetrics') === '1') {
153
            touch('/tmp/sendmetrics');
154
            $this->view->lastSentryEventId = SentrySdk::getCurrentHub()->getLastEventId();
155
        } else {
156
            if (file_exists('/tmp/sendmetrics')) {
157
                unlink('/tmp/sendmetrics');
158
            }
159
            $this->view->lastSentryEventId = null;
160
        }
161
162
        $title = 'MikoPBX';
163
        switch ($this->actionName) {
164
            case'index':
165
            case'delete':
166
            case'save':
167
            case'modify':
168
            case'*** WITHOUT ACTION ***':
169
                $title .= '|' . $this->translation->_("Breadcrumb{$this->controllerName}");
170
                break;
171
            default:
172
                $title .= '|' . $this->translation->_("Breadcrumb{$this->controllerName}{$this->actionName}");
173
        }
174
        Tag::setTitle($title);
175
        $this->view->t = $this->translation;
176
        $this->view->debugMode = $this->config->path('adminApplication.debugMode');
177
        $this->view->urlToLogo = $this->url->get('assets/img/logo-mikopbx.svg');
178
        $this->view->urlToWiki = "https://wiki.mikopbx.com/{$this->controllerNameUnCamelized}";
179
        if ($this->language === 'ru') {
180
            $this->view->urlToSupport = 'https://www.mikopbx.ru/support/?fromPBX=true';
181
        } else {
182
            $this->view->urlToSupport = 'https://www.mikopbx.com/support/?fromPBX=true';
183
        }
184
        $this->view->urlToController = $this->url->get($this->controllerNameUnCamelized);
185
        $this->view->represent = '';
186
187
        $isExternalModuleController = stripos($this->dispatcher->getNamespaceName(), '\\Module') === 0;
188
        // We can disable module status toggle from module controller, using the showModuleStatusToggle variable
189
        if (property_exists($this, 'showModuleStatusToggle')) {
190
            $this->view->setVar('showModuleStatusToggle', $this->showModuleStatusToggle);
191
        } else {
192
            $this->view->setVar('showModuleStatusToggle', true);
193
        }
194
195
        // Add module variables into view
196
        if ($isExternalModuleController) {
197
            $moduleLinks = [];
198
            /** @var PbxExtensionModules $module */
199
            $module = PbxExtensionModules::findFirstByUniqid($this->controllerName);
200
            if ($module === null) {
201
                $module = new PbxExtensionModules();
202
                $module->disabled = '1';
203
                $module->name = 'Unknown module';
204
            } else {
205
                try {
206
                    $links = json_decode($module->wiki_links, true, 512, JSON_THROW_ON_ERROR);
0 ignored issues
show
Bug introduced by
It seems like $module->wiki_links can also be of type null; however, parameter $json of json_decode() 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

206
                    $links = json_decode(/** @scrutinizer ignore-type */ $module->wiki_links, true, 512, JSON_THROW_ON_ERROR);
Loading history...
207
                    if (is_array($links)) {
208
                        $moduleLinks = $links;
209
                    }
210
                } catch (\JsonException $e) {
211
                    Util::sysLogMsg(__CLASS__, $e->getMessage());
212
                }
213
            }
214
            $this->view->setVar('module', $module);
215
            $this->customModuleWikiLinks($moduleLinks);
216
217
            // If it is module we have to use another volt template
218
            $this->view->setTemplateAfter('modules');
219
        } else {
220
            $this->customWikiLinks();
221
            $this->view->setTemplateAfter('main');
222
        }
223
224
    }
225
226
    /**
227
     * Changes the AJAX response by expected format
228
     *
229
     * @return \Phalcon\Http\ResponseInterface
230
     */
231
    public function afterExecuteRoute():ResponseInterface
232
    {
233
        if ($this->request->isAjax() === true) {
234
            $this->view->setRenderLevel(View::LEVEL_NO_RENDER);
235
            $this->response->setContentType('application/json', 'UTF-8');
236
            $data = $this->view->getParamsToView();
237
238
            /* Set global params if is not set in controller/action */
239
            if (is_array($data) && isset($data['raw_response'])) {
240
                $result = $data['raw_response'];
241
            } elseif (is_array($data)) {
242
                $data['success'] = array_key_exists('success', $data) ? $data['success'] : true;
243
                $data['reload'] = array_key_exists('reload', $data) ? $data['reload'] : false;
244
                $data['message'] = $data['message'] ?? $this->flash->getMessages();
245
246
                // Let's add information about the last error to display a dialog window for the user.
247
                if (file_exists('/tmp/sendmetrics')) {
248
                    $data['lastSentryEventId'] = SentrySdk::getCurrentHub()->getLastEventId();
249
                }
250
                $result = json_encode($data);
251
            } else {
252
                $result = '';
253
            }
254
255
            $this->response->setContent($result);
256
        }
257
258
        return $this->response->send();
259
    }
260
261
    /**
262
     * Callback before execute any route
263
     */
264
    public function beforeExecuteRoute(): void
265
    {
266
        if ($this->request->isPost()) {
267
            $data = $this->request->getPost('submitMode');
268
            if (!empty($data)) {
269
                $this->session->set('SubmitMode', $data);
270
            }
271
        }
272
    }
273
274
    /**
275
     * Change page without reload browser page
276
     *
277
     * @param string $uri
278
     */
279
    protected function forward(string $uri): void
280
    {
281
        $uriParts = explode('/', $uri);
282
        $params = array_slice($uriParts, 2);
283
284
        $this->dispatcher->forward(
285
            [
286
                'controller' => $uriParts[0],
287
                'action' => $uriParts[1],
288
                'params' => $params,
289
            ]
290
291
        );
292
    }
293
294
    /**
295
     * Removes all dangerous symbols from CallerID
296
     * @param string $callerId
297
     *
298
     * @return string
299
     */
300
    protected function sanitizeCallerId(string $callerId): string
301
    {
302
        return preg_replace('/[^a-zA-Zа-яА-Я0-9 ]/ui', '', $callerId);
303
    }
304
305
    /**
306
     * Sorts array by priority field
307
     *
308
     * @param $a
309
     * @param $b
310
     *
311
     * @return int|null
312
     */
313
    protected function sortArrayByPriority($a, $b): ?int
314
    {
315
        if (is_array($a)) {
316
            $a = (int)$a['priority'];
317
        } else {
318
            $a = (int)$a->priority;
319
        }
320
321
        if (is_array($b)) {
322
            $b = (int)$b['priority'];
323
        } else {
324
            $b = (int)$b->priority;
325
        }
326
327
        if ($a === $b) {
328
            return 0;
329
        } else {
330
            return ($a < $b) ? -1 : 1;
331
        }
332
    }
333
}
334