NEW FEATURE: settings page

This commit is contained in:
Jeroen De Meerleer 2022-09-08 12:28:38 +02:00
parent dc09ba2275
commit 56bc06e6f0
Signed by: JeroenED
GPG Key ID: 28CCCB8F62BFADD6
12 changed files with 267 additions and 56 deletions

2
assets/js/settings.js Normal file
View File

@ -0,0 +1,2 @@
import 'bootstrap';
import '/assets/scss/settings.scss';

View File

@ -0,0 +1,4 @@
@import "assets/scss/base";
@import "assets/scss/icons";
@import "/node_modules/@eonasdan/tempus-dominus/dist/css/tempus-dominus.css";
@import "assets/scss/tempus-dominus-dark";

View File

@ -1,6 +1,6 @@
default: default:
path: '/' path: '/'
controller: App\Controller\SecurityController::loginAction controller: App\Controller\UserController::loginAction
login_check: login_check:
path: '/login_check' path: '/login_check'
@ -16,13 +16,23 @@ favicon:
path: '/favicon.ico' path: '/favicon.ico'
controller: App\Controller\SiteController::faviconAction controller: App\Controller\SiteController::faviconAction
settings:
path: '/{_locale}/settings'
methods: [ 'GET' ]
controller: App\Controller\UserController::settingsAction
settings_save:
path: '/{_locale}/settings'
methods: [ 'POST' ]
controller: App\Controller\UserController::settingsSaveAction
default_locale: default_locale:
path: '/{_locale}' path: '/{_locale}'
controller: App\Controller\SecurityController::loginAction controller: App\Controller\UserController::loginAction
login: login:
path: '/{_locale}/login' path: '/{_locale}/login'
controller: App\Controller\SecurityController::loginAction controller: App\Controller\UserController::loginAction
job_index: job_index:
path: '/{_locale}/job' path: '/{_locale}/job'

View File

@ -1,45 +0,0 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
public function loginAction(Request $request, AuthenticationUtils $authenticationUtils): Response
{
if($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
$session = $request->getSession();
$user = $this->getUser();
$session->set('_locale', $user->getLocale());
return new RedirectResponse($this->generateUrl('job_index', ['_locale' => $user->getLocale()]));
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', [
'controller_name' => 'LoginController',
'last_username' => $lastUsername,
'error' => $error
]);
}
public function logoutAction(): void
{
// controller can be blank: it will never be called!
throw new \Exception('Don\'t forget to activate logout in security.yaml');
}
public function loginCheckAction(): void
{
}
}

View File

@ -0,0 +1,107 @@
<?php
namespace App\Controller;
use App\Entity\User;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class UserController extends AbstractController
{
public function loginAction(Request $request, AuthenticationUtils $authenticationUtils): Response
{
if($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
$session = $request->getSession();
$user = $this->getUser();
$session->set('_locale', $user->getLocale());
return new RedirectResponse($this->generateUrl('job_index', ['_locale' => $user->getLocale()]));
}
if($request->cookies->has('logout-notice')) {
$this->addFlash('success', 'settings.flashes.passwordsaved');
$res = new Response();
$res->headers->clearCookie('logout-notice');
$res->sendHeaders();
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', [
'controller_name' => 'LoginController',
'last_username' => $lastUsername,
'error' => $error
]);
}
public function settingsAction(Request $request)
{
$params['locales'] = $this->getParameter('enabled_locales');
$params['user'] = $this->getUser();
return $this->render('settings.html.twig', $params);
}
public function settingsSaveAction(Request $request, ManagerRegistry $em, UserPasswordHasherInterface $passwordHasher)
{
$session = $request->getSession();
$data = $request->request->all();
$locale = $request->getLocale();
$userRepo = $em->getRepository(User::class);
$route = 'settings';
/** @var User $user */
$user = $this->getUser();
if(!empty($data['locale']) && $user->getLocale() != $data['locale']) {
$error = false;
if(!array_key_exists($data['locale'], $this->getParameter('enabled_locales'))) {
$error = true;
$this->addFlash('danger', 'settings.flashes.inexistinglocale');
}
if(!$error) {
$userRepo->setLocale($user, $data['locale']);
$locale = $data['locale'];
$this->addFlash('success', 'settings.flashes.localesaved');
}
}
if(!empty($data['current']) && !empty($data['password'])) {
$error = false;
if (empty($data['repeat']) || ($data['password'] != $data['repeat'])) {
$error = true;
$this->addFlash('danger', 'settings.flashes.repeatpasswordnotok');
} elseif(!$passwordHasher->isPasswordValid($user, $data['current'])) {
$error = true;
$this->addFlash('danger', 'settings.flashes.currentpassnotok');
}
if(!$error) {
$hashedpass = $passwordHasher->hashPassword($user, $data['password']);
$userRepo->setPassword($user, $hashedpass);
$cookie = new Cookie('logout-notice', true, (time() + 2), secure: $request->isSecure());
$res = new Response();
$res->headers->setCookie( $cookie );
$res->sendHeaders();
$route = 'logout';
}
}
return $this->redirect($this->generateUrl($route, ['_locale' => $locale]));
}
public function logoutAction(): void
{
// controller can be blank: it will never be called!
throw new \Exception('Don\'t forget to activate logout in security.yaml');
}
public function loginCheckAction(): void
{
}
}

View File

@ -4,18 +4,25 @@
namespace App\Repository; namespace App\Repository;
use App\Entity\User;
use App\Service\Secret; use App\Service\Secret;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository class UserRepository extends EntityRepository
{ {
public function getMailAddresses() { public function setLocale(User $user, $locale)
$users = $this->findBy(['sendmail' => 1]); {
$em = $this->getEntityManager();
$user->setLocale($locale);
$em->persist($user);
$em->flush();
}
$return = []; public function setPassword(User $user, $hashedPassword)
foreach($users as $user) { {
$return[] = $user->getEmail(); $em = $this->getEntityManager();
} $user->setPassword($hashedPassword);
return $return; $em->persist($user);
$em->flush();
} }
} }

View File

@ -25,6 +25,7 @@
<ul class="nav flex-column"> <ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" href="{{ path('job_index') }}">{{ 'menu.overview' | trans }}</a></li> <li class="nav-item"><a class="nav-link" href="{{ path('job_index') }}">{{ 'menu.overview' | trans }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ path('job_add') }}">{{ 'menu.add' | trans }}</a></li> <li class="nav-item"><a class="nav-link" href="{{ path('job_add') }}">{{ 'menu.add' | trans }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ path('settings') }}">{{ 'menu.settings' | trans }}</a></li>
<li class="nav-item"><a class="nav-link" href="{{ path('logout') }}">{{ 'menu.logout' | trans }}</a></li> <li class="nav-item"><a class="nav-link" href="{{ path('logout') }}">{{ 'menu.logout' | trans }}</a></li>
</ul> </ul>
</div> </div>

View File

@ -0,0 +1,44 @@
{% extends "base.html.twig" %}
{% block title %}{{ "settings.title" | trans }}{% endblock %}
{% block content %}
<h2>{{ "settings.header" | trans }}</h2>
<form method="post" class="form-horizontal" enctype="multipart/form-data" action="{{ path('settings_save') }}">
<h3>{{ "settings.password.header" | trans }}</h3>
<div class="mb-3">
<label for="current">{{ "settings.password.current.label" | trans }}</label>
<input type="password" name="current" class="form-control" id="current" placeholder="{{ "settings.password.current.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="password">{{ "settings.password.password.label" | trans }}</label>
<input type="password" name="password" class="form-control" id="password" placeholder="{{ "settings.password.password.placeholder" | trans }}">
</div>
<div class="mb-3">
<label for="repeat">{{ "settings.password.repeat.label" | trans }}</label>
<input type="password" name="repeat" class="form-control" id="repeat" placeholder="{{ "settings.password.repeat.placeholder" | trans }}">
</div>
<h3>{{ "settings.other.header" | trans }}</h3>
<div class="mb-3">
<label for="locale">{{ "settings.other.locale.label" | trans }}</label>
<select name="locale" class="form-select">
{% for code,locale in locales %}
<option value="{{ code }}"{% if code == user.locale %} selected{% endif %}>{{ locale }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-outline-primary">{{ "settings.submit.label" | trans }}</button>
</form>
{% endblock %}
{% block styles %}
{{ encore_entry_link_tags('settings') }}
{% endblock %}
{% block scripts %}
{{ encore_entry_script_tags('settings') }}
{% endblock %}

View File

@ -9,6 +9,7 @@ demomode:
menu: menu:
overview: "0v3rv13w" overview: "0v3rv13w"
add: "4dd @ n3w cr0nj0b" add: "4dd @ n3w cr0nj0b"
settings: "S3tt1ngz"
logout: "L0g0ut" logout: "L0g0ut"
security: security:
login: login:
@ -22,6 +23,32 @@ security:
label: "D0 n0t f0rg3t" label: "D0 n0t f0rg3t"
submit-btn: submit-btn:
label: "3nt3r" label: "3nt3r"
settings:
title: "S3tt1ngz"
header: "S3tt1ngz"
flashes:
inexistinglocale: "L0c4L3 d03z n0t 3x1st"
localesaved: "L0c4L3 b s4v3d"
repeatpasswordnoto: "P4ssw0rdz 1z n0t 3qu4L"
currentpassnotok: "P4ssw0rd b n0t c0rr3ct"
passwordsaved: "P4ssw0rd b s4v3d"
password:
header: "P4ssw0rd"
current:
label: "Curr3nt p4ssw0rd"
placeholder: "abc123"
password:
label: "N3w p4ssw0rd"
placeholder: "abc123"
repeat:
label: "R3p34t p4ssw0rd"
placeholder: "abc123"
other:
header: "0th3r s3tt1ngz"
locale:
label: "L0c4L3"
submit:
label: "Subm1t"
job: job:
index: index:
title: "0v3rv13w" title: "0v3rv13w"

View File

@ -9,6 +9,7 @@ demomode:
menu: menu:
overview: "Overview" overview: "Overview"
add: "Add a new cronjob" add: "Add a new cronjob"
settings: "Settings"
logout: "Logout" logout: "Logout"
security: security:
login: login:
@ -22,6 +23,32 @@ security:
label: "Remember, remember" label: "Remember, remember"
submit-btn: submit-btn:
label: "Login" label: "Login"
settings:
title: "Settings"
header: "Settings"
flashes:
inexistinglocale: "Locale does not exist"
localesaved: "Locale is saved"
repeatpasswordnoto: "Passwords are not equal"
currentpassnotok: "Password is not correct"
passwordsaved: "Password is saved"
password:
header: "Password"
current:
label: "Current password"
placeholder: "abc123"
password:
label: "New password"
placeholder: "abc123"
repeat:
label: "Repeat password"
placeholder: "abc123"
other:
header: "Other settings"
locale:
label: "Locale"
submit:
label: "Submit"
job: job:
index: index:
title: "Overview" title: "Overview"

View File

@ -6,10 +6,10 @@ demomode:
header: "Aanmeldgegevens voor demo modus:" header: "Aanmeldgegevens voor demo modus:"
username: "Gebruikersnaam" username: "Gebruikersnaam"
password: "Wachtwoord" password: "Wachtwoord"
menu: menu:
overview: "Overzicht" overview: "Overzicht"
add: "Taak toevoegen" add: "Taak toevoegen"
settings: "Instellingen"
logout: "Afmelden" logout: "Afmelden"
security: security:
login: login:
@ -23,6 +23,32 @@ security:
label: "Onthoud mij!" label: "Onthoud mij!"
submit-btn: submit-btn:
label: "Aanmelden" label: "Aanmelden"
settings:
title: "Instellingen"
header: "Instellingen"
flashes:
inexistinglocale: "Taal bestaat niet"
localesaved: "Taal werd opgeslagen"
repeatpasswordnotok: "Wachtwoorden zijn niet gelijk"
currentpassnotok: "Wachtwoord is not correct"
passwordsaved: "Wachtwoord werd opgeslagen"
password:
header: "Wachtwoord"
current:
label: "Huidig wachtwoord"
placeholder: "abc123"
password:
label: "Nieuw wachtwoord"
placeholder: "abc123"
repeat:
label: "Herhaal wachtwoord"
placeholder: "abc123"
other:
header: "Andere instellingen"
locale:
label: "Taal"
submit:
label: "Verzend!"
job: job:
index: index:
title: "Overzicht" title: "Overzicht"

View File

@ -20,6 +20,7 @@ Encore
* Each entry will result in one JavaScript file (e.g. app.js) * Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if your JavaScript imports CSS. * and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/ */
.addEntry('settings', './assets/js/settings.js')
.addEntry('security.login', './assets/js/security/login.js') .addEntry('security.login', './assets/js/security/login.js')
.addEntry('job.index', './assets/js/job/index.js') .addEntry('job.index', './assets/js/job/index.js')
.addEntry('job.view', './assets/js/job/view.js') .addEntry('job.view', './assets/js/job/view.js')