Fix feed links, clean up single tick pages (#69)
Fix feed links. Clean up single-tick page. Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/69 Co-authored-by: Greg Sarjeant <greg@subcultureofone.org> Co-committed-by: Greg Sarjeant <greg@subcultureofone.org>
This commit is contained in:
		
							parent
							
								
									d3a537aa6c
								
							
						
					
					
						commit
						eeb73eccd4
					
				| @ -370,6 +370,11 @@ time { | ||||
|     font-size: 1.0em; | ||||
|     display: block; | ||||
| } | ||||
| .tick-meta { | ||||
|     color: var(--color-log-muted); | ||||
|     font-size: 0.9em; | ||||
|     margin-bottom: 0.4em; | ||||
| } | ||||
| 
 | ||||
| .tick-pagination a { | ||||
|     margin: 0 5px; | ||||
|  | ||||
| @ -118,7 +118,7 @@ if ($method === 'POST' && $path != 'tkr-setup') { | ||||
|     if ($path != 'login'){ | ||||
|         if (!Session::isValid($_POST['csrf_token'])) { | ||||
|             // Invalid session - redirect to /login
 | ||||
|             Log::info('Attempt to POST with invalid session. Redirecting to login.'); | ||||
|             Log::warning('Attempt to POST with invalid session. Redirecting to login.'); | ||||
|             header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, 'login')); | ||||
|             exit; | ||||
|         } | ||||
|  | ||||
| @ -6,8 +6,8 @@ class HomeController extends Controller { | ||||
|     // renders the homepage view.
 | ||||
|     public function index(){ | ||||
|         $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; | ||||
|         $data = $this->getHomeData($page); | ||||
|         $this->render("home.php", $data); | ||||
|         $vars = $this->getHomeData($page); | ||||
|         $this->render("home.php", $vars); | ||||
|     } | ||||
| 
 | ||||
|     public function getHomeData(int $page): array { | ||||
|  | ||||
| @ -2,22 +2,27 @@ | ||||
| declare(strict_types=1); | ||||
| 
 | ||||
| class TickController extends Controller{ | ||||
|     public function index(int $id){ | ||||
|     public function index(string $id){ | ||||
|         // This is because my router is too simplistic to cleanly handle type casting,
 | ||||
|         // so I just accept a sting here and cast it to an int immediately.
 | ||||
|         $id = (int) $id; | ||||
|         global $app; | ||||
|         $vars = ['settings' => $app['settings']]; | ||||
| 
 | ||||
|         Log::debug("Fetching tick with ID: {$id}"); | ||||
| 
 | ||||
|         try { | ||||
|             $tickModel = new TickModel($app['db'], $app['settings']); | ||||
|             $vars = $tickModel->get($id); | ||||
|             $tick = $tickModel->get($id); | ||||
| 
 | ||||
|             if (empty($vars) || !isset($vars['tick'])) { | ||||
|             if (empty($tick) || !isset($tick['tick'])) { | ||||
|                 Log::warning("Tick not found for ID: {$id}"); | ||||
|                 http_response_code(404); | ||||
|                 echo '<h1>404 - Tick Not Found</h1>'; | ||||
|                 $this->render('tick-404.php', $vars); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             $vars = array_merge($tick, $vars); | ||||
|             Log::info("Successfully loaded tick {$id}: " . substr($vars['tick'], 0, 50) . (strlen($vars['tick']) > 50 ? '...' : '')); | ||||
|             $this->render('tick.php', $vars); | ||||
| 
 | ||||
| @ -30,30 +35,30 @@ class TickController extends Controller{ | ||||
| 
 | ||||
|     public function handleDelete(string $id){ | ||||
|         global $app; | ||||
|          | ||||
| 
 | ||||
|         $id = (int) $id; | ||||
|         Log::debug("Attempting to delete tick with ID: {$id}"); | ||||
|          | ||||
| 
 | ||||
|         try { | ||||
|             $tickModel = new TickModel($app['db'], $app['settings']); | ||||
|              | ||||
| 
 | ||||
|             // TickModel->delete() handles validation and sets flash messages:
 | ||||
|             // - "Tick not found" if tick doesn't exist
 | ||||
|             // - "Tick is too old to delete" if outside deletion window  
 | ||||
|             // - "Tick is too old to delete" if outside deletion window
 | ||||
|             // - "Deleted: '{content}'" on success
 | ||||
|             $success = $tickModel->delete($id); | ||||
|              | ||||
| 
 | ||||
|             if ($success) { | ||||
|                 Log::info("Successfully deleted tick {$id}"); | ||||
|             } else { | ||||
|                 Log::warning("Failed to delete tick {$id}"); | ||||
|             } | ||||
|              | ||||
| 
 | ||||
|         } catch (Exception $e) { | ||||
|             Log::error("Exception while deleting tick {$id}: " . $e->getMessage()); | ||||
|             Session::setFlashMessage('error', 'An error occurred while deleting the tick'); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         // Redirect back to homepage
 | ||||
|         header('Location: ' . Util::buildRelativeUrl($app['settings']->basePath, '')); | ||||
|         exit(); | ||||
|  | ||||
| @ -41,7 +41,7 @@ class AtomGenerator extends FeedGenerator { | ||||
|     $tickUrl = Util::escape_xml($siteUrl . $tickPath); | ||||
|     $tickTime = date(DATE_ATOM, strtotime($tick['timestamp'])); | ||||
|     $tickTitle = Util::escape_xml($tick['tick']); | ||||
|     $tickContent = Util::linkify($tickTitle); | ||||
|     $tickContent = Util::escape_xml(Util::linkify(Util::escape_html($tick['tick']))); | ||||
| ?>
 | ||||
|   <entry> | ||||
|     <title><?= $tickTitle ?></title>
 | ||||
|  | ||||
| @ -35,10 +35,11 @@ class RssGenerator extends FeedGenerator { | ||||
|     $tickUrl = Util::escape_xml($this->buildTickUrl($tick['id'])); | ||||
|     $tickDate = date(DATE_RSS, strtotime($tick['timestamp'])); | ||||
|     $tickTitle = Util::escape_xml($tick['tick']); | ||||
|     $tickDescription = Util::linkify($tickTitle); | ||||
|     $tickDescription = Util::escape_xml(Util::linkify(Util::escape_html($tick['tick']))); | ||||
|     Log::debug("RSS item: {$tickDescription}"); | ||||
| ?>
 | ||||
|     <item> | ||||
|         <title><?php echo $tickTitle ?></title>
 | ||||
|         <title><?php echo $tickTitle; ?></title>
 | ||||
|         <link><?php echo $tickUrl; ?></link>
 | ||||
|         <description><?php echo $tickDescription; ?></description>
 | ||||
|         <pubDate><?php echo $tickDate; ?></pubDate>
 | ||||
|  | ||||
| @ -7,14 +7,14 @@ class TickModel { | ||||
|     public function getPage(int $limit, int $offset = 0): array { | ||||
|         $stmt = $this->db->prepare("SELECT id, timestamp, tick FROM tick ORDER BY timestamp DESC LIMIT ? OFFSET ?"); | ||||
|         $stmt->execute([$limit, $offset]); | ||||
|          | ||||
| 
 | ||||
|         $ticks = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||
|          | ||||
| 
 | ||||
|         return array_map(function($tick) { | ||||
|             $tickTime = new DateTimeImmutable($tick['timestamp'], new DateTimeZone('UTC')); | ||||
|             $now = new DateTimeImmutable('now', new DateTimeZone('UTC')); | ||||
|             $hoursSinceCreation = ($now->getTimestamp() - $tickTime->getTimestamp()) / 3600; | ||||
|              | ||||
| 
 | ||||
|             $tick['can_delete'] = $hoursSinceCreation <= $this->settings->tickDeleteHours; | ||||
|             return $tick; | ||||
|         }, $ticks); | ||||
| @ -41,7 +41,6 @@ class TickModel { | ||||
|         return [ | ||||
|             'tickTime' => $row['timestamp'], | ||||
|             'tick' => $row['tick'], | ||||
|             'settings' => $this->settings, | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
| @ -50,26 +49,26 @@ class TickModel { | ||||
|         $stmt = $this->db->prepare("SELECT tick, timestamp FROM tick WHERE id=?"); | ||||
|         $stmt->execute([$id]); | ||||
|         $row = $stmt->fetch(PDO::FETCH_ASSOC); | ||||
|          | ||||
| 
 | ||||
|         if ($row === false || empty($row)) { | ||||
|             Session::setFlashMessage('error', 'Tick not found'); | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         // Check deletion window
 | ||||
|         $tickTime = new DateTimeImmutable($row['timestamp'], new DateTimeZone('UTC')); | ||||
|         $now = new DateTimeImmutable('now', new DateTimeZone('UTC')); | ||||
|         $hoursSinceCreation = ($now->getTimestamp() - $tickTime->getTimestamp()) / 3600; | ||||
|          | ||||
| 
 | ||||
|         if ($hoursSinceCreation > $this->settings->tickDeleteHours) { | ||||
|             Session::setFlashMessage('error', 'Tick is too old to delete'); | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         // Delete and set success message
 | ||||
|         $stmt = $this->db->prepare("DELETE FROM tick WHERE id=?"); | ||||
|         $stmt->execute([$id]); | ||||
|          | ||||
| 
 | ||||
|         Session::setFlashMessage('success', "Deleted: '{$row['tick']}'"); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
							
								
								
									
										4
									
								
								templates/partials/tick-404.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								templates/partials/tick-404.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
|         <div class="not-found-container"> | ||||
|             <h1>Tick Not Found</h1> | ||||
|             <p>The tick you're looking for has been deleted or never existed.</p> | ||||
|         </div> | ||||
| @ -1,4 +1,14 @@ | ||||
| <?php /** @var Date $tickTime */ ?>
 | ||||
| <?php /** @var string $tick */ ?>
 | ||||
|         <h1>Tick from <?= $tickTime; ?></h1>
 | ||||
|         <p><?= Util::linkify(Util::escape_html($tick)) ?></p>
 | ||||
| <?php $displayTime = DateTimeImmutable::createFromformat('Y-m-d H:i:s', $tickTime) ?>
 | ||||
|         <div class="tick-container"> | ||||
|             <article class="tick"> | ||||
|                 <header class="tick-header"> | ||||
|                     <h1>Tick</h1> | ||||
|                     <p class="tick-meta">Posted on <time class="tick-meta" datetime="<?= $displayTime->format('c') ?>"><?= $displayTime->format('F j, Y \a\t g:i A') ?></time> UTC</p>
 | ||||
|                 </header> | ||||
|                 <div class="tick-text"> | ||||
|                     <?= Util::linkify(Util::escape_html($tick)) ?>
 | ||||
|                 </div> | ||||
|             </article> | ||||
|         </div> | ||||
|  | ||||
| @ -62,7 +62,7 @@ class TickControllerTest extends TestCase | ||||
|         ob_start(); | ||||
| 
 | ||||
|         $controller = new TickController(); | ||||
|         $controller->index(123); | ||||
|         $controller->index("123"); | ||||
| 
 | ||||
|         $output = ob_get_clean(); | ||||
| 
 | ||||
| @ -96,12 +96,12 @@ class TickControllerTest extends TestCase | ||||
|         ob_start(); | ||||
| 
 | ||||
|         $controller = new TickController(); | ||||
|         $controller->index(999); | ||||
|         $controller->index("999"); | ||||
| 
 | ||||
|         $output = ob_get_clean(); | ||||
| 
 | ||||
|         // Should return 404 error
 | ||||
|         $this->assertStringContainsString('404 - Tick Not Found', $output); | ||||
|         $this->assertStringContainsString('Tick Not Found', $output); | ||||
|     } | ||||
| 
 | ||||
|     public function testIndexWithEmptyTickData(): void | ||||
| @ -125,12 +125,12 @@ class TickControllerTest extends TestCase | ||||
|         ob_start(); | ||||
| 
 | ||||
|         $controller = new TickController(); | ||||
|         $controller->index(456); | ||||
|         $controller->index("456"); | ||||
| 
 | ||||
|         $output = ob_get_clean(); | ||||
| 
 | ||||
|         // Should return 404 error for empty data
 | ||||
|         $this->assertStringContainsString('404 - Tick Not Found', $output); | ||||
|         $this->assertStringContainsString('Tick Not Found', $output); | ||||
|     } | ||||
| 
 | ||||
|     public function testIndexWithDatabaseException(): void | ||||
| @ -145,7 +145,7 @@ class TickControllerTest extends TestCase | ||||
|         ob_start(); | ||||
| 
 | ||||
|         $controller = new TickController(); | ||||
|         $controller->index(123); | ||||
|         $controller->index("123"); | ||||
| 
 | ||||
|         $output = ob_get_clean(); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user