mirror of
https://gitlab.linphone.org/BC/public/flexisip-account-manager.git
synced 2026-01-17 10:08:05 +00:00
Add basic Statistics support for Messages and Related devices
This commit is contained in:
parent
16a26d1576
commit
0729718ccf
22 changed files with 2139 additions and 1840 deletions
|
|
@ -128,8 +128,8 @@ function resolveDomain(Request $request): string
|
||||||
&& $request->user()
|
&& $request->user()
|
||||||
&& $request->user()->admin
|
&& $request->user()->admin
|
||||||
&& config('app.admins_manage_multi_domains')
|
&& config('app.admins_manage_multi_domains')
|
||||||
? $request->get('domain')
|
? $request->get('domain')
|
||||||
: config('app.sip_domain');
|
: config('app.sip_domain');
|
||||||
}
|
}
|
||||||
|
|
||||||
function captchaConfigured(): bool
|
function captchaConfigured(): bool
|
||||||
|
|
@ -156,3 +156,25 @@ function resolveUserContacts(Request $request)
|
||||||
);
|
);
|
||||||
})->select($selected);
|
})->select($selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate date string to ISO8601
|
||||||
|
* From: https://github.com/penance316/laravel-iso8601-validator/blob/master/src/IsoDateValidator.php
|
||||||
|
*
|
||||||
|
* @param $attribute
|
||||||
|
* @param $value
|
||||||
|
* @param $parameters
|
||||||
|
* @param $validator
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function validateIsoDate($attribute, $value, $parameters, $validator): bool
|
||||||
|
{
|
||||||
|
$regex = (is_array($parameters) && in_array('utc', $parameters))
|
||||||
|
? '/^(\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)))?$/'
|
||||||
|
// 2012-04-23T18:25:43.511Z
|
||||||
|
// Regex from https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
|
||||||
|
: '/^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/';
|
||||||
|
|
||||||
|
return (bool)preg_match($regex, $value);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,10 @@ class AccountController extends Controller
|
||||||
|
|
||||||
public function store(CreateAccountRequest $request)
|
public function store(CreateAccountRequest $request)
|
||||||
{
|
{
|
||||||
$request->validate(['g-recaptcha-response' => captchaConfigured() ? 'required|captcha': '']);
|
|
||||||
|
|
||||||
$account = (new AccountService(api: false))->store($request);
|
$account = (new AccountService(api: false))->store($request);
|
||||||
|
|
||||||
|
$request->validate(['g-recaptcha-response' => captchaConfigured() ? 'required|captcha': '']);
|
||||||
|
|
||||||
Auth::login($account);
|
Auth::login($account);
|
||||||
|
|
||||||
if ($request->has('phone')) {
|
if ($request->has('phone')) {
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ class AccountController extends Controller
|
||||||
$account->created_at = Carbon::now();
|
$account->created_at = Carbon::now();
|
||||||
$account->user_agent = config('app.name');
|
$account->user_agent = config('app.name');
|
||||||
$account->dtmf_protocol = $request->get('dtmf_protocol');
|
$account->dtmf_protocol = $request->get('dtmf_protocol');
|
||||||
$account->activated = $request->has('activated');
|
$account->activated = $request->get('activated') == 'true';
|
||||||
$account->save();
|
$account->save();
|
||||||
|
|
||||||
$account->phone = $request->get('phone');
|
$account->phone = $request->get('phone');
|
||||||
|
|
@ -112,7 +112,7 @@ class AccountController extends Controller
|
||||||
$account->email = $request->get('email');
|
$account->email = $request->get('email');
|
||||||
$account->display_name = $request->get('display_name');
|
$account->display_name = $request->get('display_name');
|
||||||
$account->dtmf_protocol = $request->get('dtmf_protocol');
|
$account->dtmf_protocol = $request->get('dtmf_protocol');
|
||||||
$account->activated = $request->has('activated');
|
$account->activated = $request->get('activated') == 'true';
|
||||||
$account->save();
|
$account->save();
|
||||||
|
|
||||||
$account->phone = $request->get('phone');
|
$account->phone = $request->get('phone');
|
||||||
|
|
@ -180,7 +180,7 @@ class AccountController extends Controller
|
||||||
$account->contactsLists()->detach([$request->get('contacts_list_id')]);
|
$account->contactsLists()->detach([$request->get('contacts_list_id')]);
|
||||||
$account->contactsLists()->attach([$request->get('contacts_list_id')]);
|
$account->contactsLists()->attach([$request->get('contacts_list_id')]);
|
||||||
|
|
||||||
return redirect()->route('admin.account.edit', $id);
|
return redirect()->route('admin.account.edit', $id)->withFragment('#contacts_lists');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function contactsListRemove(Request $request, int $id)
|
public function contactsListRemove(Request $request, int $id)
|
||||||
|
|
@ -188,6 +188,6 @@ class AccountController extends Controller
|
||||||
$account = Account::findOrFail($id);
|
$account = Account::findOrFail($id);
|
||||||
$account->contactsLists()->detach([$request->get('contacts_list_id')]);
|
$account->contactsLists()->detach([$request->get('contacts_list_id')]);
|
||||||
|
|
||||||
return redirect()->route('admin.account.edit', $id);
|
return redirect()->route('admin.account.edit', $id)->withFragment('#contacts_lists');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class ContactsListContactController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('admin.contacts_list.contacts.add', [
|
return view('admin.contacts_list.contacts.add', [
|
||||||
'contacts_list' => ContactsList::firstOrFail($contactsListId),
|
'contacts_list' => ContactsList::findOrFail($contactsListId),
|
||||||
'params' => [
|
'params' => [
|
||||||
'search' => $request->get('search'),
|
'search' => $request->get('search'),
|
||||||
'contacts_list_id' => $contactsListId,
|
'contacts_list_id' => $contactsListId,
|
||||||
|
|
@ -61,7 +61,7 @@ class ContactsListContactController extends Controller
|
||||||
'contacts_ids' => 'required|exists:accounts,id'
|
'contacts_ids' => 'required|exists:accounts,id'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$contactsList = ContactsList::firstOrFail($contactsListId);
|
$contactsList = ContactsList::findOrFail($contactsListId);
|
||||||
$contactsList->contacts()->detach($request->get('contacts_ids')); // Just in case
|
$contactsList->contacts()->detach($request->get('contacts_ids')); // Just in case
|
||||||
$contactsList->contacts()->attach($request->get('contacts_ids'));
|
$contactsList->contacts()->attach($request->get('contacts_ids'));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,7 @@ class AccountTypeController extends Controller
|
||||||
|
|
||||||
public function get(int $accountTypeId)
|
public function get(int $accountTypeId)
|
||||||
{
|
{
|
||||||
return AccountType::where('id', $accountTypeId)
|
return AccountType::findOrFail($accountTypeId);
|
||||||
->firstOrFail();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
|
|
@ -57,8 +56,7 @@ class AccountTypeController extends Controller
|
||||||
'key' => ['alpha_dash', new NoUppercase],
|
'key' => ['alpha_dash', new NoUppercase],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$accountType = AccountType::where('id', $accountTypeId)
|
$accountType = AccountType::findOrFail($accountTypeId);
|
||||||
->firstOrFail();
|
|
||||||
$accountType->key = $request->get('key');
|
$accountType->key = $request->get('key');
|
||||||
$accountType->save();
|
$accountType->save();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,7 @@ class ContactsListController extends Controller
|
||||||
|
|
||||||
public function get(int $contactsListId)
|
public function get(int $contactsListId)
|
||||||
{
|
{
|
||||||
return ContactsList::where('id', $contactsListId)
|
return ContactsList::findOrFail($contactsListId);
|
||||||
->firstOrFail();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
|
|
@ -42,8 +41,7 @@ class ContactsListController extends Controller
|
||||||
'description' => ['required']
|
'description' => ['required']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$contactsList = ContactsList::where('id', $contactsListId)
|
$contactsList = ContactsList::findOrFail($contactsListId);
|
||||||
->firstOrFail();
|
|
||||||
$contactsList->title = $request->get('title');
|
$contactsList->title = $request->get('title');
|
||||||
$contactsList->description = $request->get('description');
|
$contactsList->description = $request->get('description');
|
||||||
$contactsList->save();
|
$contactsList->save();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\StatisticsMessage;
|
||||||
|
use App\StatisticsMessageDevice;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class StatisticsMessageController extends Controller
|
||||||
|
{
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'id' => 'required|string|max:64',
|
||||||
|
'from' => 'required|string|max:256',
|
||||||
|
'sent_at' => 'required|iso_date',
|
||||||
|
'encrypted' => 'required|boolean',
|
||||||
|
'conference_id' => 'string|nullable',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$statisticsMessage = new StatisticsMessage;
|
||||||
|
$statisticsMessage->id = $request->get('id');
|
||||||
|
$statisticsMessage->from = $request->get('from');
|
||||||
|
$statisticsMessage->sent_at = $request->get('sent_at');
|
||||||
|
$statisticsMessage->encrypted = $request->get('encrypted');
|
||||||
|
//$statisticsMessage->conference_id = $request->get('conference_id');
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $statisticsMessage->saveOrFail();
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
abort(422);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeDevice(Request $request, string $messageId, string $to, string $deviceId)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
// We don't validate the message_id to avoid a specific DB request, the foreign key constraint is taking care of it
|
||||||
|
'last_status' => 'required|integer',
|
||||||
|
'received_at' => 'required|iso_date'
|
||||||
|
]);
|
||||||
|
|
||||||
|
return StatisticsMessageDevice::updateOrCreate(
|
||||||
|
['message_id' => $messageId, 'to' => $to, 'device_id' => $deviceId],
|
||||||
|
['last_status' => $request->get('last_status'), 'received_at' => $request->get('received_at')]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
|
@ -13,6 +14,8 @@ class AppServiceProvider extends ServiceProvider
|
||||||
|
|
||||||
public function boot()
|
public function boot()
|
||||||
{
|
{
|
||||||
|
Validator::extend('iso_date', 'validateIsoDate');
|
||||||
|
|
||||||
if (!empty(config('app.url'))) {
|
if (!empty(config('app.url'))) {
|
||||||
// Add following lines to force laravel to use APP_URL as root url for the app.
|
// Add following lines to force laravel to use APP_URL as root url for the app.
|
||||||
$strBaseURL = $this->app['url'];
|
$strBaseURL = $this->app['url'];
|
||||||
|
|
|
||||||
31
flexiapi/app/StatisticsMessage.php
Normal file
31
flexiapi/app/StatisticsMessage.php
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||||
|
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class StatisticsMessage extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
public $incrementing = false;
|
||||||
|
protected $keyType = 'string';
|
||||||
|
}
|
||||||
35
flexiapi/app/StatisticsMessageDevice.php
Normal file
35
flexiapi/app/StatisticsMessageDevice.php
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||||
|
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class StatisticsMessageDevice extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $fillable = ['message_id', 'to', 'device_id', 'last_status', 'received_at'];
|
||||||
|
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return $this->hasOne(StatisticsMessage::class, 'id', 'message_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
"namoshek/laravel-redis-sentinel": "^0.1.2",
|
"namoshek/laravel-redis-sentinel": "^0.1.2",
|
||||||
"ovh/ovh": "^3.0",
|
"ovh/ovh": "^3.0",
|
||||||
"parsedown/laravel": "^1.2",
|
"parsedown/laravel": "^1.2",
|
||||||
|
"phpunit/phpunit": "^9.6",
|
||||||
"react/socket": "^1.10",
|
"react/socket": "^1.10",
|
||||||
"respect/validation": "^2.2"
|
"respect/validation": "^2.2"
|
||||||
},
|
},
|
||||||
|
|
@ -26,7 +27,6 @@
|
||||||
"mockery/mockery": "^1.5",
|
"mockery/mockery": "^1.5",
|
||||||
"nunomaduro/collision": "^6.1",
|
"nunomaduro/collision": "^6.1",
|
||||||
"phpmd/phpmd": "^2.13",
|
"phpmd/phpmd": "^2.13",
|
||||||
"phpunit/phpunit": "^9.0",
|
|
||||||
"squizlabs/php_codesniffer": "^3.7"
|
"squizlabs/php_codesniffer": "^3.7"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
|
|
||||||
3537
flexiapi/composer.lock
generated
3537
flexiapi/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('statistics_messages', function (Blueprint $table) {
|
||||||
|
$table->string('id', 64)->unique();
|
||||||
|
$table->string('from', 256)->index();
|
||||||
|
$table->dateTime('sent_at');
|
||||||
|
$table->boolean('encrypted')->default(false);
|
||||||
|
$table->string('conference_id')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('statistics_message_devices', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('message_id', 64);
|
||||||
|
$table->string('to', 256)->index();
|
||||||
|
$table->string('device_id', 64);
|
||||||
|
$table->integer('last_status');
|
||||||
|
$table->dateTime('received_at');
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->foreign('message_id')->references('id')->on('statistics_messages');
|
||||||
|
$table->unique(['message_id', 'to', 'device_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('statistics_message_devices');
|
||||||
|
Schema::dropIfExists('statistics_messages');
|
||||||
|
}
|
||||||
|
};
|
||||||
8
flexiapi/public/css/far.css
vendored
8
flexiapi/public/css/far.css
vendored
|
|
@ -629,9 +629,6 @@ table tr.empty td:before {
|
||||||
|
|
||||||
.chip {
|
.chip {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: var(--grey-1);
|
|
||||||
border: 1px solid var(--grey-2);
|
|
||||||
border-radius: 3rem;
|
|
||||||
line-height: 2.5rem;
|
line-height: 2.5rem;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
@ -701,4 +698,9 @@ select.list_toggle {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
10
flexiapi/public/css/form.css
vendored
10
flexiapi/public/css/form.css
vendored
|
|
@ -124,9 +124,9 @@ form label {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form input[type="radio"]~label:after,
|
||||||
form input[required]+label:after {
|
form input[required]+label:after {
|
||||||
content: '*';
|
content: '*';
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form input:not([type=checkbox]) ~ label,
|
form input:not([type=checkbox]) ~ label,
|
||||||
|
|
@ -247,13 +247,13 @@ form div textarea:active+label {
|
||||||
color: var(--main-5);
|
color: var(--main-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
form div input:invalid,
|
form div input:invalid:not(:placeholder-shown),
|
||||||
form div textarea:invalid {
|
form div textarea:invalid:not(:placeholder-shown) {
|
||||||
border-color: var(--danger-6);
|
border-color: var(--danger-6);
|
||||||
color: var(--danger-5);
|
color: var(--danger-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
form div input:invalid+label,
|
form div input:invalid:not(:placeholder-shown)+label,
|
||||||
form div textarea:invalid+label {
|
form div textarea:invalid:not(:placeholder-shown)+label {
|
||||||
color: var(--danger-5);
|
color: var(--danger-5);
|
||||||
}
|
}
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
<div>
|
<div>
|
||||||
{!! Form::email('email_confirmation', old('email_confirm'), ['placeholder' => 'bob@example.net', 'required']) !!}
|
{!! Form::email('email_confirmation', old('email_confirm'), ['placeholder' => 'bob@example.net', 'required']) !!}
|
||||||
{!! Form::label('email_confirmation', 'Confirm email') !!}
|
{!! Form::label('email_confirmation', 'Confirm email') !!}
|
||||||
|
@include('parts.errors', ['name' => 'email_confirmation'])
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -43,6 +44,7 @@
|
||||||
<div>
|
<div>
|
||||||
{!! Form::password('password_confirmation', ['required']) !!}
|
{!! Form::password('password_confirmation', ['required']) !!}
|
||||||
{!! Form::label('password_confirmation', 'Confirm password') !!}
|
{!! Form::label('password_confirmation', 'Confirm password') !!}
|
||||||
|
@include('parts.errors', ['name' => 'password_confirmation'])
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (!empty(config('app.newsletter_registration_address')))
|
@if (!empty(config('app.newsletter_registration_address')))
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,13 @@
|
||||||
{!! Form::text('username', $domain, ['disabled']) !!}
|
{!! Form::text('username', $domain, ['disabled']) !!}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{!! Form::text('phone', old('phone'), ['placeholder' => '+123456789', 'required']) !!}
|
||||||
|
{!! Form::label('phone', 'Phone number') !!}
|
||||||
|
@include('parts.errors', ['name' => 'phone'])
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{!! Form::password('password', ['required']) !!}
|
{!! Form::password('password', ['required']) !!}
|
||||||
{!! Form::label('password', 'Password') !!}
|
{!! Form::label('password', 'Password') !!}
|
||||||
|
|
@ -33,13 +40,6 @@
|
||||||
{!! Form::label('password_confirmation', 'Confirm password') !!}
|
{!! Form::label('password_confirmation', 'Confirm password') !!}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div clas="large">
|
|
||||||
{!! Form::text('phone', old('phone'), ['placeholder' => '+123456789', 'required']) !!}
|
|
||||||
{!! Form::label('phone', 'Phone number') !!}
|
|
||||||
@include('parts.errors', ['name' => 'phone'])
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
@include('parts.terms')
|
@include('parts.terms')
|
||||||
|
|
||||||
<div class="large">
|
<div class="large">
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,13 @@
|
||||||
<div></div>
|
<div></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<input placeholder="Password" name="password" type="password" value="" autocomplete="off">
|
<input placeholder="Password" name="password" type="password" value="" autocomplete="new-password">
|
||||||
<label for="password">{{ $account->id ? 'Password (fill to change)' : 'Password' }}</label>
|
<label for="password">{{ $account->id ? 'Password (fill to change)' : 'Password' }}</label>
|
||||||
@include('parts.errors', ['name' => 'password'])
|
@include('parts.errors', ['name' => 'password'])
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<input placeholder="Password" name="password_confirmation" type="password" value="">
|
<input placeholder="Password" name="password_confirmation" type="password" value="" autocomplete="off">
|
||||||
<label for="password_confirmation">Confirm password</label>
|
<label for="password_confirmation">Confirm password</label>
|
||||||
@include('parts.errors', ['name' => 'password_confirmation'])
|
@include('parts.errors', ['name' => 'password_confirmation'])
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -68,8 +68,11 @@
|
||||||
<h2>Other information</h2>
|
<h2>Other information</h2>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<input name="activated" type="checkbox" @if ($account->activated) checked @endif>
|
<input name="activated" value="true" type="radio" @if ($account->activated) checked @endif>
|
||||||
<label>Activated</label>
|
<p>Enabled</p>
|
||||||
|
<input name="activated" value="false" type="radio" @if (!$account->activated) checked @endif>
|
||||||
|
<p>Disabled</p>
|
||||||
|
<label>Status</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -101,7 +104,30 @@
|
||||||
<hr class="large">
|
<hr class="large">
|
||||||
|
|
||||||
@if ($account->id)
|
@if ($account->id)
|
||||||
<h2>Contacts Lists</h2>
|
<h2 id="contacts_lists">Contacts Lists</h2>
|
||||||
|
|
||||||
|
@if ($contacts_lists->isNotEmpty())
|
||||||
|
<form method="POST" action="{{ route('admin.account.contacts_lists.attach', $account->id) }}"
|
||||||
|
accept-charset="UTF-8">
|
||||||
|
@csrf
|
||||||
|
@method('post')
|
||||||
|
|
||||||
|
<div class="select">
|
||||||
|
<select name="contacts_list_id" onchange="this.form.submit()">
|
||||||
|
<option>
|
||||||
|
Select a contacts list
|
||||||
|
</option>
|
||||||
|
@foreach ($contacts_lists as $contacts_list)
|
||||||
|
<option value="{{ $contacts_list->id }}">
|
||||||
|
{{ $contacts_list->title }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
<label for="contacts_list_id">Add a Contacts lists</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<br />
|
||||||
|
@endif
|
||||||
|
|
||||||
@foreach ($account->contactsLists as $contactsList)
|
@foreach ($account->contactsLists as $contactsList)
|
||||||
<p class="chip">
|
<p class="chip">
|
||||||
|
|
@ -114,30 +140,6 @@
|
||||||
</p>
|
</p>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
@if ($contacts_lists->isNotEmpty())
|
|
||||||
<form method="POST" action="{{ route('admin.account.contacts_lists.attach', $account->id) }}"
|
|
||||||
accept-charset="UTF-8">
|
|
||||||
@csrf
|
|
||||||
@method('post')
|
|
||||||
|
|
||||||
<div class="select">
|
|
||||||
<select name="contacts_list_id">
|
|
||||||
@foreach ($contacts_lists as $contacts_list)
|
|
||||||
<option value="{{ $contacts_list->id }}">
|
|
||||||
{{ $contacts_list->title }}
|
|
||||||
</option>
|
|
||||||
@endforeach
|
|
||||||
</select>
|
|
||||||
<label for="contacts_list_id">Add a Contacts lists</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input class="btn btn-tertiary" type="submit" value="Add">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<h2>Individual contacts</h2>
|
<h2>Individual contacts</h2>
|
||||||
|
|
||||||
@foreach ($account->contacts as $contact)
|
@foreach ($account->contacts as $contact)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
<i class="material-icons">delete</i>
|
<i class="material-icons">delete</i>
|
||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
|
<input form="create_edit_contacts_list" class="btn" type="submit" value="{{ $contacts_list->id ? 'Update' : 'Create' }}">
|
||||||
@else
|
@else
|
||||||
<h1><i class="material-icons">account_box</i> Create a Contacts List</h1>
|
<h1><i class="material-icons">account_box</i> Create a Contacts List</h1>
|
||||||
@endif
|
@endif
|
||||||
|
|
@ -18,6 +19,7 @@
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<form method="POST"
|
<form method="POST"
|
||||||
|
id="create_edit_contacts_list"
|
||||||
action="{{ $contacts_list->id ? route('admin.contacts_lists.update', $contacts_list->id) : route('admin.contacts_lists.store') }}"
|
action="{{ $contacts_list->id ? route('admin.contacts_lists.update', $contacts_list->id) : route('admin.contacts_lists.store') }}"
|
||||||
accept-charset="UTF-8">
|
accept-charset="UTF-8">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
@ -33,14 +35,10 @@
|
||||||
<label for="description">Description</label>
|
<label for="description">Description</label>
|
||||||
@include('parts.errors', ['name' => 'description'])
|
@include('parts.errors', ['name' => 'description'])
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="large">
|
|
||||||
<input class="btn oppose" type="submit" value="{{ $contacts_list->id ? 'Update' : 'Create' }}">
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@if ($contacts_list->id)
|
@if ($contacts_list->id)
|
||||||
<hr class="clear">
|
<hr>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<p class="oppose">
|
<p class="oppose">
|
||||||
|
|
@ -55,7 +53,7 @@
|
||||||
|
|
||||||
<select name="contacts_ids[]" class="list_toggle" data-list-id="d{{ $contacts_list->id }}"></select>
|
<select name="contacts_ids[]" class="list_toggle" data-list-id="d{{ $contacts_list->id }}"></select>
|
||||||
<input type="hidden" name="contacts_list_id" value="{{ $contacts_list->id }}">
|
<input type="hidden" name="contacts_list_id" value="{{ $contacts_list->id }}">
|
||||||
<input class="btn btn-tertiary" type="submit" value="Remove" onclick="Utils.clearStorageList('d{{ $contacts_list->id }}')">
|
<input class="btn btn-tertiary" type="submit" value="Remove contacts" onclick="Utils.clearStorageList('d{{ $contacts_list->id }}')">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<a class="btn btn-secondary" href="{{ route('admin.contacts_lists.contacts.add', $contacts_list->id) }}">
|
<a class="btn btn-secondary" href="{{ route('admin.contacts_lists.contacts.add', $contacts_list->id) }}">
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ use App\Http\Controllers\Api\Admin\AccountContactController;
|
||||||
use App\Http\Controllers\Api\Admin\AccountController as AdminAccountController;
|
use App\Http\Controllers\Api\Admin\AccountController as AdminAccountController;
|
||||||
use App\Http\Controllers\Api\Admin\AccountTypeController;
|
use App\Http\Controllers\Api\Admin\AccountTypeController;
|
||||||
use App\Http\Controllers\Api\Admin\ContactsListController;
|
use App\Http\Controllers\Api\Admin\ContactsListController;
|
||||||
|
use App\Http\Controllers\Api\StatisticsMessageController;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
Route::get('/', 'Api\ApiController@documentation')->name('api');
|
Route::get('/', 'Api\ApiController@documentation')->name('api');
|
||||||
|
|
@ -124,5 +125,10 @@ Route::group(['middleware' => ['auth.digest_or_key']], function () {
|
||||||
Route::post('{id}/contacts/{contacts_id}', 'contactAdd');
|
Route::post('{id}/contacts/{contacts_id}', 'contactAdd');
|
||||||
Route::delete('{id}/contacts/{contacts_id}', 'contactRemove');
|
Route::delete('{id}/contacts/{contacts_id}', 'contactRemove');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::prefix('statistics/messages')->controller(StatisticsMessageController::class)->group(function () {
|
||||||
|
Route::post('/', 'store');
|
||||||
|
Route::patch('{message_id}/to/{to}/devices/{device_id}', 'storeDevice');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
113
flexiapi/tests/Feature/ApiStatisticsMessagesTest.php
Normal file
113
flexiapi/tests/Feature/ApiStatisticsMessagesTest.php
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
Flexisip Account Manager is a set of tools to manage SIP accounts.
|
||||||
|
Copyright (C) 2020 Belledonne Communications SARL, All rights reserved.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Admin;
|
||||||
|
use App\StatisticsMessageDevice;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ApiStatisticsMessagesTest extends TestCase
|
||||||
|
{
|
||||||
|
use WithFaker, RefreshDatabase;
|
||||||
|
|
||||||
|
protected $route = '/api/statistics/messages';
|
||||||
|
|
||||||
|
public function testMessages()
|
||||||
|
{
|
||||||
|
$admin = Admin::factory()->create();
|
||||||
|
$admin->account->generateApiKey();
|
||||||
|
|
||||||
|
$id = '1234';
|
||||||
|
|
||||||
|
$this->keyAuthenticated($admin->account)
|
||||||
|
->json('POST', $this->route, [
|
||||||
|
'id' => $id,
|
||||||
|
'from' => $this->faker->email(),
|
||||||
|
'sent_at' => $this->faker->iso8601(),
|
||||||
|
'encrypted' => false
|
||||||
|
])
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('statistics_messages', [
|
||||||
|
'id' => $id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->keyAuthenticated($admin->account)
|
||||||
|
->json('POST', $this->route, [
|
||||||
|
'id' => $id,
|
||||||
|
'from' => $this->faker->email(),
|
||||||
|
'sent_at' => $this->faker->iso8601(),
|
||||||
|
'encrypted' => false
|
||||||
|
])
|
||||||
|
->assertStatus(422);
|
||||||
|
|
||||||
|
$this->keyAuthenticated($admin->account)
|
||||||
|
->json('POST', $this->route, [
|
||||||
|
'id' => $id,
|
||||||
|
'from' => $this->faker->email(),
|
||||||
|
'sent_at' => 'bad_date',
|
||||||
|
'encrypted' => false
|
||||||
|
])
|
||||||
|
->assertJsonValidationErrors(['sent_at']);
|
||||||
|
|
||||||
|
// Patch previous message with devices
|
||||||
|
|
||||||
|
$to = $this->faker->email();
|
||||||
|
$device = $this->faker->uuid();
|
||||||
|
|
||||||
|
$receivedAt = $this->faker->iso8601();
|
||||||
|
$lastStatus = 200;
|
||||||
|
|
||||||
|
$newReceivedAt = $this->faker->iso8601();
|
||||||
|
$newLastStatus = 201;
|
||||||
|
|
||||||
|
$this->keyAuthenticated($admin->account)
|
||||||
|
->json('PATCH', $this->route . '/' . $id . '/to/' . $to . ' /devices/' . $device, [
|
||||||
|
'last_status' => $lastStatus,
|
||||||
|
'received_at' => $receivedAt
|
||||||
|
])
|
||||||
|
->assertStatus(201);
|
||||||
|
|
||||||
|
$this->keyAuthenticated($admin->account)
|
||||||
|
->json('PATCH', $this->route . '/' . $id . '/to/' . $to . ' /devices/' . $device, [
|
||||||
|
'last_status' => $newLastStatus,
|
||||||
|
'received_at' => $newReceivedAt
|
||||||
|
])
|
||||||
|
->assertStatus(200);
|
||||||
|
|
||||||
|
$this->assertSame(1, StatisticsMessageDevice::count());
|
||||||
|
$this->assertDatabaseHas('statistics_message_devices', [
|
||||||
|
'message_id' => $id,
|
||||||
|
'received_at' => $newReceivedAt,
|
||||||
|
'last_status' => $newLastStatus
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->keyAuthenticated($admin->account)
|
||||||
|
->json('PATCH', $this->route . '/' . $id . '/to/' . $this->faker->email() . ' /devices/' . $this->faker->uuid(), [
|
||||||
|
'last_status' => $newLastStatus,
|
||||||
|
'received_at' => $newReceivedAt
|
||||||
|
])
|
||||||
|
->assertStatus(201);
|
||||||
|
|
||||||
|
$this->assertSame(2, StatisticsMessageDevice::count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -84,12 +84,6 @@ cp httpd/flexisip-account-manager.conf "$RPM_BUILD_ROOT%{apache_conf_path}/"
|
||||||
mkdir -p %{var_dir}/flexiapi/bootstrap/cache
|
mkdir -p %{var_dir}/flexiapi/bootstrap/cache
|
||||||
|
|
||||||
mkdir -p %{var_dir}/log
|
mkdir -p %{var_dir}/log
|
||||||
#touch %{var_dir}/log/account-manager.log
|
|
||||||
#chown %{web_user}:%{web_user} %{var_dir}/log/account-manager.log
|
|
||||||
|
|
||||||
#%if %{without deb}
|
|
||||||
# chcon -t httpd_sys_rw_content_t %{var_dir}/log/account-manager.log
|
|
||||||
#%endif
|
|
||||||
|
|
||||||
%if %{without deb}
|
%if %{without deb}
|
||||||
setsebool -P httpd_can_network_connect_db on
|
setsebool -P httpd_can_network_connect_db on
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue