Passed
Push — develop ( 89cd6b...987c3c )
by Портнов
05:50
created

GeneralSettingsController::modifyAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 21
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
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\Controllers;
22
23
use MikoPBX\AdminCabinet\Forms\GeneralSettingsEditForm;
24
use MikoPBX\Common\Models\Codecs;
25
use MikoPBX\Common\Models\Extensions;
26
use MikoPBX\Common\Models\PbxSettings;
27
use MikoPBX\Core\System\Util;
28
29
/**
30
 * Class GeneralSettingsController
31
 *
32
 * This class handles general settings for the application.
33
 */
34
class GeneralSettingsController extends BaseController
35
{
36
    /**
37
     * Builds the general settings form.
38
     *
39
     * This action is responsible for preparing the data required to populate the general settings form.
40
     * It retrieves the audio and video codecs from the database, sorts them by priority, and assigns them to the view.
41
     * It also retrieves all PBX settings and creates an instance of the GeneralSettingsEditForm.
42
     *
43
     * @return void
44
     */
45
    public function modifyAction(): void
46
    {
47
        // Retrieve and sort audio codecs from database
48
        $audioCodecs = Codecs::find(['conditions' => 'type="audio"'])->toArray();
49
        usort($audioCodecs, [__CLASS__, 'sortArrayByPriority']);
50
        $this->view->audioCodecs = $audioCodecs;
51
52
        // Retrieve and sort video codecs from database
53
        $videoCodecs = Codecs::find(['conditions' => 'type="video"'])->toArray();
54
        usort($videoCodecs, [__CLASS__, 'sortArrayByPriority']);
55
        $this->view->videoCodecs = $videoCodecs;
56
57
        // Fetch all PBX settings
58
        $pbxSettings = PbxSettings::getAllPbxSettings();
59
60
        // Fetch and assign simple passwords for the view
61
        $this->view->simplePasswords = $this->getSimplePasswords($pbxSettings);
62
63
        // Create an instance of the GeneralSettingsEditForm and assign it to the view
64
        $this->view->form = new GeneralSettingsEditForm(null, $pbxSettings);
65
        $this->view->submitMode = null;
66
    }
67
68
    /**
69
     * Retrieves a list of simple passwords from the given data.
70
     *
71
     * This function checks if the SSHPassword and WebAdminPassword in the data array are simple passwords.
72
     * It also checks if the CloudInstanceId matches any of these passwords.
73
     * If a simple password or a matching CloudInstanceId is found, the corresponding password key is added to the list.
74
     *
75
     * @param array $data The data array containing the passwords and CloudInstanceId.
76
     * @return array The list of password keys that failed the simple password check.
77
     */
78
    private function getSimplePasswords(array $data): array
79
    {
80
        // Initialize an array to keep track of passwords that fail the check
81
        $passwordCheckFail = [];
82
83
        $cloudInstanceId = $data[PbxSettings::CLOUD_INSTANCE_ID] ?? '';
84
        $checkPasswordFields = [PbxSettings::SSH_PASSWORD, PbxSettings::WEB_ADMIN_PASSWORD];
85
86
        // If SSH is disabled, remove the SSH_PASSWORD key
87
        if ($data[PbxSettings::SSH_DISABLE_SSH_PASSWORD] === 'on') {
88
            unset($checkPasswordFields[PbxSettings::SSH_PASSWORD]);
89
        }
90
91
        // Loop through and check passwords
92
        foreach ($checkPasswordFields as $value) {
93
            if (!isset($data[$value]) || $data[$value] === GeneralSettingsEditForm::HIDDEN_PASSWORD) {
94
                continue;
95
            }
96
            if ($cloudInstanceId === $data[$value] || Util::isSimplePassword($data[$value])) {
97
                $passwordCheckFail[] = $value;
98
            }
99
        }
100
        return $passwordCheckFail;
101
    }
102
103
    /**
104
     * Saves the general settings form data.
105
     *
106
     */
107
    public function saveAction(): void
108
    {
109
        if (!$this->request->isPost()) {
110
            return;
111
        }
112
        $data = $this->request->getPost();
113
114
        $passwordCheckFail = $this->getSimplePasswords($data);
115
        if (!empty($passwordCheckFail)) {
116
            foreach ($passwordCheckFail as $settingsKey) {
117
                $this->flash->error($this->translation->_('gs_SetPasswordError', ['password' => $data[$settingsKey]]));
118
            }
119
            $this->view->success = false;
120
            $this->view->passwordCheckFail = $passwordCheckFail;
121
            return;
122
        }
123
124
        $this->db->begin();
125
126
        list($result, $messages) = $this->updatePBXSettings($data);
127
        if (!$result) {
128
            $this->view->success = false;
129
            $this->view->messages = $messages;
130
            $this->db->rollback();
131
            return;
132
        }
133
134
        list($result, $messages) = $this->updateCodecs($data['codecs']);
135
        if (!$result) {
136
            $this->view->success = false;
137
            $this->view->messages = $messages;
138
            $this->db->rollback();
139
            return;
140
        }
141
142
        list($result, $messages) = $this->createParkingExtensions(
143
            $data[PbxSettings::PBX_CALL_PARKING_START_SLOT],
144
            $data[PbxSettings::PBX_CALL_PARKING_END_SLOT],
145
            $data[PbxSettings::PBX_CALL_PARKING_EXT],
146
        );
147
148
        if (!$result) {
149
            $this->view->success = false;
150
            $this->view->messages = $messages;
151
            $this->db->rollback();
152
            return;
153
        }
154
155
        $this->flash->success($this->translation->_('ms_SuccessfulSaved'));
156
        $this->view->success = true;
157
        $this->db->commit();
158
    }
159
160
161
    /**
162
     * Create or update parking extensions by ensuring only necessary slots are modified.
163
     * This method first fetches the existing parking slots and determines which slots
164
     * need to be created or deleted based on the desired range and reserved slot.
165
     * It aims to minimize database operations by only deleting slots that are no longer needed
166
     * and creating new slots that do not exist yet, preserving all others.
167
     *
168
     * @param int $startSlot The starting number of the parking slot range.
169
     * @param int $endSlot The ending number of the parking slot range.
170
     * @param int $reservedSlot The number of the reserved slot to be included outside the range.
171
     *
172
     * @return array Returns an array with two elements:
173
     *               - bool: true if the operation was successful without any errors, false otherwise.
174
     *               - array: an array of messages, primarily errors encountered during operations.
175
     */
176
    private function createParkingExtensions(int $startSlot, int $endSlot, int $reservedSlot): array
177
    {
178
        $messages = [];
179
180
        // Retrieve all current parking slots.
181
        $currentSlots = Extensions::findByType(Extensions::TYPE_PARKING);
182
183
        // Create an array of desired numbers.
184
        $desiredNumbers = range($startSlot, $endSlot);
185
        $desiredNumbers[] = $reservedSlot;
186
187
        // Determine slots to delete.
188
        $currentNumbers = [];
189
        foreach ($currentSlots as $slot) {
190
            if (!in_array($slot->number, $desiredNumbers)) {
191
                if (!$slot->delete()) {
192
                    $messages['error'][] = $slot->getMessages();
193
                }
194
            } else {
195
                $currentNumbers[] = $slot->number;
196
            }
197
        }
198
199
        // Determine slots to create.
200
        $numbersToCreate = array_diff($desiredNumbers, $currentNumbers);
201
        foreach ($numbersToCreate as $number) {
202
            $record = new Extensions();
203
            $record->type = Extensions::TYPE_PARKING;
204
            $record->number = $number;
205
            $record->show_in_phonebook = '0';
206
            if (!$record->create()) {
207
                $messages['error'][] = $record->getMessages();
208
            }
209
        }
210
211
        // Determine the overall result.
212
        $result = count($messages['error'] ?? []) === 0;
213
        return [$result, $messages];
214
    }
215
216
    /**
217
     * Update codecs based on the provided data.
218
     *
219
     * @param string $codecsData The JSON-encoded data for codecs.
220
     *
221
     * @return array
222
     */
223
    private function updateCodecs(string $codecsData): array
224
    {
225
        $messages = [];
226
        $codecs = json_decode($codecsData, true);
227
        foreach ($codecs as $codec) {
228
            $record = Codecs::findFirstById($codec['codecId']);
229
            $record->priority = $codec['priority'];
230
            $record->disabled = $codec['disabled'] === true ? '1' : '0';
231
            if (!$record->update()) {
232
                $messages['error'][] = $record->getMessages();
233
            }
234
        }
235
        $result = count($messages) === 0;
236
        return [$result, $messages];
237
    }
238
239
    /**
240
     * Update PBX settings based on the provided data.
241
     *
242
     * @param array $data The data containing PBX settings.
243
     *
244
     * @return array
245
     */
246
    private function updatePBXSettings(array $data): array
247
    {
248
        $messages = ['error' => []];
249
        $pbxSettings = PbxSettings::getDefaultArrayValues();
250
251
        // Process SSHPassword and set SSHPasswordHash accordingly
252
        if (isset($data[PbxSettings::SSH_PASSWORD])) {
253
            if (
254
                $data[PbxSettings::SSH_PASSWORD] === $pbxSettings[PbxSettings::SSH_PASSWORD]
255
                || $data[PbxSettings::SSH_PASSWORD] === GeneralSettingsEditForm::HIDDEN_PASSWORD
256
            ) {
257
                $data[PbxSettings::SSH_PASSWORD_HASH_STRING] = md5($data[PbxSettings::WEB_ADMIN_PASSWORD]);
258
            } else {
259
                $data[PbxSettings::SSH_PASSWORD_HASH_STRING] = md5($data[PbxSettings::SSH_PASSWORD]);
260
            }
261
        }
262
263
        // Update PBX settings
264
        foreach ($pbxSettings as $key => $value) {
265
            switch ($key) {
266
                case PbxSettings::PBX_RECORD_CALLS:
267
                case PbxSettings::PBX_RECORD_CALLS_INNER:
268
                case PbxSettings::AJAM_ENABLED:
269
                case PbxSettings::AMI_ENABLED:
270
                case PbxSettings::RESTART_EVERY_NIGHT:
271
                case PbxSettings::REDIRECT_TO_HTTPS:
272
                case PbxSettings::PBX_SPLIT_AUDIO_THREAD:
273
                case PbxSettings::USE_WEB_RTC:
274
                case PbxSettings::SSH_DISABLE_SSH_PASSWORD:
275
                case PbxSettings::PBX_ALLOW_GUEST_CALLS:
276
                case PbxSettings::DISABLE_ALL_MODULES:
277
                case '***ALL CHECK BOXES ABOVE***':
278
                    $newValue = ($data[$key] === 'on') ? '1' : '0';
279
                    break;
280
                case PbxSettings::SSH_PASSWORD:
281
                    // Set newValue as WebAdminPassword if SSHPassword is the same as the default value
282
                    if ($data[$key] === $value) {
283
                        $newValue = $data[PbxSettings::WEB_ADMIN_PASSWORD];
284
                    } elseif ($data[$key] !== GeneralSettingsEditForm::HIDDEN_PASSWORD) {
285
                        $newValue = $data[$key];
286
                    } else {
287
                        continue 2;
288
                    }
289
                    break;
290
                case PbxSettings::SEND_METRICS:
291
                    $newValue = ($data[$key] === 'on') ? '1' : '0';
292
                    $this->session->set(PbxSettings::SEND_METRICS, $newValue);
293
                    break;
294
                case PbxSettings::PBX_FEATURE_TRANSFER_DIGIT_TIMEOUT:
295
                    $newValue = ceil((int)$data[PbxSettings::PBX_FEATURE_DIGIT_TIMEOUT] / 1000);
296
                    break;
297
                case PbxSettings::SIP_AUTH_PREFIX:
298
                    $newValue = trim($data[$key]);
299
                    break;
300
                case PbxSettings::WEB_ADMIN_PASSWORD:
301
                    if ($data[$key] !== GeneralSettingsEditForm::HIDDEN_PASSWORD) {
302
                        $newValue = $this->security->hash($data[$key]);
303
                    } else {
304
                        continue 2;
305
                    }
306
                    break;
307
                default:
308
                    $newValue = $data[$key];
309
            }
310
311
            if (array_key_exists($key, $data)) {
312
                PbxSettings::setValue($key, $newValue, $messages['error']);
313
            }
314
        }
315
316
        // Reset a cloud provision flag
317
        PbxSettings::setValue(PbxSettings::CLOUD_PROVISIONING, '1', $messages['error']);
318
319
        $result = count($messages['error']) === 0;
320
        return [$result, $messages];
321
    }
322
}
323