diff --git a/application/FeedBuilder.php b/application/FeedBuilder.php index fedd90e6..a1f4da48 100644 --- a/application/FeedBuilder.php +++ b/application/FeedBuilder.php @@ -62,11 +62,6 @@ class FeedBuilder */ protected $hideDates; - /** - * @var string PubSub hub URL. - */ - protected $pubsubhubUrl; - /** * @var string server locale. */ @@ -120,7 +115,6 @@ public function buildData() } $data['language'] = $this->getTypeLanguage(); - $data['pubsubhub_url'] = $this->pubsubhubUrl; $data['last_update'] = $this->getLatestDateFormatted(); $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; // Remove leading slash from REQUEST_URI. @@ -182,16 +176,6 @@ protected function buildItem($link, $pageaddr) return $link; } - /** - * Assign PubSub hub URL. - * - * @param string $pubsubhubUrl PubSub hub url. - */ - public function setPubsubhubUrl($pubsubhubUrl) - { - $this->pubsubhubUrl = $pubsubhubUrl; - } - /** * Set this to true to use permalinks instead of direct links. * diff --git a/composer.json b/composer.json index 4786fe94..cfbde1a0 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ "php": ">=5.5", "shaarli/netscape-bookmark-parser": "1.*", "erusev/parsedown": "1.6", - "slim/slim": "^3.0" + "slim/slim": "^3.0", + "pubsubhubbub/publisher": "dev-master" }, "require-dev": { "phpmd/phpmd" : "@stable", diff --git a/index.php b/index.php index eb73941d..dd9b48bd 100644 --- a/index.php +++ b/index.php @@ -910,10 +910,6 @@ function renderPage($conf, $pluginManager, $LINKSDB) $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !isLoggedIn()); $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks')); - $pshUrl = $conf->get('config.PUBSUBHUB_URL'); - if (!empty($pshUrl)) { - $feedGenerator->setPubsubhubUrl($pshUrl); - } $data = $feedGenerator->buildData(); // Process plugin hook. @@ -1289,7 +1285,6 @@ function renderPage($conf, $pluginManager, $LINKSDB) $LINKSDB[$id] = $link; $LINKSDB->save($conf->get('resource.page_cache')); - pubsubhub($conf); // If we are called from the bookmarklet, we must close the popup: if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { diff --git a/plugins/pubsubhubbub/README.md b/plugins/pubsubhubbub/README.md new file mode 100644 index 00000000..3a65492a --- /dev/null +++ b/plugins/pubsubhubbub/README.md @@ -0,0 +1,20 @@ +# PubSubHubbub plugin + +Enable this plugin to notify a Hub everytime you add or edit a link. + +This allow hub subcribers to receive update notifications in real time, +which is useful for feed syndication service which supports PubSubHubbub. + +## Public Hub + +By default, Shaarli will use [Google's public hub](http://pubsubhubbub.appspot.com/). + +[Here](https://github.com/pubsubhubbub/PubSubHubbub/wiki/Hubs) is a list of public hubs. + +You can also host your own PubSubHubbub server implementation, such as [phubb](https://github.com/cweiske/phubb). + +## cURL + +While there is a fallback function to notify the hub, it's recommended that +you have PHP cURL extension enabled to use this plugin. + diff --git a/plugins/pubsubhubbub/hub.atom.xml b/plugins/pubsubhubbub/hub.atom.xml new file mode 100644 index 00000000..24d93d3e --- /dev/null +++ b/plugins/pubsubhubbub/hub.atom.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/pubsubhubbub/hub.rss.xml b/plugins/pubsubhubbub/hub.rss.xml new file mode 100644 index 00000000..27bf67a6 --- /dev/null +++ b/plugins/pubsubhubbub/hub.rss.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/pubsubhubbub/pubsubhubbub.meta b/plugins/pubsubhubbub/pubsubhubbub.meta new file mode 100644 index 00000000..289f5cdb --- /dev/null +++ b/plugins/pubsubhubbub/pubsubhubbub.meta @@ -0,0 +1,2 @@ +description="Enable PubSubHubbub feed publishing." +parameters="PUBSUBHUB_URL" diff --git a/plugins/pubsubhubbub/pubsubhubbub.php b/plugins/pubsubhubbub/pubsubhubbub.php new file mode 100644 index 00000000..03b6757b --- /dev/null +++ b/plugins/pubsubhubbub/pubsubhubbub.php @@ -0,0 +1,101 @@ +get('plugins.PUBSUBHUB_URL'); + if (empty($hub)) { + // Default hub. + $conf->set('plugins.PUBSUBHUB_URL', 'https://pubsubhubbub.appspot.com/'); + } +} + + +/** + * Render feed hook. + * Adds the hub URL in ATOM and RSS feed. + * + * @param array $data Template data. + * @param ConfigManager $conf instance. + * + * @return array updated template data. + */ +function hook_pubsubhubbub_render_feed($data, $conf) +{ + $feedType = $data['_PAGE_'] == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM; + $template = file_get_contents(PluginManager::$PLUGINS_PATH . '/pubsubhubbub/hub.'. $feedType .'.xml'); + $data['feed_plugins_header'][] = sprintf($template, $conf->get('plugins.PUBSUBHUB_URL')); + + return $data; +} + +/** + * Save link hook. + * Publish to the hub when a link is saved. + * + * @param array $data Template data. + * @param ConfigManager $conf instance. + * + * @return array unaltered data. + */ +function hook_pubsubhubbub_save_link($data, $conf) +{ + $feeds = array( + index_url($_SERVER) .'?do=atom', + index_url($_SERVER) .'?do=rss', + ); + + $httpPost = function_exists('curl_version') ? false : 'nocurl_http_post'; + try { + $p = new Publisher($conf->get('plugins.PUBSUBHUB_URL')); + $p->publish_update($feeds, $httpPost); + } catch (Exception $e) { + error_log('Could not publish to PubSubHubbub: ' . $e->getMessage()); + } + + return $data; +} + +/** + * Http function used to post to the hub endpoint without cURL extension. + * + * @param string $url Hub endpoint. + * @param string $postString String to POST. + * + * @return bool + * + * @throws Exception An error occurred. + */ +function nocurl_http_post($url, $postString) { + $params = array('http' => array( + 'method' => 'POST', + 'content' => $postString, + 'user_agent' => 'PubSubHubbub-Publisher-PHP/1.0', + )); + + $context = stream_context_create($params); + $fp = @fopen($url, 'rb', false, $context); + if (!$fp) { + throw new Exception('Could not post to '. $url); + } + $response = @stream_get_contents($fp); + if ($response === false) { + throw new Exception('Bad response from the hub '. $url); + } + return $response; +} diff --git a/tests/FeedBuilderTest.php b/tests/FeedBuilderTest.php index 06a44506..a590306d 100644 --- a/tests/FeedBuilderTest.php +++ b/tests/FeedBuilderTest.php @@ -75,7 +75,6 @@ public function testRSSBuildData() $data = $feedBuilder->buildData(); // Test headers (RSS) $this->assertEquals(self::$RSS_LANGUAGE, $data['language']); - $this->assertEmpty($data['pubsubhub_url']); $this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']); $this->assertEquals(true, $data['show_dates']); $this->assertEquals('http://host.tld/index.php?do=feed', $data['self_link']); @@ -210,19 +209,6 @@ public function testBuildDataHideDates() $this->assertTrue($data['show_dates']); } - /** - * Test buildData with hide dates settings. - */ - public function testBuildDataPubsubhub() - { - $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, null, false); - $feedBuilder->setLocale(self::$LOCALE); - $feedBuilder->setPubsubhubUrl('http://pubsubhub.io'); - $data = $feedBuilder->buildData(); - $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); - $this->assertEquals('http://pubsubhub.io', $data['pubsubhub_url']); - } - /** * Test buildData when Shaarli is served from a subdirectory */ diff --git a/tests/plugins/PluginPubsubhubbubTest.php b/tests/plugins/PluginPubsubhubbubTest.php new file mode 100644 index 00000000..24dd7a11 --- /dev/null +++ b/tests/plugins/PluginPubsubhubbubTest.php @@ -0,0 +1,54 @@ +set('plugins.PUBSUBHUB_URL', $hub); + $data['_PAGE_'] = Router::$PAGE_FEED_RSS; + + $data = hook_pubsubhubbub_render_feed($data, $conf); + $expected = ''; + $this->assertEquals($expected, $data['feed_plugins_header'][0]); + } + + /** + * Test render_feed hook with an ATOM feed. + */ + function testPubSubAtomRenderFeed() + { + $hub = 'http://domain.hub'; + $conf = new ConfigManager(self::$configFile); + $conf->set('plugins.PUBSUBHUB_URL', $hub); + $data['_PAGE_'] = Router::$PAGE_FEED_ATOM; + + $data = hook_pubsubhubbub_render_feed($data, $conf); + $expected = ''; + $this->assertEquals($expected, $data['feed_plugins_header'][0]); + } +} diff --git a/tpl/feed.atom.html b/tpl/feed.atom.html index aead0459..ad7dd935 100644 --- a/tpl/feed.atom.html +++ b/tpl/feed.atom.html @@ -6,11 +6,9 @@ {$last_update} {/if} - {if="!empty($pubsubhub_url)"} - - - - {/if} + {loop="$feed_plugins_header"} + {$value} + {/loop} {$index_url} {$index_url} @@ -34,6 +32,9 @@ {loop="$value.taglist"} {/loop} + {loop="$value.feed_plugins"} + {$value} + {/loop} {/loop} diff --git a/tpl/feed.rss.html b/tpl/feed.rss.html index e18dbf9b..73791f63 100644 --- a/tpl/feed.rss.html +++ b/tpl/feed.rss.html @@ -8,10 +8,9 @@ {$index_url} Shaarli - {if="!empty($pubsubhub_url)"} - - - {/if} + {loop="$feed_plugins_header"} + {$value} + {/loop} {loop="$links"} {$value.title} @@ -29,6 +28,9 @@ {loop="$value.taglist"} {$value} {/loop} + {loop="$value.feed_plugins"} + {$value} + {/loop} {/loop}