Add cancel snooze

- Add common request validation function
This commit is contained in:
Daniel J. Summers 2024-06-23 06:58:47 -04:00
parent d6e8cf66cc
commit 9421bb2035
11 changed files with 96 additions and 83 deletions

View File

@ -528,7 +528,7 @@ let routes = [
GET_HEAD [ GET_HEAD [
route "journal-items" Components.journalItems // done route "journal-items" Components.journalItems // done
routef "request/%s/add-notes" Components.addNotes // done routef "request/%s/add-notes" Components.addNotes // done
routef "request/%s/item" Components.requestItem // not used routef "request/%s/item" Components.requestItem
routef "request/%s/notes" Components.notes // done routef "request/%s/notes" Components.notes // done
routef "request/%s/snooze" Components.snooze // done routef "request/%s/snooze" Components.snooze // done
] ]

View File

@ -168,6 +168,44 @@ class UI
return $dtFrom > $dtTo ? "$value ago" : "in $value"; return $dtFrom > $dtTo ? "$value ago" : "in $value";
} }
public static function requestItem(Request $req): void
{
$btnClass = "btn btn-light mx-2";
$restoreBtn = fn(string $id, string $link, string $title) =>
'<button class="' . $btnClass. '" hx-patch="/request/' . $link . '?id=' . $id
. '" title="' . htmlspecialchars($title) . '">' . self::icon('restore') . '</button>'; ?>
<div class="list-group-item px-0 d-flex flex-row align-items-start" hx-target=this
hx-swap=outerHTML><?php
self::pageLink("/request/full?id=$req->id", self::icon('description'),
['class' => $btnClass, 'title' => 'View Full Request']);
if (!$req->isAnswered()) {
self::pageLink("/request/edit?id=$req->id", self::icon('edit'),
['class' => $btnClass, 'title' => 'Edit Request']);
}
if ($req->isSnoozed()) {
echo $restoreBtn($req->id, 'cancel-snooze', 'Cancel Snooze');
} elseif ($req->isPending()) {
echo $restoreBtn($req->id, 'show', 'Show Now');
}
echo '<p class="request-text mb-0">' . $req->currentText();
if ($req->isSnoozed() || $req->isPending() || $req->isAnswered()) { ?>
<br>
<small class=text-muted><em><?php
switch (true) {
case $req->isSnoozed():
echo 'Snooze expires '; self::relativeDate($req->snoozedUntil);
break;
case $req->isPending():
echo 'Request appears next '; self::relativeDate($req->showAfter);
break;
default:
echo 'Answered '; self::relativeDate($req->history[0]->asOf);
} ?>
</em></small><?php
} ?>
</div><?php
}
/** /**
* Render the given list of requests * Render the given list of requests
* *
@ -176,44 +214,9 @@ class UI
*/ */
public static function requestList(DocumentList $reqs): void public static function requestList(DocumentList $reqs): void
{ {
$btnClass = "btn btn-light mx-2"; echo '<div class=list-group>';
$restoreBtn = fn(string $id, string $link, string $title) => foreach ($reqs->items() as $req) self::requestItem($req);
'<button class="' . $btnClass. '" hx-patch="/request/' . $link . '?id=' . $id echo '</div>';
. '" title="' . htmlspecialchars($title) . '">' . self::icon('restore') . '</button>'; ?>
<div class=list-group><?php
foreach ($reqs->items() as /** @var Request $req */ $req) { ?>
<div class="list-group-item px-0 d-flex flex-row align-items-start" hx-target=this
hx-swap=outerHTML><?php
self::pageLink("/request/full?id=$req->id", self::icon('description'),
['class' => $btnClass, 'title' => 'View Full Request']);
if (!$req->isAnswered()) {
self::pageLink("/request/edit?id=$req->id", self::icon('edit'),
['class' => $btnClass, 'title' => 'Edit Request']);
}
if ($req->isSnoozed()) {
echo $restoreBtn($req->id, 'cancel-snooze', 'Cancel Snooze');
} elseif ($req->isPending()) {
echo $restoreBtn($req->id, 'show', 'Show Now');
}
echo '<p class="request-text mb-0">' . $req->currentText();
if ($req->isSnoozed() || $req->isPending() || $req->isAnswered()) { ?>
<br>
<small class=text-muted><em><?php
switch (true) {
case $req->isSnoozed():
echo 'Snooze expires '; self::relativeDate($req->snoozedUntil);
break;
case $req->isPending():
echo 'Request appears next '; self::relativeDate($req->showAfter);
break;
default:
echo 'Answered '; self::relativeDate($req->history[0]->asOf);
} ?>
</em></small><?php
} ?>
</div><?php
} ?>
</div><?php
} }
} }

View File

@ -3,12 +3,8 @@
use MyPrayerJournal\{Auth, Layout, Request}; use MyPrayerJournal\{Auth, Layout, Request};
require '../../../start.php'; require '../../../start.php';
if ($_SERVER['REQUEST_METHOD'] <> 'GET') not_found();
Auth::requireUser(false); $req = validate_request($_GET['id'], ['GET'], false);
$req = Request::byId($_GET['id']);
if (!$req) not_found();
Layout::bareHead(); ?> Layout::bareHead(); ?>
<form hx-post="/request/note?id=<?=$req->id?>"> <form hx-post="/request/note?id=<?=$req->id?>">

View File

@ -1,14 +1,10 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
use MyPrayerJournal\{Auth, Layout, Request, UI}; use MyPrayerJournal\{Layout, UI};
require '../../../start.php'; require '../../../start.php';
if ($_SERVER['REQUEST_METHOD'] <> 'GET') not_found();
Auth::requireUser(false); $req = validate_request($_GET['id'], ['GET'], false);
$req = Request::byId($_GET['id']);
if (!$req) not_found();
Layout::bareHead();?> Layout::bareHead();?>
<p class=text-center><strong>Prior Notes for This Request</strong><?php <p class=text-center><strong>Prior Notes for This Request</strong><?php

View File

@ -1,14 +1,10 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
use MyPrayerJournal\{Auth, Layout, Request}; use MyPrayerJournal\Layout;
require '../../../start.php'; require '../../../start.php';
if ($_SERVER['REQUEST_METHOD'] <> 'GET') not_found();
Auth::requireUser(false); $req = validate_request($_GET['id'], ['GET'], false);
$req = Request::byId($_GET['id']);
if (!$req) not_found();
Layout::bareHead(); ?> Layout::bareHead(); ?>
<form hx-patch="/request/snooze?id=<?=$req->id?>" hx-target=#journalItems hx-swap=outerHTML> <form hx-patch="/request/snooze?id=<?=$req->id?>" hx-target=#journalItems hx-swap=outerHTML>

View File

@ -0,0 +1,15 @@
<?php declare(strict_types=1);
use MyPrayerJournal\{Layout, Table, UI};
use BitBadger\PDODocument\RemoveFields;
require '../../../start.php';
$req = validate_request($_GET['id'], ['GET'], false);
RemoveFields::byId(Table::REQUEST, $req->id, ['snoozedUntil']);
// TODO: message
Layout::bareHead();
UI::requestItem($req);
Layout::bareFoot();

View File

@ -1,14 +1,10 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
use MyPrayerJournal\{Auth, History, Layout, Note, Request, RequestAction, UI}; use MyPrayerJournal\{History, Layout, Note, RequestAction, UI};
require '../../start.php'; require '../../start.php';
if ($_SERVER['REQUEST_METHOD'] <> 'GET') not_found();
Auth::requireUser(); $req = validate_request($_GET['id'], ['GET']);
$req = Request::byId($_GET['id']);
if (!$req) not_found();
$answered = $req->isAnswered() ? new DateTimeImmutable($req->history[0]->asOf) : null; $answered = $req->isAnswered() ? new DateTimeImmutable($req->history[0]->asOf) : null;
$prayed = sizeof(array_filter($req->history, fn(History $hist) => $hist->action == RequestAction::Prayed)); $prayed = sizeof(array_filter($req->history, fn(History $hist) => $hist->action == RequestAction::Prayed));

View File

@ -1,15 +1,11 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
use MyPrayerJournal\{Auth, Note, Request, Table}; use MyPrayerJournal\{Note, Table};
use BitBadger\PDODocument\Patch; use BitBadger\PDODocument\Patch;
require '../../start.php'; require '../../start.php';
if ($_SERVER['REQUEST_METHOD'] <> 'POST') not_found();
Auth::requireUser(false); $req = validate_request($_GET['id'], ['POST'], false);
$req = Request::byId($_GET['id']);
if (!$req) not_found();
array_unshift($req->notes, new Note((new DateTimeImmutable('now'))->format('c'), $_POST['notes'])); array_unshift($req->notes, new Note((new DateTimeImmutable('now'))->format('c'), $_POST['notes']));
Patch::byId(Table::REQUEST, $req->id, ['notes' => $req->notes]); Patch::byId(Table::REQUEST, $req->id, ['notes' => $req->notes]);

View File

@ -1,16 +1,11 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
use MyPrayerJournal\{Auth, History, RecurrencePeriod, Request, RequestAction, Table, UI}; use MyPrayerJournal\{History, RecurrencePeriod, RequestAction, Table, UI};
use BitBadger\PDODocument\Patch; use BitBadger\PDODocument\Patch;
require '../../start.php'; require '../../start.php';
if ($_SERVER['REQUEST_METHOD'] <> 'PATCH') not_found();
Auth::requireUser(false);
$req = Request::byId($_GET['id']);
if (!$req) not_found();
$req = validate_request($_GET['id'], ['PATCH'], false);
$now = new DateTimeImmutable('now'); $now = new DateTimeImmutable('now');
array_unshift($req->history, new History($now->format('c'), RequestAction::Prayed)); array_unshift($req->history, new History($now->format('c'), RequestAction::Prayed));

View File

@ -1,15 +1,11 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
use MyPrayerJournal\{Auth, Request, Table, UI}; use MyPrayerJournal\{Table, UI};
use BitBadger\PDODocument\Patch; use BitBadger\PDODocument\Patch;
require '../../start.php'; require '../../start.php';
if ($_SERVER['REQUEST_METHOD'] <> 'PATCH') not_found();
Auth::requireUser(false); $req = validate_request($_GET['id'], ['PATCH'], false);
$req = Request::byId($_GET['id']);
if (!$req) not_found();
$until = (new DateTimeImmutable($_PATCH['until'] . 'T00:00:00', new DateTimeZone($_REQUEST['time_zone']))) $until = (new DateTimeImmutable($_PATCH['until'] . 'T00:00:00', new DateTimeZone($_REQUEST['time_zone'])))
->setTimezone(new DateTimeZone('Etc/UTC')); ->setTimezone(new DateTimeZone('Etc/UTC'));

View File

@ -1,8 +1,9 @@
<?php declare(strict_types=1); <?php declare(strict_types=1);
use BitBadger\PDODocument\{Configuration, Definition, Mode}; use Auth0\SDK\Exception\ConfigurationException;
use BitBadger\PDODocument\{Configuration, Definition, DocumentException, Mode};
use Dotenv\Dotenv; use Dotenv\Dotenv;
use MyPrayerJournal\{Auth, Layout, Table}; use MyPrayerJournal\{Auth, Request, Table};
require __DIR__ . '/vendor/autoload.php'; require __DIR__ . '/vendor/autoload.php';
@ -25,6 +26,7 @@ if (php_sapi_name() != 'cli') {
Configuration::$pdoDSN = 'sqlite:' . implode(DIRECTORY_SEPARATOR, [__DIR__, 'data', 'mpj.db']); Configuration::$pdoDSN = 'sqlite:' . implode(DIRECTORY_SEPARATOR, [__DIR__, 'data', 'mpj.db']);
Configuration::$mode = Mode::SQLite; Configuration::$mode = Mode::SQLite;
Definition::ensureTable(Table::REQUEST); Definition::ensureTable(Table::REQUEST);
Definition::ensureFieldIndex(Table::REQUEST, 'user', ['userId']);
$_PATCH = []; $_PATCH = [];
if ($_SERVER['REQUEST_METHOD'] ?? '' == 'PATCH') parse_str(file_get_contents('php://input'), $_PATCH); if ($_SERVER['REQUEST_METHOD'] ?? '' == 'PATCH') parse_str(file_get_contents('php://input'), $_PATCH);
@ -59,3 +61,25 @@ function hide_modal(string $name): void
{ {
header("X-Hide-Modal: $name"); header("X-Hide-Modal: $name");
} }
/**
* Validate the user, HTTP method, and request
*
* @param string $id The ID of the prayer request to retrieve
* @param array $methods The allowable HTTP methods
* @param bool $redirect Whether to redirect not-logged-on users (optional, defaults to true)
* @return Request The request (failures will not return)
* @throws ConfigurationException If any is encountered
* @throws DocumentException If any is encountered
*/
function validate_request(string $id, array $methods, bool $redirect = true): Request
{
if (sizeof(array_filter($methods, fn($it) => $_SERVER['REQUEST_METHOD'] == $it)) == 0) not_found();
Auth::requireUser($redirect);
$req = Request::byId($id);
if (!$req) not_found();
return $req;
}