aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--application/FeedBuilder.php16
-rw-r--r--composer.json3
-rw-r--r--index.php5
-rw-r--r--plugins/pubsubhubbub/README.md20
-rw-r--r--plugins/pubsubhubbub/hub.atom.xml1
-rw-r--r--plugins/pubsubhubbub/hub.rss.xml1
-rw-r--r--plugins/pubsubhubbub/pubsubhubbub.meta2
-rw-r--r--plugins/pubsubhubbub/pubsubhubbub.php101
-rw-r--r--tests/FeedBuilderTest.php14
-rw-r--r--tests/plugins/PluginPubsubhubbubTest.php54
-rw-r--r--tpl/feed.atom.html11
-rw-r--r--tpl/feed.rss.html10
12 files changed, 193 insertions, 45 deletions
diff --git a/application/FeedBuilder.php b/application/FeedBuilder.php
index fedd90e6..a1f4da48 100644
--- a/application/FeedBuilder.php
+++ b/application/FeedBuilder.php
@@ -63,11 +63,6 @@ class FeedBuilder
63 protected $hideDates; 63 protected $hideDates;
64 64
65 /** 65 /**
66 * @var string PubSub hub URL.
67 */
68 protected $pubsubhubUrl;
69
70 /**
71 * @var string server locale. 66 * @var string server locale.
72 */ 67 */
73 protected $locale; 68 protected $locale;
@@ -120,7 +115,6 @@ class FeedBuilder
120 } 115 }
121 116
122 $data['language'] = $this->getTypeLanguage(); 117 $data['language'] = $this->getTypeLanguage();
123 $data['pubsubhub_url'] = $this->pubsubhubUrl;
124 $data['last_update'] = $this->getLatestDateFormatted(); 118 $data['last_update'] = $this->getLatestDateFormatted();
125 $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; 119 $data['show_dates'] = !$this->hideDates || $this->isLoggedIn;
126 // Remove leading slash from REQUEST_URI. 120 // Remove leading slash from REQUEST_URI.
@@ -183,16 +177,6 @@ class FeedBuilder
183 } 177 }
184 178
185 /** 179 /**
186 * Assign PubSub hub URL.
187 *
188 * @param string $pubsubhubUrl PubSub hub url.
189 */
190 public function setPubsubhubUrl($pubsubhubUrl)
191 {
192 $this->pubsubhubUrl = $pubsubhubUrl;
193 }
194
195 /**
196 * Set this to true to use permalinks instead of direct links. 180 * Set this to true to use permalinks instead of direct links.
197 * 181 *
198 * @param boolean $usePermalinks true to force permalinks. 182 * @param boolean $usePermalinks true to force permalinks.
diff --git a/composer.json b/composer.json
index 4786fe94..cfbde1a0 100644
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,8 @@
13 "php": ">=5.5", 13 "php": ">=5.5",
14 "shaarli/netscape-bookmark-parser": "1.*", 14 "shaarli/netscape-bookmark-parser": "1.*",
15 "erusev/parsedown": "1.6", 15 "erusev/parsedown": "1.6",
16 "slim/slim": "^3.0" 16 "slim/slim": "^3.0",
17 "pubsubhubbub/publisher": "dev-master"
17 }, 18 },
18 "require-dev": { 19 "require-dev": {
19 "phpmd/phpmd" : "@stable", 20 "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)
910 $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0))); 910 $feedGenerator->setLocale(strtolower(setlocale(LC_COLLATE, 0)));
911 $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !isLoggedIn()); 911 $feedGenerator->setHideDates($conf->get('privacy.hide_timestamps') && !isLoggedIn());
912 $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks')); 912 $feedGenerator->setUsePermalinks(isset($_GET['permalinks']) || !$conf->get('feed.rss_permalinks'));
913 $pshUrl = $conf->get('config.PUBSUBHUB_URL');
914 if (!empty($pshUrl)) {
915 $feedGenerator->setPubsubhubUrl($pshUrl);
916 }
917 $data = $feedGenerator->buildData(); 913 $data = $feedGenerator->buildData();
918 914
919 // Process plugin hook. 915 // Process plugin hook.
@@ -1289,7 +1285,6 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1289 1285
1290 $LINKSDB[$id] = $link; 1286 $LINKSDB[$id] = $link;
1291 $LINKSDB->save($conf->get('resource.page_cache')); 1287 $LINKSDB->save($conf->get('resource.page_cache'));
1292 pubsubhub($conf);
1293 1288
1294 // If we are called from the bookmarklet, we must close the popup: 1289 // If we are called from the bookmarklet, we must close the popup:
1295 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { 1290 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 @@
1# PubSubHubbub plugin
2
3Enable this plugin to notify a Hub everytime you add or edit a link.
4
5This allow hub subcribers to receive update notifications in real time,
6which is useful for feed syndication service which supports PubSubHubbub.
7
8## Public Hub
9
10By default, Shaarli will use [Google's public hub](http://pubsubhubbub.appspot.com/).
11
12[Here](https://github.com/pubsubhubbub/PubSubHubbub/wiki/Hubs) is a list of public hubs.
13
14You can also host your own PubSubHubbub server implementation, such as [phubb](https://github.com/cweiske/phubb).
15
16## cURL
17
18While there is a fallback function to notify the hub, it's recommended that
19you have PHP cURL extension enabled to use this plugin.
20
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 @@
<link rel="hub" href="%s" /> \ 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 @@
<atom:link rel="hub" href="%s" /> \ 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 @@
1description="Enable PubSubHubbub feed publishing."
2parameters="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 @@
1<?php
2
3/**
4 * PubSubHubbub plugin.
5 *
6 * PubSub is a protocol which fasten up RSS fetching:
7 * - Every time a new link is posted, Shaarli notify the hub.
8 * - The hub notify all feed subscribers that a new link has been posted.
9 * - Subscribers retrieve the new link.
10 */
11
12use pubsubhubbub\publisher\Publisher;
13
14/**
15 * Plugin init function - set the hub to the default appspot one.
16 *
17 * @param ConfigManager $conf instance.
18 */
19function pubsubhubbub_init($conf)
20{
21 $hub = $conf->get('plugins.PUBSUBHUB_URL');
22 if (empty($hub)) {
23 // Default hub.
24 $conf->set('plugins.PUBSUBHUB_URL', 'https://pubsubhubbub.appspot.com/');
25 }
26}
27
28
29/**
30 * Render feed hook.
31 * Adds the hub URL in ATOM and RSS feed.
32 *
33 * @param array $data Template data.
34 * @param ConfigManager $conf instance.
35 *
36 * @return array updated template data.
37 */
38function hook_pubsubhubbub_render_feed($data, $conf)
39{
40 $feedType = $data['_PAGE_'] == Router::$PAGE_FEED_RSS ? FeedBuilder::$FEED_RSS : FeedBuilder::$FEED_ATOM;
41 $template = file_get_contents(PluginManager::$PLUGINS_PATH . '/pubsubhubbub/hub.'. $feedType .'.xml');
42 $data['feed_plugins_header'][] = sprintf($template, $conf->get('plugins.PUBSUBHUB_URL'));
43
44 return $data;
45}
46
47/**
48 * Save link hook.
49 * Publish to the hub when a link is saved.
50 *
51 * @param array $data Template data.
52 * @param ConfigManager $conf instance.
53 *
54 * @return array unaltered data.
55 */
56function hook_pubsubhubbub_save_link($data, $conf)
57{
58 $feeds = array(
59 index_url($_SERVER) .'?do=atom',
60 index_url($_SERVER) .'?do=rss',
61 );
62
63 $httpPost = function_exists('curl_version') ? false : 'nocurl_http_post';
64 try {
65 $p = new Publisher($conf->get('plugins.PUBSUBHUB_URL'));
66 $p->publish_update($feeds, $httpPost);
67 } catch (Exception $e) {
68 error_log('Could not publish to PubSubHubbub: ' . $e->getMessage());
69 }
70
71 return $data;
72}
73
74/**
75 * Http function used to post to the hub endpoint without cURL extension.
76 *
77 * @param string $url Hub endpoint.
78 * @param string $postString String to POST.
79 *
80 * @return bool
81 *
82 * @throws Exception An error occurred.
83 */
84function nocurl_http_post($url, $postString) {
85 $params = array('http' => array(
86 'method' => 'POST',
87 'content' => $postString,
88 'user_agent' => 'PubSubHubbub-Publisher-PHP/1.0',
89 ));
90
91 $context = stream_context_create($params);
92 $fp = @fopen($url, 'rb', false, $context);
93 if (!$fp) {
94 throw new Exception('Could not post to '. $url);
95 }
96 $response = @stream_get_contents($fp);
97 if ($response === false) {
98 throw new Exception('Bad response from the hub '. $url);
99 }
100 return $response;
101}
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 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
75 $data = $feedBuilder->buildData(); 75 $data = $feedBuilder->buildData();
76 // Test headers (RSS) 76 // Test headers (RSS)
77 $this->assertEquals(self::$RSS_LANGUAGE, $data['language']); 77 $this->assertEquals(self::$RSS_LANGUAGE, $data['language']);
78 $this->assertEmpty($data['pubsubhub_url']);
79 $this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']); 78 $this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']);
80 $this->assertEquals(true, $data['show_dates']); 79 $this->assertEquals(true, $data['show_dates']);
81 $this->assertEquals('http://host.tld/index.php?do=feed', $data['self_link']); 80 $this->assertEquals('http://host.tld/index.php?do=feed', $data['self_link']);
@@ -211,19 +210,6 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
211 } 210 }
212 211
213 /** 212 /**
214 * Test buildData with hide dates settings.
215 */
216 public function testBuildDataPubsubhub()
217 {
218 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, null, false);
219 $feedBuilder->setLocale(self::$LOCALE);
220 $feedBuilder->setPubsubhubUrl('http://pubsubhub.io');
221 $data = $feedBuilder->buildData();
222 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
223 $this->assertEquals('http://pubsubhub.io', $data['pubsubhub_url']);
224 }
225
226 /**
227 * Test buildData when Shaarli is served from a subdirectory 213 * Test buildData when Shaarli is served from a subdirectory
228 */ 214 */
229 public function testBuildDataServerSubdir() 215 public function testBuildDataServerSubdir()
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 @@
1<?php
2
3require_once 'plugins/pubsubhubbub/pubsubhubbub.php';
4require_once 'application/Router.php';
5
6/**
7 * Class PluginPubsubhubbubTest
8 * Unit test for the pubsubhubbub plugin
9 */
10class PluginPubsubhubbubTest extends PHPUnit_Framework_TestCase
11{
12 /**
13 * @var string Config file path (without extension).
14 */
15 protected static $configFile = 'tests/utils/config/configJson';
16
17 /**
18 * Reset plugin path
19 */
20 function setUp()
21 {
22 PluginManager::$PLUGINS_PATH = 'plugins';
23 }
24
25 /**
26 * Test render_feed hook with an RSS feed.
27 */
28 function testPubSubRssRenderFeed()
29 {
30 $hub = 'http://domain.hub';
31 $conf = new ConfigManager(self::$configFile);
32 $conf->set('plugins.PUBSUBHUB_URL', $hub);
33 $data['_PAGE_'] = Router::$PAGE_FEED_RSS;
34
35 $data = hook_pubsubhubbub_render_feed($data, $conf);
36 $expected = '<atom:link rel="hub" href="'. $hub .'" />';
37 $this->assertEquals($expected, $data['feed_plugins_header'][0]);
38 }
39
40 /**
41 * Test render_feed hook with an ATOM feed.
42 */
43 function testPubSubAtomRenderFeed()
44 {
45 $hub = 'http://domain.hub';
46 $conf = new ConfigManager(self::$configFile);
47 $conf->set('plugins.PUBSUBHUB_URL', $hub);
48 $data['_PAGE_'] = Router::$PAGE_FEED_ATOM;
49
50 $data = hook_pubsubhubbub_render_feed($data, $conf);
51 $expected = '<link rel="hub" href="'. $hub .'" />';
52 $this->assertEquals($expected, $data['feed_plugins_header'][0]);
53 }
54}
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 @@
6 <updated>{$last_update}</updated> 6 <updated>{$last_update}</updated>
7 {/if} 7 {/if}
8 <link rel="self" href="{$self_link}#" /> 8 <link rel="self" href="{$self_link}#" />
9 {if="!empty($pubsubhub_url)"} 9 {loop="$feed_plugins_header"}
10 <!-- PubSubHubbub Discovery --> 10 {$value}
11 <link rel="hub" href="{$pubsubhub_url}#" /> 11 {/loop}
12 <!-- End Of PubSubHubbub Discovery -->
13 {/if}
14 <author> 12 <author>
15 <name>{$index_url}</name> 13 <name>{$index_url}</name>
16 <uri>{$index_url}</uri> 14 <uri>{$index_url}</uri>
@@ -34,6 +32,9 @@
34 {loop="$value.taglist"} 32 {loop="$value.taglist"}
35 <category scheme="{$index_url}?searchtags=" term="{$value|strtolower}" label="{$value}" /> 33 <category scheme="{$index_url}?searchtags=" term="{$value|strtolower}" label="{$value}" />
36 {/loop} 34 {/loop}
35 {loop="$value.feed_plugins"}
36 {$value}
37 {/loop}
37 </entry> 38 </entry>
38 {/loop} 39 {/loop}
39</feed> 40</feed>
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 @@
8 <copyright>{$index_url}</copyright> 8 <copyright>{$index_url}</copyright>
9 <generator>Shaarli</generator> 9 <generator>Shaarli</generator>
10 <atom:link rel="self" href="{$self_link}" /> 10 <atom:link rel="self" href="{$self_link}" />
11 {if="!empty($pubsubhub_url)"} 11 {loop="$feed_plugins_header"}
12 <!-- PubSubHubbub Discovery --> 12 {$value}
13 <atom:link rel="hub" href="{$pubsubhub_url}" /> 13 {/loop}
14 {/if}
15 {loop="$links"} 14 {loop="$links"}
16 <item> 15 <item>
17 <title>{$value.title}</title> 16 <title>{$value.title}</title>
@@ -29,6 +28,9 @@
29 {loop="$value.taglist"} 28 {loop="$value.taglist"}
30 <category domain="{$index_url}?searchtags=">{$value}</category> 29 <category domain="{$index_url}?searchtags=">{$value}</category>
31 {/loop} 30 {/loop}
31 {loop="$value.feed_plugins"}
32 {$value}
33 {/loop}
32 </item> 34 </item>
33 {/loop} 35 {/loop}
34 </channel> 36 </channel>