From eec6b692b2aebe31c50ed01a79085eb6d730a09f Mon Sep 17 00:00:00 2001 From: Kowalski Date: Sat, 31 Oct 2026 11:34:31 +0100 Subject: [PATCH] =?UTF-8?q?Zm=C4=9Bna=20hesla=20u=C5=BEivatele=20+=20GUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Model/Facade/DatabazeFacade.php | 34 +++++++++ app/UI/@layout.latte | 11 +-- app/UI/Nastaveni/NastaveniPresenter.php | 99 ++++++++++++++++++++++++- app/UI/Nastaveni/default.latte | 92 +++++++++++++++++++++++ www/css/layout.css | 22 ++++++ 5 files changed, 252 insertions(+), 6 deletions(-) diff --git a/app/Model/Facade/DatabazeFacade.php b/app/Model/Facade/DatabazeFacade.php index 836856b..a537783 100644 --- a/app/Model/Facade/DatabazeFacade.php +++ b/app/Model/Facade/DatabazeFacade.php @@ -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 { $server ??= $this->tenantServer; diff --git a/app/UI/@layout.latte b/app/UI/@layout.latte index 7ef271d..80780c2 100644 --- a/app/UI/@layout.latte +++ b/app/UI/@layout.latte @@ -25,7 +25,7 @@ {* naše vlastní styly *} - + @@ -77,10 +77,11 @@
{if $user->isLoggedIn()} - {* Přihlášený uživatel *} - - {$user->identity->jmeno ?: $user->identity->email} - + {* Přihlášený uživatel — ikona / jméno → Nastavení *} + + + {$user->identity->jmeno ?: $user->identity->email} + Odhlásit diff --git a/app/UI/Nastaveni/NastaveniPresenter.php b/app/UI/Nastaveni/NastaveniPresenter.php index fc2278c..e3ec660 100644 --- a/app/UI/Nastaveni/NastaveniPresenter.php +++ b/app/UI/Nastaveni/NastaveniPresenter.php @@ -3,14 +3,17 @@ namespace App\UI\Nastaveni; use App\Model\Faktura\FakturaCislovani; +use App\Model\Facade\DatabazeFacade; use App\Model\Facade\FakturaFacade; use App\UI\BasePresenter; use Nette\Application\UI\Form; +use Nette\Security\SimpleIdentity; class NastaveniPresenter extends BasePresenter { public function __construct( - private FakturaFacade $fakturaFacade + private FakturaFacade $fakturaFacade, + private DatabazeFacade $databazeFacade, ) { parent::__construct(); } @@ -29,6 +32,14 @@ class NastaveniPresenter extends BasePresenter 'obdobi' => $nastaveni['obdobi'], '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 @@ -36,6 +47,9 @@ class NastaveniPresenter extends BasePresenter $firmaId = (int) $this->getUser()->getIdentity()->firma_id; $dalsi = $this->fakturaFacade->getNextCislo($firmaId); $this->template->prikladCisla = $dalsi['cislo']; + + $hesloForm = $this['hesloForm']; + $this->template->otevritHesloModal = $hesloForm->isSubmitted() && !$hesloForm->isSuccess(); } protected function createComponentNastaveniForm(): Form @@ -79,4 +93,87 @@ class NastaveniPresenter extends BasePresenter $this->flashMessage('Nastavení faktur bylo uloženo.', 'success'); $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)); + } } diff --git a/app/UI/Nastaveni/default.latte b/app/UI/Nastaveni/default.latte index 1ae2972..84325ad 100644 --- a/app/UI/Nastaveni/default.latte +++ b/app/UI/Nastaveni/default.latte @@ -53,7 +53,99 @@
+ +
+
+
    +
  • {$error}
  • +
+
+ +
+ Přihlášení + +
+ +
+
+ + +
+
+
+ +
+
Heslo:
+
+ +
+
+ +

+ Uživatelské jméno je e-mail, kterým se přihlašujete. +

+
+
+ + + + diff --git a/www/css/layout.css b/www/css/layout.css index 4cc467d..8295910 100644 --- a/www/css/layout.css +++ b/www/css/layout.css @@ -85,6 +85,24 @@ body { 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 { border-color: #5d7390; color: #e4ebf4; @@ -294,6 +312,10 @@ body { margin-bottom: 0.35rem; } +.login-form .input-group > .form-control { + width: 1%; +} + .login-form .form-control { width: 100%; padding: 0.5rem 0.75rem;