Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5489d60
Merge pull request #1 from mikopbx/develop
jorikfon Nov 24, 2020
22741db
Merge pull request #2 from mikopbx/develop
jorikfon Dec 27, 2020
689f68a
Merge pull request #5 from mikopbx/develop
jorikfon Jul 26, 2022
e1db05c
Merge pull request #6 from mikopbx/develop
jorikfon Sep 6, 2023
09fb042
Merge pull request #9 from mikopbx/develop
jorikfon Nov 11, 2024
48df33d
Merge pull request #11 from mikopbx/develop
jorikfon Jan 16, 2025
6c7d5d8
Add new field for Models
Oct 5, 2025
866c1fd
Displaying the 'Expired' mark in the table
Oct 5, 2025
cec0310
Add new field for Models - settings
Oct 6, 2025
7225420
Readme - add new field
Oct 6, 2025
e153d38
Rename field expired => created
Oct 6, 2025
562b802
Меняем action для сохранения настроек. Исправляем обработку ошибок пр…
Oct 6, 2025
49d325c
Code style
Oct 6, 2025
c4d3a09
Add new field for settings form
Oct 6, 2025
0e653ea
Опечатка в переводе ru
Oct 6, 2025
d65a066
Add new field for settings form 2
Oct 6, 2025
044ede2
Add new field for settings form 3
Oct 6, 2025
8521409
Save new settings
Oct 6, 2025
f23462a
Проверка для поля phoneBookLifeTime
Oct 6, 2025
a710698
Основной функционал - поиск имени по Api, если нет в справочнике
Oct 6, 2025
c0f5cf1
Для модели PhoneBook добаим вспомогательный метод для заполнения полей
Oct 6, 2025
c18997e
Мелкие правки
Oct 6, 2025
feda9d7
Оптимизируем изменение записей - используем setPhonebookRecord
Oct 6, 2025
bef44c4
Баг при редактировании позиций - не меняется id кнопки Удалить
Oct 6, 2025
cc2f2b5
Readme - add new method for create new contact
Oct 6, 2025
194043a
Меняем curl на GuzzleHttp
Oct 7, 2025
66414d5
Предупреждение Codacy Static Code Analysis - функция передана в HTML код
Oct 7, 2025
1672328
Предупреждение Codacy Static Code Analysis - функция передана в HTML …
Oct 7, 2025
0e42b0d
Code style
Oct 7, 2025
20f69d6
Предупреждение Codacy Static Code Analysis - функция передана в HTML …
Oct 7, 2025
89c8e11
Предупреждение Codacy Static Code Analysis - функция передана в HTML …
Oct 7, 2025
d92a599
Добавляем очистку call_id перед записью в базу
Oct 7, 2025
6d476c4
Выносим функционал поиска в отдельный класс PhoneBookFind
Oct 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 18 additions & 32 deletions App/Controllers/ModulePhoneBookController.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public function getNewRecordsAction(): void
$parameters['columns'] = [
'call_id',
'number' => 'number_rep',
'created' => 'created',
'DT_RowId' => 'id',
];
$parameters['order'] = ['call_id desc'];
Expand Down Expand Up @@ -156,20 +157,18 @@ public function saveAction(): void

$dataId = $this->request->getPost('id', ['string', 'trim']);
$callId = $this->request->getPost('call_id', ['string', 'trim']);
$number = $this->request->getPost('number', ['alnum']);
$numberRep = $this->request->getPost('number_rep', ['string', 'trim'], $number);
$numberRep = $this->request->getPost('number_rep', ['string', 'trim']);
$number = PhoneBook::cleanPhoneNumber($numberRep, TRUE);

if (empty($callId) || empty($number)) {
return;
}

// If we are unable to change the primary field, delete the old record and recreate it
$oldId = null;
$record = null;
if (stripos($dataId, 'new') === false) {
$record = PhoneBook::findFirstById($dataId);
if ($record->number !== $number) {
$oldId = $record->id;
$record->delete();
$record = null;
}
Expand All @@ -179,39 +178,17 @@ public function saveAction(): void
$record = new PhoneBook();
}

foreach ($record as $key => $value) {
switch ($key) {
case 'id':
break;
case 'number':
$record->number = $number;
break;
case 'number_rep':
$record->number_rep = $numberRep;
break;
case 'call_id':
$record->call_id = $callId;
break;
case 'search_index':
// Collect data for the search index
$username = mb_strtolower($callId);
// Combine all fields into a single string
$record->search_index = $username . $number . $numberRep;
break;
default:
break;
}
}
$record->setPhonebookRecord($callId, $numberRep);

if ($record->save() === false) {
$errors = $record->getMessages();
$this->flash->error(implode('<br>', $errors));
$this->view->success = false;

$this->response->setStatusCode(500);
return;
}

$this->view->data = ['oldId' => $oldId, 'newId' => $record->id];
$this->view->data = ['oldId' => $dataId, 'newId' => $record->id];
$this->view->success = true;
}

Expand Down Expand Up @@ -249,9 +226,9 @@ public function deleteAllRecordsAction(): void
}

/**
* Toggle input mask feature.
* Save settings
*/
public function toggleDisableInputMaskAction(): void
public function saveSettingsAction(): void
{
if (!$this->request->isPost()) {
return;
Expand All @@ -262,10 +239,19 @@ public function toggleDisableInputMaskAction(): void
$settings = new Settings();
}

$settings->disableInputMask = $this->request->getPost('disableInputMask') === 'true' ? '1' : '0';
if ($this->request->hasPost('disableInputMask')) {
$settings->disableInputMask = $this->request->getPost('disableInputMask') === 'true' ? '1' : '0';
}

if ($this->request->hasPost('phoneBookApiUrl')) {
$settings->phoneBookApiUrl = empty($this->request->getPost('phoneBookApiUrl')) ? NULL : $this->request->getPost('phoneBookApiUrl', 'trim');
$settings->phoneBookLifeTime = empty($this->request->getPost('phoneBookLifeTime')) ? 0 : $this->request->getPost('phoneBookLifeTime', 'int!');
}

if (!$settings->save()) {
$this->flash->error(implode('<br>', $settings->getMessages()));
$this->view->success = false;
$this->response->setStatusCode(500);
return;
}
$this->view->success = true;
Expand Down
18 changes: 17 additions & 1 deletion App/Forms/ModuleConfigForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
namespace Modules\ModulePhoneBook\App\Forms;

use MikoPBX\AdminCabinet\Forms\BaseForm;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Numeric;
use Phalcon\Forms\Element\Check;
use Phalcon\Forms\Element\File;

Expand All @@ -31,6 +33,20 @@ public function initialize($entity = null, $options = null): void
// DisableInputMask
$this->addCheckBox('disableInputMask', intval($entity->disableInputMask) === 1);

// phoneBookApiUrl Text field
$this->add(
new Text('phoneBookApiUrl', [
'placeholder' => 'https://',
])
);

// phoneBookLifeTime Text field
$this->add(
new Numeric('phoneBookLifeTime', [
'min' => 0
])
);

// Excel file
$excelFile = new File('excelFile');
$this->add($excelFile);
Expand All @@ -49,7 +65,7 @@ public function addCheckBox(string $fieldName, bool $checked, string $checkedVal
{
$checkAr = ['value' => null];
if ($checked) {
$checkAr = ['checked' => $checkedValue,'value' => $checkedValue];
$checkAr = ['checked' => $checkedValue, 'value' => $checkedValue];
}
$this->add(new Check($fieldName, $checkAr));
}
Expand Down
4 changes: 2 additions & 2 deletions App/Views/ModulePhoneBook/Tabs/phonebookTab.volt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<div class="ui nine wide column">
<div class="ui search right action left icon fluid input" id="search-extensions-input">
<i class="search link icon" id="search-icon"></i>
<input type="search" id="global-search" name="global-search" placeholder="{{ t._('ex_EnterSearchPhrase') }}"
<input type="search" id="global-search" name="global-search" placeholder="{{ t._('module_phnbk_Search') }}"
aria-controls="KeysTable" class="prompt">
<div class="results"></div>
<div class="ui basic floating search dropdown button" id="page-length-select">
Expand Down Expand Up @@ -36,4 +36,4 @@
</tr>
</thead>
<tbody>
</table>
</table>
15 changes: 15 additions & 0 deletions App/Views/ModulePhoneBook/Tabs/settingsTab.volt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@
<label for="disableInputMask">{{ t._('module_phnbk_disableInputMask') }}</label>
</div>
</div>
<div class="ui segment">
<div class="wide field">
<label for="phoneBookApiUrl">{{ t._('module_phnbk_ApiUrl') }}</label>
{{ form.render('phoneBookApiUrl') }}
<div>{{ t._('module_phnbk_ApiUrlDescription', {'repesent': '%number%'}) }}</div>
</div>
<div class="wide field">
<label for="phoneBookLifeTime">{{ t._('module_phnbk_СacheLifetime') }}</label>
{{ form.render('phoneBookLifeTime') }}
<div>{{ t._('module_phnbk_СacheLifetimeDescription') }}</div>
</div>
<div class="field">
<div class="ui labeled icon positive button" id="btn-save-settings-api"><i class="save icon"></i>{{ t._('module_phnbk_SaveBtn') }}</div>
</div>
</div>
</div>
<div class="field">
<div class="ui labeled icon basic button" id="delete-all-records"><i class="red trash icon"></i>{{ t._('module_phnbk_DeleteAllRecords') }}</div>
Expand Down
13 changes: 13 additions & 0 deletions Lib/MikoPBXVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,17 @@ public static function getLoggerClass(): string
return \Phalcon\Logger::class;
}
}

/**
* Return validator Callback class for the current version of PBX
* @return class-string<\Phalcon\Filter\Validation\Validator\Callback>|class-string<\Phalcon\Validation\Validator\Callback>
*/
public static function getValidatorCallbackClass(): string
{
if (self::isPhalcon5Version()) {
return \Phalcon\Filter\Validation\Validator\Callback::class;
} else {
return \Phalcon\Validation\Validator\Callback::class;
}
}
}
18 changes: 14 additions & 4 deletions Lib/PhoneBookAgi.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use MikoPBX\Core\Asterisk\AGI;
use MikoPBX\Core\System\Util;
use Modules\ModulePhoneBook\Models\PhoneBook;
use Modules\ModulePhoneBook\Models\Settings;
use Phalcon\Di\Injectable;

/**
Expand All @@ -48,15 +49,24 @@ public static function setCallerID(string $type): void
} else {
$number = $agi->request['agi_extension'];
}

$number_orig = $number;
// Normalize the phone number to match the expected format (last 9 digits)
$number = '1' . substr($number, -9);
$number = PhoneBook::cleanPhoneNumber($number, TRUE);

// Find the corresponding phonebook entry by the number
$result = PhoneBook::findFirstByNumber($number);

$settings = Settings::findFirst();
$lifeTime = $settings->phoneBookLifeTime ?? 0;

if ($result === NULL || empty($result->call_id) || ($lifeTime > 0 && $result->created > 0 && $result->created + $lifeTime < time())) {
// The record was not found - we are searching through the API
$searcher = new PhoneBookFind();
$result = $searcher->findApiByNumber($number_orig, $result);
}

// If a matching record is found and the call_id is not empty, set the appropriate caller ID
if ($result !== null && !empty($result->call_id)) {
if ($result !== NULL && !empty($result->call_id)) {
if ($type === 'in') {
$agi->set_variable('CALLERID(name)', $result->call_id);
} else {
Expand All @@ -68,4 +78,4 @@ public static function setCallerID(string $type): void
Util::sysLogMsg('PhoneBookAGI', $e->getMessage(), LOG_ERR);
}
}
}
}
131 changes: 131 additions & 0 deletions Lib/PhoneBookFind.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

/*
* MikoPBX - free phone system for small business
* Copyright © 2017-2024 Alexey Portnov and Nikolay Beketov
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/

namespace Modules\ModulePhoneBook\Lib;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
use MikoPBX\Core\System\Util;
use Modules\ModulePhoneBook\Models\PhoneBook;
use Modules\ModulePhoneBook\Models\Settings;
use Phalcon\Di\Injectable;

include_once __DIR__ . '/../vendor/autoload.php';

/**
* Class PhoneBookFind
*
*/
class PhoneBookFind extends Injectable
{
/**
* Find CallerID from API
*
* @param string $number_search
* @param PhoneBook|null $oldPhoneBook
* @return PhoneBook|null
*/
public function findApiByNumber(string $number_search, ?PhoneBook $oldPhoneBook = NULL): ?PhoneBook
{
// Normalize the phone number to match the expected format (last 9 digits)
$number = PhoneBook::cleanPhoneNumber($number_search, TRUE);

if (empty($number)) {
return NULL;
}

$settings = Settings::findFirst();
$url = !empty($settings->phoneBookApiUrl) ? str_replace(
'%number%',
$number,
$settings->phoneBookApiUrl
) : NULL;

if (empty($url)) {
return NULL;
}

$callerID = $this->getRequest($url);

// Logging
Util::sysLogMsg(
'PhoneBookAGI',
"Find CallerID from API: $number => " . (empty($callerID) ? 'NOT FOUND' : $callerID)
);

if ($callerID !== NULL) {
// Saving the number in the phonebook
$record = $oldPhoneBook !== NULL && $oldPhoneBook->number === $number ? $oldPhoneBook : PhoneBook::findFirstByNumber(
$number
);

if ($record == NULL) {
$record = new PhoneBook();
}

$record->setPhonebookRecord(
$callerID,
$record->cleanPhoneNumber($number_search),
time()
);

if (!$record->save()) {
// Log the error message if an exception occurs
Util::sysLogMsg('PhoneBookAGI', implode(' | ', $record->getMessages()), LOG_ERR);
} else {
return $record;
}
}

return NULL;
}

/**
* Get the $url content with CURL
*
* @param string $url
* @return string|null
*/
private function getRequest(string $url): ?string
{
$callerId = NULL;
try {
$client = new Client([
'timeout' => 3,
'connect_timeout' => 2
]);
$response = $client->get($url);
$status = $response->getStatusCode();
if ($status === 200) {
$callerId = trim($response->getBody()->getContents());
$callerId = trim(strip_tags(str_replace('"', "'", $callerId)));
}
} catch (ClientException $e) {
// ClientException only catches status code between 400x-499
//Util::sysLogMsg('PhoneBookAGI', $e->getMessage(), LOG_ERR);
} catch (GuzzleException $e) {
// Log the error message if an exception occurs
Util::sysLogMsg('PhoneBookAGI', $e->getMessage(), LOG_ERR);
}

return !empty($callerId) ? $callerId : NULL;
}
}
Loading