Skip to content

Commit

Permalink
Merge pull request #54 from leepeuker/add-change-date-format-to-settings
Browse files Browse the repository at this point in the history
Add way to change date format in frontend via settings page
  • Loading branch information
leepeuker authored Jul 14, 2022
2 parents 1132fb6 + 9bdf8ff commit 67178f4
Show file tree
Hide file tree
Showing 13 changed files with 227 additions and 14 deletions.
26 changes: 26 additions & 0 deletions db/migrations/20220714115426_AddDateFormatToUserTable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

use Phinx\Migration\AbstractMigration;

final class AddDateFormatToUserTable extends AbstractMigration
{
public function down() : void
{
$this->execute(
<<<SQL
ALTER TABLE user DROP COLUMN date_format_id;
SQL
);
}

public function up() : void
{
$this->execute(
<<<SQL
ALTER TABLE user ADD COLUMN date_format_id TINYINT UNSIGNED DEFAULT 0 AFTER password;
SQL
);
}
}
16 changes: 14 additions & 2 deletions public/js/logMovie.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const datepicker = new Datepicker(document.getElementById('watchDate'), {
format: 'dd.mm.yyyy',
format: document.getElementById('dateFormatJavascript').value,
title: 'Watch date',
})

Expand All @@ -9,7 +9,17 @@ function getCurrentDate () {
const mm = String(today.getMonth() + 1).padStart(2, '0') //January is 0!
const yyyy = today.getFullYear()

return dd + '.' + mm + '.' + yyyy
if (document.getElementById('dateFormatJavascript').value === 'dd.mm.yyyy') {
return dd + '.' + mm + '.' + yyyy
}
if (document.getElementById('dateFormatJavascript').value === 'dd.mm.yy') {
return dd + '.' + mm + '.' + yyyy.toString().slice(-2)
}
if (document.getElementById('dateFormatJavascript').value === 'yy-mm-dd') {
return yyyy.toString().slice(-2) + '-' + mm + '-' + dd
}

return yyyy + '-' + mm + '-' + dd
}

const watchModal = document.getElementById('watchDateModal')
Expand Down Expand Up @@ -168,6 +178,8 @@ function logMovie () {
}

function isValidDate (dateString) {
return true

// First check for the pattern
if (!/^\d{1,2}\.\d{1,2}\.\d{4}$/.test(dateString)) {
return false
Expand Down
5 changes: 5 additions & 0 deletions settings/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@
'/user/delete-account',
[\Movary\HttpController\SettingsController::class, 'deleteAccount']
);
$routeCollector->addRoute(
'POST',
'/user/date-format',
[\Movary\HttpController\SettingsController::class, 'updateDateFormatId']
);
$routeCollector->addRoute(
'DELETE',
'/user/plex-webhook-id',
Expand Down
18 changes: 17 additions & 1 deletion src/Application/User/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class Api
{
const PASSWORD_MIN_LENGTH = 8;
private const PASSWORD_MIN_LENGTH = 8;

public function __construct(private readonly Repository $repository)
{
Expand All @@ -28,6 +28,17 @@ public function deleteUser(int $userId) : void
$this->repository->deleteUser($userId);
}

public function fetchDateFormatId(int $userId) : int
{
$dateFormat = $this->repository->findDateFormatId($userId);

if ($dateFormat === null) {
throw new \RuntimeException('Could not find date format for user.');
}

return $dateFormat;
}

public function findPlexWebhookId(int $userId) : ?string
{
return $this->repository->findPlexWebhookId($userId);
Expand Down Expand Up @@ -68,6 +79,11 @@ public function regeneratePlexWebhookId(int $userId) : string
return $plexWebhookId;
}

public function updateDateFormatId(int $userId, int $dateFormat) : void
{
$this->repository->updateDateFormatId($userId, $dateFormat);
}

public function updatePassword(int $userId, string $newPassword) : void
{
if (strlen($newPassword) < self::PASSWORD_MIN_LENGTH) {
Expand Down
24 changes: 24 additions & 0 deletions src/Application/User/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ public function findAuthTokenExpirationDate(string $token) : ?DateTime
return DateTime::createFromString($expirationDate);
}

public function findDateFormatId(int $userId) : ?int
{
$dateFormat = $this->dbConnection->fetchOne('SELECT `date_format_id` FROM `user` WHERE `id` = ?', [$userId]);

if ($dateFormat === false) {
return null;
}

return (int)$dateFormat;
}

public function findPlexWebhookId(int $userId) : ?string
{
$plexWebhookId = $this->dbConnection->fetchOne('SELECT `plex_webhook_uuid` FROM `user` WHERE `id` = ?', [$userId]);
Expand Down Expand Up @@ -151,6 +162,19 @@ public function setPlexWebhookId(int $userId, ?string $plexWebhookId) : void
);
}

public function updateDateFormatId(int $userId, int $dateFormat) : void
{
$this->dbConnection->update(
'user',
[
'date_format_id' => $dateFormat,
],
[
'id' => $userId,
]
);
}

public function updatePassword(int $userId, string $passwordHash) : void
{
$this->dbConnection->update(
Expand Down
10 changes: 10 additions & 0 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Movary\Command;
use Movary\HttpController\SettingsController;
use Movary\ValueObject\Config;
use Movary\ValueObject\DateFormat;
use Movary\ValueObject\Http\Request;
use Phinx\Console\PhinxApplication;
use Psr\Container\ContainerInterface;
Expand Down Expand Up @@ -130,11 +131,20 @@ public static function createTwigEnvironment(ContainerInterface $container) : Tw
$twig->addGlobal('loggedIn', $userAuthenticated);

$currentUserId = null;
$dateFormatPhp = DateFormat::getPhpDefault();
$dataFormatJavascript = DateFormat::getJavascriptDefault();
if ($userAuthenticated === true) {
$currentUserId = $container->get(Authentication::class)->getCurrentUserId();

$userDateFormat = $container->get(User\Api::class)->fetchDateFormatId($currentUserId);

$dateFormatPhp = DateFormat::getPhpById($userDateFormat);
$dataFormatJavascript = DateFormat::getJavascriptById($userDateFormat);
}

$twig->addGlobal('routeUserId', $routeUserId ?? $currentUserId);
$twig->addGlobal('dateFormatPhp', $dateFormatPhp);
$twig->addGlobal('dateFormatJavascript', $dataFormatJavascript);

return $twig;
}
Expand Down
26 changes: 26 additions & 0 deletions src/HttpController/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Movary\Application\SyncLog\Repository;
use Movary\Application\User;
use Movary\Application\User\Service\Authentication;
use Movary\ValueObject\DateFormat;
use Movary\ValueObject\Http\Header;
use Movary\ValueObject\Http\Request;
use Movary\ValueObject\Http\Response;
Expand Down Expand Up @@ -96,6 +97,7 @@ public function render() : Response
$importHistoryError = empty($_SESSION['importHistoryError']) === false ? $_SESSION['importHistoryError'] : null;
$deletedUserHistory = empty($_SESSION['deletedUserHistory']) === false ? $_SESSION['deletedUserHistory'] : null;
$deletedUserRatings = empty($_SESSION['deletedUserRatings']) === false ? $_SESSION['deletedUserRatings'] : null;
$dateFormatUpdated = empty($_SESSION['dateFormatUpdated']) === false ? $_SESSION['dateFormatUpdated'] : null;
unset(
$_SESSION['passwordUpdated'],
$_SESSION['passwordErrorCurrentInvalid'],
Expand All @@ -107,11 +109,15 @@ public function render() : Response
$_SESSION['importHistoryError'],
$_SESSION['deletedUserHistory'],
$_SESSION['deletedUserRatings'],
$_SESSION['dateFormatUpdated'],
);

return Response::create(
StatusCode::createOk(),
$this->twig->render('page/settings.html.twig', [
'dateFormats' => DateFormat::getFormats(),
'dateFormatSelected' => $this->userApi->fetchDateFormatId($userId),
'dateFormatUpdated' => $dateFormatUpdated,
'plexWebhookUrl' => $this->userApi->findPlexWebhookId($userId) ?? '-',
'passwordErrorNotEqual' => $passwordErrorNotEqual,
'passwordErrorMinLength' => $passwordErrorMinLength,
Expand All @@ -133,6 +139,26 @@ public function render() : Response
);
}

public function updateDateFormatId(Request $request) : Response
{
if ($this->authenticationService->isUserAuthenticated() === false) {
return Response::createFoundRedirect('/');
}

$postParameters = $request->getPostParameters();
$dateFormat = empty($postParameters['dateFormat']) === true ? 0 : (int)$postParameters['dateFormat'];

$this->userApi->updateDateFormatId($this->authenticationService->getCurrentUserId(), $dateFormat);

$_SESSION['dateFormatUpdated'] = true;

return Response::create(
StatusCode::createSeeOther(),
null,
[Header::createLocation($_SERVER['HTTP_REFERER'])]
);
}

public function updatePassword(Request $request) : Response
{
if ($this->authenticationService->isUserAuthenticated() === false) {
Expand Down
64 changes: 64 additions & 0 deletions src/ValueObject/DateFormat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php declare(strict_types=1);

namespace Movary\ValueObject;

class DateFormat
{
private const DEFAULT_ID = 0;

private const FORMATS = [
self::DEFAULT_ID => [
self::KEY_PHP => 'y-m-d',
self::KEY_JAVASCRIPT => 'yy-mm-dd',
],
1 => [
self::KEY_PHP => 'Y-m-d',
self::KEY_JAVASCRIPT => 'yyyy-mm-dd',
],
2 => [
self::KEY_PHP => 'd.m.y',
self::KEY_JAVASCRIPT => 'dd.mm.yy',
],
3 => [
self::KEY_PHP => 'd.m.Y',
self::KEY_JAVASCRIPT => 'dd.mm.yyyy',
],
];

private const KEY_JAVASCRIPT = 'javascript';

private const KEY_PHP = 'php';

public static function getFormats() : array
{
return self::FORMATS;
}

public static function getJavascriptById(int $id) : string
{
if (isset(self::FORMATS[$id]) === false) {
throw new \RuntimeException('Id does not exist: ' . $id);
}

return self::FORMATS[$id][self::KEY_JAVASCRIPT];
}

public static function getJavascriptDefault() : string
{
return self::getJavascriptById(self::DEFAULT_ID);
}

public static function getPhpById(int $offset) : string
{
if (isset(self::FORMATS[$offset]) === false) {
throw new \RuntimeException('Id does not exist: ' . $offset);
}

return self::FORMATS[$offset][self::KEY_PHP];
}

public static function getPhpDefault() : string
{
return self::getPhpById(self::DEFAULT_ID);
}
}
17 changes: 11 additions & 6 deletions templates/page/dashboard.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

</div>
<div class="card-footer" style="padding: 0.1rem">
<small class="text-muted" style="font-size: 0.8rem;">{{ lastPlay.watched_at|date("d.m.y") }}</small>
<small class="text-muted" style="font-size: 0.8rem;">{{ lastPlay.watched_at|date(dateFormatPhp) }}</small>
</div>

{% if lastPlay.user_rating is not null %}
Expand Down Expand Up @@ -94,7 +94,8 @@
</div>
{% endfor %}
</div>
<a class="btn btn-outline-secondary btn-sm" href="/{{ routeUserId }}/most-watched-actors" style="margin-bottom: 1rem;width: 100%;border-color: lightgrey">more</a>
<a class="btn btn-outline-secondary btn-sm" href="/{{ routeUserId }}/most-watched-actors"
style="margin-bottom: 1rem;width: 100%;border-color: lightgrey">more</a>
</li>


Expand All @@ -106,7 +107,8 @@
<div class="row row-cols-3 row-cols-md-3 row-cols-lg-6">
{% for mostWatchedActress in mostWatchedActresses %}
<div class="col" style="padding-bottom: 1rem;">
<div class="card h-100 position-relative" onclick="window.location='/{{ routeUserId }}/person/{{ mostWatchedActress.id }}'" style="cursor: pointer;">
<div class="card h-100 position-relative" onclick="window.location='/{{ routeUserId }}/person/{{ mostWatchedActress.id }}'"
style="cursor: pointer;">
<div class="card-header text-truncate" style="padding: 0.2rem">
<small class="text-muted" style="font-size: 0.9rem;">{{ mostWatchedActress.name }}</small>
</div>
Expand All @@ -120,7 +122,8 @@
</div>
{% endfor %}
</div>
<a class="btn btn-outline-secondary btn-sm" href="/{{ routeUserId }}/most-watched-actors" style="margin-bottom: 1rem;width: 100%;border-color: lightgrey">more</a>
<a class="btn btn-outline-secondary btn-sm" href="/{{ routeUserId }}/most-watched-actors"
style="margin-bottom: 1rem;width: 100%;border-color: lightgrey">more</a>
</li>

<li class="list-group-item list-group-item-action user-select-none inactiveItemButton clearfix" onclick="toggleButton(this)">
Expand All @@ -131,7 +134,8 @@
<div class="row row-cols-3 row-cols-md-3 row-cols-lg-6">
{% for mostWatchedDirector in mostWatchedDirectors %}
<div class="col" style="padding-bottom: 1rem;">
<div class="card h-100 position-relative" onclick="window.location='/{{ routeUserId }}/person/{{ mostWatchedDirector.id }}'" style="cursor: pointer;">
<div class="card h-100 position-relative" onclick="window.location='/{{ routeUserId }}/person/{{ mostWatchedDirector.id }}'"
style="cursor: pointer;">
<div class="card-header text-truncate" style="padding: 0.2rem">
<small class="text-muted" style="font-size: 0.9rem;">{{ mostWatchedDirector.name }}</small>
</div>
Expand All @@ -144,7 +148,8 @@
</div>
</div>
{% endfor %}
<a class="btn btn-outline-secondary btn-sm" href="/{{ routeUserId }}/most-watched-directors" style="margin-bottom: 1rem;width: 100%;border-color: lightgrey">more</a>
<a class="btn btn-outline-secondary btn-sm" href="/{{ routeUserId }}/most-watched-directors"
style="margin-bottom: 1rem;width: 100%;border-color: lightgrey">more</a>
</div>
</li>

Expand Down
2 changes: 1 addition & 1 deletion templates/page/history.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<div class="card h-100 position-relative">
<img src="{{ tmdb.generatePosterImageUrl(historyEntry.tmdb_poster_path) }}" class="card-img-top" alt="...">
<div class="card-footer" style="padding: 0.1rem">
<small class="text-muted" style="font-size: 0.8rem">{{ historyEntry.watched_at|date("d.m.Y") }}</small>
<small class="text-muted" style="font-size: 0.8rem">{{ historyEntry.watched_at|date(dateFormatPhp) }}</small>
</div>

{% if historyEntry.userRating is not null %}
Expand Down
6 changes: 4 additions & 2 deletions templates/page/logMovie.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
{% block body %}
<main role="main" class="container">
{{ include('component/navbar.html.twig') }}
<input value="{{ dateFormatJavascript }}" id="dateFormatJavascript" hidden>

<div id="alerts" style="margin-top: 1rem">
</div>
Expand Down Expand Up @@ -51,7 +52,7 @@

<br>

<div class="modal fade" id="watchDateModal" tabindex="-1" aria-labelledby="watchDateModal" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false" >
<div class="modal fade" id="watchDateModal" tabindex="-1" aria-labelledby="watchDateModal" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
Expand All @@ -62,7 +63,8 @@
<input type="hidden" value="" name="tmdbId" id="tmdbId" required>
<div class="input-group" id="watchDateGroup">
<i class="bi bi-calendar-date input-group-text"></i>
<input type="text" class="datepicker_input form-control" placeholder="Watch date" aria-label="Watch date" name="watchDate" id="watchDate" required onfocusout="validateWatchDate(this.value)">
<input type="text" class="datepicker_input form-control" placeholder="Watch date" aria-label="Watch date" name="watchDate" id="watchDate" required
onfocusout="validateWatchDate(this.value)">
<div class="invalid-feedback" id="watchDateValidationRequiredErrorMessage" style="padding-bottom: 0;margin-bottom: 0">
Watch date is required!
</div>
Expand Down
Loading

0 comments on commit 67178f4

Please sign in to comment.