From 701b448cd6757c5d470ec6a99e4355ea591afe32 Mon Sep 17 00:00:00 2001 From: ghost Date: Sat, 4 Nov 2023 06:26:01 +0200 Subject: [PATCH] init torrent categories feature #26 --- .env | 3 + config/services.yaml | 1 + migrations/Version20231103235504.php | 37 ++ src/Controller/TorrentController.php | 463 ++++++++++++++++-- src/Controller/UserController.php | 77 ++- src/Entity/Torrent.php | 15 + src/Entity/TorrentCategories.php | 103 ++++ src/Entity/User.php | 15 + .../TorrentCategoriesRepository.php | 23 + src/Repository/TorrentRepository.php | 41 +- src/Service/TorrentService.php | 175 ++++++- .../default/torrent/edit/categories.html.twig | 109 +++++ .../default/torrent/edit/locales.html.twig | 2 +- templates/default/torrent/info.html.twig | 20 +- templates/default/torrent/submit.html.twig | 37 +- templates/default/user/info.html.twig | 10 +- templates/default/user/settings.html.twig | 27 +- translations/messages+intl-icu.en.xlf | 32 ++ 18 files changed, 1113 insertions(+), 77 deletions(-) create mode 100644 migrations/Version20231103235504.php create mode 100644 src/Entity/TorrentCategories.php create mode 100644 src/Repository/TorrentCategoriesRepository.php create mode 100644 templates/default/torrent/edit/categories.html.twig diff --git a/.env b/.env index b7750ba..52307ec 100644 --- a/.env +++ b/.env @@ -59,6 +59,9 @@ APP_LOCALE=en # Supported locales for interface and content filters APP_LOCALES=en|cs|nl|eo|fr|ja|ka|de|he|it|lv|pl|pt|ru|es|uk +# Content categories, lowercase, enabled by default for new users +APP_CATEGORIES=movie|series|tv|animation|music|game|audiobook|podcast|book|archive|picture|software|other + # Items per page on pagination APP_PAGINATION=10 diff --git a/config/services.yaml b/config/services.yaml index 110b2a6..b515a42 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -11,6 +11,7 @@ parameters: app.trackers: '%env(APP_TRACKERS)%' app.crawlers: '%env(APP_CRAWLERS)%' app.locales: '%env(APP_LOCALES)%' + app.categories: '%env(APP_CATEGORIES)%' app.themes: '%env(APP_THEMES)%' app.locale: '%env(APP_LOCALE)%' app.theme: '%env(APP_THEME)%' diff --git a/migrations/Version20231103235504.php b/migrations/Version20231103235504.php new file mode 100644 index 0000000..1ec3213 --- /dev/null +++ b/migrations/Version20231103235504.php @@ -0,0 +1,37 @@ +addSql('CREATE TABLE torrent_categories (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, torrent_id INTEGER NOT NULL, user_id INTEGER NOT NULL, added INTEGER NOT NULL, value CLOB NOT NULL --(DC2Type:simple_array) + , approved BOOLEAN NOT NULL)'); + $this->addSql('ALTER TABLE user ADD COLUMN categories CLOB DEFAULT "other"'); + $this->addSql('ALTER TABLE torrent ADD COLUMN categories CLOB DEFAULT "other"'); + $this->addSql('UPDATE user SET categories = "movie,series,tv,animation,music,game,audiobook,podcast,book,archive,picture,software,other"'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE torrent_categories'); + $this->addSql('ALTER TABLE user DROP COLUMN categories'); + $this->addSql('ALTER TABLE torrent DROP COLUMN categories'); + } +} diff --git a/src/Controller/TorrentController.php b/src/Controller/TorrentController.php index 9b22608..0179ebd 100644 --- a/src/Controller/TorrentController.php +++ b/src/Controller/TorrentController.php @@ -150,12 +150,13 @@ class TorrentController extends AbstractController 'peers' => (int) $torrent->getPeers(), 'leechers' => (int) $torrent->getLeechers(), ], - 'keywords' => $torrent->getKeywords(), - 'locales' => $torrent->getLocales(), - 'sensitive' => $torrent->isSensitive(), - 'approved' => $torrent->isApproved(), - 'status' => $torrent->isStatus(), - 'download' => + 'keywords' => $torrent->getKeywords(), + 'locales' => $torrent->getLocales(), + 'categories' => $torrent->getCategories(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'download' => [ 'file' => [ @@ -262,7 +263,8 @@ class TorrentController extends AbstractController $user->getId(), $query, $user->getLocales(), - !$user->isModerator() && $user->isSensitive() ? false : null, + $user->getCategories(), + $user->isSensitive() ? false : null, !$user->isModerator() ? true : null, !$user->isModerator() ? true : null, ); @@ -272,7 +274,8 @@ class TorrentController extends AbstractController $user->getId(), $query, $user->getLocales(), - !$user->isModerator() && $user->isSensitive() ? false : null, + $user->getCategories(), + $user->isSensitive() ? false : null, !$user->isModerator() ? true : null, !$user->isModerator() ? true : null, $this->getParameter('app.pagination'), @@ -448,7 +451,8 @@ class TorrentController extends AbstractController $user->getId(), [], $user->getLocales(), - !$user->isModerator() && $user->isSensitive() ? false : null, + $user->getCategories(), + $user->isSensitive() ? false : null, !$user->isModerator() ? true : null, !$user->isModerator() ? true : null, ); @@ -459,7 +463,8 @@ class TorrentController extends AbstractController $user->getId(), [], $user->getLocales(), - !$user->isModerator() && $user->isSensitive() ? false : null, + $user->getCategories(), + $user->isSensitive() ? false : null, !$user->isModerator() ? true : null, !$user->isModerator() ? true : null, $this->getParameter('app.pagination'), @@ -625,7 +630,8 @@ class TorrentController extends AbstractController $user->getId(), $query, $user->getLocales(), - !$user->isModerator() && $user->isSensitive() ? false : null, + $user->getCategories(), + $user->isSensitive() ? false : null, !$user->isModerator() ? true : null, !$user->isModerator() ? true : null, ); @@ -636,7 +642,8 @@ class TorrentController extends AbstractController $user->getId(), $query, $user->getLocales(), - !$user->isModerator() && $user->isSensitive() ? false : null, + $user->getCategories(), + $user->isSensitive() ? false : null, !$user->isModerator() ? true : null, !$user->isModerator() ? true : null, $this->getParameter('app.pagination'), @@ -700,13 +707,14 @@ class TorrentController extends AbstractController ); // Init request - $query = $request->get('query') ? explode(' ', urldecode($request->get('query'))) : []; - $page = $request->get('page') ? (int) $request->get('page') : 1; + $query = $request->get('query') ? explode(' ', urldecode($request->get('query'))) : []; + $page = $request->get('page') ? (int) $request->get('page') : 1; - $locales = $request->get('locales') ? explode('|', $request->get('locales')) : explode('|', $this->getParameter('app.locales')); - $sensitive = $request->get('sensitive') ? (bool) $request->get('sensitive') : null; + $locales = $request->get('locales') ? explode('|', $request->get('locales')) : explode('|', $this->getParameter('app.locales')); + $categories = $request->get('categories') ? explode('|', $request->get('categories')) : explode('|', $this->getParameter('app.categories')); + $sensitive = $request->get('sensitive') ? (bool) $request->get('sensitive') : null; - $yggdrasil = $request->get('yggdrasil') ? (bool) $request->get('yggdrasil') : false; + $yggdrasil = $request->get('yggdrasil') ? (bool) $request->get('yggdrasil') : false; // Init trackers $trackers = explode('|', $this->getParameter('app.trackers')); @@ -716,7 +724,8 @@ class TorrentController extends AbstractController $user->getId(), $query, $locales, - !$user->isModerator() ? $sensitive : null, + $categories, + $sensitive, !$user->isModerator() ? true : null, !$user->isModerator() ? true : null, ); @@ -727,7 +736,8 @@ class TorrentController extends AbstractController $user->getId(), $query, $locales, - !$user->isModerator() ? $sensitive : null, + $categories, + $sensitive, !$user->isModerator() ? true : null, !$user->isModerator() ? true : null, $this->getParameter('app.pagination'), @@ -873,6 +883,14 @@ class TorrentController extends AbstractController 'value' => $request->get('locales') ? $request->get('locales') : [$request->get('_locale')], ] ], + 'categories' => + [ + 'error' => [], + 'attribute' => + [ + 'value' => $request->get('categories') ? $request->get('categories') : [], + ] + ], 'torrent' => [ 'error' => [], @@ -909,6 +927,25 @@ class TorrentController extends AbstractController $form['locales']['error'][] = $translator->trans('At least one locale required'); } + /// Categories + $categories = []; + if ($request->get('categories')) + { + foreach ((array) $request->get('categories') as $locale) + { + if (in_array($locale, explode('|', $this->getParameter('app.categories')))) + { + $categories[] = $locale; + } + } + } + + //// At least one valid locale required + if (!$categories) + { + $form['categories']['error'][] = $translator->trans('At least one category required'); + } + /// Torrent if ($file = $request->files->get('torrent')) { @@ -937,7 +974,7 @@ class TorrentController extends AbstractController } // Request is valid - if (empty($form['torrent']['error']) && empty($form['locales']['error'])) + if (empty($form['torrent']['error']) && empty($form['locales']['error']) && empty($form['categories']['error'])) { // Save data $torrent = $torrentService->add( @@ -956,6 +993,7 @@ class TorrentController extends AbstractController $user->getId(), time(), (array) $locales, + (array) $categories, (bool) $request->get('sensitive'), $user->isApproved(), $user->isStatus() @@ -983,8 +1021,9 @@ class TorrentController extends AbstractController return $this->render( 'default/torrent/submit.html.twig', [ - 'locales' => explode('|', $this->getParameter('app.locales')), - 'form' => $form, + 'locales' => explode('|', $this->getParameter('app.locales')), + 'categories' => explode('|', $this->getParameter('app.categories')), + 'form' => $form, ] ); } @@ -1504,6 +1543,369 @@ class TorrentController extends AbstractController ); } + // Torrent categories + #[Route( + '/{_locale}/torrent/{torrentId}/edit/categories/{torrentCategoriesId}', + name: 'torrent_categories_edit', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentCategoriesId' => '\d+', + ], + defaults: + [ + 'torrentCategoriesId' => null, + ], + methods: + [ + 'GET', + 'POST' + ] + )] + public function editCategories( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent categories + $torrentCategoriesCurrent = [ + 'userId' => null, + 'value' => [] + ]; + + // Get from edition version requested + if ($request->get('torrentCategoriesId')) + { + if ($torrentCategories = $torrentService->getTorrentCategories($request->get('torrentCategoriesId'))) + { + $torrentCategoriesCurrent['userId'] = $torrentCategories->getUserId(); + + foreach ($torrentCategories->getValue() as $value) + { + $torrentCategoriesCurrent['value'][] = $value; + } + } + + else + { + throw $this->createNotFoundException(); + } + } + + // Otherwise, get latest available + else + { + if ($torrentCategories = $torrentService->findLastTorrentCategoriesByTorrentId($torrent->getId())) + { + $torrentCategoriesCurrent['userId'] = $torrentCategories->getUserId(); + + foreach ($torrentCategories->getValue() as $value) + { + $torrentCategoriesCurrent['value'][] = $value; + } + + // Update active categories + $request->attributes->set('torrentCategoriesId', $torrentCategories->getId()); + } + } + + // Init edition history + $editions = []; + foreach ($torrentService->findTorrentCategoriesByTorrentId($torrent->getId()) as $torrentCategoriesEdition) + { + $editions[] = + [ + 'id' => $torrentCategoriesEdition->getId(), + 'added' => $torrentCategoriesEdition->getAdded(), + 'approved' => $torrentCategoriesEdition->isApproved(), + 'active' => $torrentCategoriesEdition->getId() == $request->get('torrentCategoriesId'), + 'user' => + [ + 'id' => $torrentCategoriesEdition->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $torrentCategoriesEdition->getUserId() + )->getAddress() + ), + ] + ]; + } + + // Init form + $form = + [ + 'categories' => + [ + 'error' => [], + 'attribute' => + [ + 'value' => $request->get('categories') ? $request->get('categories') : $torrentCategoriesCurrent['value'], + ] + ] + ]; + + // Process request + if ($request->isMethod('post')) + { + /// Categories + $categories = []; + if ($request->get('categories')) + { + foreach ((array) $request->get('categories') as $category) + { + if (in_array($category, explode('|', $this->getParameter('app.categories')))) + { + $categories[] = $category; + } + } + } + + //// At least one valid category required + if (!$categories) + { + $form['categories']['error'][] = $translator->trans('At least one category required'); + } + + // Request is valid + if (empty($form['categories']['error'])) + { + // Save data + $torrentCategories = $torrentService->addTorrentCategories( + $torrent->getId(), + $user->getId(), + time(), + $categories, + $user->isApproved() + ); + + // Register activity event + /* @TODO + $activityService->addEventTorrentCategoriesAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentCategories->getId() + ); + */ + + // Redirect to info page + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + } + + // Render form template + return $this->render( + 'default/torrent/edit/categories.html.twig', + [ + 'torrentId' => $torrent->getId(), + 'categories' => explode('|', $this->getParameter('app.categories')), + 'editions' => $editions, + 'form' => $form, + 'session' => + [ + 'moderator' => $user->isModerator(), + 'owner' => $torrentCategoriesCurrent['userId'] === $user->getId(), + ] + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/approve/categories/{torrentCategoriesId}', + name: 'torrent_categories_approve', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentCategoriesId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function approveCategories( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent categories + if (!$torrentCategories = $torrentService->getTorrentCategories($request->get('torrentCategoriesId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!$user->isModerator()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Register activity event + /* @TODO + if (!$torrentCategories->isApproved()) + { + $activityService->addEventTorrentCategoriesApproveAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentCategories->getId() + ); + } + + else + { + $activityService->addEventTorrentCategoriesApproveDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentCategories->getId() + ); + } + */ + + // Update approved + $torrentService->toggleTorrentCategoriesApproved( + $torrentCategories->getId() + ); + + // Redirect back to form + return $this->redirectToRoute( + 'torrent_categories_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentCategoriesId' => $torrentCategories->getId(), + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/delete/categories/{torrentCategoriesId}', + name: 'torrent_categories_delete', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentCategoriesId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function deleteCategories( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent categories + if (!$torrentCategories = $torrentService->getTorrentCategories($request->get('torrentCategoriesId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!($user->isModerator() || $user->getId() === $torrentCategories->getUserId())) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Add activity event + /* @TODO + $activityService->addEventTorrentCategoriesDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentCategories->getId() + ); + */ + + // Update approved + $torrentService->deleteTorrentCategories( + $torrentCategories->getId() + ); + + // Redirect back to form + return $this->redirectToRoute( + 'torrent_categories_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentCategoriesId' => $torrentCategories->getId(), + ] + ); + } + // Torrent sensitive #[Route( '/{_locale}/torrent/{torrentId}/edit/sensitive/{torrentSensitiveId}', @@ -2926,14 +3328,15 @@ class TorrentController extends AbstractController 'locale' => $locale, 'locales' => $locales, 'torrents' => $torrentService->findTorrents( - 0, // no user session init, pass 0 - [], // without keywords filter - $locales, // all system locales - null, // all sensitive levels - true, // approved only - true, // enabled only - 1000, // @TODO limit - 0 // offset + 0, // no user session init, pass 0 + [], // without keywords filter + $locales, // all system locales + $categories, // all system locales + null, // all sensitive levels + true, // approved only + true, // enabled only + 1000, // @TODO limit + 0 // offset ) ], $response diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index a408a11..ae31028 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -85,6 +85,23 @@ class UserController extends AbstractController ); } + // Update categories + if ($request->get('categories')) + { + $categories = []; + foreach ((array) $request->get('categories') as $category) + { + if (in_array($category, explode('|', $this->getParameter('app.categories')))) + { + $categories[] = $category; + } + } + + $user->setCategories( + $categories + ); + } + // Update theme if (in_array($request->get('theme'), explode('|', $this->getParameter('app.themes')))) { @@ -139,19 +156,21 @@ class UserController extends AbstractController 'default/user/settings.html.twig', [ 'user' => [ - 'id' => $user->getId(), - 'sensitive' => $user->isSensitive(), - 'yggdrasil' => $user->isYggdrasil(), - 'posters' => $user->isPosters(), - 'locale' => $user->getLocale(), - 'locales' => $user->getLocales(), - 'events' => $user->getEvents(), - 'theme' => $user->getTheme(), - 'added' => $user->getAdded() + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'yggdrasil' => $user->isYggdrasil(), + 'posters' => $user->isPosters(), + 'locale' => $user->getLocale(), + 'locales' => $user->getLocales(), + 'categories' => $user->getCategories(), + 'events' => $user->getEvents(), + 'theme' => $user->getTheme(), + 'added' => $user->getAdded() ], - 'locales' => explode('|', $this->getParameter('app.locales')), - 'themes' => explode('|', $this->getParameter('app.themes')), - 'events' => $activityService->getEventsTree() + 'locales' => explode('|', $this->getParameter('app.locales')), + 'categories' => explode('|', $this->getParameter('app.categories')), + 'themes' => explode('|', $this->getParameter('app.themes')), + 'events' => $activityService->getEventsTree() ] ); } @@ -217,20 +236,21 @@ class UserController extends AbstractController 'moderator' => $user->isModerator() ], 'user' => [ - 'id' => $userTarget->getId(), - 'address' => $userTarget->getAddress(), - 'moderator' => $userTarget->isModerator(), - 'approved' => $userTarget->isApproved(), - 'status' => $userTarget->isStatus(), - 'posters' => $userTarget->isPosters(), - 'sensitive' => $userTarget->isSensitive(), - 'yggdrasil' => $userTarget->isYggdrasil(), - 'locale' => $userTarget->getLocale(), - 'locales' => $userTarget->getLocales(), - 'events' => $userTarget->getEvents(), - 'theme' => $userTarget->getTheme(), - 'added' => $userTarget->getAdded(), - 'identicon' => $userService->identicon( + 'id' => $userTarget->getId(), + 'address' => $userTarget->getAddress(), + 'moderator' => $userTarget->isModerator(), + 'approved' => $userTarget->isApproved(), + 'status' => $userTarget->isStatus(), + 'posters' => $userTarget->isPosters(), + 'sensitive' => $userTarget->isSensitive(), + 'yggdrasil' => $userTarget->isYggdrasil(), + 'locale' => $userTarget->getLocale(), + 'locales' => $userTarget->getLocales(), + 'categories' => $user->getCategories(), + 'events' => $userTarget->getEvents(), + 'theme' => $userTarget->getTheme(), + 'added' => $userTarget->getAdded(), + 'identicon' => $userService->identicon( $userTarget->getAddress(), 48 ), @@ -545,6 +565,11 @@ class UserController extends AbstractController true ); + $torrentService->setTorrentCategoriesApprovedByUserId( + $userTarget->getId(), + true + ); + $torrentService->setTorrentSensitivesApprovedByUserId( $userTarget->getId(), true diff --git a/src/Entity/Torrent.php b/src/Entity/Torrent.php index e29c448..515a62e 100644 --- a/src/Entity/Torrent.php +++ b/src/Entity/Torrent.php @@ -54,6 +54,9 @@ class Torrent #[ORM\Column(nullable: true)] private ?int $torrentPosterId = null; + #[ORM\Column(type: Types::SIMPLE_ARRAY)] + private ?array $categories = null; + public function getId(): ?int { return $this->id; @@ -221,4 +224,16 @@ class Torrent return $this; } + + public function getCategories(): ?array + { + return $this->categories; + } + + public function setCategories(?array $categories): static + { + $this->categories = $categories; + + return $this; + } } diff --git a/src/Entity/TorrentCategories.php b/src/Entity/TorrentCategories.php new file mode 100644 index 0000000..9d73809 --- /dev/null +++ b/src/Entity/TorrentCategories.php @@ -0,0 +1,103 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } + + public function getValue(): array + { + return $this->value; + } + + public function setValue(array $value): static + { + $this->value = $value; + + return $this; + } + + public function isApproved(): ?bool + { + return $this->approved; + } + + public function setApproved(bool $approved): static + { + $this->approved = $approved; + + return $this; + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php index 9f346a7..330a97a 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -50,6 +50,9 @@ class User #[ORM\Column] private ?bool $posters = null; + #[ORM\Column(type: Types::SIMPLE_ARRAY)] + private ?array $categories = null; + public function getId(): ?int { return $this->id; @@ -205,4 +208,16 @@ class User return $this; } + + public function getCategories(): ?array + { + return $this->categories; + } + + public function setCategories(?array $categories): static + { + $this->categories = $categories; + + return $this; + } } diff --git a/src/Repository/TorrentCategoriesRepository.php b/src/Repository/TorrentCategoriesRepository.php new file mode 100644 index 0000000..030f69c --- /dev/null +++ b/src/Repository/TorrentCategoriesRepository.php @@ -0,0 +1,23 @@ + + * + * @method TorrentCategories|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentCategories|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentCategories[] findAll() + * @method TorrentCategories[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentCategoriesRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentCategories::class); + } +} diff --git a/src/Repository/TorrentRepository.php b/src/Repository/TorrentRepository.php index 1ccd425..536a72f 100644 --- a/src/Repository/TorrentRepository.php +++ b/src/Repository/TorrentRepository.php @@ -24,7 +24,8 @@ class TorrentRepository extends ServiceEntityRepository public function findTorrentsTotal( int $userId, array $keywords, - array $locales, + ?array $locales, + ?array $categories, ?bool $sensitive = null, ?bool $approved = null, ?bool $status = null, @@ -36,6 +37,7 @@ class TorrentRepository extends ServiceEntityRepository $userId, $keywords, $locales, + $categories, $sensitive, $approved, $status, @@ -47,7 +49,8 @@ class TorrentRepository extends ServiceEntityRepository public function findTorrents( int $userId, array $keywords, - array $locales, + ?array $locales, + ?array $categories, ?bool $sensitive = null, ?bool $approved = null, ?bool $status = null, @@ -59,6 +62,7 @@ class TorrentRepository extends ServiceEntityRepository $userId, $keywords, $locales, + $categories, $sensitive, $approved, $status, @@ -70,17 +74,18 @@ class TorrentRepository extends ServiceEntityRepository } private function getTorrentsQueryByFilter( - int $userId, - array $keywords, - array $locales, - ?bool $sensitive = null, - ?bool $approved = null, - ?bool $status = null + int $userId, + ?array $keywords, + ?array $locales, + ?array $categories, + ?bool $sensitive = null, + ?bool $approved = null, + ?bool $status = null ): \Doctrine\ORM\QueryBuilder { $query = $this->createQueryBuilder('t'); - if ($keywords) + if (is_array($keywords)) { foreach ($keywords as $i => $keyword) { @@ -105,7 +110,7 @@ class TorrentRepository extends ServiceEntityRepository } } - if ($locales) + if (is_array($locales)) { $orLocales = $query->expr()->orX(); @@ -121,6 +126,22 @@ class TorrentRepository extends ServiceEntityRepository $query->andWhere($orLocales); } + if (is_array($categories)) + { + $orCategories = $query->expr()->orX(); + + foreach ($categories as $i => $category) + { + $orCategories->add("t.categories LIKE :category{$i}"); + $orCategories->add("t.userId = :userId"); + + $query->setParameter(":category{$i}", "%{$category}%"); + $query->setParameter('userId', $userId); + } + + $query->andWhere($orCategories); + } + if (is_bool($sensitive)) { $orSensitive = $query->expr()->orX(); diff --git a/src/Service/TorrentService.php b/src/Service/TorrentService.php index 0a5b3bf..abb902e 100644 --- a/src/Service/TorrentService.php +++ b/src/Service/TorrentService.php @@ -4,6 +4,7 @@ namespace App\Service; use App\Entity\Torrent; use App\Entity\TorrentLocales; +use App\Entity\TorrentCategories; use App\Entity\TorrentSensitive; use App\Entity\TorrentPoster; use App\Entity\TorrentStar; @@ -12,6 +13,7 @@ use App\Entity\TorrentDownloadMagnet; use App\Repository\TorrentRepository; use App\Repository\TorrentLocalesRepository; +use App\Repository\TorrentCategoriesRepository; use App\Repository\TorrentSensitiveRepository; use App\Repository\TorrentPosterRepository; use App\Repository\TorrentStarRepository; @@ -381,6 +383,7 @@ class TorrentService int $userId, int $added, array $locales, + array $categories, bool $sensitive, bool $approved, bool $status @@ -403,6 +406,7 @@ class TorrentService $wordLengthMax ), $locales, + $categories, $sensitive, $approved, $status @@ -424,6 +428,14 @@ class TorrentService $approved ); + $this->addTorrentCategories( + $torrent->getId(), + $userId, + $added, + $categories, + $approved + ); + $this->addTorrentSensitive( $torrent->getId(), $userId, @@ -449,6 +461,7 @@ class TorrentService string $md5file, array $keywords, array $locales, + array $categories, bool $sensitive, bool $approved, bool $status @@ -461,6 +474,7 @@ class TorrentService $torrent->setMd5File($md5file); $torrent->setKeywords($keywords); $torrent->setLocales($locales); + $torrent->setCategories($categories); $torrent->setSensitive($sensitive); $torrent->setApproved($approved); $torrent->setStatus($status); @@ -534,7 +548,8 @@ class TorrentService public function findTorrents( int $userId, array $keywords, - array $locales, + ?array $locales, + ?array $categories, ?bool $sensitive, ?bool $approved, ?bool $status, @@ -548,6 +563,7 @@ class TorrentService $userId, $keywords, $locales, + $categories, $sensitive, $approved, $status, @@ -559,7 +575,8 @@ class TorrentService public function findTorrentsTotal( int $userId, array $keywords, - array $locales, + ?array $locales, + ?array $categories, ?bool $sensitive, ?bool $approved, ?bool $status @@ -571,6 +588,7 @@ class TorrentService $userId, $keywords, $locales, + $categories, $sensitive, $approved, $status @@ -642,6 +660,32 @@ class TorrentService } } + public function updateTorrentCategories( + int $torrentId + ): void + { + if ($torrent = $this->getTorrent($torrentId)) + { + if ($torrentCategories = $this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->findOneBy( + [ + 'torrentId' => $torrentId, + 'approved' => true, + ], + [ + 'id' => 'DESC' + ] + )) + { + $torrent->setCategories($torrentCategories->getValue()); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + } + public function updateTorrentScraped( int $torrentId, int $time @@ -886,6 +930,133 @@ class TorrentService } } + // Torrent category + public function getTorrentCategories( + int $torrentCategoryId + ): ?TorrentCategories + { + return $this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->find($torrentCategoryId); + } + + public function findLastTorrentCategoriesByTorrentId( + int $torrentId + ): ?TorrentCategories + { + return $this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->findOneBy( + [ + 'torrentId' => $torrentId + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function findTorrentCategoriesByTorrentId(int $torrentId): array + { + return $this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->findBy( + [ + 'torrentId' => $torrentId, + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function toggleTorrentCategoriesApproved( + int $torrentCategoriesId + ): ?TorrentCategories + { + $torrentCategories = $this->getTorrentCategories($torrentCategoriesId); + + $torrentCategories->setApproved( + !$torrentCategories->isApproved() // toggle current value + ); + + $this->entityManagerInterface->persist($torrentCategories); + $this->entityManagerInterface->flush(); + + $this->updateTorrentCategories( + $torrentCategories->getTorrentId() + ); + + return $torrentCategories; + } + + public function deleteTorrentCategories( + int $torrentCategoriesId + ): ?TorrentCategories + { + $torrentCategories = $this->getTorrentCategories($torrentCategoriesId); + + $this->entityManagerInterface->remove($torrentCategories); + $this->entityManagerInterface->flush(); + + $this->updateTorrentCategories( + $torrentCategories->getTorrentId() + ); + + return $torrentCategories; + } + + public function addTorrentCategories( + int $torrentId, + int $userId, + int $added, + array $value, + bool $approved + ): ?TorrentCategories + { + $torrentCategories = new TorrentCategories(); + + $torrentCategories->setTorrentId($torrentId); + $torrentCategories->setUserId($userId); + $torrentCategories->setAdded($added); + $torrentCategories->setValue($value); + $torrentCategories->setApproved($approved); + + $this->entityManagerInterface->persist($torrentCategories); + $this->entityManagerInterface->flush(); + + $this->updateTorrentCategories( + $torrentId + ); + + return $torrentCategories; + } + + public function setTorrentCategoriesApprovedByUserId( + int $userId, + bool $value + ): void + { + foreach ($this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->findBy( + [ + 'userId' => $userId + ]) as $torrentCategories) + { + $torrentCategories->setApproved( + $value + ); + + $this->entityManagerInterface->persist($torrentCategories); + $this->entityManagerInterface->flush(); + + $this->updateTorrentCategories( + $torrentCategories->getTorrentId(), + ); + } + } + // Torrent sensitive public function getTorrentSensitive( int $torrentSensitiveId diff --git a/templates/default/torrent/edit/categories.html.twig b/templates/default/torrent/edit/categories.html.twig new file mode 100644 index 0000000..c094952 --- /dev/null +++ b/templates/default/torrent/edit/categories.html.twig @@ -0,0 +1,109 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{{'Edit categories'|trans }} - {{'Torrent'|trans }} #{{ torrentId }} - {{ name }}{% endblock %} +{% block main_content %} +
+
+

+ {{'Edit categories for torrent' | trans }} + #{{ torrentId }} +

+
+
+
+ + + + + + + {% for error in form.categories.error %} +
+ {{ error }} +
+ {% endfor %} +
+ {% for category in categories | sort %} +
+ {% if category in form.categories.attribute.value %} + + {% else %} + + {% endif %} + +
+ {% endfor %} + {# + + #} +
+
+
+ +
+
+ {% for edition in editions %} +
+ {% if edition.active %} + {{ edition.added | format_ago }} + {% else %} + + {{ edition.added | format_ago }} + + {% endif %} + {{ 'by'|trans }} + + {{'identicon'|trans }} + +
+ {% if session.moderator or session.owner %} + + + + + + + {% endif %} + {% if edition.approved %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% else %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% endif %} +
+
+ {% endfor %} +{% endblock %} diff --git a/templates/default/torrent/edit/locales.html.twig b/templates/default/torrent/edit/locales.html.twig index 1245721..dcab8db 100644 --- a/templates/default/torrent/edit/locales.html.twig +++ b/templates/default/torrent/edit/locales.html.twig @@ -24,7 +24,7 @@ {% endfor %}
- {% for locale in locales %} + {% for locale in locales | sort %}
{% if locale in form.locales.attribute.value %} diff --git a/templates/default/torrent/info.html.twig b/templates/default/torrent/info.html.twig index 5aebc08..9560e68 100644 --- a/templates/default/torrent/info.html.twig +++ b/templates/default/torrent/info.html.twig @@ -339,12 +339,28 @@ - {{ 'Locales' | trans }} + {{ 'Locale' | trans }}
{% if torrent.locales %}
- {% for i, locale in torrent.locales %}{% if i > 0 %},{% endif %} {{ locale|locale_name(locale)|u.title }}{% endfor %} + {% for i, locale in torrent.locales | sort %}{% if i > 0 %},{% endif %} {{ locale | locale_name(locale) | u.title }}{% endfor %} +
+ {% endif %} +
+
+
+ + + + + + {{ 'Category' | trans }} +
+
+ {% if torrent.categories %} +
+ {% for i, category in torrent.categories | sort %}{% if i > 0 %},{% endif %} {{ category | u.title }}{% endfor %}
{% endif %}
diff --git a/templates/default/torrent/submit.html.twig b/templates/default/torrent/submit.html.twig index 27f6a14..4d3283b 100644 --- a/templates/default/torrent/submit.html.twig +++ b/templates/default/torrent/submit.html.twig @@ -31,7 +31,7 @@
{% endfor %}
- {% for locale in locales %} + {% for locale in locales | sort %}
{% if locale in form.locales.attribute.value %} @@ -48,9 +48,42 @@ #}
+
+
+ + + + + + + {% for error in form.categories.error %} +
+ {{ error }} +
+ {% endfor %} +
+ {% for category in categories | sort %} +
+ {% if category in form.categories.attribute.value %} + + {% else %} + + {% endif %} + +
+ {% endfor %} + {# + {{'Other...'|trans }} + #} +
+
-