Skip to content

Commit 09fb042

Browse files
authored
Merge pull request #9 from mikopbx/develop
Develop
2 parents e1db05c + 75cac17 commit 09fb042

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+4321
-893
lines changed

.github/workflows/build.yml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Build and Publish
2+
on:
3+
push:
4+
branches:
5+
- master
6+
- develop
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
packages: write
12+
13+
jobs:
14+
build:
15+
uses: mikopbx/.github-workflows/.github/workflows/extension-publish.yml@master
16+
with:
17+
initial_version: "1.50"
18+
permissions:
19+
contents: write
20+
packages: write
21+
secrets:
22+
OWNCLOUD_AUTH: ${{ secrets.OWNCLOUD_AUTH }}
23+
MIKO_LIC_REST_VENDOR_ID: ${{ secrets.MIKO_LIC_REST_VENDOR_ID }}
24+
MIKO_LIC_REST_API_KEY: ${{ secrets.MIKO_LIC_REST_API_KEY }}
25+
MIKO_LIC_HOSTNAME: ${{ secrets.MIKO_LIC_HOSTNAME }}
26+
WEBDAV_ROOT: ${{ secrets.WEBDAV_ROOT }}
27+
SHARE_API_URL: ${{ secrets.SHARE_API_URL }}

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
db/module.db
3+
.DS_Store
+160-93
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,174 @@
11
<?php
2-
/**
3-
* Copyright © MIKO LLC - All Rights Reserved
4-
* Unauthorized copying of this file, via any medium is strictly prohibited
5-
* Proprietary and confidential
6-
* Written by Alexey Portnov, 11 2018
2+
3+
/*
4+
* MikoPBX - free phone system for small business
5+
* Copyright © 2017-2024 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/>.
719
*/
820

921
namespace Modules\ModulePhoneBook\App\Controllers;
1022

11-
use MikoPBX\Modules\PbxExtensionUtils;
12-
use Modules\ModulePhoneBook\Models\PhoneBook;
1323
use MikoPBX\AdminCabinet\Controllers\BaseController;
24+
use MikoPBX\AdminCabinet\Providers\AssetProvider;
25+
use Modules\ModulePhoneBook\App\Forms\ModuleConfigForm;
26+
use Modules\ModulePhoneBook\Models\PhoneBook;
27+
use Modules\ModulePhoneBook\Models\Settings;
1428

1529
class ModulePhoneBookController extends BaseController
1630
{
17-
1831
private $moduleUniqueID = 'ModulePhoneBook';
19-
private $moduleDir;
2032

21-
/**
22-
* Basic initial class
23-
*/
24-
public function initialize(): void
25-
{
26-
$this->moduleDir = PbxExtensionUtils::getModuleDir($this->moduleUniqueID);
27-
if ($this->request->isAjax() === false) {
28-
$this->view->logoImagePath = "{$this->url->get()}assets/img/cache/{$this->moduleUniqueID}/logo.svg";
29-
$this->view->submitMode = null;
30-
}
31-
parent::initialize();
32-
}
33+
public bool $showModuleStatusToggle = false;
3334

3435
/**
35-
* Index page controller
36+
* Controller for the index page.
3637
*/
3738
public function indexAction(): void
3839
{
39-
$headerCollectionCSS = $this->assets->collection('headerCSS');
40+
$this->view->logoImagePath = "{$this->url->get()}assets/img/cache/{$this->moduleUniqueID}/logo.svg";
41+
$this->view->submitMode = null;
42+
43+
// Add necessary CSS files
44+
$headerCollectionCSS = $this->assets->collection(AssetProvider::HEADER_CSS);
4045
$headerCollectionCSS
4146
->addCss('css/vendor/datatable/dataTables.semanticui.min.css', true)
4247
->addCss("css/cache/{$this->moduleUniqueID}/module-phonebook.css", true);
4348

44-
$footerCollection = $this->assets->collection('footerJS');
49+
// Add Semantic UI modal CSS
50+
$semanticCollectionCSS = $this->assets->collection(AssetProvider::SEMANTIC_UI_CSS);
51+
$semanticCollectionCSS
52+
->addCss('css/vendor/semantic/progress.min.css', true)
53+
->addCss('css/vendor/semantic/modal.min.css', true);
54+
55+
// Add Semantic UI modal JS
56+
$semanticCollectionJS = $this->assets->collection(AssetProvider::SEMANTIC_UI_JS);
57+
$semanticCollectionJS
58+
->addJs('js/vendor/semantic/progress.min.js', true)
59+
->addJs('js/vendor/semantic/modal.min.js', true);
60+
61+
// Add JS files required for this page
62+
$footerCollection = $this->assets->collection(AssetProvider::FOOTER_JS);
4563
$footerCollection
4664
->addJs('js/vendor/inputmask/inputmask.js', true)
4765
->addJs('js/vendor/inputmask/jquery.inputmask.js', true)
4866
->addJs('js/vendor/inputmask/jquery.inputmask-multi.js', true)
4967
->addJs('js/vendor/inputmask/bindings/inputmask.binding.js', true)
5068
->addJs('js/vendor/datatable/dataTables.semanticui.js', true)
5169
->addJs('js/pbx/Extensions/input-mask-patterns.js', true)
52-
->addJs("js/cache/{$this->moduleUniqueID}/module-phonebook-index.js", true);
70+
->addJs('js/vendor/resumable.js', true)
71+
->addJs("js/cache/{$this->moduleUniqueID}/module-phonebook-status.js", true)
72+
->addJs("js/cache/{$this->moduleUniqueID}/module-phonebook-index.js", true)
73+
->addJs("js/cache/{$this->moduleUniqueID}/module-phonebook-settings.js", true)
74+
->addJs("js/cache/{$this->moduleUniqueID}/module-phonebook-merging-worker.js", true)
75+
->addJs("js/cache/{$this->moduleUniqueID}/module-phonebook-import.js", true)
76+
->addJs("js/cache/{$this->moduleUniqueID}/module-phonebook-datatable.js", true);
77+
78+
$settings = Settings::findFirst();
79+
if ($settings === null) {
80+
$settings = new Settings();
81+
$settings->disableInputMask = '0';
82+
}
5383

54-
$this->view->pick("{$this->moduleDir}/App/Views/index");
84+
$this->view->form = new ModuleConfigForm($settings);
5585
}
5686

5787
/**
58-
* Запрос нового пакета истории разговоров для DataTable JSON
88+
* Request new call history records for DataTable in JSON format.
5989
*/
6090
public function getNewRecordsAction(): void
6191
{
62-
$currentPage = $this->request->getPost('draw');
63-
$position = $this->request->getPost('start');
64-
$recordsPerPage = $this->request->getPost('length');
65-
$searchPhrase = $this->request->getPost('search');
66-
$this->view->draw = $currentPage;
67-
$this->view->recordsTotal = 0;
92+
$currentPage = $this->request->getPost('draw');
93+
$position = $this->request->getPost('start');
94+
$recordsPerPage = $this->request->getPost('length');
95+
$searchPhrase = $this->request->getPost('search');
96+
97+
$this->view->draw = $currentPage;
98+
$this->view->recordsTotal = 0;
6899
$this->view->recordsFiltered = 0;
69-
$this->view->data = [];
100+
$this->view->data = [];
70101

71-
// Посчитаем количество уникальных записей в таблице телефонов
102+
// Count the total number of unique records in the phonebook table
72103
$parameters['columns'] = 'COUNT(*) as rows';
73-
$recordsTotalReq = PhoneBook::findFirst($parameters);
104+
$recordsTotalReq = PhoneBook::findFirst($parameters);
74105
if ($recordsTotalReq !== null) {
75-
$recordsTotal = $recordsTotalReq->rows;
106+
$recordsTotal = $recordsTotalReq->rows;
76107
$this->view->recordsTotal = $recordsTotal;
77108
} else {
78109
return;
79110
}
80-
// Посчитаем количество записей с учетом фильтра
81-
if ( ! empty($searchPhrase['value'])) {
111+
112+
// Count the number of records based on the search filter
113+
if (!empty($searchPhrase['value'])) {
82114
$this->prepareConditionsForSearchPhrases($searchPhrase['value'], $parameters);
83-
// Если мы не смогли расшифровать строку запроса вернем пустой результата
84-
if (empty($parameters['conditions'])) {
85-
return;
86-
}
87115
}
88116
$recordsFilteredReq = PhoneBook::findFirst($parameters);
89117
if ($recordsFilteredReq !== null) {
90-
$recordsFiltered = $recordsFilteredReq->rows;
118+
$recordsFiltered = $recordsFilteredReq->rows;
91119
$this->view->recordsFiltered = $recordsFiltered;
92120
}
93121

94-
// Найдем все записи подходящие под заданный фильтр
122+
// Retrieve all records that match the filter criteria
95123
$parameters['columns'] = [
96124
'call_id',
97-
'number' => 'number_rep',
125+
'number' => 'number_rep',
98126
'DT_RowId' => 'id',
99127
];
100-
$parameters['order'] = ['call_id desc'];
101-
$parameters['limit'] = $recordsPerPage;
102-
$parameters['offset'] = $position;
103-
$records = PhoneBook::find($parameters);
104-
$this->view->data = $records->toArray();
105-
}
128+
$parameters['order'] = ['call_id desc'];
129+
$parameters['limit'] = $recordsPerPage;
130+
$parameters['offset'] = $position;
106131

132+
$records = PhoneBook::find($parameters);
133+
$this->view->data = $records->toArray();
134+
}
107135

108136
/**
109-
* Подготовка параметров запроса для фильтрации записей телефонной книги
137+
* Prepare search conditions for filtering phonebook records.
110138
*
111-
* @param $searchPhrase - поисковая фраза, которую ввел пользователь
112-
* @param $parameters - параметры запроса к телефонной кнгие
139+
* @param string $searchPhrase The search phrase entered by the user
140+
* @param array $parameters The query parameters to filter the phonebook records
113141
*/
114-
private function prepareConditionsForSearchPhrases(&$searchPhrase, &$parameters): void
142+
private function prepareConditionsForSearchPhrases(string &$searchPhrase, array &$parameters): void
115143
{
116-
$parameters['conditions'] = '';
117-
118-
// Поищем номер телефона
119-
$searchPhrase = str_replace(['(', ')', '-', '+'], '', $searchPhrase);
120-
if (preg_match_all("/\d+/", $searchPhrase, $matches)) {
121-
if (count($matches[0]) === 1) {
122-
$seekNumber = '1'.substr($matches[0][0], -9);
123-
$parameters['conditions'] = 'number LIKE :SearchPhrase:';
124-
$parameters['bind']['SearchPhrase'] = "%{$seekNumber}%";
125-
}
126-
$searchPhrase = str_replace($matches[0][0], '', $searchPhrase);
127-
}
128-
129-
// Ищем по caller_id
130-
if (preg_match_all('/^([а-яА-ЯЁёa-zA-Z0-9_ ]+)$/u', $searchPhrase, $matches) && count($matches[0]) > 0) {
131-
$parameters['conditions'] = 'call_id like :SearchPhrase1:';
132-
$parameters['bind']['SearchPhrase1'] = "%{$matches[0][0]}%";
133-
}
144+
$parameters['conditions'] = 'search_index like :SearchPhrase:';
145+
$parameters['bind']['SearchPhrase'] = "%$searchPhrase%";
134146
}
135147

136148
/**
137-
* Save settings AJAX action
149+
* Save settings via an AJAX request.
138150
*/
139151
public function saveAction(): void
140152
{
141-
if ( ! $this->request->isPost()) {
153+
if (!$this->request->isPost()) {
142154
return;
143155
}
144-
$data = $this->request->getPost();
145156

146-
if (empty($data['call_id']) || empty($data['number'])) {
157+
$dataId = $this->request->getPost('id', ['string', 'trim']);
158+
$callId = $this->request->getPost('call_id', ['string', 'trim']);
159+
$number = $this->request->getPost('number', ['alnum']);
160+
$numberRep = $this->request->getPost('number_rep', ['string', 'trim'], $number);
161+
162+
if (empty($callId) || empty($number)) {
147163
return;
148164
}
149165

150-
// We haven't ability to change primary filed, we have to delete it and recreate
166+
// If we are unable to change the primary field, delete the old record and recreate it
151167
$oldId = null;
152168
$record = null;
153-
if (stripos($data['id'], 'new') === false) {
154-
$record = PhoneBook::findFirstById($data['id']);
155-
if ($record->number!==$data['number']){
169+
if (stripos($dataId, 'new') === false) {
170+
$record = PhoneBook::findFirstById($dataId);
171+
if ($record->number !== $number) {
156172
$oldId = $record->id;
157173
$record->delete();
158174
$record = null;
@@ -167,12 +183,23 @@ public function saveAction(): void
167183
switch ($key) {
168184
case 'id':
169185
break;
186+
case 'number':
187+
$record->number = $number;
188+
break;
189+
case 'number_rep':
190+
$record->number_rep = $numberRep;
191+
break;
192+
case 'call_id':
193+
$record->call_id = $callId;
194+
break;
195+
case 'search_index':
196+
// Collect data for the search index
197+
$username = mb_strtolower($callId);
198+
// Combine all fields into a single string
199+
$record->search_index = $username . $number . $numberRep;
200+
break;
170201
default:
171-
if (array_key_exists($key, $data)) {
172-
$record->$key = $data[$key];
173-
} else {
174-
$record->$key = '';
175-
}
202+
break;
176203
}
177204
}
178205

@@ -183,24 +210,64 @@ public function saveAction(): void
183210

184211
return;
185212
}
186-
$this->view->data = ['oldId'=>$oldId,'newId'=>$record->id];
213+
214+
$this->view->data = ['oldId' => $oldId, 'newId' => $record->id];
187215
$this->view->success = true;
188216
}
189217

190218
/**
191-
* Delete phonebook record
219+
* Delete a phonebook record.
192220
*
193-
* @param string $id record ID
221+
* @param string|null $id The record ID to delete
194222
*/
195-
public function deleteAction($id = null): void
223+
public function deleteAction(?string $id = null): void
196224
{
197225
$record = PhoneBook::findFirstById($id);
198-
if ($record !== null && ! $record->delete()) {
226+
if ($record !== null && !$record->delete()) {
199227
$this->flash->error(implode('<br>', $record->getMessages()));
200228
$this->view->success = false;
229+
return;
230+
}
231+
$this->view->success = true;
232+
}
233+
234+
/**
235+
* Delete all phonebook records.
236+
*/
237+
public function deleteAllRecordsAction(): void
238+
{
239+
$records = PhoneBook::find();
240+
foreach ($records as $record) {
241+
if (!$record->delete()) {
242+
$this->flash->error(implode('<br>', $record->getMessages()));
243+
$this->view->result = false;
244+
return;
245+
}
246+
}
247+
$this->view->result = true;
248+
$this->view->reload = 'module-phone-book/module-phone-book/index';
249+
}
201250

251+
/**
252+
* Toggle input mask feature.
253+
*/
254+
public function toggleDisableInputMaskAction(): void
255+
{
256+
if (!$this->request->isPost()) {
257+
return;
258+
}
259+
260+
$settings = Settings::findFirst();
261+
if ($settings === null) {
262+
$settings = new Settings();
263+
}
264+
265+
$settings->disableInputMask = $this->request->getPost('disableInputMask') === 'true' ? '1' : '0';
266+
if (!$settings->save()) {
267+
$this->flash->error(implode('<br>', $settings->getMessages()));
268+
$this->view->success = false;
202269
return;
203270
}
204271
$this->view->success = true;
205272
}
206-
}
273+
}

0 commit comments

Comments
 (0)