CallQueuesController::indexAction()   A
last analyzed

Complexity

Conditions 5
Paths 9

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
c 0
b 0
f 0
dl 0
loc 29
rs 9.2728
cc 5
nc 9
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\CallQueueEditForm;
24
use MikoPBX\Common\Models\CallQueueMembers;
25
use MikoPBX\Common\Models\CallQueues;
26
use MikoPBX\Common\Models\Extensions;
27
use MikoPBX\Common\Models\SoundFiles;
28
29
class CallQueuesController extends BaseController
30
{
31
    /**
32
     *  Builds call queues representation
33
     */
34
    public function indexAction(): void
35
    {
36
        $records = CallQueueMembers::find();
37
        $callQueueMembers = [];
38
        foreach ($records as $record) {
39
            $callQueueMembers[$record->queue][$record->id] = [
40
                'priority' => $record->priority,
41
                'represent' => $record->Extensions === null ? 'ERROR' : $record->Extensions->getRepresent()
42
            ];
43
        }
44
45
        $records = CallQueues::find();
46
        $callQueuesList = [];
47
        foreach ($records as $record) {
48
            $members = $callQueueMembers[$record->uniqid];
49
            if (is_array($members)) {
50
                usort($members, [__CLASS__, 'sortArrayByPriority']);
51
            } else {
52
                $members = [];
53
            }
54
            $callQueuesList[] = [
55
                'uniqid' => $record->uniqid,
56
                'name' => $record->name,
57
                'extension' => $record->extension,
58
                'members' => $members,
59
                'description' => $record->description,
60
            ];
61
        }
62
        $this->view->callQueuesList = $callQueuesList;
63
    }
64
65
    /**
66
     * Modify the call queue action.
67
     *
68
     * @param string $uniqid (optional) The identifier of the queue being modified.
69
     * @return void
70
     */
71
    public function modifyAction(string $uniqid = ''): void
72
    {
73
        $queue            = CallQueues::findFirstByUniqid($uniqid);
74
        $queueMembersList = [];
75
        $soundfilesList   = [];
76
        $extensionList    = [];
77
        if ($queue === null) {
78
            $queue                              = new CallQueues();
79
            $queue->uniqid                      = Extensions::TYPE_QUEUE . strtoupper('-' . md5($queue->id . time()));
80
            $queue->caller_hear                 = 'moh';
81
            $queue->seconds_to_ring_each_member = 60;
82
            $queue->seconds_for_wrapup          = 1;
83
            $queue->announce_position           = 0;
84
            $queue->announce_hold_time          = 0;
85
            $queue->periodic_announce_frequency = 30;
86
            $queue->extension                   = Extensions::getNextFreeApplicationNumber();
87
        } else {
88
            // Queue extension list
89
            $parameters = [
90
                'conditions' => 'queue=:queue:',
91
                'bind'       => [
92
                    'queue' => $queue->uniqid,
93
                ],
94
            ];
95
            $members    = CallQueueMembers::find($parameters);
96
            foreach ($members as $member) {
97
                $queueMembersList[] = [
98
                    'id'       => $member->id,
99
                    'number'   => $member->extension,
100
                    'priority'  => $member->priority,
101
                    'callerid' => $member->Extensions === null ? 'ERROR' : $member->Extensions->getRepresent(),
102
                ];
103
            }
104
            usort($queueMembersList, [__CLASS__, 'sortArrayByPriority']);
105
        }
106
107
        $extensionList[""] = $this->translation->_("ex_SelectNumber");
108
        // List of all used extensions
109
        $parameters = [
110
            'conditions' => 'number IN ({ids:array})',
111
            'bind'       => [
112
                'ids' => [
113
                    $queue->timeout_extension,
114
                    $queue->redirect_to_extension_if_empty,
115
                    $queue->redirect_to_extension_if_unanswered,
116
                    $queue->redirect_to_extension_if_repeat_exceeded,
117
                ],
118
            ],
119
        ];
120
        $extensions = Extensions::find($parameters);
121
        foreach ($extensions as $record) {
122
            $extensionList[$record->number] = $record->getRepresent();
123
        }
124
125
        // List of sound files for queues
126
        $soundfilesList[""] = $this->translation->_("sf_SelectAudioFile");
127
        $soundfilesList[-1] = '-';
128
        $mohSoundFilesList  = $soundfilesList;
129
130
        $soundFiles         = SoundFiles::find(['columns' => 'id,name,category']);
131
        foreach ($soundFiles as $soundFile) {
132
            if (SoundFiles::CATEGORY_CUSTOM === $soundFile->category) {
133
                $soundfilesList[$soundFile->id] = $soundFile->name;
134
            } else {
135
                $mohSoundFilesList[$soundFile->id] = $soundFile->name;
136
            }
137
        }
138
139
        $form                        = new CallQueueEditForm(
140
            $queue,
141
            [
142
                      'extensions' => $extensionList,
143
                      'soundfiles' => $soundfilesList,
144
                      'mohSoundFiles' => $mohSoundFilesList,
145
                  ]
146
        );
147
        $this->view->form            = $form;
148
        $this->view->extensionsTable = $queueMembersList;
149
        $this->view->represent       = $queue->getRepresent();
150
        $this->view->extension       = $queue->extension;
151
    }
152
153
154
    /**
155
     * Save the queue via AJAX request from the form.
156
     *
157
     * This method saves the queue by processing an AJAX request from the form.
158
     *
159
     * @return void
160
     */
161
    public function saveAction(): void
162
    {
163
        if (! $this->request->isPost()) {
164
            return;
165
        }
166
        $this->db->begin();
167
168
        $data  = $this->request->getPost();
169
        $queue = CallQueues::findFirstByUniqid($data['uniqid']);
170
        if ($queue === null) {
171
            $queue                        = new CallQueues();
172
            $extension                    = new Extensions();
173
            $extension->type              = Extensions::TYPE_QUEUE;
174
            $extension->number            = $data["extension"];
175
            $extension->callerid          = $this->sanitizeCallerId($data["name"]);
176
            $extension->userid            = null;
177
            $extension->show_in_phonebook = 1;
178
            $extension->public_access     = 1;
179
        } else {
180
            $extension = $queue->Extensions;
181
        }
182
183
        // Update the extension parameters
184
        if (! $this->updateExtension($extension, $data)) {
185
            $this->view->success = false;
186
            $this->db->rollback();
187
188
            return;
189
        }
190
191
        // Update the queue parameters
192
        if (! $this->updateQueue($queue, $data)) {
193
            $this->view->success = false;
194
            $this->db->rollback();
195
196
            return;
197
        }
198
199
        // Update the queue members
200
        if (! $this->updateQueueMembers($data)) {
201
            $this->view->success = false;
202
            $this->db->rollback();
203
204
            return;
205
        }
206
207
        $this->flash->success($this->translation->_('ms_SuccessfulSaved'));
208
        $this->view->success = true;
209
        $this->db->commit();
210
211
        // If it was a new queue card, reload the page with the specified ID
212
        if (empty($data['id'])) {
213
            $this->view->reload = "call-queues/modify/{$data['uniqid']}";
214
        }
215
    }
216
217
    /**
218
     * Update the extension parameters.
219
     *
220
     * This method updates the parameters of the internal extension.
221
     *
222
     * @param \MikoPBX\Common\Models\Extensions $extension The extension object to update.
223
     * @param array                             $data      The array of fields from the POST request.
224
     *
225
     * @return bool The update result. Returns true if the update is successful, false otherwise.
226
     */
227
    private function updateExtension(Extensions $extension, array $data): bool
228
    {
229
        $extension->number   = $data['extension'];
230
        $extension->callerid = $this->sanitizeCallerId($data['name']);
231
        if ($extension->save() === false) {
232
            $errors = $extension->getMessages();
233
            $this->flash->error(implode('<br>', $errors));
234
235
            return false;
236
        }
237
238
        return true;
239
    }
240
241
    /**
242
     * Update queue parameters.
243
     *
244
     * This method updates the parameters of a queue.
245
     *
246
     * @param \MikoPBX\Common\Models\CallQueues $queue The queue object to update.
247
     * @param array                             $data  The array of fields from the POST request.
248
     *
249
     * @return bool The update result. Returns true if the update is successful, false otherwise.
250
     */
251
    private function updateQueue(CallQueues $queue, array $data): bool
252
    {
253
        foreach ($queue as $name => $value) {
254
            switch ($name) {
255
                case "extension":
256
                case "name":
257
                    $queue->$name = $data[$name];
258
                    break;
259
                case "recive_calls_while_on_a_call":
260
                case "announce_position":
261
                case "announce_hold_time":
262
                    if (array_key_exists($name, $data)) {
263
                        $queue->$name = ($data[$name] == 'on') ? "1" : "0";
264
                    } else {
265
                        $queue->$name = "0";
266
                    }
267
                    break;
268
269
                case "periodic_announce_sound_id":
270
                case "moh_sound_id":
271
                case "redirect_to_extension_if_repeat_exceeded":
272
                case "redirect_to_extension_if_empty":
273
                    if (! array_key_exists($name, $data) || empty($data[$name])) {
274
                        $queue->$name = null;
275
                        continue 2;
276
                    }
277
                    $queue->$name = $data[$name];
278
279
                    break;
280
                case "timeout_to_redirect_to_extension":
281
                case "number_unanswered_calls_to_redirect":
282
                    if (! array_key_exists($name, $data)) {
283
                        continue 2;
284
                    }
285
                    if (empty($data[$name])) {
286
                        $queue->$name = null;
287
                    } else {
288
                        $queue->$name = $data[$name];
289
                    }
290
                    break;
291
                case "timeout_extension":
292
                    if (
293
                        ! array_key_exists($name, $data)
294
                        || empty($data[$name])
295
                        || (array_key_exists('timeout_to_redirect_to_extension', $data)
296
                            && intval($data['timeout_to_redirect_to_extension']) === 0)
297
                    ) {
298
                        $queue->$name = null;
299
                        continue 2;
300
                    }
301
                    $queue->$name = $data[$name];
302
303
                    break;
304
                case "redirect_to_extension_if_unanswered":
305
                    if (
306
                        ! array_key_exists($name, $data)
307
                        || empty($data[$name])
308
                        || (array_key_exists('number_unanswered_calls_to_redirect', $data)
309
                            && intval($data['number_unanswered_calls_to_redirect']) === 0)
310
                    ) {
311
                        $queue->$name = null;
312
                        continue 2;
313
                    }
314
                    $queue->$name = $data[$name];
315
316
                    break;
317
                default:
318
                    if (! array_key_exists($name, $data)) {
319
                        continue 2;
320
                    }
321
                    $queue->$name = $data[$name];
322
            }
323
        }
324
325
        if ($queue->save() === false) {
326
            $errors = $queue->getMessages();
327
            $this->flash->error(implode('<br>', $errors));
328
329
            return false;
330
        }
331
332
        return true;
333
    }
334
335
    /**
336
     * Update the queue members with the provided data.
337
     *
338
     * @param array $data The data containing the queue members information.
339
     * @return bool True if the update is successful, false otherwise.
340
     */
341
    private function updateQueueMembers(array $data): bool
342
    {
343
        $realMembers = [];
344
        // Update settings for existing queue members
345
        $membersTable = json_decode($data['members']);
346
        foreach ($membersTable as $member) {
347
            $parameters   = [
348
                'conditions' => 'extension = :number: AND queue=:uniqid:',
349
                'bind'       => [
350
                    'number' => $member->number,
351
                    'uniqid' => $data['uniqid'],
352
                ],
353
            ];
354
            $queueMembers = CallQueueMembers::find($parameters);
355
            if ($queueMembers->count() > 1) {
356
                // Remove extra members and create a new one
357
                if ($queueMembers->delete() === false) {
358
                    $errors = $queueMembers->getMessages();
359
                    $this->flash->error(implode('<br>', $errors));
360
361
                    return false;
362
                }
363
                $queueMember = new CallQueueMembers();
364
            } elseif ($queueMembers->count() === 1) {
365
                $queueMember = $queueMembers->getFirst();
366
            } else {
367
                $queueMember = new CallQueueMembers();
368
            }
369
370
            $queueMember->priority  = $member->priority;
371
            $queueMember->extension = $member->number;
372
            $queueMember->queue     = $data['uniqid'];
373
            $realMembers[]          = $member->number;
374
            if ($queueMember->save() === false) {
375
                $errors = $queueMember->getMessages();
376
                $this->flash->error(implode('<br>', $errors));
377
378
                return false;
379
            }
380
        }
381
382
        // Remove queue members not present in the list
383
        $parameters = [
384
            'conditions' => 'extension NOT IN  ({numbers:array}) AND queue=:uniqid:',
385
            'bind'       => [
386
                'numbers' => $realMembers,
387
                'uniqid'  => $data['uniqid'],
388
            ],
389
        ];
390
391
        $deletedMembers = CallQueueMembers::find($parameters);
392
        if ($deletedMembers && $deletedMembers->delete() === false) {
393
            $errors = $deletedMembers->getMessages();
394
            $this->flash->error(implode('<br>', $errors));
395
396
            return false;
397
        }
398
399
        return true;
400
    }
401
}
402