From 8801ac9e64fe325dae8ef21ebc3b1526d1a5befc Mon Sep 17 00:00:00 2001
From: fulmeek <36341513+fulmeek@users.noreply.github.com>
Date: Sat, 5 Jan 2019 13:20:11 +0100
Subject: [PATCH] format: Refactor JsonFormat to JSON Feed version 1 (#988)
JsonFormat now implements https://jsonfeed.org/version/1
Closes #618
---
formats/JsonFormat.php | 112 ++++++++++++++++--
phpunit.xml | 7 +-
tests/JsonFormatTest.php | 89 ++++++++++++++
.../expectedJsonFormat/feed.common.json | 51 ++++++++
.../expectedJsonFormat/feed.empty.json | 7 ++
.../expectedJsonFormat/feed.emptyItems.json | 15 +++
.../expectedJsonFormat/feed.microblog.json | 19 +++
tests/samples/feed.common.json | 42 +++++++
tests/samples/feed.empty.json | 13 ++
tests/samples/feed.emptyItems.json | 19 +++
tests/samples/feed.microblog.json | 21 ++++
11 files changed, 385 insertions(+), 10 deletions(-)
create mode 100644 tests/JsonFormatTest.php
create mode 100644 tests/samples/expectedJsonFormat/feed.common.json
create mode 100644 tests/samples/expectedJsonFormat/feed.empty.json
create mode 100644 tests/samples/expectedJsonFormat/feed.emptyItems.json
create mode 100644 tests/samples/expectedJsonFormat/feed.microblog.json
create mode 100644 tests/samples/feed.common.json
create mode 100644 tests/samples/feed.empty.json
create mode 100644 tests/samples/feed.emptyItems.json
create mode 100644 tests/samples/feed.microblog.json
diff --git a/formats/JsonFormat.php b/formats/JsonFormat.php
index c3f85f94..fafe7a5a 100644
--- a/formats/JsonFormat.php
+++ b/formats/JsonFormat.php
@@ -1,17 +1,109 @@
items and return it to browser.
-*/
+ * JsonFormat - JSON Feed Version 1
+ * https://jsonfeed.org/version/1
+ *
+ * Validators:
+ * https://validator.jsonfeed.org
+ * https://github.com/vigetlabs/json-feed-validator
+ */
class JsonFormat extends FormatAbstract {
- public function stringify(){
- $items = $this->getItems();
- $data = array();
+ const VENDOR_EXCLUDES = array(
+ 'author',
+ 'title',
+ 'uri',
+ 'timestamp',
+ 'content',
+ 'enclosures',
+ 'categories',
+ );
- foreach($items as $item) {
- $data[] = $item->toArray();
+ public function stringify(){
+ $urlScheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
+ $urlHost = (isset($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : '';
+ $urlPath = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : '';
+ $urlRequest = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '';
+
+ $extraInfos = $this->getExtraInfos();
+
+ $data = array(
+ 'version' => 'https://jsonfeed.org/version/1',
+ 'title' => (!empty($extraInfos['name'])) ? $extraInfos['name'] : $urlHost,
+ 'home_page_url' => (!empty($extraInfos['uri'])) ? $extraInfos['uri'] : REPOSITORY,
+ 'feed_url' => $urlScheme . $urlHost . $urlRequest
+ );
+
+ if (!empty($extraInfos['icon'])) {
+ $data['icon'] = $extraInfos['icon'];
+ $data['favicon'] = $extraInfos['icon'];
}
+ $items = array();
+ foreach ($this->getItems() as $item) {
+ $entry = array();
+
+ $entryAuthor = $item->getAuthor();
+ $entryTitle = $item->getTitle();
+ $entryUri = $item->getURI();
+ $entryTimestamp = $item->getTimestamp();
+ $entryContent = $this->sanitizeHtml($item->getContent());
+ $entryEnclosures = $item->getEnclosures();
+ $entryCategories = $item->getCategories();
+
+ $vendorFields = $item->toArray();
+ foreach (self::VENDOR_EXCLUDES as $key) {
+ unset($vendorFields[$key]);
+ }
+
+ $entry['id'] = $entryUri;
+
+ if (!empty($entryTitle)) {
+ $entry['title'] = $entryTitle;
+ }
+ if (!empty($entryAuthor)) {
+ $entry['author'] = array(
+ 'name' => $entryAuthor
+ );
+ }
+ if (!empty($entryTimestamp)) {
+ $entry['date_modified'] = gmdate(DATE_ATOM, $entryTimestamp);
+ }
+ if (!empty($entryUri)) {
+ $entry['url'] = $entryUri;
+ }
+ if (!empty($entryContent)) {
+ if ($this->isHTML($entryContent)) {
+ $entry['content_html'] = $entryContent;
+ } else {
+ $entry['content_text'] = $entryContent;
+ }
+ }
+ if (!empty($entryEnclosures)) {
+ $entry['attachments'] = array();
+ foreach ($entryEnclosures as $enclosure) {
+ $entry['attachments'][] = array(
+ 'url' => $enclosure,
+ 'mime_type' => getMimeType($enclosure)
+ );
+ }
+ }
+ if (!empty($entryCategories)) {
+ $entry['tags'] = array();
+ foreach ($entryCategories as $category) {
+ $entry['tags'][] = $category;
+ }
+ }
+ if (!empty($vendorFields)) {
+ $entry['_rssbridge'] = $vendorFields;
+ }
+
+ if (empty($entry['id']))
+ $entry['id'] = hash('sha1', $entryTitle . $entryContent);
+
+ $items[] = $entry;
+ }
+ $data['items'] = $items;
+
$toReturn = json_encode($data, JSON_PRETTY_PRINT);
// Remove invalid non-UTF8 characters
@@ -27,4 +119,8 @@ class JsonFormat extends FormatAbstract {
return parent::display();
}
+
+ private function isHTML($text) {
+ return (strlen(strip_tags($text)) != strlen($text));
+ }
}
diff --git a/phpunit.xml b/phpunit.xml
index 16a082de..4fe1ae0e 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -8,8 +8,11 @@
timeoutForLargeTests="6" >
We — Manton Reece and Brent Simmons — have noticed that JSON has become the developers’ choice for APIs, and that developers will often go out of their way to avoid XML. JSON is simpler to read and write, and it’s less prone to bugs.
\n\nSo we developed JSON Feed, a format similar to RSS and Atom but in JSON. It reflects the lessons learned from our years of work reading and publishing feeds.
\n\nSee the spec. It’s at version 1, which may be the only version ever needed. If future versions are needed, version 1 feeds will still be valid feeds.
\n\nWe have a WordPress plugin and, coming soon, a JSON Feed Parser for Swift. As more code is written, by us and others, we’ll update the code page.
\n\nSee Mapping RSS and Atom to JSON Feed for more on the similarities between the formats.
\n\nThis website — the Markdown files and supporting resources — is up on GitHub, and you’re welcome to comment there.
\n\nThis website is also a blog, and you can subscribe to the RSS feed or the JSON feed (if your reader supports it).
\n\nWe worked with a number of people on this over the course of several months. We list them, and thank them, at the bottom of the spec. But — most importantly — Craig Hockenberry spent a little time making it look pretty. :)
" + },{ + "id": "http://example.org/2005/04/02/atom", + "url": "http://example.org/2005/04/02/atom", + "title": "Atom draft-07 snapshot", + "date_modified": "2005-07-31T12:29:29+00:00", + "author": { + "name": "Mark Pilgrim" + }, + "content_html": "[Update: The Atom draft is finished.]
", + "attachments": [ + { + "url": "http://example.org/audio/ph34r_my_podcast.mp3", + "mime_type": "audio/mpeg" + } + ] + },{ + "id": "http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp", + "url": "http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp", + "title": "Star City", + "date_modified": "2003-06-03T09:39:21+00:00", + "content_html": "How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's Star City." + } + ] +} diff --git a/tests/samples/expectedJsonFormat/feed.empty.json b/tests/samples/expectedJsonFormat/feed.empty.json new file mode 100644 index 00000000..6ce49c94 --- /dev/null +++ b/tests/samples/expectedJsonFormat/feed.empty.json @@ -0,0 +1,7 @@ +{ + "version": "https://jsonfeed.org/version/1", + "title": "Sample feed with minimum data", + "home_page_url": "https://github.com/RSS-Bridge/rss-bridge/", + "feed_url": "https://example.com/feed", + "items": [] +} diff --git a/tests/samples/expectedJsonFormat/feed.emptyItems.json b/tests/samples/expectedJsonFormat/feed.emptyItems.json new file mode 100644 index 00000000..10cc02ae --- /dev/null +++ b/tests/samples/expectedJsonFormat/feed.emptyItems.json @@ -0,0 +1,15 @@ +{ + "version": "https://jsonfeed.org/version/1", + "title": "Sample feed with minimum data", + "home_page_url": "https://github.com/RSS-Bridge/rss-bridge/", + "feed_url": "https://example.com/feed", + "items": [ + { + "id": "29f59918d266c56a935da13e4122b524298e5a39", + "title": "Sample Item #1" + },{ + "id": "edf358cad1a7ae255d6bc97640dd9d27738f1b7b", + "title": "Sample Item #2" + } + ] +} diff --git a/tests/samples/expectedJsonFormat/feed.microblog.json b/tests/samples/expectedJsonFormat/feed.microblog.json new file mode 100644 index 00000000..290a16de --- /dev/null +++ b/tests/samples/expectedJsonFormat/feed.microblog.json @@ -0,0 +1,19 @@ +{ + "version": "https://jsonfeed.org/version/1", + "title": "Sample microblog feed", + "home_page_url": "https://example.com/blog/", + "feed_url": "https://example.com/feed", + "icon": "https://example.com/logo.png", + "favicon": "https://example.com/logo.png", + "items": [ + { + "id": "1918f084648b82057c1dd3faa3d091da82a6fac2", + "date_modified": "2018-10-07T16:53:03+00:00", + "content_text": "Oh 😲 I found three monkeys 🙈🙉🙊" + },{ + "id": "e62189168a06dfa74f61c621c79c33c4c8517e1f", + "date_modified": "2018-10-07T16:38:17+00:00", + "content_text": "Something happened" + } + ] +} diff --git a/tests/samples/feed.common.json b/tests/samples/feed.common.json new file mode 100644 index 00000000..be0c56a9 --- /dev/null +++ b/tests/samples/feed.common.json @@ -0,0 +1,42 @@ +{ + "server": { + "HTTPS": "on", + "HTTP_HOST": "example.com", + "REQUEST_URI": "/feed?type=common&items=4" + }, + "meta": { + "name": "Sample feed with common data", + "uri": "https://example.com/blog/", + "icon": "https://example.com/logo.png" + }, + "items": [ + { + "uri": "http://example.com/blog/test-entry", + "title": "Test Entry", + "timestamp": 1543665600, + "author": "fulmeek", + "content": "Hello world, this is a test entry.", + "categories": ["test", "Hello World", "example"] + },{ + "uri": "https://jsonfeed.org/2017/05/17/announcing_json_feed", + "title": "Announcing JSON Feed", + "timestamp": 1495026132, + "author": "Brent Simmons and Manton Reece", + "content": "We — Manton Reece and Brent Simmons — have noticed that JSON has become the developers’ choice for APIs, and that developers will often go out of their way to avoid XML. JSON is simpler to read and write, and it’s less prone to bugs.
\n\nSo we developed JSON Feed, a format similar to RSS and Atom but in JSON. It reflects the lessons learned from our years of work reading and publishing feeds.
\n\nSee the spec. It’s at version 1, which may be the only version ever needed. If future versions are needed, version 1 feeds will still be valid feeds.
\n\nWe have a WordPress plugin and, coming soon, a JSON Feed Parser for Swift. As more code is written, by us and others, we’ll update the code page.
\n\nSee Mapping RSS and Atom to JSON Feed for more on the similarities between the formats.
\n\nThis website — the Markdown files and supporting resources — is up on GitHub, and you’re welcome to comment there.
\n\nThis website is also a blog, and you can subscribe to the RSS feed or the JSON feed (if your reader supports it).
\n\nWe worked with a number of people on this over the course of several months. We list them, and thank them, at the bottom of the spec. But — most importantly — Craig Hockenberry spent a little time making it look pretty. :)
" + },{ + "uri": "http://example.org/2005/04/02/atom", + "title": "Atom draft-07 snapshot", + "timestamp": 1122812969, + "author": "Mark Pilgrim", + "content": "[Update: The Atom draft is finished.]
", + "enclosures": [ + "http://example.org/audio/ph34r_my_podcast.mp3" + ] + },{ + "uri": "http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp", + "title": "Star City", + "timestamp": 1054633161, + "content": "How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's Star City." + } + ] +} diff --git a/tests/samples/feed.empty.json b/tests/samples/feed.empty.json new file mode 100644 index 00000000..aac09f64 --- /dev/null +++ b/tests/samples/feed.empty.json @@ -0,0 +1,13 @@ +{ + "server": { + "HTTPS": "on", + "HTTP_HOST": "example.com", + "REQUEST_URI": "/feed" + }, + "meta": { + "name": "Sample feed with minimum data", + "uri": "", + "icon": "" + }, + "items": [] +} diff --git a/tests/samples/feed.emptyItems.json b/tests/samples/feed.emptyItems.json new file mode 100644 index 00000000..0287d428 --- /dev/null +++ b/tests/samples/feed.emptyItems.json @@ -0,0 +1,19 @@ +{ + "server": { + "HTTPS": "on", + "HTTP_HOST": "example.com", + "REQUEST_URI": "/feed" + }, + "meta": { + "name": "Sample feed with minimum data", + "uri": "", + "icon": "" + }, + "items": [ + { + "title": "Sample Item #1" + },{ + "title": "Sample Item #2" + } + ] +} diff --git a/tests/samples/feed.microblog.json b/tests/samples/feed.microblog.json new file mode 100644 index 00000000..721763cd --- /dev/null +++ b/tests/samples/feed.microblog.json @@ -0,0 +1,21 @@ +{ + "server": { + "HTTPS": "on", + "HTTP_HOST": "example.com", + "REQUEST_URI": "/feed" + }, + "meta": { + "name": "Sample microblog feed", + "uri": "https://example.com/blog/", + "icon": "https://example.com/logo.png" + }, + "items": [ + { + "timestamp": 1538931183, + "content": "Oh 😲 I found three monkeys 🙈🙉🙊" + },{ + "timestamp": 1538930297, + "content": "Something happened" + } + ] +}