Skip to content

Commit

Permalink
Merge pull request #53 from leepeuker/add-user-data-deletion
Browse files Browse the repository at this point in the history
Add deletion of user data/account to settings page
  • Loading branch information
leepeuker authored Jul 14, 2022
2 parents b06a3fa + 067f7c7 commit 1132fb6
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 8 deletions.
16 changes: 15 additions & 1 deletion settings/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -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:.+}',
Expand Down
10 changes: 10 additions & 0 deletions src/Application/Movie/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
7 changes: 6 additions & 1 deletion src/Application/Movie/History/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/Application/Movie/History/Service/Delete.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/Application/Movie/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
5 changes: 5 additions & 0 deletions src/Application/User/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/Application/User/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
1 change: 1 addition & 0 deletions src/Application/User/Service/Authentication.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public function logout() : void
}

session_destroy();
session_start();
}

private function createExpirationDate(int $days = 1) : DateTime
Expand Down
6 changes: 4 additions & 2 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
);
}
Expand Down
8 changes: 6 additions & 2 deletions src/HttpController/LandingPageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
])
);
}
}
61 changes: 61 additions & 0 deletions src/HttpController/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
{
Expand All @@ -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'],
Expand All @@ -48,6 +105,8 @@ public function render() : Response
$_SESSION['importHistorySuccessful'],
$_SESSION['importRatingsSuccessful'],
$_SESSION['importHistoryError'],
$_SESSION['deletedUserHistory'],
$_SESSION['deletedUserRatings'],
);

return Response::create(
Expand All @@ -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 ?? '-',
Expand Down
9 changes: 7 additions & 2 deletions templates/page/login.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
<h1 class="">Movary</h1>
<br>
<div class="form-floating">
<input type="email" name="email" class="form-control" id="floatingInput" placeholder="name@example.com">
<input type="email" name="email" class="form-control" id="floatingInput" placeholder="name@example.com" required>
<label for="floatingInput">Email address</label>
</div>
<div class="form-floating">
<input type="password" name="password" class="form-control" id="floatingPassword" placeholder="Password">
<input type="password" name="password" class="form-control" id="floatingPassword" placeholder="Password" required>
<label for="floatingPassword">Password</label>
</div>

Expand All @@ -28,6 +28,11 @@
</label>
</div>

{% if deletedAccount == true %}
<div class="alert alert-success" role="alert" style="margin-bottom: 0.7rem!important;">
Account deleted successfully.
</div>
{% endif %}
{% if failedLogin == true %}
<div class="alert alert-danger" role="alert" style="margin-bottom: 0.7rem!important;">
Invalid credentials
Expand Down
28 changes: 28 additions & 0 deletions templates/page/settings.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,34 @@
<hr style="margin: 0;padding: 0">
<div style="background-color: #F8F8F8;padding-top: 1rem;padding-bottom: 0.1rem">

<h5 style="margin-bottom: 1rem">Delete your data</h5>

<div style="margin-bottom: 1rem">
<a class="btn btn-warning btn-sm" href="/user/delete-history" onclick="confirm('Are you sure you want to delete your watch history?')">Delete history</a>
<a class="btn btn-warning btn-sm" href="/user/delete-ratings" onclick="confirm('Are you sure you want to delete your movie ratings?')">Delete ratings</a>
{% if deletedUserHistory == true %}
<div class="alert alert-success alert-dismissible" role="alert" style="margin-left: 10%;margin-right: 10%;margin-bottom: 0.7rem!important;margin-top: 1rem">
History deleted successfully.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{% if deletedUserRatings == true %}
<div class="alert alert-success alert-dismissible" role="alert" style="margin-left: 10%;margin-right: 10%;margin-bottom: 0.7rem!important;margin-top: 1rem">
Ratings deleted successfully.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
</div>
<div style="margin-bottom: 1rem">
<a class="btn btn-danger btn-sm" href="/user/delete-account" onclick="confirm('Are you sure you want to delete your account with all your data?')">Delete
account</a>
</div>

</div>

<hr style="margin: 0;padding: 0">
<div style="padding-top: 1rem;padding-bottom: 0.1rem">

<p>Last <a href="https://trakt.tv/" target="_blank">trakt.tv</a> sync: <span class="fw-light">{{ lastSyncTrakt }}</span></p>
<p>Last <a href="https://themoviedb.org/" target="_blank">themoviedb.org</a> sync: <span class="fw-light">{{ lastSyncTmdb }}</span></p>
<p>Last <a href="https://letterboxd.com/" target="_blank">letterboxd</a> sync: <span class="fw-light">{{ lastSyncLetterboxd }}</span></p>
Expand Down

0 comments on commit 1132fb6

Please sign in to comment.