From 806a77a756dc04de66546ae3d04d1cb6bc399542 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timoth=C3=A9e=20Jaussoin?=
Date: Tue, 27 Jun 2023 09:13:36 +0000
Subject: [PATCH] Redesign the account related pages
---
flexiapi/app/Account.php | 7 +-
.../Commands/CreateAdminAccountTest.php | 2 +-
.../Commands/GenerateExternalAccounts.php | 4 +-
.../app/Console/Commands/ImportDatabase.php | 3 +-
.../Commands/RemoveUnconfirmedAccounts.php | 2 +-
.../Controllers/Admin/AccountController.php | 89 ++---
.../Api/Account/AccountController.php | 2 +-
.../Api/Admin/AccountController.php | 2 +-
.../Http/Requests/UpdateAccountRequest.php | 11 +-
flexiapi/app/Libraries/StatisticsCruncher.php | 26 +-
flexiapi/app/Services/AccountService.php | 2 +-
flexiapi/composer.lock | 74 ++--
.../database/factories/AccountFactory.php | 2 +-
...d_at_updated_at_columns_accounts_table.php | 31 ++
.../seeds/LiblinphoneTesterAccoutSeeder.php | 2 +-
flexiapi/public/css/far.css | 359 ++++++++----------
flexiapi/public/css/form.css | 216 +++++++++++
.../views/account/dashboard.blade.php | 135 +++----
.../views/account/phone/change.blade.php | 2 +-
.../views/admin/account/create_edit.blade.php | 151 ++++----
.../views/admin/account/delete.blade.php | 12 -
.../views/admin/account/index.blade.php | 35 +-
.../views/admin/account/show.blade.php | 14 -
.../resources/views/layouts/account.blade.php | 19 -
.../resources/views/layouts/base.blade.php | 38 --
.../resources/views/layouts/main.blade.php | 20 +-
.../resources/views/parts/sidebar.blade.php | 17 +-
flexiapi/routes/web.php | 95 ++---
28 files changed, 728 insertions(+), 644 deletions(-)
create mode 100644 flexiapi/database/migrations/2023_06_14_130324_add_created_at_updated_at_columns_accounts_table.php
create mode 100644 flexiapi/public/css/form.css
delete mode 100644 flexiapi/resources/views/layouts/account.blade.php
delete mode 100644 flexiapi/resources/views/layouts/base.blade.php
diff --git a/flexiapi/app/Account.php b/flexiapi/app/Account.php
index 4af65cd..eb58f47 100644
--- a/flexiapi/app/Account.php
+++ b/flexiapi/app/Account.php
@@ -37,12 +37,10 @@ class Account extends Authenticatable
protected $with = ['passwords', 'admin', 'alias', 'activationExpiration', 'emailChangeCode', 'types', 'actions'];
protected $hidden = ['alias', 'expire_time', 'confirmation_key', 'provisioning_token', 'pivot'];
- protected $dateTimes = ['creation_time'];
protected $appends = ['realm', 'phone', 'confirmation_key_expires'];
protected $casts = [
'activated' => 'boolean',
];
- public $timestamps = false;
public static $dtmfProtocols = ['sipinfo' => 'SIPInfo', 'rfc2833' => 'RFC2833', 'sipmessage' => 'SIP Message'];
@@ -309,6 +307,11 @@ class Account extends Authenticatable
}
}
+ public function setRole(string $role)
+ {
+ $this->setAdminAttribute($role == 'admin');
+ }
+
public function hasTombstone()
{
return AccountTombstone::where('username', $this->attributes['username'])
diff --git a/flexiapi/app/Console/Commands/CreateAdminAccountTest.php b/flexiapi/app/Console/Commands/CreateAdminAccountTest.php
index 7841962..d3146e4 100644
--- a/flexiapi/app/Console/Commands/CreateAdminAccountTest.php
+++ b/flexiapi/app/Console/Commands/CreateAdminAccountTest.php
@@ -66,7 +66,7 @@ class CreateAdminAccountTest extends Command
$account->ip_address = '0.0.0.0';
// Create an "old" account to prevent unwanted deletion on the test server
- $account->creation_time = Carbon::now()->subYears(3);
+ $account->created_at = Carbon::now()->subYears(3);
$account->save();
$admin = new Admin;
diff --git a/flexiapi/app/Console/Commands/GenerateExternalAccounts.php b/flexiapi/app/Console/Commands/GenerateExternalAccounts.php
index 062c6b7..553d5c2 100644
--- a/flexiapi/app/Console/Commands/GenerateExternalAccounts.php
+++ b/flexiapi/app/Console/Commands/GenerateExternalAccounts.php
@@ -71,7 +71,7 @@ class GenerateExternalAccounts extends Command
$account->ip_address = '127.0.0.1';
$account->user_agent = 'External Account Generator';
$account->group = $this->argument('group');
- $account->creation_time = Carbon::now();
+ $account->created_at = Carbon::now();
$i++;
$account->push($account->toArray());
@@ -80,7 +80,7 @@ class GenerateExternalAccounts extends Command
Account::insert($accounts->toArray());
$insertedAccounts = Account::where('group', $this->argument('group'))
- ->orderBy('creation_time', 'desc')
+ ->latest()
->take($this->argument('amount'))
->get();
diff --git a/flexiapi/app/Console/Commands/ImportDatabase.php b/flexiapi/app/Console/Commands/ImportDatabase.php
index 2727206..b865a9b 100644
--- a/flexiapi/app/Console/Commands/ImportDatabase.php
+++ b/flexiapi/app/Console/Commands/ImportDatabase.php
@@ -28,7 +28,6 @@ use App\Admin;
use App\Alias;
use App\ApiKey;
use App\DigestNonce;
-use App\EmailChanged;
use App\Password;
use App\PhoneChangeCode;
@@ -106,7 +105,7 @@ class ImportDatabase extends Command
// Fix bad creation_time
$creationTime = strtotime($element->creation_time);
if ($creationTime == false || $creationTime < 0) {
- $element->creation_time = gmdate('Y-m-d H:i:s', 1);
+ $element->created_at = gmdate('Y-m-d H:i:s', 1);
}
return (array)$element;
})
diff --git a/flexiapi/app/Console/Commands/RemoveUnconfirmedAccounts.php b/flexiapi/app/Console/Commands/RemoveUnconfirmedAccounts.php
index 15fdea0..938f8c7 100644
--- a/flexiapi/app/Console/Commands/RemoveUnconfirmedAccounts.php
+++ b/flexiapi/app/Console/Commands/RemoveUnconfirmedAccounts.php
@@ -37,7 +37,7 @@ class RemoveUnconfirmedAccounts extends Command
public function handle()
{
$accounts = Account::where(
- 'creation_time',
+ 'created_at',
'<',
Carbon::now()->subDays($this->argument('days'))->toDateTimeString()
);
diff --git a/flexiapi/app/Http/Controllers/Admin/AccountController.php b/flexiapi/app/Http/Controllers/Admin/AccountController.php
index 7bc1d01..0bd7b04 100644
--- a/flexiapi/app/Http/Controllers/Admin/AccountController.php
+++ b/flexiapi/app/Http/Controllers/Admin/AccountController.php
@@ -26,27 +26,38 @@ use Carbon\Carbon;
use App\Account;
use App\Admin;
-use App\Alias;
use App\ExternalAccount;
use App\Http\Requests\CreateAccountRequest;
use App\Http\Requests\UpdateAccountRequest;
class AccountController extends Controller
{
- public function index(Request $request, $search = '')
+ public function index(Request $request)
{
- $accounts = Account::orderBy('creation_time', 'desc')->with('externalAccount');
+ $accounts = Account::orderBy('updated_at', $request->get('updated_at_order', 'desc'))
+ ->with('externalAccount');
- if (!empty($search)) {
- $accounts = $accounts->where('username', 'like', '%'.$search.'%');
+ if ($request->has('search')) {
+ $accounts = $accounts->where('username', 'like', '%' . $request->get('search') . '%');
+ }
+
+ if ($request->has('updated_date')) {
+ $accounts->whereDate('updated_at', $request->get('updated_date'));
}
return view('admin.account.index', [
- 'search' => $search,
- 'accounts' => $accounts->paginate(30)->appends($request->query())
+ 'search' => $request->get('search'),
+ 'updated_date' => $request->get('updated_date'),
+ 'accounts' => $accounts->paginate(20)->appends($request->query()),
+ 'updated_at_order' => $request->get('updated_at_order') == 'desc' ? 'asc' : 'desc'
]);
}
+ public function search(Request $request)
+ {
+ return redirect()->route('admin.account.index', $request->except('_token'));
+ }
+
public function show(int $id)
{
return view('admin.account.show', [
@@ -71,9 +82,10 @@ class AccountController extends Controller
$account->display_name = $request->get('display_name');
$account->domain = resolveDomain($request);
$account->ip_address = $request->ip();
- $account->creation_time = Carbon::now();
+ $account->created_at = Carbon::now();
$account->user_agent = config('app.name');
$account->dtmf_protocol = $request->get('dtmf_protocol');
+ $account->activated = $request->has('activated');
$account->save();
$account->phone = $request->get('phone');
@@ -94,26 +106,28 @@ class AccountController extends Controller
public function update(UpdateAccountRequest $request, $id)
{
+ $request->validate([
+ 'password' => 'confirmed',
+ ]);
+
$account = Account::findOrFail($id);
$account->username = $request->get('username');
$account->email = $request->get('email');
$account->display_name = $request->get('display_name');
$account->dtmf_protocol = $request->get('dtmf_protocol');
+ $account->activated = $request->has('activated');
$account->save();
$account->phone = $request->get('phone');
$account->fillPassword($request);
+ $account->setRole($request->get('role'));
+
Log::channel('events')->info('Web Admin: Account updated', ['id' => $account->identifier]);
return redirect()->route('admin.account.show', $id);
}
- public function search(Request $request)
- {
- return redirect()->route('admin.account.index', $request->get('search'));
- }
-
public function attachExternalAccount(int $id)
{
$account = Account::findOrFail($id);
@@ -124,28 +138,6 @@ class AccountController extends Controller
return redirect()->back();
}
- public function activate(int $id)
- {
- $account = Account::findOrFail($id);
- $account->activated = true;
- $account->save();
-
- Log::channel('events')->info('Web Admin: Account activated', ['id' => $account->identifier]);
-
- return redirect()->back();
- }
-
- public function deactivate(int $id)
- {
- $account = Account::findOrFail($id);
- $account->activated = false;
- $account->save();
-
- Log::channel('events')->info('Web Admin: Account deactivated', ['id' => $account->identifier]);
-
- return redirect()->back();
- }
-
public function provision(int $id)
{
$account = Account::findOrFail($id);
@@ -157,33 +149,6 @@ class AccountController extends Controller
return redirect()->back();
}
- public function admin(int $id)
- {
- $account = Account::findOrFail($id);
-
- $admin = new Admin;
- $admin->account_id = $account->id;
- $admin->save();
-
- Log::channel('events')->info('Web Admin: Account set as admin', ['id' => $account->identifier]);
-
- return redirect()->back();
- }
-
- public function unadmin(Request $request, $id)
- {
- $account = Account::findOrFail($id);
-
- // An admin cannot remove it's own permission
- if ($account->id == $request->user()->id) abort(403);
-
- if ($account->admin) $account->admin->delete();
-
- Log::channel('events')->info('Web Admin: Account unset as admin', ['id' => $account->identifier]);
-
- return redirect()->back();
- }
-
public function delete(int $id)
{
$account = Account::findOrFail($id);
diff --git a/flexiapi/app/Http/Controllers/Api/Account/AccountController.php b/flexiapi/app/Http/Controllers/Api/Account/AccountController.php
index 90bb42e..1e8a1df 100644
--- a/flexiapi/app/Http/Controllers/Api/Account/AccountController.php
+++ b/flexiapi/app/Http/Controllers/Api/Account/AccountController.php
@@ -134,7 +134,7 @@ class AccountController extends Controller
? $request->get('domain')
: config('app.sip_domain');
$account->ip_address = $request->ip();
- $account->creation_time = Carbon::now();
+ $account->created_at = Carbon::now();
$account->user_agent = $request->header('User-Agent') ?? config('app.name');
$account->provision();
$account->save();
diff --git a/flexiapi/app/Http/Controllers/Api/Admin/AccountController.php b/flexiapi/app/Http/Controllers/Api/Admin/AccountController.php
index a2af064..0ee61ee 100644
--- a/flexiapi/app/Http/Controllers/Api/Admin/AccountController.php
+++ b/flexiapi/app/Http/Controllers/Api/Admin/AccountController.php
@@ -124,7 +124,7 @@ class AccountController extends Controller
$account->activated = $request->has('activated') ? (bool)$request->get('activated') : false;
$account->ip_address = $request->ip();
$account->dtmf_protocol = $request->get('dtmf_protocol');
- $account->creation_time = Carbon::now();
+ $account->created_at = Carbon::now();
$account->domain = resolveDomain($request);
$account->user_agent = $request->header('User-Agent') ?? config('app.name');
diff --git a/flexiapi/app/Http/Requests/UpdateAccountRequest.php b/flexiapi/app/Http/Requests/UpdateAccountRequest.php
index 2b2b12a..5096ced 100644
--- a/flexiapi/app/Http/Requests/UpdateAccountRequest.php
+++ b/flexiapi/app/Http/Requests/UpdateAccountRequest.php
@@ -29,23 +29,24 @@ class UpdateAccountRequest extends FormRequest
new BlacklistedUsername,
new SIPUsername,
Rule::unique('accounts', 'username')->where(function ($query) {
- $query->where('domain', config('app.sip_domain'));
- })->ignore($this->route('id'), 'id'),
+ $query->where('domain', resolveDomain($this));
+ })->ignore($this->route('account_id'), 'id'),
'filled',
],
'email' => [
'nullable',
'email',
- config('app.account_email_unique') ? Rule::unique('accounts', 'email')->ignore($this->route('id')) : null
+ config('app.account_email_unique') ? Rule::unique('accounts', 'email')->ignore($this->route('account_id')) : null
],
+ 'role' => 'in:admin,end_user',
'password_sha256' => 'nullable|min:3',
'dtmf_protocol' => 'nullable|in:' . Account::dtmfProtocolsRule(),
'phone' => [
'nullable',
Rule::unique('accounts', 'username')->where(function ($query) {
$query->where('domain', config('app.sip_domain'));
- })->ignore($this->route('id'), 'id'),
- Rule::unique('aliases', 'alias')->ignore($this->route('id'), 'account_id'),
+ })->ignore($this->route('account_id'), 'id'),
+ Rule::unique('aliases', 'alias')->ignore($this->route('account_id'), 'account_id'),
new WithoutSpaces, 'starts_with:+'
]
];
diff --git a/flexiapi/app/Libraries/StatisticsCruncher.php b/flexiapi/app/Libraries/StatisticsCruncher.php
index 9833f8b..3e7ac09 100644
--- a/flexiapi/app/Libraries/StatisticsCruncher.php
+++ b/flexiapi/app/Libraries/StatisticsCruncher.php
@@ -33,7 +33,7 @@ class StatisticsCruncher
{
$data = self::getAccountFrom(Carbon::now()->subMonth())
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
@@ -43,14 +43,14 @@ class StatisticsCruncher
->from('aliases');
})
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
$dataActivated = self::getAccountFrom(Carbon::now()->subMonth())
->where('activated', true)
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
@@ -61,7 +61,7 @@ class StatisticsCruncher
->from('aliases');
})
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
@@ -78,7 +78,7 @@ class StatisticsCruncher
{
$data = self::getAccountFrom(Carbon::now()->subWeek())
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
@@ -88,14 +88,14 @@ class StatisticsCruncher
->from('aliases');
})
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
$dataActivated = self::getAccountFrom(Carbon::now()->subWeek())
->where('activated', true)
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
@@ -106,7 +106,7 @@ class StatisticsCruncher
->from('aliases');
})
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
@@ -123,7 +123,7 @@ class StatisticsCruncher
{
$data = self::getAccountFrom(Carbon::now()->subDay())
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d %H') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d %H') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
@@ -133,14 +133,14 @@ class StatisticsCruncher
->from('aliases');
})
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d %H') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d %H') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
$dataActivated = self::getAccountFrom(Carbon::now()->subDay())
->where('activated', true)
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d %H') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d %H') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
@@ -151,7 +151,7 @@ class StatisticsCruncher
->from('aliases');
})
->get(array(
- DB::raw("date_format(creation_time,'%Y-%m-%d %H') as moment"),
+ DB::raw("date_format(created_at,'%Y-%m-%d %H') as moment"),
DB::raw('COUNT(*) as "count"')
))->each->setAppends([])->pluck('count', 'moment');
@@ -166,7 +166,7 @@ class StatisticsCruncher
private static function getAccountFrom($date)
{
- return Account::where('creation_time', '>=', $date)
+ return Account::where('created_at', '>=', $date)
->groupBy('moment')
->orderBy('moment', 'DESC')
->setEagerLoads([]);
diff --git a/flexiapi/app/Services/AccountService.php b/flexiapi/app/Services/AccountService.php
index 7976647..1dd833c 100644
--- a/flexiapi/app/Services/AccountService.php
+++ b/flexiapi/app/Services/AccountService.php
@@ -73,7 +73,7 @@ class AccountService
$account->activated = false;
$account->domain = config('app.sip_domain');
$account->ip_address = $request->ip();
- $account->creation_time = Carbon::now();
+ $account->created_at = Carbon::now();
$account->user_agent = config('app.name');
$account->dtmf_protocol = $request->get('dtmf_protocol');
$account->confirmation_key = generatePin();
diff --git a/flexiapi/composer.lock b/flexiapi/composer.lock
index f42054f..ff0f160 100644
--- a/flexiapi/composer.lock
+++ b/flexiapi/composer.lock
@@ -650,28 +650,28 @@
},
{
"name": "doctrine/inflector",
- "version": "2.0.6",
+ "version": "2.0.8",
"source": {
"type": "git",
"url": "https://github.com/doctrine/inflector.git",
- "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024"
+ "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024",
- "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff",
+ "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
- "doctrine/coding-standard": "^10",
+ "doctrine/coding-standard": "^11.0",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
"phpstan/phpstan-strict-rules": "^1.3",
"phpunit/phpunit": "^8.5 || ^9.5",
- "vimeo/psalm": "^4.25"
+ "vimeo/psalm": "^4.25 || ^5.4"
},
"type": "library",
"autoload": {
@@ -721,7 +721,7 @@
],
"support": {
"issues": "https://github.com/doctrine/inflector/issues",
- "source": "https://github.com/doctrine/inflector/tree/2.0.6"
+ "source": "https://github.com/doctrine/inflector/tree/2.0.8"
},
"funding": [
{
@@ -737,7 +737,7 @@
"type": "tidelift"
}
],
- "time": "2022-10-20T09:10:12+00:00"
+ "time": "2023-06-16T13:40:37+00:00"
},
{
"name": "doctrine/lexer",
@@ -1119,16 +1119,16 @@
},
{
"name": "fakerphp/faker",
- "version": "v1.22.0",
+ "version": "v1.23.0",
"source": {
"type": "git",
"url": "https://github.com/FakerPHP/Faker.git",
- "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2"
+ "reference": "e3daa170d00fde61ea7719ef47bb09bb8f1d9b01"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/f85772abd508bd04e20bb4b1bbe260a68d0066d2",
- "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2",
+ "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e3daa170d00fde61ea7719ef47bb09bb8f1d9b01",
+ "reference": "e3daa170d00fde61ea7719ef47bb09bb8f1d9b01",
"shasum": ""
},
"require": {
@@ -1181,9 +1181,9 @@
],
"support": {
"issues": "https://github.com/FakerPHP/Faker/issues",
- "source": "https://github.com/FakerPHP/Faker/tree/v1.22.0"
+ "source": "https://github.com/FakerPHP/Faker/tree/v1.23.0"
},
- "time": "2023-05-14T12:31:37+00:00"
+ "time": "2023-06-12T08:44:38+00:00"
},
{
"name": "fruitcake/php-cors",
@@ -1729,16 +1729,16 @@
},
{
"name": "laravel/framework",
- "version": "v9.52.8",
+ "version": "v9.52.9",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "d4c62cc12544b8acc8d89fcb510f4aefb12e6dea"
+ "reference": "c512ece7b1ee393eac5893f37cb2b029a5413b97"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/d4c62cc12544b8acc8d89fcb510f4aefb12e6dea",
- "reference": "d4c62cc12544b8acc8d89fcb510f4aefb12e6dea",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/c512ece7b1ee393eac5893f37cb2b029a5413b97",
+ "reference": "c512ece7b1ee393eac5893f37cb2b029a5413b97",
"shasum": ""
},
"require": {
@@ -1923,7 +1923,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2023-05-30T14:45:57+00:00"
+ "time": "2023-06-08T20:06:23+00:00"
},
{
"name": "laravel/serializable-closure",
@@ -4149,16 +4149,16 @@
},
{
"name": "react/stream",
- "version": "v1.2.0",
+ "version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/stream.git",
- "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9"
+ "reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9",
- "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9",
+ "url": "https://api.github.com/repos/reactphp/stream/zipball/6fbc9672905c7d5a885f2da2fc696f65840f4a66",
+ "reference": "6fbc9672905c7d5a885f2da2fc696f65840f4a66",
"shasum": ""
},
"require": {
@@ -4168,12 +4168,12 @@
},
"require-dev": {
"clue/stream-filter": "~1.2",
- "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
+ "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35"
},
"type": "library",
"autoload": {
"psr-4": {
- "React\\Stream\\": "src"
+ "React\\Stream\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -4215,19 +4215,15 @@
],
"support": {
"issues": "https://github.com/reactphp/stream/issues",
- "source": "https://github.com/reactphp/stream/tree/v1.2.0"
+ "source": "https://github.com/reactphp/stream/tree/v1.3.0"
},
"funding": [
{
- "url": "https://github.com/WyriHaximus",
- "type": "github"
- },
- {
- "url": "https://github.com/clue",
- "type": "github"
+ "url": "https://opencollective.com/reactphp",
+ "type": "open_collective"
}
],
- "time": "2021-07-11T12:37:55+00:00"
+ "time": "2023-06-16T10:52:11+00:00"
},
{
"name": "respect/stringifier",
@@ -8207,16 +8203,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "9.6.8",
+ "version": "9.6.9",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e"
+ "reference": "a9aceaf20a682aeacf28d582654a1670d8826778"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e",
- "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a9aceaf20a682aeacf28d582654a1670d8826778",
+ "reference": "a9aceaf20a682aeacf28d582654a1670d8826778",
"shasum": ""
},
"require": {
@@ -8290,7 +8286,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.9"
},
"funding": [
{
@@ -8306,7 +8302,7 @@
"type": "tidelift"
}
],
- "time": "2023-05-11T05:14:45+00:00"
+ "time": "2023-06-11T06:13:56+00:00"
},
{
"name": "sebastian/cli-parser",
diff --git a/flexiapi/database/factories/AccountFactory.php b/flexiapi/database/factories/AccountFactory.php
index de37bf2..4acb294 100644
--- a/flexiapi/database/factories/AccountFactory.php
+++ b/flexiapi/database/factories/AccountFactory.php
@@ -39,7 +39,7 @@ class AccountFactory extends Factory
'confirmation_key' => Str::random(WebAuthenticateController::$emailCodeSize),
'provisioning_token' => Str::random(WebAuthenticateController::$emailCodeSize),
'ip_address' => $this->faker->ipv4,
- 'creation_time' => $this->faker->dateTime,
+ 'created_at' => $this->faker->dateTime,
'dtmf_protocol' => array_rand(Account::$dtmfProtocols),
'activated' => true
];
diff --git a/flexiapi/database/migrations/2023_06_14_130324_add_created_at_updated_at_columns_accounts_table.php b/flexiapi/database/migrations/2023_06_14_130324_add_created_at_updated_at_columns_accounts_table.php
new file mode 100644
index 0000000..4cc27f7
--- /dev/null
+++ b/flexiapi/database/migrations/2023_06_14_130324_add_created_at_updated_at_columns_accounts_table.php
@@ -0,0 +1,31 @@
+renameColumn('creation_time', 'created_at');
+ });
+
+ // Two different migrations to handle SQLite
+ Schema::table('accounts', function(Blueprint $table) {
+ $table->dateTime('updated_at')->nullable();
+ });
+
+ DB::statement('update accounts set updated_at = created_at');
+ }
+
+ public function down()
+ {
+ Schema::table('accounts', function(Blueprint $table) {
+ $table->renameColumn('created_at', 'creation_time');
+ $table->dropColumn('updated_at');
+ });
+ }
+};
diff --git a/flexiapi/database/seeds/LiblinphoneTesterAccoutSeeder.php b/flexiapi/database/seeds/LiblinphoneTesterAccoutSeeder.php
index 246ecf8..2d2e12f 100644
--- a/flexiapi/database/seeds/LiblinphoneTesterAccoutSeeder.php
+++ b/flexiapi/database/seeds/LiblinphoneTesterAccoutSeeder.php
@@ -115,7 +115,7 @@ class LiblinphoneTesterAccoutSeeder extends Seeder
'ip_address' => '',
'confirmation_key' => $confirmationKey,
'user_agent' => 'FlexiAPI Seeder',
- 'creation_time' => '2010-01-03 04:30:43'
+ 'created_at' => '2010-01-03 04:30:43'
];
}
diff --git a/flexiapi/public/css/far.css b/flexiapi/public/css/far.css
index f163d77..cf6c1a6 100644
--- a/flexiapi/public/css/far.css
+++ b/flexiapi/public/css/far.css
@@ -90,6 +90,11 @@ body {
--danger-9: rgba(104, 26, 54, 1);
}
+body.show_menu {
+ max-height: 100vh;
+ overflow: hidden;
+}
+
p,
a {
font-size: 1.5rem;
@@ -104,6 +109,11 @@ p b {
font-weight: bold;
}
+p i {
+ vertical-align: middle;
+ margin-right: 1rem;
+}
+
p>a:not(.btn),
label>a {
text-decoration: underline;
@@ -186,12 +196,54 @@ header nav a i {
font-size: 2.5rem;
}
-header nav a:first-child {
+@media screen and (max-width: 800px) {
+ header nav {
+ padding: 1rem;
+ }
+
+ header nav a {
+ padding: 1rem;
+ }
+
+ header nav a i {
+ margin-right: 0;
+ }
+}
+
+header nav a#logo {
font-weight: 700;
padding-left: 2rem;
}
-header nav a:first-child::before {
+header nav a#menu {
+ color: var(--main-5);
+}
+
+header nav a#menu:after {
+ display: block;
+ font-family: 'Material Icons';
+ content: "\e5d2";
+ font-size: 3rem;
+}
+
+body.show_menu header nav a#menu:after {
+ content: "\e5cd";
+}
+
+header nav a#logo span {
+ margin-left: 1.5rem;
+}
+
+@media screen and (max-width: 800px) {
+ header nav a#logo {
+ position: absolute;
+ left: calc(50% - 1.5rem);
+ top: 0.75rem;
+ padding: 0;
+ }
+}
+
+header nav a#logo::before {
content: '';
width: 3rem;
height: 3rem;
@@ -202,17 +254,16 @@ header nav a:first-child::before {
background-position: center;
background-repeat: no-repeat;
display: block;
- margin-right: 1.5rem;
border-radius: 1rem;
box-shadow: 0 0 2rem rgba(0, 0, 0, 0.2);
}
-header nav a:nth-child(2) {
+header nav a.oppose {
margin-left: auto;
}
-header nav a:last-child {
- padding-right: 2rem;
+header nav a.oppose ~ a.oppose {
+ margin-left: 0;
}
/** Section **/
@@ -224,9 +275,13 @@ content section {
box-sizing: border-box;
}
+content nav + section {
+ min-width: calc(80% - 20rem);
+}
+
/** Sidenav **/
-content nav {
+content > nav {
background-color: var(--main-5);
width: 20rem;
margin-left: 0;
@@ -239,9 +294,11 @@ content nav {
background-position: bottom center;
background-repeat: repeat-x;
background-image: url('/img/footer.svg');
+
+ z-index: 1;
}
-content nav a {
+content > nav a {
color: white;
font-weight: 600;
font-size: 1.75rem;
@@ -254,14 +311,34 @@ content nav a {
position: relative;
}
-content nav a.current {
+@media screen and (max-width: 800px) {
+ content > nav {
+ width: 100%;
+ position: absolute;
+ min-height: 100vh;
+ max-height: 100%;
+ overflow-y: scroll;
+ box-sizing: border-box;
+ border-radius: 0;
+ padding-top: 2rem;
+
+ transition: transform 0.5s ease-in-out;
+ transform: translateX(-100%);
+ }
+
+ body.show_menu content > nav {
+ transform: translateX(0);
+ }
+}
+
+content > nav a.current {
background-color: white;
border-radius: 4rem;
color: var(--main-5);
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.2);
}
-content nav a.current:before {
+content > nav a.current:before {
content: '';
display: block;
width: 1rem;
@@ -274,7 +351,7 @@ content nav a.current:before {
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.2);
}
-content nav a i {
+content > nav a i {
margin: 0 1rem;
margin-left: 2rem;
font-size: 2rem;
@@ -312,199 +389,18 @@ h1 i {
margin-right: 1rem;
}
-/** Forms **/
-
-.btn {
- display: inline-block;
- background-color: var(--main-5);
- font-weight: 600;
- border: 1px solid var(--main-5);
- border-radius: 3rem;
- font-size: 1.5rem;
- line-height: 2rem;
- padding: 1rem 2rem;
- color: white;
- margin: 0 1rem;
+h2 {
+ font-size: 2.25rem;
+ font-weight: 800;
+ padding: 1rem 0;
+ color: var(--second-7);
}
-.btn i {
- margin-right: 0.5rem;
- margin-left: -0.5rem;
+h2 i {
font-size: 2rem;
- vertical-align: middle;
-}
-
-.oppose {
- float: right;
-}
-
-.btn[disabled] {
- color: var(--main-5);
- border-color: var(--main-5);
- background-color: var(--main-1);
- opacity: 0.5;
- pointer-events: none;
-}
-
-.btn:hover {
- background-color: var(--main-6);
- border-color: var(--main-6);
- cursor: pointer;
-}
-
-.btn:active {
- background-color: var(--main-7);
- border-color: var(--main-7);
-}
-
-.btn.btn-secondary {
- background-color: transparent;
- color: var(--main-5);
-}
-
-.btn.btn-secondary:hover {
- background-color: var(--main-1);
-}
-
-.btn.btn-secondary:active {
- background-color: var(--main-5);
- border-color: var(--main-5);
- color: white;
-}
-
-form {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 1.5rem 0.5rem;
-}
-
-form div {
- position: relative;
- min-height: 4rem;
-}
-
-form .large {
- grid-column: 1/-1;
-}
-
-@media screen and (max-width: 1024px) {
- form div {
- grid-column: 1/-1;
- }
-}
-
-form small {
- display: block;
- font-weight: 300;
- color: var(--second-6);
- font-size: 1.25rem;
- margin-top: 0.25rem;
-}
-
-form label {
- color: var(--second-6);
- font-size: 1.5rem;
-}
-
-form input[required]+label:after {
- content: '*';
- margin-left: 0.5rem;
-}
-
-form input:not([type=checkbox])+label,
-form select+label {
- position: absolute;
- top: 0;
- left: 0;
- font-weight: 700;
-}
-
-form div .btn {
- position: absolute;
- bottom: 0;
-}
-
-form div .btn.oppose {
- right: 0;
-}
-
-form div input,
-form div select {
- padding: 1rem 2rem;
- background-color: var(--grey-1);
- border-radius: 3rem;
- border: 1px solid var(--grey-2);
- font-size: 1.5rem;
-}
-
-form div select {
- appearance: none;
- -moz-appearance: none;
- -webkit-appearance: none;
-}
-
-form div.checkbox {
- min-height: 2rem;
-}
-
-form div.select:after {
- font-family: 'Material Icons';
- content: "\e5cf";
- display: block;
- font-size: 3rem;
- color: var(--second-6);
- position: absolute;
- right: 1rem;
- bottom: 0rem;
- pointer-events: none;
- line-height: 4rem;
-}
-
-form div input[disabled] {
- border-color: var(--grey-4);
- color: var(--grey-4);
- background-color: var(--grey-2);
- pointer-events: none;
-}
-
-form div input[type=checkbox] {
margin-right: 1rem;
}
-form div input:not([type=checkbox]):not(.btn),
-form div select {
- margin-top: 2.5rem;
- box-sizing: border-box;
- min-width: 65%;
-}
-
-form div input:autofill {
- background: var(--grey-1);
-}
-
-form div input:hover,
-form div select:hover {
- border-color: var(--second-4);
-}
-
-form div input:focus-visible, form div input:active {
- color: var(--main-5);
- border-color: var(--main-5);
-}
-
-form div input:focus-visible+label, form div input:active+label {
- color: var(--main-5);
-}
-
-form div input:invalid {
- border-color: var(--danger-6);
- color: var(--danger-5);
-}
-
-form div input:invalid+label {
- color: var(--danger-5);
-}
-
/** Badge **/
.badge {
@@ -524,23 +420,30 @@ table {
width: 100%;
}
-table tr th {
+table tr td,
+table tr th{
+ line-height: 4rem;
+ padding: 0 2rem;
+ font-size: 1.5rem;
+}
+
+table tr th,
+table tr th a {
text-transform: uppercase;
font-weight: 600;
color: var(--second-4);
text-align: left;
}
+table tr th a i {
+ font-size: 3rem;
+ vertical-align: middle;
+}
+
table thead {
border-bottom: 1px solid var(--second-4);
}
-table tr td,
-table tr th {
- line-height: 4rem;
- padding: 0 2rem;
- font-size: 1.5rem;
-}
table tr:nth-child(2n) {
background-color: var(--grey-1);
@@ -556,7 +459,7 @@ table tr:nth-child(2n) {
display: none !important;
}
-@media screen and (max-width: 1024px) {
+@media screen and (max-width: 800px) {
.on_mobile,
.on_mobile_after:after {
@@ -571,4 +474,46 @@ table tr:nth-child(2n) {
.on_desktop {
display: none !important;
}
-}
\ No newline at end of file
+}
+
+/** Pagination **/
+
+ul.pagination {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ margin: 1rem 0;
+}
+
+ul.pagination li {
+ display: block;
+}
+
+ul.pagination li .page-link {
+ font-size: 1.6rem;
+ padding: 0.5rem;
+ line-height: 2rem;
+ border-radius: 2rem;
+ margin: 0 0.5rem;
+ text-align: center;
+ min-width: 2rem;
+ display: block;
+ border: 1px solid transparent;
+}
+
+ul.pagination li:not(.disabled).active .page-link {
+ color: white;
+ background-color: var(--main-5);
+}
+
+ul.pagination li:not(.disabled) .page-link {
+ color: var(--main-5);
+}
+ul.pagination li:not(.disabled):not(.active) .page-link:hover {
+ background-color: var(--main-1);
+}
+
+ul.pagination li:not(.disabled) .page-link:hover {
+ border-color: var(--main-5);
+}
diff --git a/flexiapi/public/css/form.css b/flexiapi/public/css/form.css
new file mode 100644
index 0000000..21107c6
--- /dev/null
+++ b/flexiapi/public/css/form.css
@@ -0,0 +1,216 @@
+/** Forms **/
+
+.btn {
+ display: inline-block;
+ background-color: var(--main-5);
+ font-weight: 600;
+ border: 1px solid var(--main-5);
+ border-radius: 3rem;
+ font-size: 1.5rem;
+ line-height: 2rem;
+ padding: 1rem 2rem;
+ color: white;
+ margin: 0 1rem;
+}
+
+.btn i {
+ margin-right: 0.5rem;
+ margin-left: -0.5rem;
+ font-size: 2rem;
+ vertical-align: middle;
+}
+
+.oppose {
+ float: right;
+}
+
+.btn[disabled] {
+ color: var(--main-5);
+ border-color: var(--main-5);
+ background-color: var(--main-1);
+ opacity: 0.5;
+ pointer-events: none;
+}
+
+.btn:hover {
+ background-color: var(--main-6);
+ border-color: var(--main-6);
+ cursor: pointer;
+}
+
+.btn:active {
+ background-color: var(--main-7);
+ border-color: var(--main-7);
+}
+
+.btn.btn-secondary {
+ background-color: transparent;
+ color: var(--main-5);
+}
+
+.btn.btn-secondary:hover {
+ background-color: var(--main-1);
+}
+
+.btn.btn-secondary:active {
+ background-color: var(--main-5);
+ border-color: var(--main-5);
+ color: white;
+}
+
+form {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1.5rem 0.5rem;
+}
+
+form.inline {
+ display: block;
+}
+
+form div {
+ position: relative;
+ min-height: 4rem;
+}
+
+form.inline div {
+ display: inline-block;
+ position: relative;
+ margin-right: 1rem;
+ margin-bottom: 1rem;
+}
+
+form .large,
+form h2 {
+ grid-column: 1/-1;
+}
+
+@media screen and (max-width: 1024px) {
+ form div {
+ grid-column: 1/-1;
+ }
+}
+
+form small {
+ display: block;
+ font-weight: 300;
+ color: var(--second-6);
+ font-size: 1.25rem;
+ margin-top: 0.25rem;
+}
+
+form label {
+ color: var(--second-6);
+ font-size: 1.5rem;
+}
+
+form input[required]+label:after {
+ content: '*';
+ margin-left: 0.5rem;
+}
+
+form input:not([type=checkbox]) ~ label,
+form select ~ label {
+ position: absolute;
+ top: 0;
+ left: 0;
+ font-weight: 700;
+}
+
+form:not(.inline) div .btn {
+ position: absolute;
+ bottom: 0;
+}
+
+form div .btn.oppose {
+ right: 0;
+}
+
+form div input,
+form div select {
+ padding: 1rem 2rem;
+ background-color: var(--grey-1);
+ border-radius: 3rem;
+ border: 1px solid var(--grey-2);
+ font-size: 1.5rem;
+}
+
+form div select {
+ appearance: none;
+ -moz-appearance: none;
+ -webkit-appearance: none;
+}
+
+form div.checkbox {
+ min-height: 2rem;
+}
+
+form div.select:after {
+ font-family: 'Material Icons';
+ content: "\e5cf";
+ display: block;
+ font-size: 3rem;
+ color: var(--second-6);
+ position: absolute;
+ right: 1rem;
+ bottom: 0rem;
+ pointer-events: none;
+ line-height: 4rem;
+}
+
+form div input[disabled] {
+ border-color: var(--grey-4);
+ color: var(--grey-4);
+ background-color: var(--grey-2);
+ pointer-events: none;
+}
+
+form div input[type=checkbox] {
+ margin-right: 1rem;
+ accent-color: var(--main-5);
+}
+
+form div input:not([type=checkbox]):not([type=radio]):not(.btn),
+form div select {
+ margin-top: 2.5rem;
+ box-sizing: border-box;
+ width: 100%;
+}
+
+form div input[type=radio] {
+ width: auto;
+ margin: 1rem;
+ margin-top: 3.75rem;
+ accent-color: var(--main-5);
+}
+
+form div input[type=radio] + p {
+ display: inline-block;
+}
+
+form div input:autofill {
+ background: var(--grey-1);
+}
+
+form div input:hover,
+form div select:hover {
+ border-color: var(--second-4);
+}
+
+form div input:focus-visible, form div input:active {
+ color: var(--main-5);
+ border-color: var(--main-5);
+}
+
+form div input:focus-visible+label, form div input:active+label {
+ color: var(--main-5);
+}
+
+form div input:invalid {
+ border-color: var(--danger-6);
+ color: var(--danger-5);
+}
+
+form div input:invalid+label {
+ color: var(--danger-5);
+}
\ No newline at end of file
diff --git a/flexiapi/resources/views/account/dashboard.blade.php b/flexiapi/resources/views/account/dashboard.blade.php
index 0eb4a48..0806227 100644
--- a/flexiapi/resources/views/account/dashboard.blade.php
+++ b/flexiapi/resources/views/account/dashboard.blade.php
@@ -3,86 +3,60 @@
@section('content')
dashboard Dashboard
-
-
+ Change my current account email
+
-@if($account->admin)
- Admin area
-
+
+ call
+ @if (!empty($account->phone))
+ {{ $account->phone }}
+ @else
+ No phone yet
+ @endif
+ Change my current account phone
+
+@if (config('app.devices_management') == true)
+
+ laptop
+ Manage my devices
+
@endif
+
+ lock
+
+ @if ($account->passwords()->count() > 0)
+ Change my password
+ @else
+ Set my password
+ @endif
+
+
+
-
Account information
+
+ delete
+ Delete my account
+
+
+person Account information
+
+alternate_email SIP address: sip:{{ $account->identifier }}
+person Username: {{ $account->username }}
+dns Domain: {{ $account->domain }}
-
-
SIP address: sip:{{ $account->identifier }}
-
Username: {{ $account->username }}
-
Domain: {{ $account->domain }}
-
@if (!empty(config('app.proxy_registrar_address')))
-
Proxy/registrar address: sip:{{ config('app.proxy_registrar_address') }}
+
lan Proxy/registrar address: sip:{{ config('app.proxy_registrar_address') }}
@endif
@if (!empty(config('app.transport_protocol_text')))
-
Transport: {{ config('app.transport_protocol_text') }}
+
settings_ethernet Transport: {{ config('app.transport_protocol_text') }}
@endif
-
-API Key
+keyAPI Key
You can generate an API key and use it to request the different API endpoints, check the related API documentation to know how to use that key.
{!! Form::open(['route' => 'account.api_key.generate']) !!}
-