diff --git a/backend/bin/console b/backend/bin/console old mode 100644 new mode 100755 diff --git a/backend/config/routes.php b/backend/config/routes.php index b6348ab..1276299 100644 --- a/backend/config/routes.php +++ b/backend/config/routes.php @@ -37,6 +37,59 @@ ->setName('dashboard'); $group->group('/admin', function (RouteCollectorProxy $group) { + $group->group('/groups', function (RouteCollectorProxy $group) { + $group->get( + '', + App\Controller\Dashboard\Admin\GroupsController::class . ':listAction' + )->setName('dashboard:admin:groups'); + + $group->map( + ['GET', 'POST'], + '/create', + App\Controller\Dashboard\Admin\GroupsController::class . ':createAction' + )->setName('dashboard:admin:groups:create'); + + $group->map( + ['GET', 'POST'], + '/edit[/{id}]', + App\Controller\Dashboard\Admin\GroupsController::class . ':editAction' + )->setName('dashboard:admin:groups:edit'); + + $group->get( + '/delete[/{id}]', + App\Controller\Dashboard\Admin\GroupsController::class . ':deleteAction' + )->setName('dashboard:admin:groups:delete'); + }); + + $group->group('/poster_types', function (RouteCollectorProxy $group) { + $group->get( + '', + App\Controller\Dashboard\Admin\PosterTypesController::class . ':listAction' + )->setName('dashboard:admin:poster_types'); + + $group->map( + ['GET', 'POST'], + '/create', + App\Controller\Dashboard\Admin\PosterTypesController::class . ':createAction' + )->setName('dashboard:admin:poster_types:create'); + + $group->map( + ['GET', 'POST'], + '/edit[/{id}]', + App\Controller\Dashboard\Admin\PosterTypesController::class . ':editAction' + )->setName('dashboard:admin:poster_types:edit'); + + $group->get( + '/delete[/{id}]', + App\Controller\Dashboard\Admin\PosterTypesController::class . ':deleteAction' + )->setName('dashboard:admin:poster_types:delete'); + }); + + $group->get( + '/users', + App\Controller\Dashboard\Admin\UsersAction::class + )->setName('dashboard:admin:users'); + $group->group('/worlds', function (RouteCollectorProxy $group) { $group->get( '', @@ -54,11 +107,6 @@ App\Controller\Dashboard\Admin\WorldsController::class . ':deleteAction' )->setName('dashboard:admin:worlds:delete'); }); - - $group->get( - '/users', - App\Controller\Dashboard\Admin\UsersAction::class - )->setName('dashboard:admin:users'); })->add(new App\Middleware\Auth\RequireAdmin()); $group->map( diff --git a/backend/src/Controller/Dashboard/AbstractCrudController.php b/backend/src/Controller/Dashboard/AbstractCrudController.php new file mode 100644 index 0000000..2f2e195 --- /dev/null +++ b/backend/src/Controller/Dashboard/AbstractCrudController.php @@ -0,0 +1,175 @@ +db->createQueryBuilder() + ->select('*') + ->from($this->tableName) + ->fetchAllAssociative(); + + return $request->getView()->renderToResponse( + $response, + $this->listTemplateName, + [ + 'records' => $records, + ] + ); + } + + public function createAction( + ServerRequest $request, + Response $response, + array $params + ): ResponseInterface { + $row = []; + $error = null; + + if ($request->isPost()) { + try { + $row = $this->modifyFromRequest($request); + + $this->db->insert($this->tableName, $row); + + $request->getFlash()->success(sprintf('%s created.', $this->itemName)); + + return $response->withRedirect( + $request->getRouter()->urlFor($this->listRouteName) + ); + } catch (\Throwable $e) { + $error = $e->getMessage(); + } + } + + return $request->getView()->renderToResponse( + $response, + $this->editTemplateName, + [ + 'isEditMode' => false, + 'row' => $row, + 'error' => $error, + ] + ); + } + + public function editAction( + ServerRequest $request, + Response $response, + array $params + ): ResponseInterface { + $id = $this->requireId($request, $params); + + $row = $this->getRecord($id); + unset($row[$this->idField]); + + $error = null; + + if ($request->isPost()) { + try { + $row = $this->modifyFromRequest( + $request, + $row, + true + ); + + $this->db->update($this->tableName, $row, [$this->idField => $id]); + + $request->getFlash()->success(sprintf('%s updated.', $this->itemName)); + + return $response->withRedirect( + $request->getRouter()->urlFor($this->listRouteName) + ); + } catch (\Throwable $e) { + $error = $e->getMessage(); + } + } + + return $request->getView()->renderToResponse( + $response, + $this->editTemplateName, + [ + 'isEditMode' => true, + 'row' => $row, + 'error' => $error, + ] + ); + } + + protected function requireId( + ServerRequest $request, + array $params + ): int|string { + $id = $params[$this->idField] ?? $request->getParam($this->idField); + if (empty($id)) { + throw new \InvalidArgumentException('ID is required.'); + } + + return $id; + } + + protected function getRecord( + int|string $id + ): array { + $row = $this->db->createQueryBuilder() + ->select('*') + ->from($this->tableName) + ->where($this->db->createExpressionBuilder()->eq($this->idField, ':id')) + ->setParameter('id', $id) + ->fetchAssociative(); + + if ($row === false) { + throw new \InvalidArgumentException(sprintf('%s not found!', $this->itemName)); + } + + return $row; + } + + abstract protected function modifyFromRequest( + ServerRequest $request, + array $row = [], + bool $isEditMode = false + ): array; + + public function deleteAction( + ServerRequest $request, + Response $response, + array $params + ): ResponseInterface { + $id = $this->requireId($request, $params); + + $this->db->delete( + $this->tableName, + [ + $this->idField => $id, + ] + ); + + $request->getFlash()->success(sprintf('%s deleted.', $this->itemName)); + + return $response->withRedirect( + $request->getRouter()->urlFor($this->listRouteName) + ); + } +} diff --git a/backend/src/Controller/Dashboard/Admin/GroupsController.php b/backend/src/Controller/Dashboard/Admin/GroupsController.php new file mode 100644 index 0000000..0c1f9dd --- /dev/null +++ b/backend/src/Controller/Dashboard/Admin/GroupsController.php @@ -0,0 +1,43 @@ +getParsedBody(); + + if (!$isEditMode) { + $row['id'] = $postData['id'] ?? null; + if (empty($row['id'])) { + throw new \InvalidArgumentException('ID is required.'); + } + } + + $row['name'] = $postData['name'] ?? null; + + if (empty($row['name'])) { + throw new \InvalidArgumentException('Name is required.'); + } + + return $row; + } +} diff --git a/backend/src/Controller/Dashboard/Admin/PosterTypesController.php b/backend/src/Controller/Dashboard/Admin/PosterTypesController.php new file mode 100644 index 0000000..afbaeb4 --- /dev/null +++ b/backend/src/Controller/Dashboard/Admin/PosterTypesController.php @@ -0,0 +1,43 @@ +getParsedBody(); + + if (!$isEditMode) { + $row['id'] = $postData['id'] ?? null; + if (empty($row['id'])) { + throw new \InvalidArgumentException('ID is required.'); + } + } + + $row['name'] = $postData['name'] ?? null; + + if (empty($row['name'])) { + throw new \InvalidArgumentException('Name is required.'); + } + + return $row; + } +} diff --git a/backend/src/Controller/Dashboard/Admin/UsersAction.php b/backend/src/Controller/Dashboard/Admin/UsersAction.php index 98717e1..7ca0d57 100644 --- a/backend/src/Controller/Dashboard/Admin/UsersAction.php +++ b/backend/src/Controller/Dashboard/Admin/UsersAction.php @@ -31,7 +31,7 @@ public function __invoke( $response, 'dashboard/admin/users', [ - 'users' => $users, + 'records' => $users, ] ); } diff --git a/backend/src/Controller/Dashboard/Admin/WorldsController.php b/backend/src/Controller/Dashboard/Admin/WorldsController.php index f2af8db..77c17be 100644 --- a/backend/src/Controller/Dashboard/Admin/WorldsController.php +++ b/backend/src/Controller/Dashboard/Admin/WorldsController.php @@ -2,118 +2,79 @@ namespace App\Controller\Dashboard\Admin; -use App\Exception\NotFoundException; +use App\Controller\Dashboard\AbstractCrudController; use App\Http\Response; use App\Http\ServerRequest; use App\Media; use App\Service\VrcApi; -use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use GuzzleHttp\Client; use Intervention\Image\ImageManager; -use League\Flysystem\UnableToDeleteFile; use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\UploadedFileInterface; -final readonly class WorldsController +final class WorldsController extends AbstractCrudController { private Client $vrcApiClient; public function __construct( - private Connection $db, private ImageManager $imageManager, private Client $httpClient, + Connection $db, VrcApi $vrcApi ) { + parent::__construct( + $db, + 'Spotlighted World', + 'dashboard:admin:worlds', + 'dashboard/admin/worlds/list', + 'dashboard/admin/worlds/create', + 'web_worlds' + ); + $this->vrcApiClient = $vrcApi->getHttpClient(); } - public function listAction( - ServerRequest $request, - Response $response, - array $params - ): ResponseInterface { - $worlds = $this->db->fetchAllAssociative( - <<<'SQL' - SELECT w.* - FROM web_worlds AS w - ORDER BY id DESC - SQL - ); - - return $request->getView()->renderToResponse( - $response, - 'dashboard/admin/worlds/list', - [ - 'worlds' => $worlds, - ] - ); + public function editAction(ServerRequest $request, Response $response, array $params): ResponseInterface + { + throw new \RuntimeException('Not available!'); } - public function createAction( - ServerRequest $request, - Response $response, - array $params - ): ResponseInterface { + protected function modifyFromRequest(ServerRequest $request, array $row = [], bool $isEditMode = false): array + { $currentUser = $request->getCurrentUser(); assert($currentUser !== null); - $error = null; - - if ($request->isPost()) { - try { - $worldId = $request->getParam('id'); - if (empty($worldId)) { - throw new \InvalidArgumentException('World ID not specified.'); - } - - $worldId = VrcApi::parseWorldId($worldId); - - // Fetch world info from the VRC API. - $worldInfo = VrcApi::processResponse( - $this->vrcApiClient->get(sprintf('worlds/%s', $worldId)) - ); - - // Pull the world image - $imageUrl = $worldInfo['imageUrl']; - $imageData = $this->httpClient->get($imageUrl)->getBody()->getContents(); - - $imageRelativePath = Media::worldPath($worldId . '.png'); - - $image = $this->imageManager->read($imageData); - - $fs = Media::getFilesystem(); - $fs->write($imageRelativePath, $image->encodeByPath($imageRelativePath)->toString()); - - // Add the DB record - $this->db->insert( - 'web_worlds', - [ - 'title' => $worldInfo['name'], - 'creator' => $currentUser['id'], - 'image' => $imageRelativePath, - 'description' => $worldInfo['description'], - 'world_id' => $worldId, - 'world_creator' => $worldInfo['authorName'], - ] - ); - - $request->getFlash()->success('World successfully imported!'); - return $response->withRedirect( - $request->getRouter()->urlFor('dashboard:admin:worlds') - ); - } catch (\Throwable $e) { - $error = $e->getMessage(); - } + $worldId = $request->getParam('id'); + if (empty($worldId)) { + throw new \InvalidArgumentException('World ID not specified.'); } - return $request->getView()->renderToResponse( - $response, - 'dashboard/admin/worlds/create', - [ - 'error' => $error, - ] + $worldId = VrcApi::parseWorldId($worldId); + + // Fetch world info from the VRC API. + $worldInfo = VrcApi::processResponse( + $this->vrcApiClient->get(sprintf('worlds/%s', $worldId)) ); + + // Pull the world image + $imageUrl = $worldInfo['imageUrl']; + $imageData = $this->httpClient->get($imageUrl)->getBody()->getContents(); + + $imageRelativePath = Media::worldPath($worldId . '.png'); + + $image = $this->imageManager->read($imageData); + + $fs = Media::getFilesystem(); + $fs->write($imageRelativePath, $image->encodeByPath($imageRelativePath)->toString()); + + return [ + 'title' => $worldInfo['name'], + 'creator' => $currentUser['id'], + 'image' => $imageRelativePath, + 'description' => $worldInfo['description'], + 'world_id' => $worldId, + 'world_creator' => $worldInfo['authorName'], + ]; } public function deleteAction( @@ -121,36 +82,23 @@ public function deleteAction( Response $response, array $params ): ResponseInterface { - $id = $params['id']; - - $world = $this->db->fetchAssociative( - <<<'SQL' - SELECT id, image - FROM web_worlds - WHERE id = :id - SQL, - [ - 'id' => $id, - ] - ); + $id = $this->requireId($request, $params); - if ($world === false) { - throw NotFoundException::world($request); - } + $world = $this->getRecord($id); $fs = Media::getFilesystem(); $fs->delete($world['image']); $this->db->delete( - 'web_worlds', + $this->tableName, [ - 'id' => $world['id'], + $this->idField => $world[$this->idField], ] ); - $request->getFlash()->success('World removed.'); + $request->getFlash()->success(sprintf('%s removed.', $this->itemName)); return $response->withRedirect( - $request->getRouter()->urlFor('dashboard:admin:worlds') + $request->getRouter()->urlFor($this->listRouteName) ); } } diff --git a/backend/src/Controller/Dashboard/ShortUrlsController.php b/backend/src/Controller/Dashboard/ShortUrlsController.php index fb9a9a0..478afb4 100644 --- a/backend/src/Controller/Dashboard/ShortUrlsController.php +++ b/backend/src/Controller/Dashboard/ShortUrlsController.php @@ -2,181 +2,39 @@ namespace App\Controller\Dashboard; -use App\Http\Response; use App\Http\ServerRequest; use Doctrine\DBAL\Connection; -use Psr\Http\Message\ResponseInterface; -final readonly class ShortUrlsController +final class ShortUrlsController extends AbstractCrudController { public function __construct( - private Connection $db + Connection $db ) { - } - - public function listAction( - ServerRequest $request, - Response $response, - array $params - ): ResponseInterface { - $shortUrls = $this->db->fetchAllAssociative( - <<<'SQL' - SELECT * - FROM web_short_urls - ORDER BY short_url ASC - SQL - ); - - return $request->getView()->renderToResponse( - $response, + parent::__construct( + $db, + 'Short URL', + 'dashboard:short_urls', 'dashboard/short_urls/list', - [ - 'shortUrls' => $shortUrls, - ] + 'dashboard/short_urls/edit', + 'web_short_urls' ); } - public function createAction( - ServerRequest $request, - Response $response, - array $params - ): ResponseInterface { + protected function modifyFromRequest(ServerRequest $request, array $row = [], bool $isEditMode = false): array + { $currentUser = $request->getCurrentUser(); assert($currentUser !== null); - $row = []; - $error = null; - - if ($request->isPost()) { - try { - $postData = $request->getParsedBody(); - $row['short_url'] = trim($postData['short_url'] ?? '', '/'); - $row['long_url'] = $postData['long_url'] ?? null; - - if (empty($row['short_url']) || empty($row['long_url'])) { - throw new \InvalidArgumentException('Short and Long URL are required.'); - } - - $this->db->insert( - 'web_short_urls', - [ - 'short_url' => $row['short_url'], - 'long_url' => $row['long_url'], - 'creator' => $currentUser['id'], - ] - ); - - $request->getFlash()->success('Short URL created.'); + $row['creator'] = $currentUser['id']; - return $response->withRedirect( - $request->getRouter()->urlFor('dashboard:short_urls') - ); - } catch (\Throwable $e) { - $error = $e->getMessage(); - } - } - - return $request->getView()->renderToResponse( - $response, - 'dashboard/short_urls/edit', - [ - 'isEditMode' => false, - 'row' => $row, - 'error' => $error, - ] - ); - } - - public function editAction( - ServerRequest $request, - Response $response, - array $params - ): ResponseInterface { - $id = $params['id'] ?? $request->getParam('id'); - if (empty($id)) { - throw new \InvalidArgumentException('ID is required.'); - } - - $row = $this->db->fetchAssociative( - <<<'SQL' - SELECT * - FROM web_short_urls - WHERE id = :id - SQL, - [ - 'id' => $id, - ] - ); + $postData = $request->getParsedBody(); + $row['short_url'] = trim($postData['short_url'] ?? '', '/'); + $row['long_url'] = $postData['long_url'] ?? null; - if ($row === false) { - throw new \InvalidArgumentException('Record not found!'); + if (empty($row['short_url']) || empty($row['long_url'])) { + throw new \InvalidArgumentException('Short and Long URL are required.'); } - $error = null; - - if ($request->isPost()) { - try { - $postData = $request->getParsedBody(); - $row['short_url'] = trim($postData['short_url'] ?? '', '/'); - $row['long_url'] = $postData['long_url'] ?? null; - - if (empty($row['short_url']) || empty($row['long_url'])) { - throw new \InvalidArgumentException('Short and Long URL are required.'); - } - - $this->db->update( - 'web_short_urls', - [ - 'short_url' => $row['short_url'], - 'long_url' => $row['long_url'], - ], - [ - 'id' => $row['id'], - ] - ); - - $request->getFlash()->success('Short URL updated.'); - - return $response->withRedirect( - $request->getRouter()->urlFor('dashboard:short_urls') - ); - } catch (\Throwable $e) { - $error = $e->getMessage(); - } - } - - return $request->getView()->renderToResponse( - $response, - 'dashboard/short_urls/edit', - [ - 'isEditMode' => true, - 'row' => $row, - 'error' => $error, - ] - ); - } - - public function deleteAction( - ServerRequest $request, - Response $response, - array $params - ): ResponseInterface { - $id = $params['id'] ?? $request->getParam('id'); - if (empty($id)) { - throw new \InvalidArgumentException('ID is required.'); - } - - $this->db->delete( - 'web_short_urls', - [ - 'id' => $id, - ] - ); - - $request->getFlash()->success('Short URL deleted.'); - - return $response->withRedirect( - $request->getRouter()->urlFor('dashboard:short_urls') - ); + return $row; } } diff --git a/backend/src/Controller/TalentAction.php b/backend/src/Controller/TalentAction.php index 1f34902..9de5792 100644 --- a/backend/src/Controller/TalentAction.php +++ b/backend/src/Controller/TalentAction.php @@ -26,6 +26,7 @@ public function __invoke( JOIN web_users u ON s.creator = u.id WHERE u.banned != 1 GROUP BY skill + ORDER BY occurrences DESC SQL ); diff --git a/backend/src/Service/VrcApi.php b/backend/src/Service/VrcApi.php index a6be4eb..3cf7812 100644 --- a/backend/src/Service/VrcApi.php +++ b/backend/src/Service/VrcApi.php @@ -104,8 +104,9 @@ public static function parseWorldId(string $worldId): string // URLs in the form of: // https://vrchat.com/home/launch?worldId=wrld_4cf554b4-430c-4f8f-b53e-1f294eed230b&... $queryParams = Query::parse($uri->getQuery()); - if (!empty($queryParams['worldid'])) { - return trim($queryParams['worldid']); + + if (!empty($queryParams['worldId'])) { + return trim($queryParams['worldId']); } } diff --git a/backend/templates/account/recover.twig b/backend/templates/account/recover.twig index ddebb6c..3922623 100644 --- a/backend/templates/account/recover.twig +++ b/backend/templates/account/recover.twig @@ -3,6 +3,8 @@ {% block head %} {{ parent() }} + {{ renderAssets('frontend/passwordStrength.js') }} + +{% endblock %} + +{% block content %} + {{ breadcrumbs.body( + { + 'dashboard': 'My Dashboard' + }, + 'Manage Groups' + ) }} + +

Manage Groups

+ +
+

View Groups

+
+ + + + + + + + + + + + {% for row in records %} + + + + + + {% endfor %} + +
Group IDGroup NameActions
{{ row.id }}{{ row.name }} + + Edit + + + Delete + +
+
+
+{% endblock %} diff --git a/backend/templates/dashboard/admin/poster_types/edit.twig b/backend/templates/dashboard/admin/poster_types/edit.twig new file mode 100644 index 0000000..1bd1ea0 --- /dev/null +++ b/backend/templates/dashboard/admin/poster_types/edit.twig @@ -0,0 +1,63 @@ +{% extends "layouts/dashboard.twig" %} + +{% import "macros/breadcrumbs.twig" as breadcrumbs %} + +{% block content %} + {{ breadcrumbs.body( + { + 'dashboard': 'My Dashboard', + 'dashboard:admin:poster_types': 'Manage Poster Types' + }, + (isEditMode) ? 'Edit Poster Type' : 'Create Poster Type' + ) }} + +

Manage Poster Types

+ +
+

+ {% if isEditMode %} + Edit Poster Type + {% else %} + Create Poster Type + {% endif %} +

+
+ {% if error %} +
+ Error: {{ error }} +
+ {% endif %} + +
+
+
+ {% if not isEditMode %} +
+ + + +

+ The programmatic ID of the poster type. Should be only letters, numbers and + underscores (i.e. my_poster_type_1). +

+
+ {% endif %} + +
+ + +
+
+
+ +
+ +
+
+
+
+{% endblock %} diff --git a/backend/templates/dashboard/admin/poster_types/list.twig b/backend/templates/dashboard/admin/poster_types/list.twig new file mode 100644 index 0000000..630256d --- /dev/null +++ b/backend/templates/dashboard/admin/poster_types/list.twig @@ -0,0 +1,68 @@ +{% extends "layouts/dashboard.twig" %} + +{% import "macros/breadcrumbs.twig" as breadcrumbs %} + +{% block head %} + {{ parent() }} + + {{ renderAssets('frontend/dataTable.js') }} + + +{% endblock %} + +{% block content %} + {{ breadcrumbs.body( + { + 'dashboard': 'My Dashboard' + }, + 'Manage Poster Types' + ) }} + +

Manage Poster Types

+ +
+

View Poster Types

+
+ + + + + + + + + + + + {% for row in records %} + + + + + + {% endfor %} + +
Group IDGroup NameActions
{{ row.id }}{{ row.name }} + + Edit + + + Delete + +
+
+
+{% endblock %} diff --git a/backend/templates/dashboard/admin/users.twig b/backend/templates/dashboard/admin/users.twig index 02ba827..8f8da6b 100644 --- a/backend/templates/dashboard/admin/users.twig +++ b/backend/templates/dashboard/admin/users.twig @@ -5,38 +5,11 @@ {% block head %} {{ parent() }} - {% endblock %} @@ -53,10 +26,6 @@
- - -
- @@ -67,50 +36,50 @@ - + - {% for row in users %} - - - - {% if row.is_admin is same as 1 %} - - {% else %} - - {% endif %} - {% if row.is_mod is same as 1 %} - - {% else %} - - {% endif %} - {% if row.is_team is same as 1 %} - - {% else %} - - {% endif %} - {% if row.is_dj is same as 1 %} - - {% else %} - - {% endif %} - {% if row.banned is same as 1 %} - - {% else %} - - {% endif %} - - + {% for row in records %} + + + + {% if row.is_admin is same as 1 %} + + {% else %} + + {% endif %} + {% if row.is_mod is same as 1 %} + + {% else %} + + {% endif %} + {% if row.is_team is same as 1 %} + + {% else %} + + {% endif %} + {% if row.is_dj is same as 1 %} + + {% else %} + + {% endif %} + {% if row.banned is same as 1 %} + + {% else %} + + {% endif %} + + {% endfor %}
Team? DJ? Banned?ActionsActions
{{ row.id }}YesNoYesNoYesNoYesNoYesNo - - Edit - - - View - -
{{ row.id }}YesNoYesNoYesNoYesNoYesNo + + Edit + + + View + +
diff --git a/backend/templates/dashboard/admin/worlds/list.twig b/backend/templates/dashboard/admin/worlds/list.twig index f658e0a..e3f6579 100644 --- a/backend/templates/dashboard/admin/worlds/list.twig +++ b/backend/templates/dashboard/admin/worlds/list.twig @@ -5,38 +5,11 @@ {% block head %} {{ parent() }} - {% endblock %} @@ -53,33 +26,30 @@
-
-
- -
- + - +
- + - + - {% for row in worlds %} + {% for row in records %} -
ImageImage World NameActionsActions
- + World Image {{ row.title }} + View diff --git a/backend/templates/dashboard/index.twig b/backend/templates/dashboard/index.twig index 2fef771..51546ea 100644 --- a/backend/templates/dashboard/index.twig +++ b/backend/templates/dashboard/index.twig @@ -1,96 +1,104 @@ {% extends "layouts/site.twig" %} {% block content %} -
-

My Dashboard

+
+

My Dashboard

-
-
-
-
-
-
-
-

{{ user.username }}

-
- {% set pronouns = user.pronouns|trim %} - {% if pronouns is not empty %} - +
+
+
+
+
+
+
+

{{ user.username }}

+
+ {% set pronouns = user.pronouns|trim %} + {% if pronouns is not empty %} + {{ pronouns }} - {% endif %} + {% endif %} - {% set title = user.title|trim|default('Team Member') %} - {% if user.isTeam() %} - + {% set title = user.title|trim|default('Team Member') %} + {% if user.isTeam() %} + {{ title }} - {% endif %} + {% endif %} +
-
-
-
-
-
-
-
-

Site Features

-
- - Poster Network - - - My Skills - - - {% if user.isMod() %} +
+
+
+
+

Site Features

+
-
- {% if user.isAdmin() %} -
-
-

Administration

+ {% if user.isAdmin() %} + + {% endif %}
- {% endif %}
-
{% endblock %} diff --git a/backend/templates/dashboard/password.twig b/backend/templates/dashboard/password.twig index 8941a80..2d0d928 100644 --- a/backend/templates/dashboard/password.twig +++ b/backend/templates/dashboard/password.twig @@ -5,6 +5,8 @@ {% block head %} {{ parent() }} + {{ renderAssets('frontend/passwordStrength.js') }} + +{% endblock %} + {% block content %} {{ breadcrumbs.body( { @@ -22,32 +34,32 @@
- +
- + - {% for row in shortUrls %} - - - - - - + {% for row in records %} + + + + + + {% endfor %}
Short URL Long URL ViewsActionsActions
{{ row.short_url }}{{ row.long_url }}{{ row.views }} - - Edit - - - Delete - -
{{ row.short_url }}{{ row.long_url }}{{ row.views }} + + Edit + + + Delete + +
diff --git a/backend/templates/events/defective.twig b/backend/templates/events/defective.twig index a204bf0..99593f7 100644 --- a/backend/templates/events/defective.twig +++ b/backend/templates/events/defective.twig @@ -3,6 +3,8 @@ {% block head %} {{ parent() }} + {{ renderAssets('frontend/dateTime.js') }} +