FreePBX — это наиболее популярный web интерфейс для настройки серверов на базе Asterisk. FreePBX — это гибкая, модульная система. Предлагает богатый функционал по настройке станций. Самое приятное — это проект с открытым исходным кодом.
На практике, часто возникает необходимость решить уникальную задачу, для которой не достаточно типовых возможностей FreePBX.
В рамках статьи, я опишу возможности расширения функционала дополнительными модулями.
Опишу процесс разработки нового модуля…
Когда нужна разработка модуля?
Тиражируемый продукт — в случае, если поддерживать приходится несколько однотипных АТС и одна и та же фича необходима многим. К примеру (обратный звонок, интеграция с битрикс чатом)
Удобство настройки — загрузить модуль проще и быстрее, чем править конфиги вручную. Один раз заложили логику, и каждая последующая инсталляция требует меньше времени.
Снижаем шанс на ошибку — базовые настройки обычно производим на тестовом стенде, при переносе в продакшн могут возникнуть сложности с зависимостями. Мы можем поместить все необходимые файлы в наш модуль и установить их вместе с ним.
БОЛЬШИЕ Возможности — разработка модулей позволяет более гибко / тонко настроить АТС, к примеру возможно переопределять diaplan, или добавлять в существующий свои строчки с точностью до номера приоритета.
Схема работы Asterisk — FreePBX — DB
Asterisk может использовать базу данных для хранения истории звонков.
FreePBX — взаимодействует с базой данных, сохраняет и получает настройки. Модули FreePBX могут обращаться к базе данных для анализа истории звонков.
Одна из функций FreePBX — создание конфигурационных файлов и поставка AGI скриптов Asterisk. FreePBX знает все о системных директориях Asterisk, может управлять Asterisk.
FreePBX — модульная система
Основа FreePBX — модуль “FreePBX Framework” (далее просто Framework). По своей сути — это модуль управляющий другими модулями. Framework предоставляет базовый web интерфейс:
- Окно авторизации
- Главное меню
- Страница “Module Admin”
Каждый модуль может расширить функционал FreePBX, к примеру добавить конфиги и расширить web интерфейс.
Все модули существенно зависят от Framework, и могут совсем не зависить от прочих модулей. Примеры модулей:
- Сore — предоставляет функционал “Trunk” / “In. rout” / “Out. rout” и функционал “Application” — “Extensions”.
- IVR — предоставляет функции голосового меню
- Queues — функции очередей вызовов и т.д
Сохранение конфигурации FreePBX
После того, как мы что либо изменили в настройках FreePBX появляется кнопка “Apply Config”. На схеме ниже описан процесс генерации конфигурационных файлов.
Первым в дело вступает модуль Framework
- Производит сохранение настроек в базу данных
- Создает базовые конфиги, к примеру extensions.conf и extensions_additional.conf
Далее Framework обращается к дополнительным модулям, к примеру модуль core
- Использует функционал модуля Framework и дополняет файл extensions_additional.conf своим dialplan
- Создает файлы sip_additional.conf, res_odbc_additional.conf и прочие
Мы, как разработчики, можем добавить свой модуль
- Использовать функции Framework для генерации extensions_additional.conf
- Использовать функции прочих модулей, к примеру core для правки конфигов этого модуля: sip_additional.conf, res_odbc_additional.conf
- Создавать свои конфигурационные файлы
- Использовать свои AGI скрипты
- Расширять web интерфейс своими страницами / или своими web интерфейсами
Структура модуля
module.xml
В этом файле описываются основные свойства модуля.
Наиболее важные свойства:
- “rawname” — уникальное имя модуля, не должно содержать спецсимволы
- “name” — представление модуля, как он будет отображаться в “Module admin”, не должно содержать спецсимволы
- “version” — версия модуля
- “category” — категория основного меню
- “menuitems” — представление модуля в меню, может содержать в себе теги ”menuitems” определяющие имена страниц
- “depends” — основные зависимости модуля
Структура файла детально описана в документации.
Пример файла приведен ниже:
<module>
<rawname>pt1ctraining</rawname>
<name>AA Training module</name>
<version>2.11.0.6</version>
<publisher>telefon1c.ru</publisher>
<license>GPLv3+</license>
<licenselink>http://www.gnu.org/licenses/gpl-3.0.txt</licenselink>
<category>Applications</category>
<menuitems>
<pt1ctraining>AA Training module (MIKO LCC)</pt1ctraining>
</menuitems>
<changelog>
*2.11.0.6* Первый релиз
</changelog>
<depends>
<phpversion>5.3.3</phpversion>
<module>pt1c ge 2.11.3.18</module>
</depends>
</module>
install.php
В этом файле описываем инструкции по установке модуля.
В этом скрипте возможно обратиться к “global” переменным FreePBX.
Переменная “$db” позволяет взаимодействовать с базой данных FreePBX.
Пример создания таблицы для хранения настроек модуля:
out("Начало установки модуля.");
global $db;
$sql = "CREATE TABLE IF NOT EXISTS pt1ctraining (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
pt1ctraining_id INTEGER NOT NULL,
description VARCHAR( 150 ),
destination VARCHAR( 50 ),
content_app text NOT NULL,
path_to_php_agi VARCHAR( 50 )
);";
$check = $db->query($sql);
if (DB::IsError($check)) {
die_freepbx( "Can not create `pt1ctraining` table: " . $check->getMessage() . "\n");
}
Обратите внимание, что нет необходимости указывать параметры подключения к базе данных.
Переменная “$amp_conf” содержит параметры конфигурации FreePBX (обычно определены в /etc/freepbx.conf и в /etc/amportal.conf). Пример использования:
global $amp_conf;
out("AMPDBENGINET: " + $amp_conf["AMPDBENGINE"]);
Функция out() позволяет выводить информацию о ходе установки модуля в окно сообщений:
Пример добавления настройки в раздел “Settings — Advanced Settings”:
$freepbx_conf =& freepbx_conf::create();
if (!$freepbx_conf->conf_setting_exists('pt1ctraining_test')) {
$set = array();
$set['value'] = 'all';
$set['defaultval'] = &$set['value'];
$set['readonly'] = 0;
$set['hidden'] = 0;
$set['level'] = 3;
$set['module'] = 'pt1ctraining';
$set['category'] = 'AA Test Module';
$set['emptyok'] = 0;
$set['sortorder'] = 11;
$set['name'] = 'Test settings.';
$set['description'] = 'Description test settings.';
$set['type'] = CONF_TYPE_TEXT;
$freepbx_conf->define_conf_setting('pt1ctraining_test',$set,true);
}
Директории etc и agi-bin
При установке модуля содержимое директорий будет скопировано в соответсвующие директории asterisk:
Uninstall.php
Скрипт описывает инструкции по удалению модуля. Чистим за собой.
<?php
if (!defined('FREEPBX_IS_AUTH')) { die('No direct script access allowed'); }
sql('DROP TABLE pt1ctraining');
?>
В примере выше описан пример использования глобальной функции с именем “sql”. В качестве аргумента передаем тест запроса для удаления таблицы.
Кроме того, используется интересный вызов “defined('FREEPBX_IS_AUTH')”, проверка авторизован ли пользователь. Лучше ее использовать для всех php скриптов.
Все скрипты и файлы с именем index.php публикуются на web сервере и доступы извне.
Если пользователь попробует обратиться к скрипту без авторизации — получит уведомление “No direct script access allowed”
Сердце модуля — functions.inc.php
В этом файле мы реализуем функции нашего модуля, ключевые возможности:
- Создавать свои конфигурационные файлы
- Добавлять варианты “destination”
- Использовать функции других модулей
- Править файл extensions_additional.conf и другие
В functions.inc.php могут быть определены hook-функции, которые будут вызваны модулем framework при генерации конфигов.
Такие функции обычно именуются следующим образом:
function ModuleName_FunctionName($engine) {
//
//
}
Пример реальной функции
Определим dialplan в extensions_additional.conf
function pt1ctraining_get_config($engine) {
global $ext;
switch ($engine) {
case 'asterisk':
// Создаем свой контекст.
$context = 'ext-pt1ctraining';
$exten = '_X!';
$ext->add($context, $exten, '', new ext_agi('pt1ctraining_AGI.php'));
$ext->add($context, $exten, '', new ext_hangup(''));
}
}
Функция “get_config” будет вызвана при создании конфигурационных файлов, когда будет нажата кнопка “Apply Config”.
В качестве параметра будет передана строка “$engine” — имя движка, обычно “asterisk”.
Для создания dialplan использовалась глобальная переменная “$ext”, содержит экземпляр класса “extensions”, класс определен в файле “/var/www/html/admin/libraries/extensions.class.php” и предоставляет нам набор инструментов для генерации dialplan.
Результат работы будет добавлен в extensions_additional.conf:
[ext-pt1ctraining]
exten => _X!,1,AGI(pt1ctraining_AGI.php)
exten => _X!,n,Hangup
Классы “ext_agi” и “ext_hangup” также определены в “extensions.class.php”.
Пример добавления в секцию “[globals]” файла extensions_additional.conf
global $ext;
// Добавим инициализацию переменной в сеции "global"
$ext->addGlobal('PT1C_TR', 'test');
$ext->addGlobal("#include extension_add_pt1c.conf"."\n;", '\n');
Результат работы:
[globals]
CFDEVSTATE = TRUE
CAMPONTOGGLE = *84
DNDDEVSTATE = TRUE
FMDEVSTATE = TRUE
PT1C_TR = test
#include extension_add_pt1c.conf
; = \n
QUEDEVSTATE = TRUE
Для подключения дополнительного файла использовал хитрость в виде переноса строки — другого решения пока нет.
Используем настройку из «Advanced Settings»
Получение глобальной настройки FreePBX.
$freepbx_conf =& freepbx_conf::create();
$pt1c_events = $freepbx_conf->get_conf_setting('pt1ctraining_test',true);
// Созадем dialplan
$ext->add('ext-pt1ctraining-test', '_X!', '', new ext_noop("$pt1c_events"));
$ext->add('ext-pt1ctraining-test', '_X!', '', new ext_hangup(''));
Результат в файле extensions_additional.conf:
[ext-pt1ctraining-test]
exten => _X!,1,Noop(Privet!!!)
exten => _X!,n,Hangup
Правка res_odbc_additional.conf
Используем функционал модуля core:
global $core_conf, $amp_conf;
$section = 'PT1C_asteriskcdrdb';
$core_conf->addResOdbc($section, array('enabled' => 'yes'));
$core_conf->addResOdbc($section, array('dsn' => 'MySQL-asteriskcdrdb'));
$core_conf->addResOdbc($section, array('pooling' => 'no'));
$core_conf->addResOdbc($section, array('limit' => '1'));
$core_conf->addResOdbc($section, array('pre-connect' => 'yes'));
$core_conf->addResOdbc($section, array('username' => $amp_conf['AMPDBUSER']));
$core_conf->addResOdbc($section, array('password' => $amp_conf['AMPDBPASS']));
Результат работы в res_odbc_additional.conf:
[PT1C_asteriskcdrdb]
enabled=>yes
dsn=>MySQL-asteriskcdrdb
pooling=>no
limit=>1
pre-connect=>yes
username=>freepbxuser
password=>d52d251931c2
Создаем свой конфиг
Пожет появиться потребность создать свои конфигурационные файлы.
Для этих целей мы должны определить клас с именем “ModuleName_conf”. Пример класса приведен ниже:
// Класс будет использоваться для генерации конфигурационных файлов.
class pt1ctraining_conf {
function get_filename() {
$files = array(
'extension_additional_pt1ctraining.conf',
);
return $files;
}
function generateConf($file) {
switch ($file) {
case 'extension_additional_pt1ctraining.conf':
return $this->generate_conf();
break;
}
}
function generate_conf() {
$output = "[test] ; row 1 \n; Privet";
return $output;
}
}
Метод “get_filename” возвращает массив файлов, которые будут созданы.
Метод “generateConf” принимает в качестве параметра имя файла и возвращает текстовое содержимое этого файла.
Определим свои destination
Каждый, кто работал с FreePBX видел поле Set Destination. Каждый модуль может добавлять свои точки назначения. Для этого необходимо определить две процедуры:
// Влияет на появление позиции в списке в поле destination.
//
function pt1ctraining_getdest($exten) {
return array('pt1ctraining,'.$exten.',1');
}
// После выбора "application" появится поле для выбора сохраненного маршрута.
//
function pt1ctraining_destinations() {
$extens[] = array('destination' => 'ext-pt1ctraining,${EXTEN},1' , 'description' => 'IVR');
$extens[] = array('destination' => 'ext-pt1ctraining_2,${EXTEN},1', 'description' => 'IVR_2');
$extens[] = array('destination' => 'ext-pt1ctraining_3,${EXTEN},1', 'description' => 'IVR_3');
return $extens;
}
Функция “pt1ctraining_getdest” должна вернуть массив с одним значением “стока”. Формат “array('ModuleName,'.$exten.',1')”
Функция “pt1ctraining_destinations” должна вернуть массив с точками назначения.
Каждый элемент массива — ассоциативный массив с двумя ключами:
'Destination' — содержит Goto совместимые параметры, в дальнейшем перенаправление будет осуществляться в dialplan средствами Goto
'Description' содержит название точки назначение, именно так будет представлено значение пользователю
Страницы модуля
В директории модуля может быть определено сколько угодно файлов с именем формата “page.ModuleName.php”, пример “page.pt1ctraining.php”.
В этих файлах мы можем определить форму html для взаимодействия с пользователем.
Чтобы web интерфейс “узнал” о существовании страницы, мы должны указать на идентификатор страницы в файле “module.xml” в теге “menuitems” тегом с именем страницы:
<menuitems>
<pt1ctraining>AA Training module (MIKO LCC)</pt1ctraining>
</menuitems>
Пример страницы
<form autocomplete="off" name="edit" action="<?php $_SERVER['PHP_SELF'] ?>" method="post" onsubmit="return edit_onsubmit();">
<input type="hidden" name="itemid" value="<?php echo $itemid?>">
<input type="hidden" name="action" value="<?php echo ($itemid ? 'edit' : 'add') ?>">
<table>
<tr><td colspan="4"><h5>Разрабатваем модуль<hr></h5></td></tr>
<tr>
<td><a href="#" class="info">Наименование<span>Имя приложения</span></a></td>
<td><input type="text" name="description" class="" value="<?php echo($thisItem['description']); ?>"></td>
</tr>
<tr>
<td><a href="#" class="info">Dialplan:<span>Text dialplan application.</span></a></td>
<td><textarea name="content_app" cols=50 rows=5><?php echo($thisItem['content_app']); ?></textarea></td>
</tr>
</table>
</form>
Сборка модуля
Тут все просто, упаковка модуля должна быть произведена средствами tar:
tar -czf pt1ctraining “pt1ctraining-2.11.0.6.tgz";
Если модуль подготавливается для FreePBX 12+ то желательно подписать модуль цифровой подписью разработичка, подробности описаны в официальной документации.
Подписать модуль можно используя пакет утилит devtools:
sign.php pt1ctraining "KEY"
Где, “pt1ctraining” — каталог с модулем.
Заключение
FreePBX интересная платформа. Предлагает обширный функционал для настройки АТС.
Для случая, когда фукнционала FreePBX не достаточно есть возможность расширить набор функции средствами дополнительного модуля.
Собственный модуль позволит получить более тонкую настройку АТС:
- Добавление своих dialplan
- Дополнение существующих конфигов, к примеру extensions_additional.conf
- Добавление новых конфигурационных файлов
- Правка dialplan, созданных другими модулями
В качестве источников знаний рекомендую использовать:
- wiki.freepbx.org
- Исходный код — github.com/FreePBX
silverjoe
Круто! Алексей только вчера проводил мастер-класс на конференции Asterconf, а сегодня уже статья на Хабре!
Спасибо!
boffart
Пожалуйста. Организаторам предоставил все материалы, должны будут участникам разослать.
Дополнительно очень жду видео выступления ))