From 67ff1018c31ff726c04b6405548294404789c391 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg Date: Thu, 26 May 2016 13:37:01 -0400 Subject: [PATCH] rss feed for news, better header handling --- controller/Controller.class.php | 119 +++++++++++++++++--- controller/Session.class.php | 2 +- controller/action/BlogActions.class.php | 25 +++- dev.sh | 2 +- model/Post.class.php | 87 +++++++++++++- view/View.class.php | 7 +- view/posts/40-tipping-in-LBC.md | 2 +- view/template/blog/rss.php | 25 ++++ view/template/internal/dl-not-supported.php | 2 + view/template/internal/redirect.php | 1 + view/template/layout/basic.php | 5 +- view/template/social/_listDev.php | 4 +- web/index.php | 5 + 13 files changed, 253 insertions(+), 33 deletions(-) create mode 100644 view/template/blog/rss.php create mode 100644 view/template/internal/dl-not-supported.php create mode 100644 view/template/internal/redirect.php diff --git a/controller/Controller.class.php b/controller/Controller.class.php index df0b1b84..ec08eba2 100644 --- a/controller/Controller.class.php +++ b/controller/Controller.class.php @@ -1,12 +1,9 @@ View::render($viewTemplate, $viewParameters + ['fullPage' => true]) - ]); + $layout = !(isset($viewParameters['_no_layout']) && $viewParameters['_no_layout']); + unset($viewParameters['_no_layout']); + + $layoutParams = isset($viewParameters[View::LAYOUT_PARAMS]) ? $viewParameters[View::LAYOUT_PARAMS] : []; + unset($viewParameters[View::LAYOUT_PARAMS]); + + $content = View::render($viewTemplate, $viewParameters + ['fullPage' => true]); + + echo $layout ? View::render('layout/basic', ['content' => $content] + $layoutParams) : $content; } catch (StopException $e) { @@ -59,7 +65,7 @@ class Controller case '/LBRY-deck.pdf': return static::redirect('https://s3.amazonaws.com/files.lbry.io/LBRY-deck.pdf', 307); case '/dl/lbry_setup.sh': - return static::redirect('https://raw.githubusercontent.com/lbryio/lbry-setup/master/lbry_setup.sh', 307); + return ['internal/dl-not-supported', ['_no_layout' => true]]; case '/lbry-osx-latest.dmg': return static::redirect('https://github.com/lbryio/lbry/releases/download/v0.2.4/lbry.0.2.4.dmg', 307); case '/lbry-linux-latest.deb': @@ -67,10 +73,14 @@ class Controller case '/art': return static::redirect('/what'); default: - $blogPattern = '#^/news(/|$)#'; + $blogPattern = '#^' . BlogActions::URL_STEM . '(/|$)#'; if (preg_match($blogPattern, $uri)) { $slug = preg_replace($blogPattern, '', $uri); + if ($slug == BlogActions::RSS_SLUG) + { + return BlogActions::executeRss(); + } return $slug ? BlogActions::executePost($slug) : BlogActions::executeIndex(); } $noSlashUri = ltrim($uri, '/'); @@ -80,29 +90,102 @@ class Controller } else { - return ['page/404', []]; + return ['page/404', [], [static::HEADER_STATUS => 404]]; } } } public static function redirect($url, $statusCode = 302) { - if (empty($url)) + if (!$url) { throw new InvalidArgumentException('Cannot redirect to an empty URL.'); } $url = str_replace('&', '&', $url); + $headers = [static::HEADER_STATUS => $statusCode]; + + if ($statusCode == 201 || ($statusCode >= 300 && $statusCode < 400)) { - header('Location: ' . $url, true, $statusCode); - } - else - { - echo sprintf('', htmlspecialchars($url, ENT_QUOTES)); + $headers['Location'] = $url; } - throw new StopException('Time to redirect'); + return ['internal/redirect', ['url' => $url], $headers]; + } + + protected static function sendHeaders(array $headers) + { + if (isset($headers[static::HEADER_STATUS])) + { + $status = 'HTTP/1.0 ' . $headers[static::HEADER_STATUS] . ' ' . static::getStatusTextForCode($headers[static::HEADER_STATUS]); + header($status); + + if (substr(php_sapi_name(), 0, 3) == 'cgi') + { + // fastcgi servers cannot send this status information because it was sent by them already due to the HTT/1.0 line + // so we can safely unset them. see ticket #3191 + unset($headers[static::HEADER_STATUS]); + } + } + + foreach($headers as $name => $value) + { + header($name . ': ' . $value); + } + } + + public static function getStatusTextForCode($code) + { + $statusTexts = [ + '100' => 'Continue', + '101' => 'Switching Protocols', + '200' => 'OK', + '201' => 'Created', + '202' => 'Accepted', + '203' => 'Non-Authoritative Information', + '204' => 'No Content', + '205' => 'Reset Content', + '206' => 'Partial Content', + '300' => 'Multiple Choices', + '301' => 'Moved Permanently', + '302' => 'Found', + '303' => 'See Other', + '304' => 'Not Modified', + '305' => 'Use Proxy', + '306' => '(Unused)', + '307' => 'Temporary Redirect', + '400' => 'Bad Request', + '401' => 'Unauthorized', + '402' => 'Payment Required', + '403' => 'Forbidden', + '404' => 'Not Found', + '405' => 'Method Not Allowed', + '406' => 'Not Acceptable', + '407' => 'Proxy Authentication Required', + '408' => 'Request Timeout', + '409' => 'Conflict', + '410' => 'Gone', + '411' => 'Length Required', + '412' => 'Precondition Failed', + '413' => 'Request Entity Too Large', + '414' => 'Request-URI Too Long', + '415' => 'Unsupported Media Type', + '416' => 'Requested Range Not Satisfiable', + '417' => 'Expectation Failed', + '419' => 'Authentication Timeout', + '422' => 'Unprocessable Entity', + '426' => 'Upgrade Required', + '429' => 'Too Many Requests', + '500' => 'Internal Server Error', + '501' => 'Not Implemented', + '502' => 'Bad Gateway', + '503' => 'Service Unavailable', + '504' => 'Gateway Timeout', + '505' => 'HTTP Version Not Supported', + ]; + + return isset($statusTexts[$code]) ? $statusTexts[$code] : null; } } diff --git a/controller/Session.class.php b/controller/Session.class.php index 1dafef9d..a7d9a49c 100644 --- a/controller/Session.class.php +++ b/controller/Session.class.php @@ -12,7 +12,7 @@ class Session KEY_LIST_SUB_SIGNATURE = 'list_sub_sig', KEY_LIST_SUB_SUCCESS = 'list_success', KEY_LIST_SUB_FB_EVENT = 'list_sub_fb_event'; - + public static function init() { session_start(); diff --git a/controller/action/BlogActions.class.php b/controller/action/BlogActions.class.php index 792cae18..171ce5ba 100644 --- a/controller/action/BlogActions.class.php +++ b/controller/action/BlogActions.class.php @@ -3,6 +3,7 @@ class BlogActions extends Actions { const URL_STEM = '/news'; + const RSS_SLUG = 'rss.xml'; public static function executeIndex() { @@ -13,7 +14,24 @@ class BlogActions extends Actions $page = isset($_GET['page']) ? (int)$_GET['page'] : 1; return ['blog/index', [ 'posts' => $posts, - 'page' => $page + 'page' => $page, + View::LAYOUT_PARAMS => [ + 'showRssLink' => true + ] + ]]; + } + + public static function executeRss() + { + $posts = Blog::getPosts(); + usort($posts, function(Post $a, Post $b) { + return strcasecmp($b->getDate()->format('Y-m-d'), $a->getDate()->format('Y-m-d')); + }); + return ['blog/rss', [ + 'posts' => array_slice($posts, 0, 10), + '_no_layout' => true + ], [ + 'Content-Type' => 'text/xml; charset=utf-8' ]]; } @@ -25,7 +43,10 @@ class BlogActions extends Actions return ['page/404', []]; } return ['blog/post', [ - 'post' => $post + 'post' => $post, + View::LAYOUT_PARAMS => [ + 'showRssLink' => true + ] ]]; } diff --git a/dev.sh b/dev.sh index 022eb5a2..415ebe73 100755 --- a/dev.sh +++ b/dev.sh @@ -1,3 +1,3 @@ #!/bin/bash -php --server localhost:8000 --docroot web/ \ No newline at end of file +php --server localhost:8000 --docroot web/ web/index.php \ No newline at end of file diff --git a/model/Post.class.php b/model/Post.class.php index 2aa3c021..219f0e00 100644 --- a/model/Post.class.php +++ b/model/Post.class.php @@ -2,7 +2,7 @@ class Post { - protected $slug, $title, $author, $date, $contentHtml, $cover; + protected $slug, $title, $author, $date, $markdown, $contentText, $contentHtml, $cover; protected $isCoverLight = false; public static function fromFile($filename) @@ -14,10 +14,10 @@ class Post public function __construct($slug, $frontMatter, $markdown) { $this->slug = $slug; + $this->markdown = $markdown; $this->title = isset($frontMatter['title']) ? $frontMatter['title'] : null; $this->author = isset($frontMatter['author']) ? $frontMatter['author'] : null; $this->date = isset($frontMatter['date']) ? new DateTime($frontMatter['date']) : null; - $this->contentHtml = ParsedownExtra::instance()->text(trim($markdown)); $this->cover = isset($frontMatter['cover']) ? $frontMatter['cover'] : null; $this->isCoverLight = isset($frontMatter['cover-light']) && $frontMatter['cover-light'] == 'true'; } @@ -57,8 +57,23 @@ class Post return $this->isCoverLight; } + public function getContentText($wordLimit = null, $appendEllipsis = false) + { + if ($this->markdown && !$this->contentText) + { +// $this->contentText = $this->markdownToText(trim($this->markdown)); + $this->contentText = html_entity_decode(str_replace(' ', ' ', strip_tags($this->getContentHtml())), ENT_COMPAT, 'utf-8'); + } + + return $wordLimit === null ? $this->contentText : $this->limitWords($this->contentText, $wordLimit, $appendEllipsis); + } + public function getContentHtml() { + if ($this->markdown && !$this->contentHtml) + { + $this->contentHtml = ParsedownExtra::instance()->text(trim($this->markdown)); + } return $this->contentHtml; } @@ -99,6 +114,24 @@ class Post } } + public function getAuthorEmail() + { + switch(strtolower($this->author)) + { + case 'jeremy': + return 'jeremy@lbry.io'; + case 'mike': + return 'mike@lbry.io'; + case 'jimmy': + return 'jimmy@lbry.io'; + case 'jack': + return 'jack@lbry.io'; + case 'lbry': + default: + return 'hello@lbry.io'; + } + } + public function getAuthorPhoto() { switch(strtolower($this->author)) @@ -138,4 +171,54 @@ class Post { return $this->getPostNum() % $maxStyles + 1; } + + protected function markdownToText($markdown) + { + $replacements = [ +// '/<(.*?)>/' => '$1', // HTML tags + '/^[=\-]{2,}\s*$/' => '', // setext-style headers + '/\[\^.+?\](\: .*?$)?/' => '', // footnotes + '/\s{0,2}\[.*?\]: .*?$/' => '', // footnotes + '/\!\[.*?\][\[\(].*?[\]\)]/' => '', // images + '/\[(.*?)\][\[\(].*?[\]\)]/' => '$1', // inline links + '/^\s*>/' => '', // blockquotes + '/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/' => '', // reference-style links + + '/\n={2,}/' => '\n', // underlined headers + '/^\#{1,6}\s*([^#]*)\s*(\#{1,6})?/m' => '$1', // atx-style headers + '/([\*_]{1,3})(\S.*?\S)\1/' => '$2', // bold/italics + '/~~/' => '', // strikethrough + '/(`{3,})(.*?)\1/m' => '$2', // codeblocks +// '/`{3}.*\n/' => '', // fenced codeblocks + '/^-{3,}\s*$/' => '', // hr + '/`(.+?)`/' => '$1', // inline code + '/\n{2,}/' => '\n\n', // multiple newlines + ]; + + return preg_replace(array_keys($replacements), array_values($replacements), strip_tags($markdown)); + } + + protected function limitWords($string, $wordLimit, $appendEllipsis = false) + { + $regexp = '/\s+/u'; + $words = preg_split($regexp, $string, $wordLimit + 1); + $numWords = count($words); + + # TBB: if there are $wordLimit words or less, this check is necessary + # to prevent the last word from being lost. + if ($numWords > $wordLimit) + { + array_pop($words); + } + + $string = implode(' ', $words); + + if ($appendEllipsis && $numWords > $wordLimit) + { + $ellipsis = '…'; + $string .= $ellipsis; + } + + return $string; + } } \ No newline at end of file diff --git a/view/View.class.php b/view/View.class.php index 9424e23e..7c1aa703 100644 --- a/view/View.class.php +++ b/view/View.class.php @@ -10,13 +10,10 @@ function js_end() ob_end_flush(); } -/** - * Description of View - * - * @author jeremy - */ class View { + const LAYOUT_PARAMS = '_layout_params'; + protected static $metaDescription = '', $metaImg = ''; diff --git a/view/posts/40-tipping-in-LBC.md b/view/posts/40-tipping-in-LBC.md index 26cdb305..413f03d7 100644 --- a/view/posts/40-tipping-in-LBC.md +++ b/view/posts/40-tipping-in-LBC.md @@ -17,4 +17,4 @@ We believe cryptocurrencies and blockchain technology are the key to solving thi Want to help us get LBRY launched and get in on the crypto action? [Test the LBRY Alpha and fill out our survey to earn 1,000 LBC](https://lbry.io/get). -If you have a strong interest in getting involved in LBRY, [join our Slack channel](https://lbry-slackin.herokuapp.com). Come and join the fun! +If you have a strong interest in getting involved in LBRY, [join our Slack channel](http://slack.lbry.io). Come and join the fun! diff --git a/view/template/blog/rss.php b/view/template/blog/rss.php new file mode 100644 index 00000000..4e4464f8 --- /dev/null +++ b/view/template/blog/rss.php @@ -0,0 +1,25 @@ + + + + LBRY News + https://lbry.io + Recent news about LBRY + https://github.com/lbryio/lbry.io + en + Sat, 07 Sep 2002 09:42:31 GMT ?> + + + + <?php echo htmlspecialchars($post->getTitle()) ?> + https://lbry.iogetRelativeUrl() ?> + https://lbry.iogetRelativeUrl() ?> + getDate()->format('r') ?> + getAuthorEmail()) ?> (getAuthorName()) ?>) + getContentText(50, true)) ?> + getContentHtml() ?> + ]]> + + + + \ No newline at end of file diff --git a/view/template/internal/dl-not-supported.php b/view/template/internal/dl-not-supported.php new file mode 100644 index 00000000..266b6ccc --- /dev/null +++ b/view/template/internal/dl-not-supported.php @@ -0,0 +1,2 @@ +#!/bin/bash +echo "This install method is no longer supported. Please see https://github.com/lbryio/lbry for more information." \ No newline at end of file diff --git a/view/template/internal/redirect.php b/view/template/internal/redirect.php new file mode 100644 index 00000000..652b7e3e --- /dev/null +++ b/view/template/internal/redirect.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/view/template/layout/basic.php b/view/template/layout/basic.php index d24031ef..f71740c3 100644 --- a/view/template/layout/basic.php +++ b/view/template/layout/basic.php @@ -12,7 +12,7 @@ 'LBRY' ?> <?php echo $title ?> - + @@ -25,6 +25,9 @@ + + + diff --git a/view/template/social/_listDev.php b/view/template/social/_listDev.php index 8a200e29..4c40fb58 100644 --- a/view/template/social/_listDev.php +++ b/view/template/social/_listDev.php @@ -4,8 +4,8 @@ */ ?>
- GitHub (source code) + GitHub (source code)
- Slack (chat) + Slack (chat)
diff --git a/web/index.php b/web/index.php index ac948e00..c56fb261 100644 --- a/web/index.php +++ b/web/index.php @@ -1,5 +1,10 @@