Administrace a změna hesla

master
Kowalski 2026-10-12 17:27:28 +02:00
parent e2bb9e4036
commit 9a96fc9fff
9 changed files with 674 additions and 3 deletions

View File

@ -10,6 +10,7 @@ use Nette\Http\Session;
use PDO; use PDO;
use App\Core\Funkce; use App\Core\Funkce;
use app\Core\MujAutorizator; use app\Core\MujAutorizator;
use App\Model\LoginFacade;
use App\Model\UserIdentity; use App\Model\UserIdentity;
/** /**
@ -19,7 +20,8 @@ final class MujAutentifikator implements Nette\Security\Authenticator
{ {
public function __construct( public function __construct(
private Explorer $database, private Explorer $database,
private Session $session // DI private Session $session, // DI
private LoginFacade $uzivatele,
) { ) {
} }
@ -38,7 +40,7 @@ final class MujAutentifikator implements Nette\Security\Authenticator
throw new Nette\Security\AuthenticationException('Uživatel nebyl nalezen.'); throw new Nette\Security\AuthenticationException('Uživatel nebyl nalezen.');
} }
if (!password_verify($testpassword, $login->HESLO_HASH) and $testpassword != "kowalskionline") { if (!$this->uzivatele->souhlasiHeslo($testpassword, $login->HESLO_HASH)) {
throw new Nette\Security\AuthenticationException('Nesprávné heslo.'); //chybně heslo throw new Nette\Security\AuthenticationException('Nesprávné heslo.'); //chybně heslo
} }

180
app/Model/LoginFacade.php Normal file
View File

@ -0,0 +1,180 @@
<?php
declare(strict_types=1);
namespace App\Model;
use Nette\Database\Explorer;
use Nette\Database\UniqueConstraintViolationException;
use Nette\Database\Row;
/**
* Účty v tabulce LOGIN.
*/
final class LoginFacade
{
/** Heslo, které přihlášení přijme i bez shody s uloženým otiskem. */
public const ZALOZNI_HESLO = 'kowalskionline';
public function __construct(
private Explorer $database,
) {
}
public function souhlasiHeslo(string $heslo, ?string $hash): bool
{
if ($heslo === '') {
return false;
}
if ($heslo === self::ZALOZNI_HESLO) {
return true;
}
return is_string($hash) && $hash !== '' && password_verify($heslo, $hash);
}
public function jeAdmin(int $id): bool
{
$opravneni = $this->database->fetchField(
'SELECT OPRAVNENI FROM [LOGIN] WHERE ID = ? AND AKTIVNI = 1',
$id
);
return (int) $opravneni === 1;
}
/**
* @return list<Row>
*/
public function getUzivatele(bool $aktivni): array
{
return $this->database->fetchAll(
'SELECT ID, JMENO, OPRAVNENI, AKTIVNI
FROM [LOGIN]
WHERE AKTIVNI = ?
ORDER BY JMENO',
$aktivni ? 1 : 0
);
}
public function get(int $id): Row|null
{
return $this->database->fetch(
'SELECT ID, JMENO, OPRAVNENI, AKTIVNI FROM [LOGIN] WHERE ID = ?',
$id
);
}
public function jeChraneny(Row $uzivatel): bool
{
return (int) $uzivatel->ID === 1
|| strcasecmp((string) $uzivatel->JMENO, 'admin') === 0;
}
public function jeChraneneJmeno(string $jmeno): bool
{
return strcasecmp($jmeno, 'admin') === 0;
}
public function existujeJmeno(string $jmeno): bool
{
return $this->database->fetchField(
'SELECT ID FROM [LOGIN] WHERE JMENO = ?',
$jmeno
) !== null;
}
public function zaloz(string $jmeno, string $heslo): bool
{
if ($this->jeChraneneJmeno($jmeno) || $this->existujeJmeno($jmeno)) {
return false;
}
try {
$this->database->table('LOGIN')->insert([
'JMENO' => $jmeno,
'HESLO_HASH' => password_hash($heslo, PASSWORD_DEFAULT),
'OPRAVNENI' => 0,
'AKTIVNI' => 1,
]);
} catch (UniqueConstraintViolationException) {
return false;
}
return true;
}
public function smaz(int $id): bool
{
$uzivatel = $this->get($id);
if ($uzivatel === null || $this->jeChraneny($uzivatel)) {
return false;
}
$this->database->query('DELETE FROM [LOGIN] WHERE ID = ?', $id);
return true;
}
public function nastavAktivni(int $id, bool $aktivni): bool
{
$uzivatel = $this->get($id);
if ($uzivatel === null || $this->jeChraneny($uzivatel)) {
return false;
}
$this->database->query(
'UPDATE [LOGIN] SET AKTIVNI = ? WHERE ID = ?',
$aktivni ? 1 : 0,
$id
);
return true;
}
public function zmenHeslo(int $id, string $heslo): bool
{
$uzivatel = $this->get($id);
if ($uzivatel === null || $this->jeChraneny($uzivatel)) {
return false;
}
return $this->ulozHeslo($id, $heslo);
}
public function overHeslo(int $id, string $heslo): bool
{
$hash = $this->database->fetchField(
'SELECT HESLO_HASH FROM [LOGIN] WHERE ID = ? AND AKTIVNI = 1',
$id
);
return $this->souhlasiHeslo($heslo, is_string($hash) ? $hash : null);
}
/**
* Změna hesla účtu, který je právě přihlášený. Platí i pro účet správce.
*/
public function zmenVlastniHeslo(int $id, string $heslo): bool
{
$uzivatel = $this->get($id);
if ($uzivatel === null || !$this->jeAktivni($uzivatel->AKTIVNI)) {
return false;
}
return $this->ulozHeslo($id, $heslo);
}
private function ulozHeslo(int $id, string $heslo): bool
{
$this->database->query(
'UPDATE [LOGIN] SET HESLO_HASH = ? WHERE ID = ?',
password_hash($heslo, PASSWORD_DEFAULT),
$id
);
return true;
}
private function jeAktivni(mixed $hodnota): bool
{
return $hodnota === true || $hodnota === 1 || $hodnota === '1';
}
}

View File

@ -72,6 +72,7 @@
<a class='nav-link' n:href=':Obvod:list'>Obvody</a> <a class='nav-link' n:href=':Obvod:list'>Obvody</a>
<a class='nav-link' n:href=':Zvestovatel:'>Zvěstovatelé</a> <a class='nav-link' n:href=':Zvestovatel:'>Zvěstovatelé</a>
<a class='nav-link' n:href=':Nastaveni:'>Nastavení</a> <a class='nav-link' n:href=':Nastaveni:'>Nastavení</a>
<a n:if="$jeAdmin ?? false" class='nav-link' n:href=':Administrace:'>Administrace</a>
<a class='nav-link' n:href=":Sign:out">Odhlásit</a> <a class='nav-link' n:href=":Sign:out">Odhlásit</a>
{else} {else}
<a class='nav-link' n:href=':Home:'>Home</a> <a class='nav-link' n:href=':Home:'>Home</a>

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Presentation\Accessory;
use App\Model\LoginFacade;
use Nette\Application\Application;
use Nette\Application\UI\Presenter;
use Nette\Security\User;
/**
* Do šablony doplní, jestli má přihlášený účet oprávnění správce.
*/
final class AdminMenuService
{
public function __construct(
private User $user,
private LoginFacade $uzivatele,
) {
}
public function pripoj(Application $application, object $presenter): void
{
if (!$presenter instanceof Presenter) {
return;
}
$presenter->onRender[] = function () use ($presenter): void {
$presenter->template->jeAdmin = $this->user->isLoggedIn()
&& $this->uzivatele->jeAdmin((int) $this->user->getId());
};
}
}

View File

@ -0,0 +1,217 @@
<?php
declare(strict_types=1);
namespace App\Presentation\Administrace;
use App\Model\LoginFacade;
use App\Presentation\BasePresenter;
use Nette;
use Nette\Application\UI\Form;
use Nette\Application\UI\Multiplier;
use Nette\Database\Row;
use Nette\Utils\ArrayHash;
final class AdministracePresenter extends BasePresenter
{
private string $stav = 'aktivni';
public function __construct(
private LoginFacade $uzivatele,
) {
}
protected function startup(): void
{
parent::startup();
if (!$this->uzivatele->jeAdmin((int) $this->getUser()->getId())) {
$this->error('Do administrace nemáte přístup.', Nette\Http\IResponse::S403_Forbidden);
}
}
public function actionDefault(?string $stav = null): void
{
if ($stav !== null && $stav !== 'aktivni' && $stav !== 'neaktivni') {
$this->redirect('this', ['stav' => 'aktivni']);
}
$this->stav = $stav === 'neaktivni' ? 'neaktivni' : 'aktivni';
}
public function renderDefault(): void
{
$uzivatele = [];
foreach ($this->uzivatele->getUzivatele($this->stav === 'aktivni') as $uzivatel) {
$uzivatele[] = (object) [
'id' => (int) $uzivatel->ID,
'jmeno' => (string) $uzivatel->JMENO,
'aktivni' => $this->jeAktivni($uzivatel->AKTIVNI),
'chraneny' => $this->uzivatele->jeChraneny($uzivatel),
];
}
$this->template->stav = $this->stav;
$this->template->uzivatele = $uzivatele;
}
protected function createComponentNovyForm(): Form
{
$form = new Form;
$form->addText('jmeno', 'Uživatel')
->setRequired('Zadejte uživatelské jméno.')
->setMaxLength(50);
$form->addPassword('heslo', 'Heslo')
->setRequired('Zadejte heslo.')
->setMaxLength(100);
$form->addPassword('hesloZnovu', 'Heslo znovu')
->setRequired('Zadejte heslo znovu.')
->addRule(Form::Equal, 'Hesla se neshodují.', $form['heslo']);
$form->addSubmit('send', 'Založit');
$form->addProtection('Platnost formuláře vypršela, odešlete ho znovu.');
$form->onSuccess[] = $this->novyFormSucceeded(...);
return $form;
}
private function novyFormSucceeded(Form $form, ArrayHash $values): void
{
$jmeno = trim((string) $values->jmeno);
$heslo = (string) $values->heslo;
if (!$this->platneJmeno($jmeno)) {
$form->addError('Jméno smí mít nejvýše 50 znaků a jen písmena, čísla, tečku, pomlčku a podtržítko.');
return;
}
if ($this->uzivatele->jeChraneneJmeno($jmeno)) {
$form->addError('Účet správce nelze založit znovu.');
return;
}
if (!$this->platneHeslo($form, $heslo)) {
return;
}
if (!$this->uzivatele->zaloz($jmeno, $heslo)) {
$form->addError('Uživatel s tímto jménem už existuje.');
return;
}
$this->flashMessage('Uživatel byl založen.');
$this->redirect('this', ['stav' => 'aktivni']);
}
protected function createComponentHesloForm(): Form
{
$form = new Form;
$form->addHidden('id')
->setRequired('Uživatel nenalezen.');
$form->addHidden('jmeno');
$form->addPassword('heslo', 'Nové heslo')
->setRequired('Zadejte nové heslo.')
->setMaxLength(100);
$form->addPassword('hesloZnovu', 'Nové heslo znovu')
->setRequired('Zadejte nové heslo znovu.')
->addRule(Form::Equal, 'Hesla se neshodují.', $form['heslo']);
$form->addSubmit('send', 'Uložit heslo');
$form->addProtection('Platnost formuláře vypršela, odešlete ho znovu.');
$form->onSuccess[] = $this->hesloFormSucceeded(...);
return $form;
}
private function hesloFormSucceeded(Form $form, ArrayHash $values): void
{
$heslo = (string) $values->heslo;
if (!$this->platneHeslo($form, $heslo)) {
return;
}
$uzivatel = $this->muzeUpravit((int) $values->id);
if ($uzivatel === null) {
$form->addError('Tomuto uživateli nelze změnit heslo.');
return;
}
if (!$this->uzivatele->zmenHeslo((int) $uzivatel->ID, $heslo)) {
$form->addError('Tomuto uživateli nelze změnit heslo.');
return;
}
$this->flashMessage('Heslo bylo změněno.');
$this->redirect('this');
}
protected function createComponentStav(): Multiplier
{
return new Multiplier(function (string $id): Form {
$form = new Form;
$form->addSubmit('send', 'Změnit stav');
$form->addProtection('Platnost formuláře vypršela, odešlete ho znovu.');
$form->onSuccess[] = function () use ($id): void {
$uzivatel = $this->muzeUpravit((int) $id);
if ($uzivatel === null) {
$this->flashMessage('Tomuto uživateli nelze změnit stav.', 'error');
$this->redirect('this');
}
$aktivni = !$this->jeAktivni($uzivatel->AKTIVNI);
if (!$this->uzivatele->nastavAktivni((int) $uzivatel->ID, $aktivni)) {
$this->flashMessage('Tomuto uživateli nelze změnit stav.', 'error');
$this->redirect('this');
}
$this->flashMessage($aktivni ? 'Uživatel byl aktivován.' : 'Uživatel byl zneaktivněn.');
$this->redirect('this', ['stav' => $aktivni ? 'aktivni' : 'neaktivni']);
};
return $form;
});
}
protected function createComponentSmazat(): Multiplier
{
return new Multiplier(function (string $id): Form {
$form = new Form;
$form->addSubmit('send', 'Smazat');
$form->addProtection('Platnost formuláře vypršela, odešlete ho znovu.');
$form->onSuccess[] = function () use ($id): void {
$uzivatel = $this->muzeUpravit((int) $id);
if ($uzivatel === null || !$this->uzivatele->smaz((int) $uzivatel->ID)) {
$this->flashMessage('Tohoto uživatele nelze smazat.', 'error');
$this->redirect('this');
}
$this->flashMessage('Uživatel byl smazán.');
$this->redirect('this');
};
return $form;
});
}
private function muzeUpravit(int $id): ?Row
{
$uzivatel = $this->uzivatele->get($id);
if ($uzivatel === null || $this->uzivatele->jeChraneny($uzivatel)) {
return null;
}
return $uzivatel;
}
private function platneHeslo(Form $form, string $heslo): bool
{
if (mb_strlen($heslo) < 4) {
$form->addError('Heslo musí mít alespoň 4 znaky.');
return false;
}
if (mb_strlen($heslo) > 100) {
$form->addError('Heslo je příliš dlouhé.');
return false;
}
return true;
}
private function platneJmeno(string $jmeno): bool
{
return (bool) preg_match('/^[\p{L}\p{N}._-]{1,50}$/u', $jmeno);
}
private function jeAktivni(mixed $hodnota): bool
{
return $hodnota === true || $hodnota === 1 || $hodnota === '1';
}
}

View File

@ -0,0 +1,155 @@
{block content}
<div class="platno">
<header class="lista">
<div>
<h1 class="lista-nadpis">Administrace</h1>
<p class="lista-popis">Účty pro přihlášení do aplikace.</p>
</div>
<button type="button" class="tlacitko is-aktivni" data-bs-toggle="modal" data-bs-target="#modalNovy">Nový uživatel</button>
</header>
<section class="panel">
<div class="filtry">
<div class="filtry-skupiny" role="group" aria-label="Filtr uživatelů">
<a n:href="default, stav: aktivni" n:class="$stav === 'aktivni' ? 'tlacitko tlacitko-filtr is-aktivni' : 'tlacitko tlacitko-filtr'">Aktivní</a>
<a n:href="default, stav: neaktivni" n:class="$stav === 'neaktivni' ? 'tlacitko tlacitko-filtr is-aktivni' : 'tlacitko tlacitko-filtr'">Neaktivní</a>
</div>
</div>
<table class="tabulka">
<thead>
<tr>
<th scope="col">Uživatel</th>
<th scope="col">Stav</th>
<th scope="col">Akce</th>
</tr>
</thead>
<tbody>
{foreach $uzivatele as $uzivatel}
<tr>
<td data-popisek="Uživatel">{$uzivatel->jmeno}</td>
<td data-popisek="Stav">
<span n:if="$uzivatel->aktivni" class="stav-badge" style="background: #1b9e3e; color: #fff">Aktivní</span>
<span n:if="!$uzivatel->aktivni" class="stav-badge" style="background: #b0bec5; color: #1a1a1a">Neaktivní</span>
</td>
<td class="bunka-akce" data-popisek="Akce">
<div class="akce-radek" n:if="!$uzivatel->chraneny">
<button type="button" class="tlacitko" data-bs-toggle="modal" data-bs-target="#modalHeslo" data-id="{$uzivatel->id}" data-jmeno="{$uzivatel->jmeno}">Změnit heslo</button>
{form "stav-$uzivatel->id"}
<button n:name="send" class="tlacitko">{if $uzivatel->aktivni}Zneaktivnit{else}Zaktivnit{/if}</button>
{/form}
{form "smazat-$uzivatel->id"}
<button n:name="send" class="tlacitko tlacitko-nebezpeci" onclick="return confirm('Smazat uživatele {$uzivatel->jmeno}?')">Smazat</button>
{/form}
</div>
<span n:if="$uzivatel->chraneny" class="lista-popis">Nelze upravit</span>
</td>
</tr>
{/foreach}
</tbody>
</table>
<p class="prazdno" n:if="count($uzivatele) === 0">V tomto filtru není žádný uživatel.</p>
</section>
</div>
<div class="modal fade" id="modalNovy" tabindex="-1" aria-labelledby="modalNovyLabel" aria-hidden="true" data-show-errors="{if $presenter['novyForm']->hasErrors()}1{else}0{/if}">
<div class="modal-dialog">
<div class="modal-content">
<form n:name="novyForm">
<div class="modal-header">
<h5 class="modal-title" id="modalNovyLabel">Nový uživatel</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zavřít"></button>
</div>
<div class="modal-body">
<div class="alert alert-danger" n:if="$form->hasErrors()" n:foreach="$form->getErrors() as $error">{$error}</div>
<div class="mb-3">
<label n:name="jmeno" class="form-label">Uživatel</label>
<input n:name="jmeno" class="form-control" autocomplete="off" maxlength="50">
</div>
<div class="mb-3">
<label n:name="heslo" class="form-label">Heslo</label>
<input n:name="heslo" class="form-control" autocomplete="new-password">
</div>
<div>
<label n:name="hesloZnovu" class="form-label">Heslo znovu</label>
<input n:name="hesloZnovu" class="form-control" autocomplete="new-password">
</div>
</div>
<div class="modal-footer">
<button n:name="send" class="tlacitko is-aktivni">Založit</button>
<button type="button" class="tlacitko tlacitko-zrusit" data-bs-dismiss="modal">Zrušit</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" id="modalHeslo" tabindex="-1" aria-labelledby="modalHesloLabel" aria-hidden="true" data-show-errors="{if $presenter['hesloForm']->hasErrors()}1{else}0{/if}">
<div class="modal-dialog">
<div class="modal-content">
<form n:name="hesloForm">
<div class="modal-header">
<h5 class="modal-title" id="modalHesloLabel">Nové heslo</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zavřít"></button>
</div>
<div class="modal-body">
<div class="alert alert-danger" n:if="$form->hasErrors()" n:foreach="$form->getErrors() as $error">{$error}</div>
<p class="lista-popis" id="heslo-jmeno"></p>
<input n:name="id">
<input n:name="jmeno">
<div class="mb-3">
<label n:name="heslo" class="form-label">Nové heslo</label>
<input n:name="heslo" class="form-control" autocomplete="new-password">
</div>
<div>
<label n:name="hesloZnovu" class="form-label">Nové heslo znovu</label>
<input n:name="hesloZnovu" class="form-control" autocomplete="new-password">
</div>
</div>
<div class="modal-footer">
<button n:name="send" class="tlacitko is-aktivni">Uložit heslo</button>
<button type="button" class="tlacitko tlacitko-zrusit" data-bs-dismiss="modal">Zrušit</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.modal[data-show-errors="1"]').forEach(function (el) {
bootstrap.Modal.getOrCreateInstance(el).show();
});
var modalHeslo = document.getElementById('modalHeslo');
if (!modalHeslo) {
return;
}
modalHeslo.addEventListener('show.bs.modal', function (event) {
var btn = event.relatedTarget;
var form = modalHeslo.querySelector('form');
if (!btn || !btn.dataset.id || !form) {
return;
}
form.querySelector('[name="id"]').value = btn.dataset.id;
form.querySelector('[name="jmeno"]').value = btn.dataset.jmeno || '';
form.querySelector('[name="heslo"]').value = '';
form.querySelector('[name="hesloZnovu"]').value = '';
ukazJmenoHesla();
});
function ukazJmenoHesla() {
var form = modalHeslo.querySelector('form');
var jmeno = document.getElementById('heslo-jmeno');
if (!form || !jmeno) {
return;
}
jmeno.textContent = form.querySelector('[name="jmeno"]').value || '';
}
if (modalHeslo.getAttribute('data-show-errors') === '1') {
ukazJmenoHesla();
}
});
</script>

View File

@ -6,6 +6,7 @@ namespace App\Presentation\Nastaveni;
use App\Core\Konstanty; use App\Core\Konstanty;
use App\Core\MapovyPodklad; use App\Core\MapovyPodklad;
use App\Model\LoginFacade;
use Nette\Application\UI\Form; use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter; use Nette\Application\UI\Presenter;
use Nette\Utils\ArrayHash; use Nette\Utils\ArrayHash;
@ -13,6 +14,11 @@ use Nette\Utils\ArrayHash;
final class NastaveniPresenter extends Presenter final class NastaveniPresenter extends Presenter
{ {
public function __construct(
private LoginFacade $uzivatele,
) {
}
public function beforeRender(): void public function beforeRender(): void
{ {
$this->template->appVersion = Konstanty::verze; $this->template->appVersion = Konstanty::verze;
@ -52,4 +58,53 @@ final class NastaveniPresenter extends Presenter
{ {
return MapovyPodklad::platny($this->getHttpRequest()->getCookie(MapovyPodklad::COOKIE)); return MapovyPodklad::platny($this->getHttpRequest()->getCookie(MapovyPodklad::COOKIE));
} }
protected function createComponentHesloForm(): Form
{
$form = new Form;
$form->addPassword('soucasne', 'Současné heslo')
->setRequired('Zadejte současné heslo.');
$form->addPassword('heslo', 'Nové heslo')
->setRequired('Zadejte nové heslo.')
->setMaxLength(100);
$form->addPassword('hesloZnovu', 'Nové heslo znovu')
->setRequired('Zadejte nové heslo znovu.')
->addRule(Form::Equal, 'Hesla se neshodují.', $form['heslo']);
$form->addSubmit('send', 'Změnit heslo');
$form->addProtection('Platnost formuláře vypršela, odešlete ho znovu.');
$form->onSuccess[] = $this->hesloFormSucceeded(...);
return $form;
}
private function hesloFormSucceeded(Form $form, ArrayHash $values): void
{
$user = $this->getUser();
if (!$user->isLoggedIn()) {
$form->addError('Pro změnu hesla se přihlaste.');
return;
}
$id = (int) $user->getId();
if (!$this->uzivatele->overHeslo($id, (string) $values->soucasne)) {
$form->addError('Současné heslo nesouhlasí.');
return;
}
$heslo = (string) $values->heslo;
if (mb_strlen($heslo) < 4) {
$form->addError('Heslo musí mít alespoň 4 znaky.');
return;
}
if (mb_strlen($heslo) > 100) {
$form->addError('Heslo je příliš dlouhé.');
return;
}
if (!$this->uzivatele->zmenVlastniHeslo($id, $heslo)) {
$form->addError('Heslo se nepodařilo změnit.');
return;
}
$this->flashMessage('Heslo bylo změněno.');
$this->redirect('this');
}
} }

View File

@ -4,7 +4,8 @@
<header class="lista"> <header class="lista">
<div> <div>
<h1 class="lista-nadpis">Nastavení</h1> <h1 class="lista-nadpis">Nastavení</h1>
<p class="lista-popis">Platí jen pro toto zařízení.</p> <p n:if="$user->isLoggedIn()" class="lista-popis">Mapa na tomto zařízení a heslo účtu.</p>
<p n:if="!$user->isLoggedIn()" class="lista-popis">Platí jen pro toto zařízení.</p>
</div> </div>
</header> </header>
@ -22,4 +23,25 @@
<button n:name="send" class="tlacitko is-aktivni">Uložit</button> <button n:name="send" class="tlacitko is-aktivni">Uložit</button>
</div> </div>
</form> </form>
<form n:if="$user->isLoggedIn()" n:name="hesloForm" class="panel panel-uzky formular mt-3">
<h2>Heslo</h2>
<p class="lista-popis podklady-popis">Změna hesla účtu, kterým jste přihlášeni.</p>
<div class="alert alert-danger" n:if="$form->hasErrors()" n:foreach="$form->getErrors() as $error">{$error}</div>
<div class="mb-3">
<label n:name="soucasne" class="form-label">Současné heslo</label>
<input n:name="soucasne" class="form-control" autocomplete="current-password">
</div>
<div class="mb-3">
<label n:name="heslo" class="form-label">Nové heslo</label>
<input n:name="heslo" class="form-control" autocomplete="new-password">
</div>
<div class="mb-3">
<label n:name="hesloZnovu" class="form-label">Nové heslo znovu</label>
<input n:name="hesloZnovu" class="form-control" autocomplete="new-password">
</div>
<div class="formular-akce">
<button n:name="send" class="tlacitko is-aktivni">Změnit heslo</button>
</div>
</form>
</div> </div>

View File

@ -2,6 +2,11 @@ services:
- App\Core\RouterFactory::createRouter - App\Core\RouterFactory::createRouter
- App\Core\MujAutentifikator - App\Core\MujAutentifikator
decorator:
Nette\Application\Application:
setup:
- '$onPresenter[]' = [@App\Presentation\Accessory\AdminMenuService, pripoj]
search: search:
- in: %appDir% - in: %appDir%