From 067f7c70b32fad17feb174d0f569c568a47396d6 Mon Sep 17 00:00:00 2001 From: Lee Peuker Date: Thu, 14 Jul 2022 13:27:19 +0200 Subject: [PATCH] Add deletion of user data/account to settings page --- settings/routes.php | 16 ++++- src/Application/Movie/Api.php | 10 +++ src/Application/Movie/History/Repository.php | 7 ++- .../Movie/History/Service/Delete.php | 5 ++ src/Application/Movie/Repository.php | 5 ++ src/Application/User/Api.php | 5 ++ src/Application/User/Repository.php | 5 ++ .../User/Service/Authentication.php | 1 + src/Factory.php | 6 +- src/HttpController/LandingPageController.php | 8 ++- src/HttpController/SettingsController.php | 61 +++++++++++++++++++ templates/page/login.html.twig | 9 ++- templates/page/settings.html.twig | 28 +++++++++ 13 files changed, 158 insertions(+), 8 deletions(-) diff --git a/settings/routes.php b/settings/routes.php index dceaf651..dfc4c97f 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -117,12 +117,26 @@ '/user/password', [\Movary\HttpController\SettingsController::class, 'updatePassword'] ); + $routeCollector->addRoute( + 'GET', + '/user/delete-ratings', + [\Movary\HttpController\SettingsController::class, 'deleteRatings'] + ); + $routeCollector->addRoute( + 'GET', + '/user/delete-history', + [\Movary\HttpController\SettingsController::class, 'deleteHistory'] + ); + $routeCollector->addRoute( + 'GET', + '/user/delete-account', + [\Movary\HttpController\SettingsController::class, 'deleteAccount'] + ); $routeCollector->addRoute( 'DELETE', '/user/plex-webhook-id', [\Movary\HttpController\PlexController::class, 'deletePlexWebhookId'] ); - $routeCollector->addRoute( 'POST', '/plex/{id:.+}', diff --git a/src/Application/Movie/Api.php b/src/Application/Movie/Api.php index db8828cd..5581ee84 100644 --- a/src/Application/Movie/Api.php +++ b/src/Application/Movie/Api.php @@ -82,6 +82,16 @@ public function deleteHistoryByTraktId(TraktId $traktId) : void $this->historyDeleteService->deleteByTraktId($traktId); } + public function deleteHistoryByUserId(int $userId) : void + { + $this->historyDeleteService->deleteByUserId($userId); + } + + public function deleteRatingsByUserId(int $userId) : void + { + $this->movieRepository->deleteAllUserRatings($userId); + } + public function fetchAll() : EntityList { return $this->movieSelectService->fetchAll(); diff --git a/src/Application/Movie/History/Repository.php b/src/Application/Movie/History/Repository.php index 42b97864..67b720f7 100644 --- a/src/Application/Movie/History/Repository.php +++ b/src/Application/Movie/History/Repository.php @@ -36,12 +36,17 @@ public function deleteByTraktId(TraktId $traktId) : void ); } + public function deleteByUserId(int $userId) : void + { + $this->dbConnection->delete('movie_user_watch_dates', ['user_id' => $userId]); + } + public function deleteHistoryByIdAndDate(int $movieId, int $userId, Date $watchedAt) : void { $this->dbConnection->executeStatement( 'DELETE movie_user_watch_dates FROM movie_user_watch_dates - WHERE movie_id = ? AND watched_at = ? and user_id = ?', + WHERE movie_id = ? AND watched_at = ? AND user_id = ?', [$movieId, (string)$watchedAt, $userId] ); } diff --git a/src/Application/Movie/History/Service/Delete.php b/src/Application/Movie/History/Service/Delete.php index 1bac3eba..23000352 100644 --- a/src/Application/Movie/History/Service/Delete.php +++ b/src/Application/Movie/History/Service/Delete.php @@ -20,6 +20,11 @@ public function deleteByTraktId(TraktId $traktId) : void $this->repository->deleteByTraktId($traktId); } + public function deleteByUserId(int $userId) : void + { + $this->repository->deleteByUserId($userId); + } + public function deleteHistoryByIdAndDate(int $movieId, int $userId, Date $watchedAt) : void { $this->repository->deleteHistoryByIdAndDate($movieId, $userId, $watchedAt); diff --git a/src/Application/Movie/Repository.php b/src/Application/Movie/Repository.php index c3b871dd..d2fc4441 100644 --- a/src/Application/Movie/Repository.php +++ b/src/Application/Movie/Repository.php @@ -51,6 +51,11 @@ public function create( return $this->fetchById((int)$this->dbConnection->lastInsertId()); } + public function deleteAllUserRatings(int $userId) : void + { + $this->dbConnection->delete('movie_user_rating', ['user_id' => $userId]); + } + public function deleteUserRating(int $movieId, int $userId) : void { $this->dbConnection->executeQuery( diff --git a/src/Application/User/Api.php b/src/Application/User/Api.php index f2270ace..fe984d14 100644 --- a/src/Application/User/Api.php +++ b/src/Application/User/Api.php @@ -23,6 +23,11 @@ public function deletePlexWebhookId(int $userId) : void $this->repository->setPlexWebhookId($userId, null); } + public function deleteUser(int $userId) : void + { + $this->repository->deleteUser($userId); + } + public function findPlexWebhookId(int $userId) : ?string { return $this->repository->findPlexWebhookId($userId); diff --git a/src/Application/User/Repository.php b/src/Application/User/Repository.php index 77f87bdd..0e91c801 100644 --- a/src/Application/User/Repository.php +++ b/src/Application/User/Repository.php @@ -45,6 +45,11 @@ public function deleteAuthToken(string $token) : void ); } + public function deleteUser(int $userId) : void + { + $this->dbConnection->delete('user', ['id' => $userId]); + } + public function findAuthTokenExpirationDate(string $token) : ?DateTime { $expirationDate = $this->dbConnection->fetchOne('SELECT `expiration_date` FROM `user_auth_token` WHERE `token` = ?', [$token]); diff --git a/src/Application/User/Service/Authentication.php b/src/Application/User/Service/Authentication.php index 8cedb5ed..6e734770 100644 --- a/src/Application/User/Service/Authentication.php +++ b/src/Application/User/Service/Authentication.php @@ -99,6 +99,7 @@ public function logout() : void } session_destroy(); + session_start(); } private function createExpirationDate(int $days = 1) : DateTime diff --git a/src/Factory.php b/src/Factory.php index 2c1508b2..93eb8700 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -10,8 +10,9 @@ use Movary\Api\Tmdb; use Movary\Api\Trakt; use Movary\Api\Trakt\Cache\User\Movie\Watched; +use Movary\Application\Movie; use Movary\Application\SyncLog; -use Movary\Application\User\Api; +use Movary\Application\User; use Movary\Application\User\Service\Authentication; use Movary\Command; use Movary\HttpController\SettingsController; @@ -95,7 +96,8 @@ public static function createSettingsController(ContainerInterface $container, C $container->get(Twig\Environment::class), $container->get(SyncLog\Repository::class), $container->get(Authentication::class), - $container->get(Api::class), + $container->get(User\Api::class), + $container->get(Movie\Api::class), $applicationVersion ); } diff --git a/src/HttpController/LandingPageController.php b/src/HttpController/LandingPageController.php index f41606cd..0e3a430e 100644 --- a/src/HttpController/LandingPageController.php +++ b/src/HttpController/LandingPageController.php @@ -24,11 +24,15 @@ public function render() : Response } $failedLogin = $_SESSION['failedLogin'] ?? null; - unset($_SESSION['failedLogin']); + $deletedAccount = $_SESSION['deletedAccount'] ?? null; + unset($_SESSION['failedLogin'], $_SESSION['deletedAccount']); return Response::create( StatusCode::createOk(), - $this->twig->render('page/login.html.twig', ['failedLogin' => empty($failedLogin) === false]) + $this->twig->render('page/login.html.twig', [ + 'failedLogin' => empty($failedLogin) === false, + 'deletedAccount' => empty($deletedAccount) === false, + ]) ); } } diff --git a/src/HttpController/SettingsController.php b/src/HttpController/SettingsController.php index 83a5f78e..f339e0ab 100644 --- a/src/HttpController/SettingsController.php +++ b/src/HttpController/SettingsController.php @@ -2,6 +2,7 @@ namespace Movary\HttpController; +use Movary\Application\Movie; use Movary\Application\SyncLog\Repository; use Movary\Application\User; use Movary\Application\User\Service\Authentication; @@ -18,10 +19,64 @@ public function __construct( private readonly Repository $syncLogRepository, private readonly Authentication $authenticationService, private readonly User\Api $userApi, + private readonly Movie\Api $movieApi, private readonly ?string $applicationVersion = null, ) { } + public function deleteAccount() : Response + { + if ($this->authenticationService->isUserAuthenticated() === false) { + return Response::createFoundRedirect('/'); + } + + $this->userApi->deleteUser($this->authenticationService->getCurrentUserId()); + + $this->authenticationService->logout(); + + $_SESSION['deletedAccount'] = true; + + return Response::create( + StatusCode::createSeeOther(), + null, + [Header::createLocation($_SERVER['HTTP_REFERER'])] + ); + } + + public function deleteHistory() : Response + { + if ($this->authenticationService->isUserAuthenticated() === false) { + return Response::createFoundRedirect('/'); + } + + $this->movieApi->deleteHistoryByUserId($this->authenticationService->getCurrentUserId()); + + $_SESSION['deletedUserHistory'] = true; + + return Response::create( + StatusCode::createSeeOther(), + null, + [Header::createLocation($_SERVER['HTTP_REFERER'])] + ); + } + + public function deleteRatings() : Response + { + if ($this->authenticationService->isUserAuthenticated() === false) { + return Response::createFoundRedirect('/'); + } + + $this->movieApi->deleteRatingsByUserId($this->authenticationService->getCurrentUserId()); + + $_SESSION['deletedUserRatings'] = true; + + return Response::create( + StatusCode::createSeeOther(), + null, + [Header::createLocation($_SERVER['HTTP_REFERER'])] + ); + } + // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh public function render() : Response { @@ -39,6 +94,8 @@ public function render() : Response $importHistorySuccessful = empty($_SESSION['importHistorySuccessful']) === false ? $_SESSION['importHistorySuccessful'] : null; $importRatingsSuccessful = empty($_SESSION['importRatingsSuccessful']) === false ? $_SESSION['importRatingsSuccessful'] : null; $importHistoryError = empty($_SESSION['importHistoryError']) === false ? $_SESSION['importHistoryError'] : null; + $deletedUserHistory = empty($_SESSION['deletedUserHistory']) === false ? $_SESSION['deletedUserHistory'] : null; + $deletedUserRatings = empty($_SESSION['deletedUserRatings']) === false ? $_SESSION['deletedUserRatings'] : null; unset( $_SESSION['passwordUpdated'], $_SESSION['passwordErrorCurrentInvalid'], @@ -48,6 +105,8 @@ public function render() : Response $_SESSION['importHistorySuccessful'], $_SESSION['importRatingsSuccessful'], $_SESSION['importHistoryError'], + $_SESSION['deletedUserHistory'], + $_SESSION['deletedUserRatings'], ); return Response::create( @@ -62,6 +121,8 @@ public function render() : Response 'importRatingsSuccessful' => $importRatingsSuccessful, 'passwordUpdated' => $passwordUpdated, 'importHistoryError' => $importHistoryError, + 'deletedUserHistory' => $deletedUserHistory, + 'deletedUserRatings' => $deletedUserRatings, 'traktClientId' => $this->userApi->findTraktClientId($userId), 'traktUserName' => $this->userApi->findTraktUserName($userId), 'applicationVersion' => $this->applicationVersion ?? '-', diff --git a/templates/page/login.html.twig b/templates/page/login.html.twig index 8347fd71..ef36f8d4 100644 --- a/templates/page/login.html.twig +++ b/templates/page/login.html.twig @@ -14,11 +14,11 @@

Movary


- +
- +
@@ -28,6 +28,11 @@ + {% if deletedAccount == true %} + + {% endif %} {% if failedLogin == true %}