alpha1 #8
| @ -36,10 +36,26 @@ header { | ||||
| } | ||||
| main { | ||||
|     padding: 0 .5rem; | ||||
| 
 | ||||
|     .item_heading { | ||||
|         margin-bottom: 0; | ||||
|     } | ||||
| 
 | ||||
|     .item_published { | ||||
|         margin-bottom: 1rem; | ||||
|         line-height: 1.2; | ||||
|     } | ||||
| } | ||||
| article { | ||||
|     max-width: 60rem; | ||||
|     margin: auto; | ||||
| 
 | ||||
|     .item_content { | ||||
|         border: solid 1px navy; | ||||
|         border-radius: .5rem; | ||||
|         background-color: white; | ||||
|         padding: .5rem; | ||||
|     } | ||||
| } | ||||
| input[type=url], input[type=text] { | ||||
|     width: 50%; | ||||
| @ -47,11 +63,27 @@ input[type=url], input[type=text] { | ||||
|     padding: .25rem; | ||||
|     border-radius: .25rem; | ||||
| } | ||||
| button { | ||||
| button, | ||||
| .action_buttons a:link, | ||||
| .action_buttons a:visited { | ||||
|     font-size: 1rem; | ||||
|     font-weight: normal; | ||||
|     background-color: navy; | ||||
|     color: white; | ||||
|     padding: .25rem 1rem; | ||||
|     padding: .5rem 1rem; | ||||
|     border-radius: .25rem; | ||||
|     cursor: pointer; | ||||
| } | ||||
|     border: none; | ||||
| } | ||||
| button:hover, | ||||
| .action_buttons a:hover { | ||||
|     text-decoration: none; | ||||
|     cursor: pointer; | ||||
|     background: linear-gradient(navy, #000032); | ||||
| } | ||||
| .action_buttons { | ||||
|     margin: 1rem 0; | ||||
|     display: flex; | ||||
|     flex-flow: row nowrap; | ||||
|     justify-content: space-evenly; | ||||
| } | ||||
|  | ||||
| @ -24,14 +24,9 @@ page_head('Welcome'); ?> | ||||
| <h1>Your Unread Items</h1> | ||||
| <article><?php | ||||
| if ($item) { | ||||
|     while ($item) { | ||||
|         try { | ||||
|             $asOf = (new DateTimeImmutable($item['as_of']))->format(DATE_TIME_FORMAT); | ||||
|         } catch (Exception) { | ||||
|             $asOf = '(invalid date)'; | ||||
|         } ?>
 | ||||
|         <p><a href=/item/view?id=<?=$item['id']?>><?=htmlentities($item['item_title'])?></a><br>
 | ||||
|             <?=htmlentities($item['feed_title'])?><br><small><em><?=$asOf?></em></small><?php
 | ||||
|     while ($item) { ?>
 | ||||
|         <p><a href=/item?id=<?=$item['id']?>><?=htmlentities($item['item_title'])?></a><br>
 | ||||
|             <?=htmlentities($item['feed_title'])?><br><small><em><?=date_time($item['as_of'])?></em></small><?php
 | ||||
|         $item = $result->fetchArray(SQLITE3_ASSOC); | ||||
|     } | ||||
| } else { ?>
 | ||||
|  | ||||
							
								
								
									
										79
									
								
								src/public/item.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/public/item.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| <?php | ||||
| 
 | ||||
| /** | ||||
|  * Item View Page | ||||
|  * | ||||
|  * Retrieves and displays an item from a feed belonging to the current user | ||||
|  */ | ||||
| 
 | ||||
| include '../start.php'; | ||||
| 
 | ||||
| Security::verifyUser(); | ||||
| 
 | ||||
| $db = Data::getConnection(); | ||||
| 
 | ||||
| if ($_SERVER['REQUEST_METHOD'] == 'POST') { | ||||
|     // "Keep as New" button sends a POST request to reset the is_read flag before going back to the list of unread items
 | ||||
|     $isValidQuery = $db->prepare(<<<'SQL' | ||||
|         SELECT COUNT(*) | ||||
|           FROM item INNER JOIN feed ON feed.id = item.feed_id | ||||
|          WHERE item.id = :id AND feed.user_id = :user | ||||
|         SQL); | ||||
|     $isValidQuery->bindValue(':id',   $_POST['id']); | ||||
|     $isValidQuery->bindValue(':user', $_REQUEST[Key::USER_ID]); | ||||
|     $isValidResult = $isValidQuery->execute(); | ||||
|     if ($isValidResult && $isValidResult->fetchArray(SQLITE3_NUM)[0] == 1) { | ||||
|         $keepUnread = $db->prepare('UPDATE item SET is_read = 0 WHERE id = :id'); | ||||
|         $keepUnread->bindValue(':id', $_POST['id']); | ||||
|         $keepUnread->execute(); | ||||
|     } | ||||
|     $db->close(); | ||||
|     frc_redirect('/'); | ||||
| } | ||||
| 
 | ||||
| $query = $db->prepare(<<<'SQL' | ||||
|     SELECT item.title AS item_title, item.item_link, item.published_on, item.updated_on, item.content, item.is_encoded, | ||||
|            feed.title AS feed_title | ||||
|       FROM item INNER JOIN feed ON feed.id = item.feed_id | ||||
|      WHERE item.id      = :id | ||||
|        AND feed.user_id = :user | ||||
|     SQL); | ||||
| $query->bindValue(':id',   $_GET['id']); | ||||
| $query->bindValue(':user', $_REQUEST[Key::USER_ID]); | ||||
| $result = $query->execute(); | ||||
| $item = $result ? $result->fetchArray(SQLITE3_ASSOC) : false; | ||||
| 
 | ||||
| if ($item) { | ||||
|     $markRead = $db->prepare('UPDATE item SET is_read = 1 WHERE id = :id'); | ||||
|     $markRead->bindValue(':id', $_GET['id']); | ||||
|     $markRead->execute(); | ||||
| } | ||||
| 
 | ||||
| $published = date_time($item['published_on']); | ||||
| $updated   = isset($item['updated_on']) ? date_time($item['updated_on']) : null; | ||||
| $isEncoded = (bool) $item['is_encoded']; | ||||
| 
 | ||||
| page_head(htmlentities("{$item['item_title']} | {$item['feed_title']}")); ?>
 | ||||
| <h1 class=item_heading> | ||||
|     <a href="<?=$item['item_link']?>" target=_blank rel=noopener><?=$item['item_title']?></a><br>
 | ||||
| </h1> | ||||
| <div class=item_published> | ||||
|     From <strong><?=htmlentities($item['feed_title'])?></strong><br>
 | ||||
|     Published <?=date_time($item['published_on'])?><?=$updated && $updated != $published ? " (Updated $updated)" : ''?>
 | ||||
| </div> | ||||
| <article> | ||||
|     <div class=item_content><?php | ||||
|         if ($isEncoded) { | ||||
|             echo str_replace('<a ', '<a target=_blank rel=noopener ', $item['content']); | ||||
|         } else { | ||||
|             echo htmlentities($item['content']); | ||||
|         } ?>
 | ||||
|     </div> | ||||
|     <form class=action_buttons action=/item method=POST> | ||||
|         <input type=hidden name=id value=<?=$_GET['id']?>>
 | ||||
|         <a href="/">Done</a> | ||||
|         <button type=submit>Keep as New</button> | ||||
|     </form> | ||||
| </article><?php | ||||
| page_foot(); | ||||
| $db->close(); | ||||
| @ -49,6 +49,7 @@ function page_head(string $title): void { | ||||
|     ?><!DOCTYPE html>
 | ||||
| <html lang=en> | ||||
| <head> | ||||
|     <meta name=viewport content="width=device-width, initial-scale=1"> | ||||
|     <title><?=$title?> | Feed Reader Central</title>
 | ||||
|     <link href=/assets/style.css rel=stylesheet> | ||||
| </head> | ||||
| @ -79,3 +80,32 @@ function page_head(string $title): void { | ||||
| function page_foot(): void { | ||||
|     ?></main></body></html><?php
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Redirect the user to the given URL | ||||
|  * | ||||
|  * @param string $value A local URL to which the user should be redirected | ||||
|  */ | ||||
| function frc_redirect(string $value) { | ||||
|     if (str_starts_with($value, 'http')) { | ||||
|         http_response_code(400); | ||||
|         die(); | ||||
|     } | ||||
|     header("Location: $value"); | ||||
|     http_response_code(303); | ||||
|     die(); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Convert a date/time string to a date/time in the configured format | ||||
|  * | ||||
|  * @param string $value The date/time string | ||||
|  * @return string The standard format of a date/time, or '(invalid date)' if the date could not be parsed | ||||
|  */ | ||||
| function date_time(string $value): string { | ||||
|     try { | ||||
|         return (new DateTimeImmutable($value))->format(DATE_TIME_FORMAT); | ||||
|     } catch (Exception) { | ||||
|         return '(invalid date)'; | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user