Passed
Push — develop ( dcb728...9b177f )
by Портнов
05:21
created

findModuleByExtensionNumber()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 17
rs 9.9
cc 4
nc 6
nop 1
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 MikoPBX\AdminCabinet\Forms\ExtensionEditForm;
23
use MikoPBX\Common\Models\{
24
    ExtensionForwardingRights,
25
    Extensions,
26
    ExternalPhones,
27
    NetworkFilters,
28
    PbxExtensionModules,
29
    PbxSettings,
30
    Sip,
31
    Users
32
};
33
use Phalcon\Text;
34
35
use function MikoPBX\Common\Config\appPath;
36
37
class ExtensionsController extends BaseController
38
{
39
40
    /**
41
     * Построение списка внутренних номеров и сотрудников
42
     */
43
    public function indexAction(): void
44
    {
45
        $extensionTable = [];
46
47
        $parameters = [
48
            'models'     => [
49
                'Extensions' => Extensions::class,
50
            ],
51
            'conditions' => 'Extensions.is_general_user_number = "1"',
52
            'columns'    => [
53
                'id'       => 'Extensions.id',
54
                'username' => 'Users.username',
55
                'number'   => 'Extensions.number',
56
                'userid'   => 'Extensions.userid',
57
                'disabled' => 'Sip.disabled',
58
                'secret'   => 'Sip.secret',
59
                'email'    => 'Users.email',
60
                'type'     => 'Extensions.type',
61
                'avatar'   => 'Users.avatar',
62
63
            ],
64
            'order'      => 'number',
65
            'joins'      => [
66
                'Sip'   => [
67
                    0 => Sip::class,
68
                    1 => 'Sip.extension=Extensions.number',
69
                    2 => 'Sip',
70
                    3 => 'LEFT',
71
                ],
72
                'Users' => [
73
                    0 => Users::class,
74
                    1 => 'Users.id = Extensions.userid',
75
                    2 => 'Users',
76
                    3 => 'INNER',
77
                ],
78
            ],
79
        ];
80
        $query      = $this->di->get('modelsManager')->createBuilder($parameters)->getQuery();
81
        $extensions = $query->execute();
82
83
        foreach ($extensions as $extension) {
84
            switch ($extension->type) {
85
                case Extensions::TYPE_SIP:
86
                    $extensionTable[$extension->userid]['userid']   = $extension->userid;
87
                    $extensionTable[$extension->userid]['number']   = $extension->number;
88
                    $extensionTable[$extension->userid]['status']   = ($extension->disabled === '1') ? 'disabled' : '';
89
                    $extensionTable[$extension->userid]['id']       = $extension->id;
90
                    $extensionTable[$extension->userid]['username'] = $extension->username;
91
                    $extensionTable[$extension->userid]['email']    = $extension->email;
92
                    $extensionTable[$extension->userid]['secret']   = $extension->secret;
93
94
                    if ( ! array_key_exists('mobile', $extensionTable[$extension->userid])) {
95
                        $extensionTable[$extension->userid]['mobile'] = '';
96
                    }
97
                    if ($extension->avatar) {
98
                        $filename    = md5($extension->avatar);
99
                        $imgCacheDir = appPath('sites/admin-cabinet/assets/img/cache');
100
                        $imgFile     = "{$imgCacheDir}/$filename.jpg";
101
                        if ( ! file_exists($imgFile)) {
102
                            $this->base64ToJpeg($extension->avatar, $imgFile);
103
                        }
104
105
                        $extensionTable[$extension->userid]['avatar'] = "{$this->url->get()}assets/img/cache/{$filename}.jpg";
106
                    } else {
107
                        $extensionTable[$extension->userid]['avatar'] = "{$this->url->get()}assets/img/unknownPerson.jpg";
108
                    }
109
110
                    break;
111
                case Extensions::TYPE_EXTERNAL:
112
                    $extensionTable[$extension->userid]['mobile'] = $extension->number;
113
                    break;
114
                default:
115
            }
116
        }
117
        $this->view->extensions = $extensionTable;
118
    }
119
120
    /**
121
     * Создает файл jpeg из переданной картинки
122
     *
123
     * @param $base64_string
124
     * @param $output_file
125
     *
126
     * @return void
127
     */
128
    private function base64ToJpeg($base64_string, $output_file): void
129
    {
130
        // open the output file for writing
131
        $ifp = fopen($output_file, 'wb');
132
133
        if ($ifp === false) {
134
            return;
135
        }
136
        // split the string on commas
137
        // $data[ 0 ] == "data:image/png;base64"
138
        // $data[ 1 ] == <actual base64 string>
139
        $data = explode(',', $base64_string);
140
141
        // we could add validation here with ensuring count( $data ) > 1
142
        fwrite($ifp, base64_decode($data[1]));
143
144
        // clean up the file resource
145
        fclose($ifp);
146
    }
147
148
    /**
149
     * Change extension settings
150
     *
151
     * @param ?string $id modified extension id
152
     */
153
    public function modifyAction($id = null): void
154
    {
155
        $extension = Extensions::findFirstById($id);
156
157
        if ($extension === null) {
158
            $extension                         = new Extensions();
159
            $extension->show_in_phonebook      = '1';
160
            $extension->public_access          = '0';
161
            $extension->is_general_user_number = '1';
162
            $extension->type                   = Extensions::TYPE_SIP;
163
            $extension->Sip                    = new Sip();
164
            $extension->Sip->disabled          = 0;
165
            $extension->Sip->type              = 'peer';
166
            $extension->Sip->uniqid            = Extensions::TYPE_SIP.strtoupper('-PHONE-' . md5(time()));
167
            $extension->Sip->busylevel         = 1;
168
            $extension->Sip->qualify           = '1';
169
            $extension->Sip->qualifyfreq       = 60;
170
            $extension->number                 = $this->getNextInternalNumber();
171
172
            $extension->Users       = new Users();
173
            $extension->Users->role = 'user';
174
175
            $extension->ExtensionForwardingRights = new ExtensionForwardingRights();
176
177
            $this->view->avatar = '';
178
        } else {
179
            $this->view->avatar = $extension->Users->avatar;
180
        }
181
        $arrNetworkFilters         = [];
182
        $networkFilters            = NetworkFilters::getAllowedFiltersForType(['SIP']);
183
        $arrNetworkFilters['none'] = $this->translation->_('ex_NoNetworkFilter');
184
        foreach ($networkFilters as $filter) {
185
            $arrNetworkFilters[$filter->id] = $filter->getRepresent();
186
        }
187
188
        $parameters        = [
189
            'conditions' => 'type = "'.Extensions::TYPE_EXTERNAL.'" AND is_general_user_number = "1" AND userid=:userid:',
190
            'bind'       => [
191
                'userid' => $extension->userid,
192
            ],
193
        ];
194
        $externalExtension = Extensions::findFirst($parameters);
195
        if ($externalExtension === null) {
196
            $externalExtension                           = new Extensions();
197
            $externalExtension->userid                   = $extension->userid;
198
            $externalExtension->type                     = Extensions::TYPE_EXTERNAL;
199
            $externalExtension->is_general_user_number   = '1';
200
            $externalExtension->ExternalPhones           = new ExternalPhones();
201
            $externalExtension->ExternalPhones->uniqid   = Extensions::TYPE_EXTERNAL.strtoupper('-' . md5(time()));
202
            $externalExtension->ExternalPhones->disabled = '0';
203
        }
204
205
        $forwardingExtensions  = [];
206
        $forwardingExtensions[''] = $this->translation->_('ex_SelectNumber');
207
208
        $parameters = [
209
            'conditions' => 'number IN ({ids:array})',
210
            'bind'       => [
211
                'ids' => [
212
                    $extension->ExtensionForwardingRights->forwarding,
213
                    $extension->ExtensionForwardingRights->forwardingonbusy,
214
                    $extension->ExtensionForwardingRights->forwardingonunavailable,
215
                ],
216
            ],
217
        ];
218
        $extensions = Extensions::find($parameters);
219
        foreach ($extensions as $record) {
220
            $forwardingExtensions[$record->number] = $record->getRepresent();
221
        }
222
223
        // Ограничим длинну внутреннего номера согласно настройкам
224
        $extensionsLength      = PbxSettings::getValueByKey('PBXInternalExtensionLength');
225
        $internalExtensionMask = "9{2,{$extensionsLength}}";
226
227
        $form = new ExtensionEditForm(
228
            $extension, [
229
                          'network_filters'        => $arrNetworkFilters,
230
                          'external_extension'     => $externalExtension,
231
                          'forwarding_extensions'  => $forwardingExtensions,
232
                          'internalextension_mask' => $internalExtensionMask,
233
                      ]
234
        );
235
236
        $this->view->form      = $form;
237
        $this->view->represent = $extension->getRepresent();
238
    }
239
240
    /**
241
     * Получает из базы следующий за последним введенным внутренним номером
242
     */
243
    private function getNextInternalNumber()
244
    {
245
        $parameters = [
246
            'conditions' => 'type = "'.Extensions::TYPE_SIP.'"',
247
            'column'     => 'number',
248
        ];
249
        $query      = Extensions::maximum($parameters);
250
        if ($query === null) {
251
            $query = 200;
252
        }
253
        $result       = (int)$query + 1;
254
        $extensionsLength = PbxSettings::getValueByKey('PBXInternalExtensionLength');
255
        $maxExtension = (10 ** $extensionsLength) - 1;
256
257
        return ($result <= $maxExtension) ? $result : '';
258
    }
259
260
    /**
261
     * Сохранение карточки пользователя с его номерами
262
     *
263
     * @return void параметры помещаются в view и обрабатваются через ControllerBase::afterExecuteRoute()
264
     */
265
    public function saveAction(): void
266
    {
267
        if ( ! $this->request->isPost()) {
268
            return;
269
        }
270
271
        $this->db->begin();
272
273
        $data = $this->request->getPost();
274
275
        $sipEntity = null;
276
277
        if (array_key_exists('sip_uniqid', $data)) {
278
            $sipEntity = SIP::findFirstByUniqid($data['sip_uniqid']);
279
        }
280
281
        if ($sipEntity === null) {
282
            $sipEntity             = new SIP();
283
            $extension             = new Extensions();
284
            $userEntity            = new Users();
285
            $fwdEntity             = new ExtensionForwardingRights();
286
            $fwdEntity->ringlength = 45;
287
        } else {
288
            $extension = $sipEntity->Extensions;
289
            if ( ! $extension) {
290
                $extension = new Extensions();
291
            }
292
            $userEntity = $extension->Users;
293
            if ( ! $userEntity) {
294
                $userEntity = new Users();
295
            }
296
            $fwdEntity = $extension->ExtensionForwardingRights;
297
            if ( ! $fwdEntity) {
298
                $fwdEntity = new ExtensionForwardingRights();
299
            }
300
        }
301
302
        // Заполним параметры пользователя
303
        if ( ! $this->saveUser($userEntity, $data)) {
304
            $this->view->success = false;
305
            $this->db->rollback();
306
307
            return;
308
        }
309
310
        // Заполним параметры внутреннего номера
311
        if ( ! $this->saveExtension($extension, $userEntity, $data, false)) {
312
            $this->view->success = false;
313
            $this->db->rollback();
314
315
            return;
316
        }
317
318
        // Заполним параметры SIP учетки
319
        if ( ! $this->saveSip($sipEntity, $data)) {
320
            $this->view->success = false;
321
            $this->db->rollback();
322
323
            return;
324
        }
325
326
327
        // Заполним параметры маршрутизации
328
        if ( ! $this->saveForwardingRights($fwdEntity, $data)) {
329
            $this->view->success = false;
330
            $this->db->rollback();
331
332
            return;
333
        }
334
335
        // Если мобильный не указан, то не будем его добавлять в базу
336
        if ( ! empty($data['mobile_number'])) {
337
            $externalPhone = ExternalPhones::findFirstByUniqid($data['mobile_uniqid']);
338
            if ($externalPhone === null) {
339
                $externalPhone   = new ExternalPhones();
340
                $mobileExtension = new Extensions();
341
            } else {
342
                $mobileExtension = $externalPhone->Extensions;
343
            }
344
345
            // Заполним параметры Extension для мобильного
346
            if ( ! $this->saveExtension($mobileExtension, $userEntity, $data, true)) {
347
                $this->view->success = false;
348
                $this->db->rollback();
349
350
                return;
351
            }
352
353
            // Заполним параметры ExternalPhones для мобильного
354
            if ( ! $this->saveExternalPhones($externalPhone, $data)) {
355
                $this->view->success = false;
356
                $this->db->rollback();
357
358
                return;
359
            }
360
        } else {
361
            // Удалить номер мобильного если он был привязан к пользователю
362
            $parameters          = [
363
                'conditions' => 'type="'.Extensions::TYPE_EXTERNAL.'" AND is_general_user_number = "1" AND userid=:userid:',
364
                'bind'       => [
365
                    'userid' => $userEntity->id,
366
                ],
367
            ];
368
            $deletedMobileNumber = Extensions::findFirst($parameters);
369
            if ($deletedMobileNumber !== null
370
                && $deletedMobileNumber->delete() === false) {
371
                $errors = $deletedMobileNumber->getMessages();
372
                $this->flash->error(implode('<br>', $errors));
373
                $this->view->success = false;
374
                $this->db->rollback();
375
376
                return;
377
            }
378
        }
379
380
        $this->flash->success($this->translation->_('ms_SuccessfulSaved'));
381
        $this->view->success = true;
382
        $this->db->commit();
383
384
        // Если это было создание карточки то надо перегрузить страницу с указанием ID
385
        if (empty($data['id'])) {
386
            $this->view->reload = "extensions/modify/{$extension->id}";
387
        }
388
    }
389
390
    /**
391
     * Сохранение параметров в таблицу Users
392
     *
393
     * @param Users $userEntity
394
     * @param array $data - POST дата
395
     *
396
     * @return bool результат сохранения
397
     */
398
    private function saveUser(Users $userEntity, array $data)
399
    {
400
        // Заполним параметры пользователя
401
        foreach ($userEntity as $name => $value) {
402
            switch ($name) {
403
                case 'role':
404
                    if (array_key_exists('user_' . $name, $data)) {
405
                        $userEntity->$name = ($userEntity->$name === 'user') ? 'user' : $data['user_' . $name]; // не повышаем роль
406
                    }
407
                    break;
408
                case 'language':
409
                    $userEntity->$name = PbxSettings::getValueByKey('PBXLanguage');
410
                    break;
411
                default:
412
                    if (array_key_exists('user_' . $name, $data)) {
413
                        $userEntity->$name = $data['user_' . $name];
414
                    }
415
            }
416
        }
417
418
        if ($userEntity->save() === false) {
419
            $errors = $userEntity->getMessages();
420
            $this->flash->error(implode('<br>', $errors));
421
422
            return false;
423
        }
424
425
        return true;
426
    }
427
428
    /**
429
     * Сохранение параметров в таблицу Extensions
430
     *
431
     * @param Extensions $extension
432
     * @param Users      $userEntity
433
     * @param array      $data - POST дата
434
     * @param bool isMobile - это мобильный телефон
435
     *
436
     * @return bool результат сохранения
437
     */
438
    private function saveExtension(Extensions $extension, Users $userEntity, array $data, $isMobile = false): bool
439
    {
440
        foreach ($extension as $name => $value) {
441
            switch ($name) {
442
                case 'id':
443
                    break;
444
                case 'show_in_phonebook':
445
                case 'is_general_user_number':
446
                    $extension->$name = '1';
447
                    break;
448
                case 'type':
449
                    $extension->$name = $isMobile ? Extensions::TYPE_EXTERNAL : Extensions::TYPE_SIP;
450
                    break;
451
                case 'public_access':
452
                    if (array_key_exists($name, $data)) {
453
                        $extension->$name = ($data[$name] === 'on') ? '1' : '0';
454
                    } else {
455
                        $extension->$name = '0';
456
                    }
457
                    break;
458
                case 'callerid':
459
                    $extension->$name = $this->sanitizeCallerId($data['user_username']);
460
                    break;
461
                case 'userid':
462
                    $extension->$name = $userEntity->id;
463
                    break;
464
                case 'number':
465
                    $extension->$name = $isMobile ? $data['mobile_number'] : $data['number'];
466
                    break;
467
                default:
468
                    if (array_key_exists($name, $data)) {
469
                        $extension->$name = $data[$name];
470
                    }
471
            }
472
        }
473
474
        if ($extension->save() === false) {
475
            $errors = $extension->getMessages();
476
            $this->flash->error(implode('<br>', $errors));
477
478
            return false;
479
        }
480
481
        return true;
482
    }
483
484
    /**
485
     * Сохранение параметров в таблицу SIP
486
     *
487
     * @param Sip   $sipEntity
488
     * @param array $data - POST дата
489
     *
490
     * @return bool результат сохранения
491
     */
492
    private function saveSip(Sip $sipEntity, array $data): bool
493
    {
494
        foreach ($sipEntity as $name => $value) {
495
            switch ($name) {
496
                case 'qualify':
497
                    if (array_key_exists($name, $data)) {
498
                        $sipEntity->$name = ($data[$name] === 'on') ? '1' : '0';
499
                    } else {
500
                        $sipEntity->$name = "0";
501
                    }
502
                    break;
503
                case 'disabled':
504
                case 'enableRecording':
505
                case 'disablefromuser':
506
                    if (array_key_exists('sip_' . $name, $data)) {
507
                        $sipEntity->$name = ($data['sip_' . $name] === 'on') ? '1' : '0';
508
                    } else {
509
                        $sipEntity->$name = "0";
510
                    }
511
                    break;
512
                case 'networkfilterid':
513
                    if ( ! array_key_exists('sip_' . $name, $data)) {
514
                        continue 2;
515
                    }
516
                    if ($data['sip_' . $name] === 'none') {
517
                        $sipEntity->$name = null;
518
                    } else {
519
                        $sipEntity->$name = $data['sip_' . $name];
520
                    }
521
                    break;
522
                case 'extension':
523
                    $sipEntity->$name = $data['number'];
524
                    break;
525
                case 'description':
526
                    $sipEntity->$name = $data['user_username'];
527
                    break;
528
                case 'manualattributes':
529
                    $sipEntity->setManualAttributes($data['sip_manualattributes']);
530
                    break;
531
                default:
532
                    if (array_key_exists('sip_' . $name, $data)) {
533
                        $sipEntity->$name = $data['sip_' . $name];
534
                    }
535
            }
536
        }
537
        if ($sipEntity->save() === false) {
538
            $errors = $sipEntity->getMessages();
539
            $this->flash->error(implode('<br>', $errors));
540
541
            return false;
542
        }
543
544
        return true;
545
    }
546
547
    /**
548
     * Заполним параметры переадресации
549
     *
550
     * @param \MikoPBX\Common\Models\ExtensionForwardingRights $forwardingRight
551
     * @param                                                  $data
552
     *
553
     * @return bool
554
     */
555
    private function saveForwardingRights(ExtensionForwardingRights $forwardingRight, $data): bool
556
    {
557
        foreach ($forwardingRight as $name => $value) {
558
            switch ($name) {
559
                case 'extension':
560
                    $forwardingRight->$name = $data['number'];
561
                    break;
562
                default:
563
                    if (array_key_exists('fwd_' . $name, $data)) {
564
                        $forwardingRight->$name = ($data['fwd_' . $name] === -1) ? '' : $data['fwd_' . $name];
565
                    }
566
            }
567
        }
568
        if (empty($forwardingRight->forwarding)) {
569
            $forwardingRight->ringlength = null;
570
        }
571
572
        if ($forwardingRight->save() === false) {
573
            $errors = $forwardingRight->getMessages();
574
            $this->flash->error(implode('<br>', $errors));
575
576
            return false;
577
        }
578
579
        return true;
580
    }
581
582
    /**
583
     * Заполним параметры ExternalPhones для мобильного номера
584
     *
585
     * @param ExternalPhones $externalPhone
586
     * @param array          $data - POST дата
587
     *
588
     * @return bool результат сохранения
589
     */
590
    private function saveExternalPhones(ExternalPhones $externalPhone, array $data): bool
591
    {
592
        foreach ($externalPhone as $name => $value) {
593
            switch ($name) {
594
                case 'extension':
595
                    $externalPhone->$name = $data['mobile_number'];
596
                    break;
597
                case 'description':
598
                    $externalPhone->$name = $data['user_username'];
599
                    break;
600
                case 'disabled':
601
                    if (array_key_exists('mobile_' . $name, $data)) {
602
                        $externalPhone->$name = ($data['mobile_' . $name] === 'on') ? '1' : '0';
603
                    } else {
604
                        $externalPhone->$name = '0';
605
                    }
606
                    break;
607
                default:
608
                    if (array_key_exists('mobile_' . $name, $data)) {
609
                        $externalPhone->$name = $data['mobile_' . $name];
610
                    }
611
            }
612
        }
613
        if ($externalPhone->save() === false) {
614
            $errors = $externalPhone->getMessages();
615
            $this->flash->error(implode('<br>', $errors));
616
617
            return false;
618
        }
619
620
        return true;
621
    }
622
623
    /**
624
     * Удаление внутреннего номера и всех зависимых от него записей в том числе мобильного и переадресаций
625
     *
626
     * @param string $id - записи внутренненго номера
627
     */
628
    public function deleteAction(string $id = '')
629
    {
630
        $this->db->begin();
631
        $extension = Extensions::findFirstById($id);
632
633
        // Чтобы не было зацикливания при удалении сначала удалим
634
        // настройки переадресации у этой же учетной записи, т.к. она может ссылаться на себя
635
636
        $errors = null;
637
        if ($extension !== null && $extension->ExtensionForwardingRights
638
            && ! $extension->ExtensionForwardingRights->delete()) {
639
            $errors = $extension->ExtensionForwardingRights->getMessages();
640
        }
641
642
        if ( ! $errors && $extension) {
643
            $user = $extension->Users;
644
            if ( ! $user->delete()) {
645
                $errors = $user->getMessages();
646
            }
647
        }
648
649
        if ($errors) {
650
            $this->flash->error(implode('<br>', $errors));
651
            $this->view->success = false;
652
            $this->db->rollback();
653
        } else {
654
            $this->db->commit();
655
            $this->view->success = true;
656
        }
657
    }
658
659
    /**
660
     * Проверка на доступность номера JS скрипта extensions.js
661
     *
662
     * @param string $number - внутренний номер пользователя
663
     *
664
     * @return void параметры помещаются в view и обрабатваются через ControllerBase::afterExecuteRoute()
665
     */
666
    public function availableAction(string $number = ''): void
667
    {
668
        $result = true;
669
        // Проверим пересечение с внутренним номерным планом
670
        $extension = Extensions::findFirstByNumber($number);
671
        if ($extension !== null) {
672
            $result             = false;
673
            $this->view->userId = $extension->userid;
674
        }
675
        // Проверим пересечение с парковочными слотами
676
        if ($result) {
677
            $parkExt       = PbxSettings::getValueByKey('PBXCallParkingExt');
678
            $parkStartSlot = PbxSettings::getValueByKey('PBXCallParkingStartSlot');
679
            $parkEndSlot   = PbxSettings::getValueByKey('PBXCallParkingEndSlot');
680
            if ($number === $parkExt || ($number >= $parkStartSlot && $number <= $parkEndSlot)) {
681
                $result             = false;
682
                $this->view->userId = 0;
683
            }
684
        }
685
686
        $this->view->numberAvailable = $result;
687
    }
688
689
    /**
690
     * Отключение всех номеров пользователя
691
     *
692
     * @param string $number - внутренний номер пользователя
693
     *
694
     * @return void
695
     */
696
    public function disableAction(string $number = ''): void
697
    {
698
        $extension = Extensions::findFirstByNumber($number);
699
        if ($extension !== null) {
700
            $extensions = Extensions::findByUserid($extension->userid);
701
            foreach ($extensions as $extension) {
702
                switch ($extension->type) {
703
                    case Extensions::TYPE_SIP:
704
                        $extension->Sip->disabled = '1';
705
                        break;
706
                    case Extensions::TYPE_EXTERNAL:
707
                        $extension->ExternalPhones->disabled = '1';
708
                        break;
709
                }
710
                if ($extension->save() === true) {
711
                    $this->view->success = true;
712
                } else {
713
                    $this->view->success = false;
714
                    $errors              = $extension->getMessages();
715
                    $this->flash->error(implode('<br>', $errors));
716
717
                    return;
718
                }
719
            }
720
        }
721
    }
722
723
    /**
724
     * Включение всех номеров пользователя
725
     *
726
     * @param string $number - внутренний номер пользователя
727
     *
728
     * @return void
729
     */
730
    public function enableAction(string $number = ''): void
731
    {
732
        $extension = Extensions::findFirstByNumber($number);
733
        if ($extension !== null) {
734
            $extensions = Extensions::findByUserid($extension->userid);
735
            foreach ($extensions as $extension) {
736
                switch ($extension->type) {
737
                    case Extensions::TYPE_SIP:
738
                        $extension->Sip->disabled = '0';
739
                        break;
740
                    case Extensions::TYPE_EXTERNAL:
741
                        $extension->ExternalPhones->disabled = '1';
742
                        break;
743
                }
744
                if ($extension->save() === true) {
745
                    $this->view->success = true;
746
                } else {
747
                    $this->view->success = false;
748
                    $errors              = $extension->getMessages();
749
                    $this->flash->error(implode('<br>', $errors));
750
751
                    return;
752
                }
753
            }
754
        }
755
    }
756
757
    /**
758
     * Возвращает представление для списка нормеров телефонов по AJAX запросу
759
     *
760
     * @return void
761
     */
762
    public function GetPhonesRepresentAction(): void
763
    {
764
        if ( ! $this->request->isPost()) {
765
            return;
766
        }
767
        $numbers = $this->request->getPost('numbers');
768
        $result  = [];
769
        foreach ($numbers as $number) {
770
            $result[$number] = [
771
                'number'    => $number,
772
                'represent' => $this->GetPhoneRepresentAction($number),
773
            ];
774
        }
775
        $this->view->success = true;
776
        $this->view->message = $result;
777
    }
778
779
    /**
780
     * Возвращает представление нормера телефона по AJAX запросу
781
     *
782
     * @param $phoneNumber
783
     *
784
     * @return string
785
     */
786
    public function GetPhoneRepresentAction($phoneNumber): string
787
    {
788
        $response = $phoneNumber;
789
790
        if (strlen($phoneNumber) > 10) {
791
            $seekNumber = substr($phoneNumber, -9);
792
            $parameters = [
793
                'conditions' => 'number LIKE :SearchPhrase1:',
794
                'bind'       => [
795
                    'SearchPhrase1' => "%{$seekNumber}",
796
                ],
797
            ];
798
        } else {
799
            $parameters = [
800
                'conditions' => 'number = :SearchPhrase1:',
801
                'bind'       => [
802
                    'SearchPhrase1' => $phoneNumber,
803
                ],
804
            ];
805
        }
806
        $result = Extensions::findFirst($parameters);
807
        if ($result !== null) {
808
            $response = $result->getRepresent();
809
        }
810
811
        return $response;
812
    }
813
814
    /**
815
     * Используется для генерации списка выбора пользователей из JS скрипта extensions.js
816
     *
817
     * @param string $type {all, phones, internal} - отображать только телефоны или все возможные номера
818
     *
819
     * @return void параметры помещаются в view и обрабатваются через ControllerBase::afterExecuteRoute()
820
     */
821
    public function getForSelectAction(string $type = 'all'): void
822
    {
823
        $results = [];
824
        switch ($type) {
825
            case 'all':
826
            {
827
                $parameters = [
828
                    'conditions' => 'show_in_phonebook="1" AND number NOT IN ({exclude:array})',
829
                    'bind' => [
830
                        'exclude' => ['did2user']
831
                    ]
832
                ];
833
                break;
834
            }
835
            case 'routing':
836
            {
837
                $parameters = [
838
                    'conditions' => 'show_in_phonebook="1"',
839
                ];
840
                break;
841
            }
842
            case 'phones':
843
            {
844
                // Список телефоонных эктеншенов
845
                $parameters = [
846
                    'conditions' => 'type IN ({ids:array}) AND show_in_phonebook="1"',
847
                    'bind'       => [
848
                        'ids' => [Extensions::TYPE_SIP, Extensions::TYPE_EXTERNAL],
849
                    ],
850
                ];
851
                break;
852
            }
853
            case 'internal':
854
            {
855
                // Только внутренние
856
                $parameters = [
857
                    'conditions' => 'type IN ({ids:array}) AND show_in_phonebook="1"',
858
                    'bind'       => [
859
                        'ids' => [Extensions::TYPE_SIP],
860
                    ],
861
                ];
862
                break;
863
            }
864
            default:
865
            {
866
                $parameters = [
867
                    'conditions' => 'show_in_phonebook="1"',
868
                ];
869
            }
870
        }
871
        $extensions = Extensions::find($parameters);
872
        foreach ($extensions as $record) {
873
            $type = ($record->userid > 0) ? ' USER'
874
                : $record->type; // Пользователи будут самыми первыми в списке
875
            $type = Text::underscore(strtoupper($type));
876
877
878
            // Необходимо проверить к какому модулю относится эта запись
879
            // и включен ли этот модуль в данный момент
880
            if ($type === Extensions::TYPE_MODULES) {
881
                $module = $this->findModuleByExtensionNumber($record->number);
882
                if ($module === null || $module->disabled === '1') {
883
                    continue; // исключаем отключенные модули
884
                }
885
            }
886
            $represent        = $record->getRepresent();
887
            $clearedRepresent = strip_tags($represent);
888
            $results[]        = [
889
                'name'          => $represent,
890
                'value'         => $record->number,
891
                'type'          => $type,
892
                'typeLocalized' => $this->translation->_("ex_dropdownCategory_{$type}"),
893
                'sorter'        => ($record->userid > 0) ? "{$type}{$clearedRepresent}{$record->number}" : "{$type}{$clearedRepresent}"
894
                // 'avatar' => ( $record->userid > 0 )
895
                // 	? $record->Users->avatar : '',
896
            ];
897
        }
898
899
        usort(
900
            $results,
901
            [__CLASS__, 'sortExtensionsArray']
902
        );
903
904
        $this->view->success = true;
905
        $this->view->results = $results;
906
    }
907
908
    /**
909
     * Try to find module by extension number
910
     *
911
     * @param string $number
912
     *
913
     * @return mixed|null
914
     */
915
    private function findModuleByExtensionNumber(string $number)
916
    {
917
        $result         = null;
918
        $extension      = Extensions::findFirst("number ='{$number}'");
919
        $relatedLinks   = $extension->getRelatedLinks();
920
        $moduleUniqueID = false;
921
        foreach ($relatedLinks as $relation) {
922
            $obj = $relation['object'];
923
            if (strpos(get_class($obj), 'Modules\\') === 0) {
924
                $moduleUniqueID = explode('Models\\', get_class($obj))[1];
925
            }
926
        }
927
        if ($moduleUniqueID) {
928
            $result = PbxExtensionModules::findFirstByUniqid($moduleUniqueID);
929
        }
930
931
        return $result;
932
    }
933
934
    /**
935
     * Сортировка массива extensions
936
     *
937
     * @param $a
938
     * @param $b
939
     *
940
     * @return int
941
     */
942
    private function sortExtensionsArray($a, $b): int
943
    {
944
        return strcmp($a['sorter'], $b['sorter']);
945
    }
946
947
948
}