Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Build and Publish

on:
push:
branches:
- master
- develop
workflow_dispatch:

jobs:
build:
uses: mikopbx/.github-workflows/.github/workflows/extension-publish.yml@master
with:
initial_version: "1.7"
secrets: inherit
107 changes: 107 additions & 0 deletions App/Controllers/ModuleConnectorFMCController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php
/**
* Copyright © MIKO LLC - All Rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* Written by Alexey Portnov, 11 2018
*/
namespace Modules\ModuleConnectorFMC\App\Controllers;
use MikoPBX\AdminCabinet\Controllers\BaseController;
use MikoPBX\Common\Models\Extensions;
use MikoPBX\Common\Models\Sip;
use MikoPBX\Modules\PbxExtensionUtils;
use Modules\ModuleConnectorFMC\App\Forms\ModuleConnectorFMCForm;
use Modules\ModuleConnectorFMC\Models\ModuleConnectorFMC;
use Modules\ModuleConnectorFMC\Models\TrunksFMC;

class ModuleConnectorFMCController extends BaseController
{
private $moduleUniqueID = 'ModuleConnectorFMC';
private $moduleDir;

/**
* Basic initial class
*/
public function initialize(): void
{
$this->moduleDir = PbxExtensionUtils::getModuleDir($this->moduleUniqueID);
$this->view->logoImagePath = "{$this->url->get()}assets/img/cache/{$this->moduleUniqueID}/logo.svg";
$this->view->submitMode = null;
parent::initialize();
}
/**
* Index page controller
*/
public function indexAction(): void
{
$footerCollection = $this->assets->collection('footerJS');
$footerCollection->addJs('js/pbx/main/form.js', true);
$footerCollection->addJs('js/vendor/datatable/dataTables.semanticui.js', true);
$footerCollection->addJs("js/cache/{$this->moduleUniqueID}/module-connectorfmc-index.js", true);
$footerCollection->addJs('js/vendor/jquery.tablednd.min.js', true);
$headerCollectionCSS = $this->assets->collection('headerCSS');
$headerCollectionCSS->addCss("css/cache/{$this->moduleUniqueID}/module-connectorfmc.css", true);
$headerCollectionCSS->addCss('css/vendor/datatable/dataTables.semanticui.min.css', true);

$settings = ModuleConnectorFMC::findFirst();
if ($settings === null) {
$settings = new ModuleConnectorFMC();
}
$options = TrunksFMC::findFirst();
if ($options === null) {
$options = new TrunksFMC();
}

$filterSip = [
"type = '".Extensions::TYPE_SIP."'",
'columns' => 'concat(callerid, " <", number, ">") as name, number as id',
];
$peers = Extensions::find($filterSip);

$options = $options->toArray();
$options['peers'] = $peers;
$this->view->form = new ModuleConnectorFMCForm($settings, $options);
$this->view->pick("$this->moduleDir/App/Views/index");
}

/**
* Save settings AJAX action
*/
public function saveAction() :void
{
$data = $this->request->getPost();
$settings = ModuleConnectorFMC::findFirst();
if ($settings === null) {
$settings = new ModuleConnectorFMC();
}
$trunk = TrunksFMC::findFirst();
if ($trunk === null) {
$trunk = new TrunksFMC();
$trunk->outputEndpoint = Sip::generateUniqueID('SIP-FMC-');
$trunk->outputEndpointSecret = md5($trunk->outputEndpoint.time());
}
$this->db->begin();
foreach ($settings as $key => $value) {
if(isset($data[$key])){
$settings->$key = trim($data[$key]);
}
}
foreach ($trunk as $key => $value) {
if(isset($data[$key])){
if($key === 'useDelayedResponse'){
$trunk->$key = ($data[$key] === 'on') ? '1' : '0';
}else{
$trunk->$key = trim($data[$key]);
}
}
}
if ($settings->save() === FALSE || $trunk->save() === FALSE) {
$this->view->success = false;
$this->db->rollback();
return;
}
$this->flash->success($this->translation->_('ms_SuccessfulSaved'));
$this->view->success = true;
$this->db->commit();
}
}
109 changes: 109 additions & 0 deletions App/Forms/ModuleConnectorFMCForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?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\ModuleConnectorFMC\App\Forms;

use Modules\ModuleConnectorFMC\Models\TrunksFMC;
use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Password;
use Phalcon\Forms\Element\Check;
use Phalcon\Forms\Element\Hidden;
use Phalcon\Forms\Element\Select;

class ModuleConnectorFMCForm extends Form
{
public function initialize($entity = null, $options = null): void
{
$this->add(new Hidden('id', ['value' => $entity->id]));
$this->add(new Text('userAgent'));
$this->add(new Text('sipPort'));
$this->add(new Text('rtpPortStart'));
$this->add(new Text('rtpPortEnd'));
$this->add(new Text('amiPort'));

$arrLibraryType = [
TrunksFMC::PROVIDER_TYPE_B24 => $this->translation->_('module_connectorfmc_PROVIDER_TYPE_B24'),
TrunksFMC::PROVIDER_TYPE_MCN => $this->translation->_('module_connectorfmc_PROVIDER_TYPE_MCN'),
];

foreach ($options as $key => $value) {
if ($key === 'id') {
continue;
}
if ($key === 'extensions') {
$this->add(new Hidden($key, ['value' => $value]));
} elseif ($key === 'providerType') {
$providerType = new Select(
'providerType',
$arrLibraryType,
[
'using' => [
'id',
'name',
],
'useEmpty' => false,
'value' => $value,
'class' => 'ui selection dropdown',
]
);
$this->add($providerType);
} elseif ($key === 'useDelayedResponse') {
$this->addCheckBox($key, intval($value) === 1);
} elseif ($key === 'incomingEndpointSecret') {
$this->add(new Password($key, ['value' => $value]));
} elseif (in_array($key, ['outputEndpoint','outputEndpointSecret'], true)) {
$this->add(new Text($key, ['value' => $value, 'readonly' => '']));
} elseif ('peers' === $key) {
$peers = new Select('peers', $value, [
'using' => [
'id',
'name',
],
'value' => explode(',', $options['extensions']),
'useEmpty' => false,
'multiple' => '',
'class' => 'ui fluid search dropdown',
]);
$this->add($peers);
} else {
$this->add(new Text($key, ['value' => $value]));
}
}
}

/**
* Adds a checkbox to the form field with the given name.
* Can be deleted if the module depends on MikoPBX later than 2024.3.0
*
* @param string $fieldName The name of the form field.
* @param bool $checked Indicates whether the checkbox is checked by default.
* @param string $checkedValue The value assigned to the checkbox when it is checked.
* @return void
*/
public function addCheckBox(string $fieldName, bool $checked, string $checkedValue = 'on'): void
{
$checkAr = ['value' => null];
if ($checked) {
$checkAr = ['checked' => $checkedValue,'value' => $checkedValue];
}
$this->add(new Check($fieldName, $checkAr));
}
}
101 changes: 101 additions & 0 deletions App/Views/index.volt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@

<form class="ui large grey segment form" id="module-connectorfmc-form">
{{ form.render('id') }}
<div class="ui equal width aligned grid">
<div class="row">
<div class="column">
<h4>{{ t._('module_connectorfmc_TitleOutgoingFMC') }}</h4>
<hr>
<div class="field">
<label >{{ t._('module_connectorfmc_outputEndpoint') }}</label>
<div class="ui icon input">
{{ form.render('outputEndpoint') }}
<i class="circular sync alternate link icon"></i>
</div>
</div>
<div class="field">
<label >{{ t._('module_connectorfmc_outputEndpointSecret') }}</label>
<div class="ui icon input">
{{ form.render('outputEndpointSecret') }}
<i class="circular sync alternate link icon"></i>
</div>
</div>
<div class="ui message">
<p> {{ t._('module_connectorfmc_TitleOutgoingMessage') }} </p>
</div>
</div>
<div class="column">
<h4>{{ t._('module_connectorfmc_TitleIncomingFMC') }}</h4>
<hr>
<div class="field disability">
<label >{{ t._('module_connectorfmc_incomingEndpointHost') }}</label>
{{ form.render('incomingEndpointHost') }}
</div>
<div class="field disability">
<label >{{ t._('module_connectorfmc_incomingEndpointPort') }}</label>
{{ form.render('incomingEndpointPort') }}
</div>
<div class="field disability">
<label >{{ t._('module_connectorfmc_incomingEndpointLogin') }}</label>
{{ form.render('incomingEndpointLogin') }}
</div>
<div class="field disability">
<label >{{ t._('module_connectorfmc_incomingEndpointSecret') }}</label>
{{ form.render('incomingEndpointSecret') }}
</div>
</div>
</div>
</div>
<div class="field">
<label >{{ t._('module_connectorfmc_extensions') }}</label>
{{ form.render('extensions') }}
{{ form.render('peers') }}
</div>
<div class="ten wide field">
<label>{{ t._('module_connectorfmc_providerType') }}</label>
{{ form.render('providerType') }}
</div>
<br>
<div class="ui styled fluid accordion">
<div class="title">
<i class="dropdown icon"></i>
{{ t._('module_connectorfmc_FmcServerSettings') }}
</div>
<div class="content">
<div class="ui form">
<div class="inline fields">
<div class="three wide field">
<label>{{ t._('module_connectorfmc_sipPort') }}</label>
</div>
<div class="two wide field">
{{ form.render('sipPort') }}
</div>
</div>
<div class="inline fields">
<div class="three wide field">
<label>{{ t._('module_connectorfmc_amiPort') }}</label>
</div>
<div class="two wide field">
{{ form.render('amiPort') }}
</div>
</div>
<div class="inline fields">
<div class="three wide field">
<label>{{ t._('module_connectorfmc_rtpPorts') }}</label>
</div>
<div class="two wide field">
{{ form.render('rtpPortStart') }}
</div>
<div class="field">
<i class="minus icon"></i>
</div>
<div class="two wide field">
{{ form.render('rtpPortEnd') }}
</div>
</div>
</div>
</div>
</div>
<br>
{{ partial("partials/submitbutton",['indexurl':'pbx-extension-modules/index/']) }}
</form>
Empty file added Calls/agi-bin/log
Empty file.
Empty file added Calls/asterisk/acl.conf
Empty file.
2 changes: 2 additions & 0 deletions Calls/asterisk/ccss.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[general]
cc_max_requests = 20
8 changes: 8 additions & 0 deletions Calls/asterisk/cdr.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[general]
enable=no
unanswered=yes

[sqlite]
usegmtime=no
loguniqueid=yes
loguserfield=yes
8 changes: 8 additions & 0 deletions Calls/asterisk/cel.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[general]
enable=no
events=BRIDGE_ENTER,BRIDGE_EXIT
dateformat = %F %T

[manager]
enabled = yes

Empty file added Calls/asterisk/codecs.conf
Empty file.
Empty file added Calls/asterisk/confbridge.conf
Empty file.
12 changes: 12 additions & 0 deletions Calls/asterisk/features.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[general]
featuredigittimeout = 2500
atxfernoanswertimeout = 45
transferdigittimeout = 3
pickupexten = *80000
atxferabort = *00000

[featuremap]
atxfer => ######
disconnect = *000000
blindxfer => **00000
parkcall => *20000
Loading