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> | ||