-
Notifications
You must be signed in to change notification settings - Fork 3
Admin dashboard na správu novinek, souborů atd. #109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
b730608
ea3308a
a99a22d
460a6dd
968ab3a
1862105
7a3b4a0
3d8db11
de955a0
4e8eec0
3b9e215
807bac1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Components\Forms; | ||
|
|
||
| use Nette\Application\UI\Form; | ||
|
|
||
| class BaseForm extends Form | ||
| { | ||
| /** | ||
| * File upload form | ||
| */ | ||
|
|
||
| protected function createComponentFileUploadForm(): Form | ||
| { | ||
| $form = new Form(); | ||
| $form->addMultiUpload('file', 'Nahrát soubor:'); | ||
| $form->addSubmit('submit', 'Nahrát'); | ||
| /*$form->onSuccess[] = [$this, 'formSucceded'];*/ | ||
| $form->onRender[] = fn(Form $form) => $this->styleForm($form); | ||
| return $form; | ||
| } | ||
|
|
||
| /** | ||
| * Style form using bootstrap | ||
| * Code from: https://github.com/nette/forms/blob/master/examples/bootstrap5-rendering.php | ||
| */ | ||
| final protected function styleForm(Form $form): void | ||
| { | ||
| /** @var DefaultFormRenderer $renderer */ | ||
| $renderer = $form->getRenderer(); | ||
|
Check failure on line 32 in app/Components/Forms/BaseForm.php
|
||
| $renderer->wrappers['controls']['container'] = null; | ||
|
Check failure on line 33 in app/Components/Forms/BaseForm.php
|
||
| $renderer->wrappers['pair']['container'] = 'div class="mb-3 row"'; | ||
|
Check failure on line 34 in app/Components/Forms/BaseForm.php
|
||
| $renderer->wrappers['label']['container'] = 'div class="col-sm-3 col-form-label"'; | ||
|
Check failure on line 35 in app/Components/Forms/BaseForm.php
|
||
| $renderer->wrappers['control']['container'] = 'div class=col-sm-9'; | ||
|
Check failure on line 36 in app/Components/Forms/BaseForm.php
|
||
| $renderer->wrappers['control']['description'] = 'span class=form-text'; | ||
| $renderer->wrappers['control']['errorcontainer'] = 'span class=invalid-feedback'; | ||
| $renderer->wrappers['control']['.error'] = 'is-invalid'; | ||
| $renderer->wrappers['error']['container'] = 'div class="alert alert-danger"'; | ||
|
|
||
| foreach ($form->getControls() as $control) { | ||
| $type = $control->getOption('type'); | ||
| if ($type === 'button') { | ||
| $control->getControlPrototype()->addClass( | ||
| empty($usedPrimary) ? 'btn btn-primary' : 'btn btn-secondary' | ||
| ); | ||
| $usedPrimary = true; | ||
| } elseif (in_array($type, ['text', 'textarea', 'select', 'datetime', 'file'], true)) { | ||
| $control->getControlPrototype()->addClass('form-control'); | ||
| } elseif (in_array($type, ['checkbox', 'radio'], true)) { | ||
| $control->getControlPrototype()->addClass('form-check-input'); | ||
| $control->getContainerPrototype()->setName('div')->addClass('form-check'); | ||
| } elseif ($type === 'color') { | ||
| $control->getControlPrototype()->addClass('form-control form-control-color'); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Models\Authentication; | ||
|
|
||
| use Jumbojett\OpenIDConnectClient; | ||
|
|
||
| class Authenticator { | ||
| public function __construct( | ||
| readonly private string $providerUrl, | ||
| readonly private string $clientId, | ||
| readonly private string $clientSecret, | ||
| readonly public string $requiredGroup | ||
| ) {} | ||
|
|
||
| public function authenticateOIDC(): UserModel | ||
| { | ||
| $oidc = new OpenIDConnectClient($this->providerUrl, | ||
| $this->clientId, | ||
| $this->clientSecret); | ||
|
|
||
| $oidc->setRedirectURL('http://localhost:8080/admin'); // TODO smazat před použitím | ||
|
|
||
| $oidc->authenticate(); | ||
|
|
||
| $personId = (int) $oidc->requestUserInfo('person_id'); | ||
| $name = $oidc->requestUserInfo('name'); | ||
| $groups = $oidc->requestUserInfo('groups'); | ||
|
|
||
| return new UserModel($personId, $name, $groups); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Models\Authentication; | ||
|
|
||
| use Nette\Security\IIdentity; | ||
|
|
||
| class UserModel implements IIdentity | ||
| { | ||
| public function __construct( | ||
| readonly public int $id, | ||
| readonly public string $name, | ||
| readonly public array $groups | ||
| ) {} | ||
|
|
||
| public function getId(): int | ||
| { | ||
| return $this->id; | ||
| } | ||
|
|
||
| public function getRoles(): array | ||
| { | ||
| return []; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /* Admin layout styles */ | ||
| body { | ||
| background-color: $panel-color-light; | ||
| } | ||
|
|
||
| /* Files */ | ||
| .file-editor-container { | ||
| max-width: 50rem; | ||
| width: fit-content; | ||
| } | ||
|
|
||
| .file-editor{ | ||
| width: 100%; | ||
| padding: 0.35rem; | ||
| } | ||
|
|
||
| /* Files and media event selector */ | ||
| .event-selector { | ||
| .dropdown-toggle { | ||
| min-width: 16rem; | ||
| max-width: 100%; | ||
| text-align: left; | ||
| white-space: wrap; | ||
| } | ||
|
|
||
| .dropdown-menu { | ||
| min-width: 16rem; | ||
| max-width: 100%; | ||
| max-height: 14rem; | ||
| overflow: auto; | ||
| text-align: left; | ||
| } | ||
| } | ||
|
|
||
| .file-upload-container { | ||
| max-width: 50rem; | ||
| width: fit-content; | ||
| } | ||
|
|
||
| .file-upload { | ||
| width: 100%; | ||
| padding: 0.35rem; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Modules\Vyfuk\DefaultModule; | ||
|
|
||
| use Fykosak\Utils\UI\Navigation\NavItem; | ||
| use Fykosak\Utils\UI\PageTitle; | ||
| use App\Models\Authentication\Authenticator; | ||
| use App\Models\Authentication\UserModel; | ||
| use Nette\Application\ForbiddenRequestException; | ||
| use App\Models\Downloader\EventService; | ||
|
|
||
| use Nette\Utils\Finder; | ||
|
|
||
| class AdminPresenter extends BasePresenter | ||
| { | ||
| protected Authenticator $authenticator; | ||
| protected EventService $eventService; | ||
|
|
||
| /** @persistent */ | ||
| public ?int $eventId = null; | ||
|
|
||
| public function injectService(Authenticator $authenticator, EventService $eventService): void | ||
| { | ||
| $this->authenticator = $authenticator; | ||
| $this->eventService = $eventService; | ||
| } | ||
|
|
||
| public function getMediaDir(): string | ||
| { | ||
| $mediaDir = $this->getContext()->getParameters()['mediaDir']; | ||
| return $mediaDir; | ||
| } | ||
|
|
||
| public function checkRequirements($element): void | ||
| { | ||
| parent::checkRequirements($element); | ||
|
|
||
| if (!$this->getUser()->isLoggedIn()) { | ||
| $user = $this->authenticator->authenticateOIDC(); | ||
| $this->getUser()->login($user); | ||
| } | ||
|
|
||
| if (!in_array($this->authenticator->requiredGroup, $this->getLoggedUser()->groups)) { | ||
| throw new ForbiddenRequestException(); | ||
| } | ||
| } | ||
|
|
||
| public function getLoggedUser(): ?UserModel | ||
| { | ||
| return $this->getUser()->getIdentity(); | ||
| } | ||
|
|
||
| public function actionLogout(): void | ||
| { | ||
| $this->getUser()->logout(); | ||
| $this->redirect(':Default:Admin:page'); | ||
| } | ||
|
|
||
| public function renderMedia(): void | ||
| { | ||
| $this->template->events = array_reverse($this->eventService->getEvents([10, 11, 12, 18])); | ||
|
|
||
| $event = $this->eventId ? $this->eventService->getEvent($this->eventId) : $this->eventService->getNewest([10, 11, 12, 18]); | ||
| $this->template->selectedEvent = $event; | ||
|
|
||
| $this->template->media = $this->getMedia($event->eventId); | ||
| } | ||
|
|
||
| public function getMedia($eventId): array | ||
| { | ||
| $mediaDir = $this->getMediaDir(); | ||
| $media = []; | ||
|
|
||
| try { | ||
| $iterator = Finder::findFiles('*.jpg', '*.jpeg', '*.png', '*.JPG', '*.gif', '*.bmp', '*.webp')->in($mediaDir . '/photos/event/' . $eventId)->getIterator(); | ||
| } catch (\Exception $e) { | ||
| return []; | ||
| } | ||
|
|
||
| foreach ($iterator as $file) { | ||
| $name = pathinfo($file->getPathname())['filename']; | ||
| $media[] = $name; | ||
| }; | ||
|
|
||
| return $media; | ||
| } | ||
|
|
||
| public function renderFiles(): void | ||
| { | ||
| $this->template->events = array_reverse($this->eventService->getEvents([10, 11, 12, 18])); | ||
|
|
||
| $event = $this->eventId ? $this->eventService->getEvent($this->eventId) : $this->eventService->getNewest([10, 11, 12, 18]); | ||
| $this->template->selectedEvent = $event; | ||
|
|
||
| $this->template->files = $this->getFiles($event->eventId); | ||
| } | ||
|
|
||
| public function getFiles($eventId): array | ||
| { | ||
| $mediaDir = $this->getMediaDir(); | ||
| $files = []; | ||
|
|
||
| try { | ||
| $iterator = Finder::findFiles('*.pdf')->in($mediaDir . '/download/event/' . $eventId)->getIterator(); | ||
| } catch (\Exception $e) { | ||
| return []; | ||
| } | ||
|
|
||
| foreach ($iterator as $file) { | ||
| $name = $file->getBasename('.pdf'); | ||
| $path = '/media' . substr($file->getPathname(), strlen($mediaDir)); | ||
| $files[] = [ | ||
| 'path' => $path, | ||
| 'name' => $name, | ||
| ]; | ||
| }; | ||
|
|
||
| return $files; | ||
| } | ||
|
|
||
| public function render(): void | ||
| { | ||
| // Nothing to do here yet | ||
| } | ||
|
|
||
| /** | ||
| * @return NavItem[] | ||
| */ | ||
| protected function getNavItems(): array | ||
| { | ||
| $items = []; | ||
|
|
||
| $items[] = new NavItem( | ||
| new PageTitle('Správa novinek', 'fa-solid fa-newspaper'), | ||
| ':Default:Admin:news' | ||
| ); | ||
|
|
||
| $items[] = new NavItem( | ||
| new PageTitle('Správa souborů', 'fa-solid fa-file-pen'), | ||
| ':Default:Admin:files' | ||
| ); | ||
|
|
||
| $items[] = new NavItem( | ||
| new PageTitle('Správa fotek', 'fa-solid fa-images'), | ||
| ':Default:Admin:media' | ||
| ); | ||
|
|
||
| $items[] = new NavItem( | ||
| new PageTitle(sprintf('%s (#%d)', $this->getLoggedUser()->name, $this->getLoggedUser()->id), 'fa-solid fa-user-gear'), | ||
| ':Default:Admin:default' | ||
| ); | ||
|
|
||
| $items[] = new NavItem( | ||
| new PageTitle('Odhlásit se', 'fa-solid fa-arrow-right-from-bracket'), | ||
| ':Default:Admin:logout' | ||
| ); | ||
|
|
||
| return $items; | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tento oficialny návod od nette je fakt zlý.
Prekopírval som do nette-fks-utils FormControl z FKSDB. Teda by som to nahradil tým.
https://github.com/fykosak/nette-fks-utils/tree/dev-form-control