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}