Změna hesla uživatele + GUI
parent
e1da2619a8
commit
eec6b692b2
|
|
@ -307,6 +307,40 @@ class DatabazeFacade
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Změna přihlašovacího jména (e-mailu) přihlášeného uživatele.
|
||||||
|
*/
|
||||||
|
public function changeOwnEmail(int $loginId, string $email): string
|
||||||
|
{
|
||||||
|
$this->updateUserCredentials($loginId, $email, null);
|
||||||
|
|
||||||
|
return mb_strtolower(trim($email));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Změna hesla. Původní heslo musí sedět s uloženým hashem.
|
||||||
|
*/
|
||||||
|
public function changeOwnPassword(int $loginId, string $currentPassword, string $newPassword): void
|
||||||
|
{
|
||||||
|
$row = $this->masterDb->fetch(
|
||||||
|
'SELECT HESLO_HASH FROM dbo.LOGIN WHERE ID = ? AND AKTIVNI = 1',
|
||||||
|
$loginId
|
||||||
|
);
|
||||||
|
$hash = $row !== null ? (string) ($row->HESLO_HASH ?? '') : '';
|
||||||
|
if ($hash === '' || !$this->passwords->verify($currentPassword, $hash)) {
|
||||||
|
throw new \InvalidArgumentException('Původní heslo není správné.');
|
||||||
|
}
|
||||||
|
if (mb_strlen($newPassword) < 6) {
|
||||||
|
throw new \InvalidArgumentException('Heslo musí mít alespoň 6 znaků.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->masterDb->query(
|
||||||
|
'UPDATE dbo.LOGIN SET HESLO_HASH = ? WHERE ID = ?',
|
||||||
|
$this->passwords->hash($newPassword),
|
||||||
|
$loginId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function findLoginIdForFirma(string $database, int $firmaId, ?string $server = null): ?int
|
public function findLoginIdForFirma(string $database, int $firmaId, ?string $server = null): ?int
|
||||||
{
|
{
|
||||||
$server ??= $this->tenantServer;
|
$server ??= $this->tenantServer;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{* naše vlastní styly *}
|
{* naše vlastní styly *}
|
||||||
<link href="{$basePath}/css/layout.css?v={$appVersion ?? '1.0'}-7" rel="stylesheet">
|
<link href="{$basePath}/css/layout.css?v={$appVersion ?? '1.0'}-9" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
@ -77,10 +77,11 @@
|
||||||
<!-- Uživatel a Odhlášení / Přihlášení -->
|
<!-- Uživatel a Odhlášení / Přihlášení -->
|
||||||
<div class="user-panel ms-auto">
|
<div class="user-panel ms-auto">
|
||||||
{if $user->isLoggedIn()}
|
{if $user->isLoggedIn()}
|
||||||
{* Přihlášený uživatel *}
|
{* Přihlášený uživatel — ikona / jméno → Nastavení *}
|
||||||
<span class="text-white d-none d-sm-inline me-2">
|
<a n:href="Nastaveni:default" class="user-panel-link text-white me-2" title="Nastavení">
|
||||||
<i class="bi bi-person-circle me-1"></i> {$user->identity->jmeno ?: $user->identity->email}
|
<i class="bi bi-person-circle me-1"></i>
|
||||||
</span>
|
<span class="d-none d-sm-inline">{$user->identity->jmeno ?: $user->identity->email}</span>
|
||||||
|
</a>
|
||||||
<a n:href="Sign:out" class="btn btn-sm btn-outline-light" title="Odhlásit se">
|
<a n:href="Sign:out" class="btn btn-sm btn-outline-light" title="Odhlásit se">
|
||||||
<i class="bi bi-box-arrow-right me-1"></i> Odhlásit
|
<i class="bi bi-box-arrow-right me-1"></i> Odhlásit
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,17 @@
|
||||||
namespace App\UI\Nastaveni;
|
namespace App\UI\Nastaveni;
|
||||||
|
|
||||||
use App\Model\Faktura\FakturaCislovani;
|
use App\Model\Faktura\FakturaCislovani;
|
||||||
|
use App\Model\Facade\DatabazeFacade;
|
||||||
use App\Model\Facade\FakturaFacade;
|
use App\Model\Facade\FakturaFacade;
|
||||||
use App\UI\BasePresenter;
|
use App\UI\BasePresenter;
|
||||||
use Nette\Application\UI\Form;
|
use Nette\Application\UI\Form;
|
||||||
|
use Nette\Security\SimpleIdentity;
|
||||||
|
|
||||||
class NastaveniPresenter extends BasePresenter
|
class NastaveniPresenter extends BasePresenter
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private FakturaFacade $fakturaFacade
|
private FakturaFacade $fakturaFacade,
|
||||||
|
private DatabazeFacade $databazeFacade,
|
||||||
) {
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
@ -29,6 +32,14 @@ class NastaveniPresenter extends BasePresenter
|
||||||
'obdobi' => $nastaveni['obdobi'],
|
'obdobi' => $nastaveni['obdobi'],
|
||||||
'splatnost' => $nastaveni['splatnostDny'],
|
'splatnost' => $nastaveni['splatnostDny'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$login = $this->databazeFacade->getById((int) $this->getUser()->getId());
|
||||||
|
$email = $login !== null
|
||||||
|
? (string) $login->EMAIL
|
||||||
|
: (string) ($this->getUser()->getIdentity()->email ?? '');
|
||||||
|
$this['jmenoForm']->setDefaults([
|
||||||
|
'email' => $email,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderDefault(): void
|
public function renderDefault(): void
|
||||||
|
|
@ -36,6 +47,9 @@ class NastaveniPresenter extends BasePresenter
|
||||||
$firmaId = (int) $this->getUser()->getIdentity()->firma_id;
|
$firmaId = (int) $this->getUser()->getIdentity()->firma_id;
|
||||||
$dalsi = $this->fakturaFacade->getNextCislo($firmaId);
|
$dalsi = $this->fakturaFacade->getNextCislo($firmaId);
|
||||||
$this->template->prikladCisla = $dalsi['cislo'];
|
$this->template->prikladCisla = $dalsi['cislo'];
|
||||||
|
|
||||||
|
$hesloForm = $this['hesloForm'];
|
||||||
|
$this->template->otevritHesloModal = $hesloForm->isSubmitted() && !$hesloForm->isSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createComponentNastaveniForm(): Form
|
protected function createComponentNastaveniForm(): Form
|
||||||
|
|
@ -79,4 +93,87 @@ class NastaveniPresenter extends BasePresenter
|
||||||
$this->flashMessage('Nastavení faktur bylo uloženo.', 'success');
|
$this->flashMessage('Nastavení faktur bylo uloženo.', 'success');
|
||||||
$this->redirect('this');
|
$this->redirect('this');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function createComponentJmenoForm(): Form
|
||||||
|
{
|
||||||
|
$form = new Form;
|
||||||
|
|
||||||
|
$form->addText('email', 'Uživatelské jméno:')
|
||||||
|
->setHtmlType('email')
|
||||||
|
->setHtmlAttribute('autocomplete', 'username')
|
||||||
|
->setRequired('Zadejte uživatelské jméno.')
|
||||||
|
->addRule(Form::Email, 'Zadejte platný e-mail.')
|
||||||
|
->addRule(Form::MaxLength, 'Uživatelské jméno může mít nejvýše 100 znaků.', 100);
|
||||||
|
|
||||||
|
$form->addSubmit('save', 'Uložit jméno');
|
||||||
|
$form->onSuccess[] = [$this, 'jmenoFormSucceeded'];
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jmenoFormSucceeded(Form $form, \stdClass $data): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$email = $this->databazeFacade->changeOwnEmail(
|
||||||
|
(int) $this->getUser()->getId(),
|
||||||
|
(string) $data->email
|
||||||
|
);
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$form->addError($e->getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->replaceIdentityEmail($email);
|
||||||
|
$this->flashMessage('Uživatelské jméno bylo uloženo.', 'success');
|
||||||
|
$this->redirect('this');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createComponentHesloForm(): Form
|
||||||
|
{
|
||||||
|
$form = new Form;
|
||||||
|
|
||||||
|
$form->addPassword('current', 'Původní heslo:')
|
||||||
|
->setHtmlAttribute('autocomplete', 'current-password')
|
||||||
|
->setRequired('Zadejte původní heslo.');
|
||||||
|
|
||||||
|
$form->addPassword('password', 'Nové heslo:')
|
||||||
|
->setHtmlAttribute('autocomplete', 'new-password')
|
||||||
|
->setRequired('Zadejte nové heslo.')
|
||||||
|
->addRule(Form::MinLength, 'Heslo musí mít alespoň 6 znaků.', 6);
|
||||||
|
|
||||||
|
$form->addPassword('passwordVerify', 'Nové heslo znovu:')
|
||||||
|
->setHtmlAttribute('autocomplete', 'new-password')
|
||||||
|
->setRequired('Zadejte nové heslo znovu.')
|
||||||
|
->addRule(Form::Equal, 'Hesla se neshodují.', $form['password']);
|
||||||
|
|
||||||
|
$form->addSubmit('save', 'Uložit heslo');
|
||||||
|
$form->onSuccess[] = [$this, 'hesloFormSucceeded'];
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hesloFormSucceeded(Form $form, \stdClass $data): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->databazeFacade->changeOwnPassword(
|
||||||
|
(int) $this->getUser()->getId(),
|
||||||
|
(string) $data->current,
|
||||||
|
(string) $data->password
|
||||||
|
);
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
$form['current']->addError($e->getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->flashMessage('Heslo bylo změněno.', 'success');
|
||||||
|
$this->redirect('this');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function replaceIdentityEmail(string $email): void
|
||||||
|
{
|
||||||
|
$identity = $this->getUser()->getIdentity();
|
||||||
|
$data = $identity->getData();
|
||||||
|
$data['email'] = $email;
|
||||||
|
$this->getUser()->login(new SimpleIdentity($identity->getId(), $identity->getRoles(), $data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,99 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<form n:name="jmenoForm" class="login-form mt-4">
|
||||||
|
<div n:if="$form->errors" class="alert alert-danger mb-3">
|
||||||
|
<ul class="mb-0 ps-3">
|
||||||
|
<li n:foreach="$form->errors as $error">{$error}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset class="border rounded p-3">
|
||||||
|
<legend class="float-none w-auto px-2 fs-6 fw-bold mb-0">Přihlášení</legend>
|
||||||
|
|
||||||
|
<div class="row align-items-center mb-3">
|
||||||
|
<label n:name="email" class="col-sm-5 col-form-label">Uživatelské jméno:</label>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<div class="input-group">
|
||||||
|
<input n:name="email" class="form-control">
|
||||||
|
<button n:name="save" class="btn btn-outline-primary">Uložit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-sm-5 col-form-label">Heslo:</div>
|
||||||
|
<div class="col-sm-7">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#hesloModal">
|
||||||
|
<i class="bi bi-key me-1"></i> Změnit heslo
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-muted small mt-3 mb-0">
|
||||||
|
Uživatelské jméno je e-mail, kterým se přihlašujete.
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="hesloModal" tabindex="-1" aria-hidden="true" aria-labelledby="hesloModalLabel">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<form n:name="hesloForm">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="hesloModalLabel">
|
||||||
|
<i class="bi bi-key me-1"></i>Změna hesla
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zavřít"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div n:if="$form->errors" class="alert alert-danger mb-3">
|
||||||
|
<ul class="mb-0 ps-3">
|
||||||
|
<li n:foreach="$form->errors as $error">{$error}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label n:name="current" class="form-label">Původní heslo</label>
|
||||||
|
<input n:name="current" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label n:name="password" class="form-label">Nové heslo</label>
|
||||||
|
<input n:name="password" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="mb-0">
|
||||||
|
<label n:name="passwordVerify" class="form-label">Nové heslo znovu</label>
|
||||||
|
<input n:name="passwordVerify" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Zrušit</button>
|
||||||
|
<button n:name="save" class="btn btn-primary">Uložit heslo</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var el = document.getElementById('hesloModal');
|
||||||
|
if (!el || typeof bootstrap === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
el.addEventListener('shown.bs.modal', function () {
|
||||||
|
var input = el.querySelector('input[name="current"]');
|
||||||
|
if (input) {
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{if $otevritHesloModal}
|
||||||
|
bootstrap.Modal.getOrCreateInstance(el).show();
|
||||||
|
{/if}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,24 @@ body {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-panel-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
text-decoration: none;
|
||||||
|
opacity: 0.92;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: 0.2rem 0.35rem;
|
||||||
|
transition: opacity 0.15s ease, background-color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-panel-link:hover,
|
||||||
|
.user-panel-link:focus-visible {
|
||||||
|
opacity: 1;
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(255, 255, 255, 0.08);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.user-panel .btn-outline-light {
|
.user-panel .btn-outline-light {
|
||||||
border-color: #5d7390;
|
border-color: #5d7390;
|
||||||
color: #e4ebf4;
|
color: #e4ebf4;
|
||||||
|
|
@ -294,6 +312,10 @@ body {
|
||||||
margin-bottom: 0.35rem;
|
margin-bottom: 0.35rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form .input-group > .form-control {
|
||||||
|
width: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
.login-form .form-control {
|
.login-form .form-control {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue