Updated user settings page and user forms
This commit is contained in:
@@ -13,3 +13,5 @@ Fixes/tweaks a few "issues" with the default UserFrosting installation, includin
|
||||
- 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 '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
15
routes/routes.php
Normal 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());
|
||||
111
src/Controller/AccountController.php
Normal file
111
src/Controller/AccountController.php
Normal 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),
|
||||
]
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
11
templates/forms/partials/input-current_password.html.twig
Normal file
11
templates/forms/partials/input-current_password.html.twig
Normal 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 %}
|
||||
18
templates/forms/partials/input-email.html.twig
Normal file
18
templates/forms/partials/input-email.html.twig
Normal 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 %}
|
||||
11
templates/forms/partials/input-first_name.html.twig
Normal file
11
templates/forms/partials/input-first_name.html.twig
Normal 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 %}
|
||||
21
templates/forms/partials/input-group.html.twig
Normal file
21
templates/forms/partials/input-group.html.twig
Normal 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 %}
|
||||
11
templates/forms/partials/input-last_name.html.twig
Normal file
11
templates/forms/partials/input-last_name.html.twig
Normal 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 %}
|
||||
19
templates/forms/partials/input-locale.html.twig
Normal file
19
templates/forms/partials/input-locale.html.twig
Normal 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 %}
|
||||
18
templates/forms/partials/input-new_password.html.twig
Normal file
18
templates/forms/partials/input-new_password.html.twig
Normal 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 %}
|
||||
19
templates/forms/partials/input-theme.html.twig
Normal file
19
templates/forms/partials/input-theme.html.twig
Normal 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 %}
|
||||
11
templates/forms/partials/input-user_name.html.twig
Normal file
11
templates/forms/partials/input-user_name.html.twig
Normal 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 %}
|
||||
39
templates/forms/settings-account.html.twig
Normal file
39
templates/forms/settings-account.html.twig
Normal 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>
|
||||
33
templates/forms/settings-profile.html.twig
Normal file
33
templates/forms/settings-profile.html.twig
Normal 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>
|
||||
46
templates/forms/user.html.twig
Normal file
46
templates/forms/user.html.twig
Normal 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 %}
|
||||
Reference in New Issue
Block a user