Commit 468bc55ada
Unsigned
Layout: unified · split
LICENSE added +24
| @@ -0,0 +1,24 @@ | ||
| 1 | This is free and unencumbered software released into the public domain. | |
| 2 | ||
| 3 | Anyone is free to copy, modify, publish, use, compile, sell, or | |
| 4 | distribute this software, either in source code form or as a compiled | |
| 5 | binary, for any purpose, commercial or non-commercial, and by any | |
| 6 | means. | |
| 7 | ||
| 8 | In jurisdictions that recognize copyright laws, the author or authors | |
| 9 | of this software dedicate any and all copyright interest in the | |
| 10 | software to the public domain. We make this dedication for the benefit | |
| 11 | of the public at large and to the detriment of our heirs and | |
| 12 | successors. We intend this dedication to be an overt act of | |
| 13 | relinquishment in perpetuity of all present and future rights to this | |
| 14 | software under copyright law. | |
| 15 | ||
| 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
| 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 22 | OTHER DEALINGS IN THE SOFTWARE. | |
| 23 | ||
| 24 | For more information, please refer to <http://unlicense.org/> | |
README.md added +5
| @@ -0,0 +1,5 @@ | ||
| 1 | # hn | |
| 2 | ||
| 3 | [hn](https://hn.cleberg.net) is a simple front-end alternative for Hacker News. | |
| 4 | hn focuses on the links shared on Hacker News, currently leaving out an options | |
| 5 | to login or view comments. | |
index.php added +112
| @@ -0,0 +1,112 @@ | ||
| 1 | <?php | |
| 2 | ||
| 3 | $full_domain = 'https://hn.cleberg.net'; | |
| 4 | $path = ltrim($_SERVER['REQUEST_URI'], '/'); | |
| 5 | $elements = explode('/', $path); | |
| 6 | ||
| 7 | if (empty($elements[0])) { | |
| 8 | get_stories( | |
| 9 | 'https://hacker-news.firebaseio.com/v0/topstories.json?limitToFirst=30&orderBy="$key"', | |
| 10 | 'Top' | |
| 11 | ); | |
| 12 | echo_html( | |
| 13 | $GLOBALS['full_domain'] . '/best/', | |
| 14 | 'The top 30 stories from Hacker News. Proxied by hn.', | |
| 15 | 'hn', | |
| 16 | $html_output | |
| 17 | ); | |
| 18 | } else { | |
| 19 | switch (array_shift($elements)) { | |
| 20 | case 'top': | |
| 21 | get_stories( | |
| 22 | 'https://hacker-news.firebaseio.com/v0/topstories.json?limitToFirst=30&orderBy="$key"', | |
| 23 | 'Top' | |
| 24 | ); | |
| 25 | echo_html( | |
| 26 | $GLOBALS['full_domain'] . '/best/', | |
| 27 | 'The top 30 stories from Hacker News. Proxied by hn.', | |
| 28 | 'hn', | |
| 29 | $html_output | |
| 30 | ); | |
| 31 | break; | |
| 32 | ||
| 33 | case 'best': | |
| 34 | echo 'best'; | |
| 35 | break; | |
| 36 | ||
| 37 | case 'new': | |
| 38 | echo 'new'; | |
| 39 | break; | |
| 40 | ||
| 41 | case 'ask': | |
| 42 | echo 'ask'; | |
| 43 | break; | |
| 44 | ||
| 45 | case 'show': | |
| 46 | echo 'show'; | |
| 47 | break; | |
| 48 | ||
| 49 | case 'job': | |
| 50 | echo 'job'; | |
| 51 | break; | |
| 52 | ||
| 53 | default: | |
| 54 | header('HTTP/1.1 404 Not Found'); | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | /** | |
| 59 | * Extract a set of stories from Hacker News API and format in HTML | |
| 60 | * | |
| 61 | * @access public | |
| 62 | * @author cmc <hello@cleberg.net> | |
| 63 | * @param string $api_url The API endpoint to use for extraction | |
| 64 | * @param string $inline_title The <h1> title to use in the HTML | |
| 65 | * @return string $html_output The formatted HTML result of stories from the API | |
| 66 | */ | |
| 67 | function get_stories($api_url, $inline_title) | |
| 68 | { | |
| 69 | $response_raw = file_get_contents($api_url); | |
| 70 | $response = json_decode($response_raw, true); | |
| 71 | ||
| 72 | $html_output = '<h1>' . $inline_title . '</h1>'; | |
| 73 | ||
| 74 | for ($i = 0; $i < count($response); $i++) { | |
| 75 | $sub_url = 'https://hacker-news.firebaseio.com/v0/item/' . $response[$i] . '.json'; | |
| 76 | $sub_response_raw = file_get_contents($sub_url); | |
| 77 | $sub_response = json_decode($sub_response_raw, true); | |
| 78 | ||
| 79 | $html = '<div><a href="' . $sub_response['url'] . '">' . $sub_response['title'] . '</a>'; | |
| 80 | $html .= '<p><time datetime="' . date('Y-m-d h:m:s', $sub_response['time']) . '">'; | |
| 81 | $html .= date('Y-m-d h:m:s', $sub_response['time']) . '</time> by '; | |
| 82 | $html .= $sub_response['by'] . ' | ' . $sub_response['score'] . ' points</p></div>'; | |
| 83 | $html_output .= $html; | |
| 84 | } | |
| 85 | ||
| 86 | return $html_output; | |
| 87 | } | |
| 88 | ||
| 89 | /** | |
| 90 | * Send formatted HTML results to the user via a template | |
| 91 | * | |
| 92 | * @access public | |
| 93 | * @author cmc <hello@cleberg.net> | |
| 94 | * @param string $page_url Canoncial URL for HTML header | |
| 95 | * @param string $page_description Page description for HTML header | |
| 96 | * @param string $page_title Page title for HTML header | |
| 97 | * @param string $page_content Page content to display in <main> | |
| 98 | */ | |
| 99 | function echo_html(string $page_url, string $page_description, string $page_title, string $page_content) { | |
| 100 | include_once '_classes/template.php'; | |
| 101 | ||
| 102 | $template = new HN\View\Template( | |
| 103 | $page_url, | |
| 104 | $page_description, | |
| 105 | $page_title, | |
| 106 | $page_content | |
| 107 | ); | |
| 108 | ||
| 109 | $template->echo_template(); | |
| 110 | } | |
| 111 | ||
| 112 | // EOF | |
src/View/class-template.php added +39
| @@ -0,0 +1,39 @@ | ||
| 1 | <?php | |
| 2 | ||
| 3 | namespace HN\View; | |
| 4 | ||
| 5 | /** | |
| 6 | * Template View | |
| 7 | * | |
| 8 | * @author cmc <hello@cleberg.net> | |
| 9 | */ | |
| 10 | class Template | |
| 11 | { | |
| 12 | public function __construct(string $canonical_url, string $page_description, string $page_title, string $content_col) | |
| 13 | { | |
| 14 | $this->canonical_url = $canonical_url; | |
| 15 | $this->description = $page_description; | |
| 16 | $this->title = $page_title; | |
| 17 | $this->content = $content_col; | |
| 18 | $this->current_year = date("Y"); | |
| 19 | } | |
| 20 | ||
| 21 | public function echo_template() | |
| 22 | { | |
| 23 | // Get the template file | |
| 24 | $template_file = 'templates/template.html'; | |
| 25 | $page = file_get_contents($template_file); | |
| 26 | ||
| 27 | // Replace the template variables | |
| 28 | $page = str_replace('{page_title}', $this->title, $page); | |
| 29 | $page = str_replace('{page_description}', $this->description, $page); | |
| 30 | $page = str_replace('{canonical_url}', $this->canonical_url, $page); | |
| 31 | $page = str_replace('{content}', $this->content, $page); | |
| 32 | $page = str_replace('{current_year}', $this->current_year, $page); | |
| 33 | ||
| 34 | // Echo the filled-out template | |
| 35 | echo $page; | |
| 36 | } | |
| 37 | } | |
| 38 | ||
| 39 | // EOF | |
templates/template.html added +37
| @@ -0,0 +1,37 @@ | ||
| 1 | <!doctype html> | |
| 2 | <html lang="en"> | |
| 3 | ||
| 4 | <head> | |
| 5 | <title>{page_title}</title> | |
| 6 | <meta charset="UTF-8"> | |
| 7 | <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
| 8 | <meta http-equiv="x-ua-compatible" content="ie=edge"> | |
| 9 | <meta name="author" content="My Name"> | |
| 10 | <meta name="description" content="{page_description}"> | |
| 11 | <link rel="canonical" href="{canoncial_url}"> | |
| 12 | <link rel="icon" href="/favicon.ico"> | |
| 13 | <link rel="stylesheet" href="/static/styles.css"> | |
| 14 | </head> | |
| 15 | ||
| 16 | <body> | |
| 17 | <main id="main"> | |
| 18 | <nav class="links"> | |
| 19 | <span><a href="/">Top</a> · </span> | |
| 20 | <span><a href="/best/">Best</a> ·</span> | |
| 21 | <span><a href="/new/">New</a> ·</span> | |
| 22 | <span><a href="/ask/">Ask</a> ·</span> | |
| 23 | <span><a href="/show/">Show</a> ·</span> | |
| 24 | <span><a href="/job/">Job</a></span> | |
| 25 | </nav> | |
| 26 | {content} | |
| 27 | </main> | |
| 28 | ||
| 29 | <footer> | |
| 30 | <h2>Contact</h2> | |
| 31 | <p><a href="https://sr.ht/~cmc/hn/"></a></p> | |
| 32 | <p>Copyright © 2023 - {current_year}</p> | |
| 33 | </footer> | |
| 34 | ||
| 35 | </body> | |
| 36 | ||
| 37 | </html> | |