mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-21 19:47:48 +00:00
Extended the user admin.
This commit is contained in:
@@ -78,5 +78,41 @@ class UserController extends Controller
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserRepositoryInterface $repository
|
||||||
|
* @param User $user
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function show(UserRepositoryInterface $repository, User $user)
|
||||||
|
{
|
||||||
|
$title = strval(trans('firefly.administration'));
|
||||||
|
$mainTitleIcon = 'fa-hand-spock-o';
|
||||||
|
$subTitle = strval(trans('firefly.single_user_administration', ['email' => $user->email]));
|
||||||
|
$subTitleIcon = 'fa-user';
|
||||||
|
$defaultIp = '0.0.0.0';
|
||||||
|
$registration = Preferences::getForUser($user, 'registration_ip_address', $defaultIp)->data;
|
||||||
|
$confirmation = Preferences::getForUser($user, 'confirmation_ip_address', $defaultIp)->data;
|
||||||
|
$registrationHost = '';
|
||||||
|
$confirmationHost = '';
|
||||||
|
|
||||||
|
if ($registration != $defaultIp) {
|
||||||
|
$registrationHost = gethostbyaddr($registration);
|
||||||
|
}
|
||||||
|
if ($confirmation != $defaultIp) {
|
||||||
|
$confirmationHost = gethostbyaddr($confirmation);
|
||||||
|
}
|
||||||
|
|
||||||
|
$information = $repository->getUserData($user);
|
||||||
|
|
||||||
|
return view(
|
||||||
|
'admin.users.show',
|
||||||
|
compact(
|
||||||
|
'title', 'mainTitleIcon', 'subTitle', 'subTitleIcon', 'information',
|
||||||
|
'user', 'registration', 'confirmation', 'registrationHost', 'confirmationHost'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@ use FireflyIII\Models\RuleGroup;
|
|||||||
use FireflyIII\Models\Tag;
|
use FireflyIII\Models\Tag;
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use FireflyIII\User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HOME
|
* HOME
|
||||||
@@ -115,6 +116,13 @@ Breadcrumbs::register(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Breadcrumbs::register(
|
||||||
|
'admin.users.show', function (BreadCrumbGenerator $breadcrumbs, User $user) {
|
||||||
|
$breadcrumbs->parent('admin.users');
|
||||||
|
$breadcrumbs->push(trans('firefly.single_user_administration', ['email' => $user->email]), route('admin.users.show', $user->id));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Breadcrumbs::register(
|
Breadcrumbs::register(
|
||||||
'admin.users.domains', function (BreadCrumbGenerator $breadcrumbs) {
|
'admin.users.domains', function (BreadCrumbGenerator $breadcrumbs) {
|
||||||
$breadcrumbs->parent('admin.index');
|
$breadcrumbs->parent('admin.index');
|
||||||
|
@@ -14,9 +14,11 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Repositories\User;
|
namespace FireflyIII\Repositories\User;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\Role;
|
use FireflyIII\Models\Role;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Preferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class UserRepository
|
* Class UserRepository
|
||||||
@@ -56,4 +58,59 @@ class UserRepository implements UserRepositoryInterface
|
|||||||
{
|
{
|
||||||
return $this->all()->count();
|
return $this->all()->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return basic user information.
|
||||||
|
*
|
||||||
|
* @param User $user
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getUserData(User $user): array
|
||||||
|
{
|
||||||
|
$return = [];
|
||||||
|
|
||||||
|
// two factor:
|
||||||
|
$is2faEnabled = Preferences::getForUser($user, 'twoFactorAuthEnabled', false)->data;
|
||||||
|
$has2faSecret = !is_null(Preferences::getForUser($user, 'twoFactorAuthSecret'));
|
||||||
|
$return['has_2fa'] = false;
|
||||||
|
if ($is2faEnabled && $has2faSecret) {
|
||||||
|
$return['has_2fa'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is user activated?
|
||||||
|
$confirmAccount = env('MUST_CONFIRM_ACCOUNT', false);
|
||||||
|
$isConfirmed = Preferences::getForUser($user, 'user_confirmed', false)->data;
|
||||||
|
$return['is_activated'] = true;
|
||||||
|
if ($isConfirmed === false && $confirmAccount === true) {
|
||||||
|
$return['is_activated'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$return['is_admin'] = $user->hasRole('owner');
|
||||||
|
$return['blocked'] = intval($user->blocked) === 1;
|
||||||
|
$return['blocked_code'] = $user->blocked_code;
|
||||||
|
$return['accounts'] = $user->accounts()->count();
|
||||||
|
$return['journals'] = $user->transactionJournals()->count();
|
||||||
|
$return['transactions'] = $user->transactions()->count();
|
||||||
|
$return['attachments'] = $user->attachments()->count();
|
||||||
|
$return['attachments_size'] = $user->attachments()->sum('size');
|
||||||
|
$return['bills'] = $user->bills()->count();
|
||||||
|
$return['categories'] = $user->categories()->count();
|
||||||
|
$return['budgets'] = $user->budgets()->count();
|
||||||
|
$return['budgets_with_limits'] = BudgetLimit
|
||||||
|
::distinct()
|
||||||
|
->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
|
||||||
|
->where('amount', '>', 0)
|
||||||
|
->whereNull('budgets.deleted_at')
|
||||||
|
->where('budgets.user_id', $user->id)->get(['budget_limits.budget_id'])->count();
|
||||||
|
$return['export_jobs'] = $user->exportJobs()->count();
|
||||||
|
$return['export_jobs_success'] = $user->exportJobs()->where('status', 'export_downloaded')->count();
|
||||||
|
$return['import_jobs'] = $user->exportJobs()->count();
|
||||||
|
$return['import_jobs_success'] = $user->exportJobs()->where('status', 'import_complete')->count();
|
||||||
|
$return['rule_groups'] = $user->ruleGroups()->count();
|
||||||
|
$return['rules'] = $user->rules()->count();
|
||||||
|
$return['tags'] = $user->tags()->count();
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,15 @@ interface UserRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function all(): Collection;
|
public function all(): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return basic user information.
|
||||||
|
*
|
||||||
|
* @param User $user
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getUserData(User $user): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives a user a role.
|
* Gives a user a role.
|
||||||
*
|
*
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,7 @@ return [
|
|||||||
'matchedOn' => 'Matched on',
|
'matchedOn' => 'Matched on',
|
||||||
'matchesOn' => 'Matched on',
|
'matchesOn' => 'Matched on',
|
||||||
'account_type' => 'Account type',
|
'account_type' => 'Account type',
|
||||||
|
'created_at' => 'Created at',
|
||||||
'new_balance' => 'New balance',
|
'new_balance' => 'New balance',
|
||||||
'account' => 'Account',
|
'account' => 'Account',
|
||||||
'matchingAmount' => 'Amount',
|
'matchingAmount' => 'Amount',
|
||||||
@@ -71,4 +72,15 @@ return [
|
|||||||
'blocked_code' => 'Block code',
|
'blocked_code' => 'Block code',
|
||||||
'domain' => 'Domain',
|
'domain' => 'Domain',
|
||||||
'registration_attempts' => 'Registration attempts',
|
'registration_attempts' => 'Registration attempts',
|
||||||
|
|
||||||
|
'accounts_count' => 'Number of accounts',
|
||||||
|
'journals_count' => 'Number of journals',
|
||||||
|
'attachments_count' => 'Number of attachments',
|
||||||
|
'bills_count' => 'Number of bills',
|
||||||
|
'categories_count' => 'Number of categories',
|
||||||
|
'export_jobs_count' => 'Number of export jobs',
|
||||||
|
'import_jobs_count' => 'Number of import jobs',
|
||||||
|
'budget_count' => 'Number of budgets',
|
||||||
|
'rule_and_groups_count' => 'Number of rules and rule groups',
|
||||||
|
'tags_count' => 'Number of tags',
|
||||||
];
|
];
|
||||||
|
@@ -35,7 +35,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>#{{ user.id }}</td>
|
<td>#{{ user.id }}</td>
|
||||||
<td>{{ user.email }}</td>
|
<td>
|
||||||
|
<a href="{{ route('admin.users.show',user.id) }}">{{ user.email }}</a></td>
|
||||||
<td>
|
<td>
|
||||||
{{ user.created_at.formatLocalized(monthAndDayFormat) }}
|
{{ user.created_at.formatLocalized(monthAndDayFormat) }}
|
||||||
{{ user.created_at.format('H:i') }}
|
{{ user.created_at.format('H:i') }}
|
||||||
|
151
resources/views/admin/users/show.twig
Normal file
151
resources/views/admin/users/show.twig
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
{% extends "./layout/default.twig" %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, user) }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
|
||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">{{ 'user_information'|_ }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body table-responsive">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.id') }}</td>
|
||||||
|
<td>#{{ user.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.email') }}</td>
|
||||||
|
<td><a href="mailto:{{ user.email }}">{{ user.email }}</a>
|
||||||
|
<td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.created_at') }}</td>
|
||||||
|
<td>
|
||||||
|
{{ user.created_at.formatLocalized(monthAndDayFormat) }}
|
||||||
|
{{ user.created_at.format('H:i') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.registered_from') }}</td>
|
||||||
|
<td>{{ registration }} ({{ registrationHost }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.confirmed_from') }}</td>
|
||||||
|
<td>{{ confirmation }} ({{ confirmationHost }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.is_admin') }}</td>
|
||||||
|
<td>
|
||||||
|
{% if information.is_admin %}
|
||||||
|
<small class="text-success"><i class="fa fa-fw fa-check"></i></small> Yes
|
||||||
|
{% else %}
|
||||||
|
<small class="text-danger"><i class="fa fa-fw fa-times"></i></small> No
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.has_two_factor') }}</td>
|
||||||
|
<td>
|
||||||
|
{% if information.has_2fa %}
|
||||||
|
<small class="text-success"><i class="fa fa-fw fa-check"></i></small> Yes
|
||||||
|
{% else %}
|
||||||
|
<small class="text-danger"><i class="fa fa-fw fa-times"></i></small> No
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.is_activated') }}</td>
|
||||||
|
<td>
|
||||||
|
{% if information.activated %}
|
||||||
|
<small class="text-success"><i class="fa fa-fw fa-check"></i></small> Yes
|
||||||
|
{% else %}
|
||||||
|
<small class="text-danger"><i class="fa fa-fw fa-times"></i></small> No
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.is_blocked') }}</td>
|
||||||
|
<td>
|
||||||
|
{% if information.blocked == 1 %}
|
||||||
|
<small class="text-danger"><i class="fa fa-fw fa-check" title="{{ 'yes'|_ }}"></i></small> Yes:
|
||||||
|
|
||||||
|
{% if information.blocked_code == "" %}
|
||||||
|
<em>~</em>
|
||||||
|
{% else %}
|
||||||
|
{{ information.blocked_code }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<small class="text-success"><i class="fa fa-fw fa-times" title="{{ 'no'|_ }}"></i></small> No
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
|
||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h3 class="box-title">{{ 'user_data_information'|_ }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body table-responsive">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.accounts_count') }}</td>
|
||||||
|
<td>{{ information.accounts }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.journals_count') }}</td>
|
||||||
|
<td>{{ information.journals }} ({{ information.transactions }} {{ trans('firefly.transactions') }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.attachments_count') }}</td>
|
||||||
|
<td>{{ information.attachments }} ({{ trans('firefly.total_size') }}: {{ information.attachments_size|filesize }})</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.bills_count') }}</td>
|
||||||
|
<td>{{ information.bills }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.categories_count') }}</td>
|
||||||
|
<td>{{ information.categories }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.export_jobs_count') }}</td>
|
||||||
|
<td>{{ information.export_jobs }}, {{ trans('firefly.successful_count', {count: information.export_jobs_success}) }}
|
||||||
|
<!-- of which x successful --></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.import_jobs_count') }}</td>
|
||||||
|
<td>{{ information.import_jobs }}, {{ trans('firefly.successful_count', {count: information.import_jobs_success}) }}
|
||||||
|
<!-- of which x successful --></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.budget_count') }}</td>
|
||||||
|
<td>{{ information.budgets }} {{ trans('firefly.budget_or_budgets') }},
|
||||||
|
{{ information.budgets_with_limits }} {{ trans('firefly.budgets_with_limits') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.rule_and_groups_count') }}</td>
|
||||||
|
<td>
|
||||||
|
{{ information.rules }} {{ 'rule_or_rules'|_ }} {{ 'in'|_ }} {{ information.rule_groups }}
|
||||||
|
|
||||||
|
{{ 'rulegroup_or_groups'|_ }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ trans('list.tags_count') }}</td>
|
||||||
|
<td>{{ information.tags }} tags</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@@ -435,6 +435,7 @@ Route::group(
|
|||||||
// user manager
|
// user manager
|
||||||
Route::get('/admin/users', ['uses' => 'Admin\UserController@index', 'as' => 'admin.users']);
|
Route::get('/admin/users', ['uses' => 'Admin\UserController@index', 'as' => 'admin.users']);
|
||||||
Route::get('/admin/users/edit/{user}', ['uses' => 'Admin\UserController@edit', 'as' => 'admin.users.edit']);
|
Route::get('/admin/users/edit/{user}', ['uses' => 'Admin\UserController@edit', 'as' => 'admin.users.edit']);
|
||||||
|
Route::get('/admin/users/show/{user}', ['uses' => 'Admin\UserController@show', 'as' => 'admin.users.show']);
|
||||||
|
|
||||||
// user domains:
|
// user domains:
|
||||||
Route::get('/admin/domains', ['uses' => 'Admin\DomainController@domains', 'as' => 'admin.users.domains']);
|
Route::get('/admin/domains', ['uses' => 'Admin\DomainController@domains', 'as' => 'admin.users.domains']);
|
||||||
|
Reference in New Issue
Block a user