Updated user settings page and user forms

This commit is contained in:
2023-06-01 10:59:51 +01:00
parent fc707b1abf
commit a4da55aa47
15 changed files with 386 additions and 1 deletions

View File

@@ -12,4 +12,6 @@ Fixes/tweaks a few "issues" with the default UserFrosting installation, includin
- Allow site-admins to view roles & permissions - Allow site-admins to view roles & permissions
- Allow site-admins to edit basic role details (name, slug & description) - Allow site-admins to edit basic role details (name, slug & description)
- Added 'hasRole' twig function to check if a user has a role (if a role doesn't exist, always returns false) - Added 'hasRole' twig function to check if a user has a role (if a role doesn't exist, always returns false)
- Added 'Auditer' role and split the activities permission away from site-admins (exclusive only) and everyone else - Added 'Auditer' role and split the activities permission away from site-admins (exclusive only) and everyone else
- Made the input elements on the account settings page more in line with the other inputs (including the "hidden" and "disabled" mechanisms)
- Update the "User" form to use the partial form elements for common styling with accounts page

15
routes/routes.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
/*
* AVSDev UF Tweaks (https://avsdev.uk)
*
* @link https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks
* @license https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks/blob/master/LICENSE.md (LGPL-3.0 License)
*/
use UserFrosting\Sprinkle\Core\Util\NoCache;
$app->group('/account', function () {
$this->get('/settings', 'UserFrosting\Sprinkle\UFTweaks\Controller\AccountController:pageSettings')
->add('authGuard');
})->add(new NoCache());

View File

@@ -0,0 +1,111 @@
<?php
/*
* AVSDev UF Tweaks (https://avsdev.uk)
*
* @link https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks
* @license https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks/blob/master/LICENSE.md (LGPL-3.0 License)
*/
namespace UserFrosting\Sprinkle\UFTweaks\Controller;
use Carbon\Carbon;
use Illuminate\Database\Capsule\Manager as Capsule;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use UserFrosting\Fortress\Adapter\JqueryValidationAdapter;
use UserFrosting\Fortress\RequestDataTransformer;
use UserFrosting\Fortress\RequestSchema;
use UserFrosting\Fortress\ServerSideValidator;
use UserFrosting\Sprinkle\Account\Controller\AccountController as UFAccountController;
use UserFrosting\Support\Exception\BadRequestException;
use UserFrosting\Support\Exception\ForbiddenException;
use UserFrosting\Support\Exception\NotFoundException;
/**
* Override account controller class to tweak the Settings page
*
* @author Craig Williams (craig@avsdev.uk)
*/
class AccountController extends UFAccountController
{
/**
* Account settings page.
*
* Provides a form for users to modify various properties of their account, such as name, email, locale, etc.
* Any fields that the user does not have permission to modify will be automatically disabled.
* This page requires authentication.
*
* AuthGuard: true
* Route: /account/settings
* Route Name: {none}
* Request type: GET
*
* @param Request $request
* @param Response $response
* @param array $args
*
* @throws ForbiddenException If user is not authorized to access page
*/
public function pageSettings(Request $request, Response $response, $args)
{
/** @var \UserFrosting\Support\Repository\Repository $config */
$config = $this->ci->config;
/** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */
$authorizer = $this->ci->authorizer;
/** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */
$currentUser = $this->ci->currentUser;
// Access-controlled page
if (!$authorizer->checkAccess($currentUser, 'uri_account_settings')) {
throw new ForbiddenException();
}
// Load validation rules
$schema = new RequestSchema('schema://requests/account-settings.yaml');
$schema->set('password.validators.length.min', $config['site.password.length.min']);
$schema->set('password.validators.length.max', $config['site.password.length.max']);
$schema->set('passwordc.validators.length.min', $config['site.password.length.min']);
$schema->set('passwordc.validators.length.max', $config['site.password.length.max']);
$validatorAccountSettings = new JqueryValidationAdapter($schema, $this->ci->translator);
$schema = new RequestSchema('schema://requests/profile-settings.yaml');
$validatorProfileSettings = new JqueryValidationAdapter($schema, $this->ci->translator);
// Get a list of all locales
$locales = $this->ci->locale->getAvailableOptions();
// Hide the locale field if there is only 1 locale available
$fields = [
'hidden' => [],
'disabled' => [],
];
if (count($locales) <= 1) {
$fields['hidden'][] = 'locale';
}
if (!$authorizer->checkAccess($currentUser, 'update_account_settings')) {
$fields['disabled'][] = 'name';
$fields['disabled'][] = 'locale';
$fields['disabled'][] = 'email';
$fields['disabled'][] = 'copy';
$fields['hidden'][] = 'password';
$fields['hidden'][] = 'submit';
}
return $this->ci->view->render($response, 'pages/account-settings.html.twig', [
'user' => $currentUser,
'locales' => $locales,
'form' => [
'fields' => $fields,
],
'page' => [
'validators' => [
'account_settings' => $validatorAccountSettings->rules('json', false),
'profile_settings' => $validatorProfileSettings->rules('json', false),
]
],
]);
}
}

View File

@@ -0,0 +1,11 @@
{% block input_current_password %}
{% if 'password' not in form.fields.hidden %}
<div class="form-group">
<label for="input-passwordcheck" class="control-label">{{translate("PASSWORD.CURRENT")}}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fas fa-key"></i></span>
<input type="password" id="input-passwordcheck" class="form-control" name="passwordcheck" placeholder="{{translate("PASSWORD.CURRENT_EXPLAIN")}}" {% if 'password' in form.fields.disabled %}disabled{% endif %}>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,18 @@
{% block input_email %}
{% if 'email' not in form.fields.hidden %}
<div class="form-group">
<label for="input-email" class="control-label">{{translate('EMAIL')}}</label>
<div class="input-group js-copy-container">
<span class="input-group-addon"><i class="fas fa-envelope fa-fw"></i></span>
<input type="text" class="form-control js-copy-target" name="email" autocomplete="off" value="{{user.email}}" placeholder="{{translate('EMAIL')}}" {% if 'email' in form.fields.disabled %}disabled{% endif %}>
{% if 'email' in form.fields.disabled %}
{% if 'copy' not in form.fields.disabled %}
<span class="input-group-btn">
<button class="btn btn-default uf-copy-trigger js-copy-trigger" type="button"><i class="fas fa-clipboard"></i></button>
</span>
{% endif %}
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,11 @@
{% block input_first_name %}
{% if 'first_name' not in form.fields.hidden %}
<div class="form-group">
<label for="input-first-name" class="control-label">{{translate('FIRST_NAME')}}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fas fa-edit fa-fw"></i></span>
<input type="text" id="input-first-name" name="first_name" class="form-control" autocomplete="off" value="{{user.first_name}}" placeholder="{{translate('FIRST_NAME')}}" {% if 'name' in form.fields.disabled %}disabled{% endif %}>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,21 @@
{% block input_group %}
{% if 'group' not in form.fields.hidden %}
<div class="form-group">
<label for="input-group">{{translate('GROUP')}}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fas fa-users fa-fw"></i></span>
{% if 'group' in form.fields.disabled %}
<input type="text" class="form-control" name="theme" value="{{user.group.name}}" disabled>
{% else %}
<select id="input-group" class="form-control js-select2" name="group_id">
<option value="0">{{translate('GROUP.NONE')}}</option>
<option disabled="disabled">-----</option>
{% for group in groups %}
<option value="{{group.id}}" {% if (group.id == user.group_id) %}selected{% endif %}>{{group.name}}</option>
{% endfor %}
</select>
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,11 @@
{% block input_last_name %}
{% if 'last_name' not in form.fields.hidden %}
<div class="form-group">
<label for="input-last-name" class="control-label">{{translate('LAST_NAME')}}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fas fa-edit fa-fw"></i></span>
<input type="text" id="input-last-name" name="last_name" class="form-control" autocomplete="off" value="{{user.last_name}}" placeholder="{{translate('LAST_NAME')}}" {% if 'name' in form.fields.disabled %}disabled{% endif %}>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,19 @@
{% block input_locale %}
{% if 'locale' not in form.fields.hidden %}
<div class="form-group">
<label for="input-locale" class="control-label">{{translate('LOCALE')}}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fas fa-language fa-fw"></i></span>
{% if 'locale' in form.fields.disabled %}
<input type="text" class="form-control" name="theme" value="{{locales[user.locale]}}" disabled>
{% else %}
<select id="input-locale" class="form-control js-select2" name="locale">
{% for option, label in locales %}
<option value="{{option}}" {% if (option == user.locale) %}selected{% endif %}>{{label}}</option>
{% endfor %}
</select>
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,18 @@
{% block input_new_password %}
{% if 'password' not in form.fields.hidden %}
<div class="form-group">
<label for="input-password" class="control-label">{{translate("PASSWORD.NEW")}}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fas fa-key"></i></span>
<input type="password" id="input-password" class="form-control" name="password" placeholder="{{translate("PASSWORD.BETWEEN", {min: site.password.length.min, max: site.password.length.max})}} ({{translate("OPTIONAL")}})" {% if 'password' in form.fields.disabled %}disabled{% endif %}>
</div>
</div>
<div class="form-group">
<label for="input-passwordc" class="control-label">{{translate("PASSWORD.CONFIRM_NEW")}}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fas fa-key"></i></span>
<input type="password" id="input-passwordc" class="form-control" name="passwordc" placeholder="{{translate("PASSWORD.CONFIRM_NEW_HELP")}}" {% if 'password' in form.fields.disabled %}disabled{% endif %}>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,19 @@
{% block input_theme %}
{% if 'theme' not in form.fields.hidden %}
<div class="form-group">
<label for="input-theme">{{translate('THEME')}}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fas fa-puzzle-piece fa-fw"></i></span>
{% if 'theme' in form.fields.disabled %}
<input type="text" class="form-control" name="theme" value="{{themes[user.theme]}}" disabled>
{% else %}
<select id="input-theme" class="form-control js-select2" name="theme">
{% for option, label in theme %}
<option value="{{option}}" {% if (option == user.theme) %}selected{% endif %}>{{label}}</option>
{% endfor %}
</select>
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,11 @@
{% block input_user_name %}
{% if 'user_name' not in form.fields.hidden %}
<div class="form-group">
<label>{{translate('USERNAME')}}</label>
<div class="input-group">
<span class="input-group-addon"><i class="fas fa-edit fa-fw"></i></span>
<input type="text" class="form-control" name="user_name" autocomplete="off" value="{{user.user_name}}" placeholder="{{translate('USERNAME')}}" {% if 'user_name' in form.fields.disabled %}disabled{% endif %}>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,39 @@
<form id="account-settings" role="form" action="{{site.uri.public}}/account/settings" method="post">
<div class="box-header">
<h3 class="box-title"><i class="fas fa-cog fa-fw"></i> {{translate("ACCOUNT.SETTINGS")}}</h3>
</div>
<div class="box-body">
{% include "forms/csrf.html.twig" %}
<!-- Prevent browsers from trying to autofill the password field. See http://stackoverflow.com/a/23234498/2970321 -->
<input type="text" style="display:none">
<input type="password" style="display:none">
{% block settings_account %}
<div class="row">
<div class="col-sm-12">
{% include "forms/partials/input-email.html.twig" %}
</div>
</div>
<div class="row">
<div class="col-sm-12">
{% include "forms/partials/input-new_password.html.twig" %}
</div>
</div>
{% if 'password' not in form.fields.hidden %}
<hr>
{% endif %}
<div class="row">
<div class="col-sm-12">
{% include "forms/partials/input-current_password.html.twig" %}
</div>
</div>
{% endblock %}
</div>
{% if 'submit' not in form.fields.hidden %}
<div class="box-footer text-center">
<button type="reset" class="btn btn-default">{{translate('RESET')}}</button>
<button type="submit" class="btn btn-primary js-submit">{{translate('SAVE')}}</button>
</div>
{% endif %}
</form>

View File

@@ -0,0 +1,33 @@
<form id="profile-settings" role="form" action="{{site.uri.public}}/account/settings/profile" method="post">
<div class="box-header">
<h3 class="box-title"><i class="fas fa-user fa-fw"></i> {{translate("PROFILE.SETTINGS")}}</h3>
</div>
<div class="box-body">
{% include "forms/csrf.html.twig" %}
{% block settings_profile %}
{% if 'name' not in form.fields.hidden %}
<div class="row">
<div class="col-sm-6">
{% include "forms/partials/input-first_name.html.twig" %}
</div>
<div class="col-sm-6">
{% include "forms/partials/input-last_name.html.twig" %}
</div>
</div>
{% endif %}
<div class="row">
<div class="col-sm-12">
{% include "forms/partials/input-locale.html.twig" %}
</div>
</div>
{% endblock %}
</div>
{% if 'submit' not in form.fields.hidden %}
<div class="box-footer text-center">
<button type="reset" class="btn btn-default">{{translate('RESET')}}</button>
<button type="submit" class="btn btn-primary js-submit">{{translate('SAVE')}}</button>
</div>
{% endif %}
</form>

View File

@@ -0,0 +1,46 @@
{% extends "@admin/forms/user.html.twig" %}
{% block user_form %}
{% if 'user_name' not in form.fields.hidden %}
<div class="col-sm-6">
{% include "forms/partials/input-user_name.html.twig" %}
</div>
{% endif %}
{% if 'group' not in form.fields.hidden %}
<div class="col-sm-6">
{% include "forms/partials/input-group.html.twig" %}
</div>
{% endif %}
{% if 'name' not in form.fields.hidden %}
<div class="col-sm-6">
{% include "forms/partials/input-first_name.html.twig" %}
</div>
<div class="col-sm-6">
{% include "forms/partials/input-last_name.html.twig" %}
</div>
{% endif %}
{% if 'email' not in form.fields.hidden %}
<div class="col-sm-6">
{% include "forms/partials/input-email.html.twig" %}
</div>
{% endif %}
{% if 'theme' not in form.fields.hidden %}
<div class="col-sm-6">
{% include "forms/partials/input-theme.html.twig" %}
</div>
{% endif %}
{% if 'locale' not in form.fields.hidden %}
<div class="col-sm-6">
{% include "forms/partials/input-locale.html.twig" %}
</div>
{% endif %}
{% if 'password' not in form.fields.hidden %}
{% include "forms/partials/user-set-password.html.twig" %}
{% endif %}
{% endblock %}