From: x2003bruneau
Date: Sun, 25 Feb 2007 16:48:39 +0000 (+0000)
Subject: Add RSS Feeds
X-Git-Url: http://git.polytechnique.org/?a=commitdiff_plain;h=295b7ff47fc2fd31f93293e341c43159ccdb4e5c;p=banana.git
Add RSS Feeds
git-svn-id: svn+ssh://murphy/home/svn/banana/trunk@215 9869982d-c50d-0410-be91-f2a2ec7c7c7b
---
diff --git a/Changelog b/Changelog
index 8ded7e9..399f0f1 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,11 @@
+Sun, 25 Feb 2007 Florent Bruneau
+
+ * RSS Feeds
+
+Wed, 21 Feb 2007 Florent Bruneau
+
+ * MBox helper written in C
+
================================================================================
VERSION 1.5
diff --git a/banana/banana.inc.php.in b/banana/banana.inc.php.in
index 0670860..fde1931 100644
--- a/banana/banana.inc.php.in
+++ b/banana/banana.inc.php.in
@@ -49,7 +49,7 @@ class Banana
static public $msgshow_mimeparts = array('multipart/report', 'multipart/mixed',
'text/html', 'text/plain', 'text/enriched', 'text', 'message');
static public $msgshow_xface = true;
- static public $msgshow_wrap = 78;
+ static public $msgshow_wrap = 80;
static public $msgshow_externalimages = false;
static public $msgshow_hasextimages = false;
static public $msgshow_withthread = true;
@@ -77,6 +77,14 @@ class Banana
*/
static public $msgedit_mimeparts = array('multipart/report', 'multipart/mixed', 'text/plain', 'text/enriched', 'text/html', 'text', 'message');
+### Feed configuration ###
+ static public $feed_active = true;
+ static public $feed_format = 'rss2';
+ static public $feed_updateOnDemand = false; // Update the feed each time sbd check it
+ static public $feed_copyright = null;
+ static public $feed_namePrefix = 'Banana :: ';
+ static public $feed_size = 15; // Number of messages in the feed
+
### Protocole ###
/** News serveur to use
*/
@@ -99,6 +107,7 @@ class Banana
const ACTION_BOX_NEEDED = 1; // mask
const ACTION_BOX_LIST = 2;
const ACTION_BOX_SUBS = 4;
+ const ACTION_BOX_FEED = 8;
const ACTION_MSG_LIST = 3;
const ACTION_MSG_READ = 5;
const ACTION_MSG_NEW = 9;
@@ -185,16 +194,26 @@ class Banana
Banana::$first = isset($this->params['first']) ? $this->params['first'] : null;
Banana::$part = isset($this->params['part']) ? $this->params['part'] : 'text';
+ $action = @$this->params['action'];
+ if ($action == 'rss' || $action == 'rss2' || $action == 'atom') {
+ if ($action == 'rss') {
+ $action = 'rss2';
+ }
+ Banana::$feed_format = $action;
+ Banana::$action = Banana::ACTION_BOX_FEED;
+ return;
+ }
+
// Look for the action to execute
if (is_null(Banana::$group)) {
- if (isset($this->params['action']) && $this->params['action'] == 'subscribe') {
+ if ($action == 'subscribe') {
Banana::$action = Banana::ACTION_BOX_SUBS;
} else {
Banana::$action = Banana::ACTION_BOX_LIST;
}
return;
}
- $action = isset($this->params['action']) ? $this->params['action'] : null;
+
if (is_null(Banana::$artid)) {
if ($action == 'new') {
Banana::$action = Banana::ACTION_MSG_NEW;
@@ -249,6 +268,8 @@ class Banana
case Banana::ACTION_BOX_LIST:
$error = $this->action_listBoxes();
break;
+ case Banana::ACTION_BOX_FEED:
+ return $this->action_feed(); // generate its own xml
case Banana::ACTION_MSG_LIST:
$error = $this->action_showThread(Banana::$group, Banana::$first);
break;
@@ -281,6 +302,16 @@ class Banana
return Banana::$page->css;
}
+ /** Return the Link to the feed of the page
+ */
+ public function feed()
+ {
+ if (!Banana::$feed_active) {
+ return null;
+ }
+ return Banana::$page->makeURL(array('group' => Banana::$group, 'action' => Banana::$feed_format));
+ }
+
/** Return the execution backtrace of the current BananaProtocole
*/
public function backtrace()
@@ -324,6 +355,28 @@ class Banana
return true;
}
+ protected function action_feed()
+ {
+ Banana::load('feed');
+ if (Banana::$group) {
+ $feed =& BananaFeed::getFeed();
+ return $feed->toXML();
+ }
+ if (Banana::$profile['subscribe']) {
+ $subfeed = null;
+ foreach (Banana::$profile['subscribe'] as $group) {
+ Banana::$group = $group;
+ if (Banana::$feed_updateOnDemand) {
+ $this->loadSpool($group);
+ }
+ $feed =& BananaFeed::getFeed();
+ $subfeed =& BananaFeed::merge($subfeed, $feed, _b_('Abonnements'), _b_('Mes abonnements Banana'));
+ }
+ return $subfeed->toXML();
+ }
+ return Banana::$page->feed();
+ }
+
protected function action_showThread($group, $first)
{
Banana::$page->setPage('thread');
@@ -502,7 +555,7 @@ class Banana
return false;
}
return true;
- }
+ }
static public function createAllSpool(array $protos)
{
@@ -519,7 +572,7 @@ class Banana
print "** $proto **\n";
foreach (array_keys($groups) as $g) {
- print "Generating spool for $g : ";
+ print "Generating spool for $g: ";
Banana::$group = $g;
$spool = $banana->loadSpool($g);
if (!$banana->checkErrors()) {
@@ -527,11 +580,45 @@ class Banana
}
print "done.\n";
unset($spool);
+ Banana::$spool = null;
}
print "\n";
}
}
+ static public function refreshAllFeeds(array $protos)
+ {
+ Banana::load('feed');
+ Banana::$feed_updateOnDemand = true; // In order to force update
+ foreach ($protos as $proto) {
+ $banana = new Banana(array(), $proto);
+
+ if (!$banana->checkErrors()) {
+ continue;
+ }
+ $groups = Banana::$protocole->getBoxList();
+ if (!$banana->checkErrors()) {
+ continue;
+ }
+
+ print "** $proto **\n";
+ foreach (array_keys($groups) as $g) {
+ print "Generating feed cache for $g: ";
+ Banana::$group = $g;
+ $spool = $banana->loadSpool($g);
+ if (!$banana->checkErrors()) {
+ break;
+ }
+ $feed =& BananaFeed::getFeed();
+ print "done.\n";
+ unset($feed);
+ unset($spool);
+ Banana::$spool = null;
+ }
+ print "\n";
+ }
+ }
+
/**************************************************************************/
/* Private functions */
/**************************************************************************/
diff --git a/banana/feed.inc.php b/banana/feed.inc.php
new file mode 100644
index 0000000..e4b3f90
--- /dev/null
+++ b/banana/feed.inc.php
@@ -0,0 +1,185 @@
+ author, 'date' => date (UNIX TS), 'title' => subject, 'body' => html body,
+ * 'link' => link, 'reply' => reply)
+ */
+ public $messages = array();
+
+ /** Create an empty feed
+ */
+ private function __construct()
+ {
+ $this->version = BANANA_FEED_VERSION;
+ $this->group = Banana::$group;
+ $this->description = Banana::$protocole->getDescription();
+ }
+
+ /** Update the feed, using current settings of Banana
+ * Calling this function suppose that Banana::$spool is the spool of the current box
+ */
+ public function update()
+ {
+ if (!Banana::$spool || Banana::$spool->group != $this->group) {
+ return false;
+ }
+ if (!Banana::$spool->ids) {
+ $spool_indexes = array();
+ } else {
+ $spool_indexes = Banana::$spool->ids;
+ sort($spool_indexes, SORT_NUMERIC);
+ $spool_indexes = array_slice($spool_indexes, -Banana::$feed_size, Banana::$feed_size);
+ }
+ $feed_indexes = array_keys($this->messages);
+ $old = array_diff($feed_indexes, $spool_indexes);
+ foreach ($old as $key) {
+ unset($this->messages[$key]);
+ }
+ $new = array_diff($spool_indexes, $feed_indexes);
+ foreach ($new as $key) {
+ $message =& Banana::$protocole->getMessage($key);
+ $array = array();
+ $array['author'] = $message->getAuthorName();
+ $array['date'] = $message->getHeaderValue('Date');
+ $array['title'] = $message->getHeaderValue('Subject');
+ $array['body'] = $message->toHtml();
+ $array['link'] = Banana::$page->makeUrl(array('group' => $this->group, 'artid' => $key));
+ if (Banana::$protocole->canSend()) {
+ $array['reply'] = Banana::$page->makeUrl(array('group' => $this->group, 'artid' => $key, 'action' => 'new'));
+ }
+ $this->messages[$key] = $array;
+ }
+ $this->writeToFile();
+ }
+
+ /** Get the spool corresponding with the current settings of Banana
+ */
+ static public function &getFeed()
+ {
+ $feed =& BananaFeed::readFromFile();
+ if (!$feed) {
+ $feed = new BananaFeed();
+ }
+ if (Banana::$feed_updateOnDemand) {
+ $feed->update();
+ }
+ return $feed;
+ }
+
+ /** Return the cache file name
+ */
+ static private function filename()
+ {
+ $file = Banana::$spool_root . '/' . Banana::$protocole->name() . '/';
+ if (!is_dir($file)) {
+ mkdir($file);
+ }
+ return $file . Banana::$protocole->filename() . '_feed';
+ }
+
+ /** Read a feed from a cache file
+ */
+ static private function &readFromFile()
+ {
+ $feed = null;
+ $file = BananaFeed::filename();
+ if (!file_exists($file)) {
+ return $feed;
+ }
+ $feed = unserialize(file_get_contents($file));
+ if ($feed->version != BANANA_FEED_VERSION) {
+ $feed = null;
+ }
+ return $feed;
+ }
+
+ /** Write a feed to a cache file
+ */
+ private function writeToFile()
+ {
+ $file = BananaFeed::filename();
+ file_put_contents($file, serialize($this));
+ }
+
+ /** Merge to feeds into a new one
+ */
+ static public function &merge(&$feed1, &$feed2, $name, $description = null)
+ {
+ if (!$feed1) {
+ $feed = null;
+ $feed1 =& $feed2;
+ $feed2 =& $feed;
+ }
+ if ($feed1->group == $name) {
+ $master =& $feed1;
+ $slave =& $feed2;
+ } else if ($feed2 && $feed2->group == $name) {
+ $master =& $feed2;
+ $slave =& $feed1;
+ } else {
+ $master = new BananaFeed();
+ $master->group = $name;
+ $master->description = $description;
+ foreach ($feed1->messages as $key=>$message) {
+ $message['title'] = '[' . $feed1->group . '] ' . $message['title'];
+ $master->messages[$feed1->group . '-' . $key] = $message;
+ }
+ $slave =& $feed2;
+ }
+ if (!$slave) {
+ return $master;
+ }
+ $messages = array();
+ $m1 = reset($master->messages);
+ $m2 = reset($slave->messages);
+ for ($i = 0 ; $i < 2 * Banana::$feed_size && ($m1 || $m2) ; $i++) {
+ if ($m2 && (!$m1 || $m1['date'] < $m2['date'])) {
+ $m2['title'] = '[' . $feed2->group . '] ' . $m2['title'];
+ $messages[$slave->group . '-' . key($slave->messages)] = $m2;
+ $m2 = next($slave->messages);
+ } else {
+ $messages[key($master->messages)] = $m1;
+ $m1 = next($master->messages);
+ }
+ }
+ $master->messages =& $messages;
+ return $master;
+ }
+
+ /** Generate the feed xml
+ */
+ public function toXML()
+ {
+ Banana::$page->assign_by_ref('feed', $this);
+ return Banana::$page->feed();
+ }
+}
+
+// vim:set et sw=4 sts=4 ts=4 enc=utf-8:
+?>
diff --git a/banana/message.inc.php b/banana/message.inc.php
index 66786da..02eeffc 100644
--- a/banana/message.inc.php
+++ b/banana/message.inc.php
@@ -155,10 +155,7 @@ final class BananaMessage extends BananaMimePart
$mailto = '' . banana_htmlentities($regs[1]) . '';
- }
- if (preg_match("/^<(.+@.+)>$/", $text, $regs)) {
+ if (preg_match("/^([^< ]+@[^> ]+)>?$/", $text, $regs)) {
$result = $mailto . $regs[1] . '">' . banana_htmlentities($regs[1]) . '';
}
if (preg_match("/^([^ ]+@[^ ]+) \((.*)\)$/", $text, $regs)) {
@@ -172,6 +169,31 @@ final class BananaMessage extends BananaMimePart
return preg_replace("/\\\(\(|\))/","\\1",$result);
}
+ public function getAuthorName()
+ {
+ $text = $this->getHeaderValue('From');
+ $name = null;
+ if (preg_match("/^([^ ]+@[^ ]+) \((.*)\)$/", $text, $regs)) {
+ $name = $regs[2];
+ }
+ if (preg_match("/^\"?([^<>\"]+)\"? +<(.+@.+)>$/", $text, $regs)) {
+ $name = preg_replace("/^'(.*)'$/", '\1', $regs[1]);
+ $name = stripslashes($name);
+ }
+ if ($name) {
+ return preg_replace("/\\\(\(|\))/","\\1", $name);
+ }
+
+ if (function_exists('hook_getAuthorName') && $name = hook_getAuthorName($this)) {
+ return $name;
+ }
+
+ if (preg_match("/([^< ]+)@([^> ]+)/", $text, $regs)) {
+ return $regs[1];
+ }
+ return 'Anonymous';
+ }
+
static public function formatDate($text)
{
return strftime("%A %d %B %Y, %H:%M (fuseau serveur)", strtotime($text));
diff --git a/banana/mimepart.inc.php b/banana/mimepart.inc.php
index 1ca1ec8..e2fea25 100644
--- a/banana/mimepart.inc.php
+++ b/banana/mimepart.inc.php
@@ -417,7 +417,7 @@ class BananaMimePart
public function toHtml()
{
- list($type, $subtype) = $this->getType();
+ @list($type, $subtype) = $this->getType();
if ($type == 'image') {
$part = $this->id ? $this->id : $this->filename;
return '
{$act.text}
{/foreach}
{if $page eq 'forums'}
- {include file="banana-boxlist.inc.tpl" grouplist=$groups withstats=true}
+ {include file="banana-boxlist.inc.tpl" grouplist=$groups withstats=true withfeed=$feed_active}
{if $newgroups|@count}
{"Les nouveaux groupes suivants ont été créés depuis votre dernière visite"|b}
{include file="banana-boxlist.inc.tpl" grouplist=$newgroups withstats=true}
diff --git a/banana/templates/banana-boxlist.inc.tpl b/banana/templates/banana-boxlist.inc.tpl
index 921b5d3..102bf07 100644
--- a/banana/templates/banana-boxlist.inc.tpl
+++ b/banana/templates/banana-boxlist.inc.tpl
@@ -15,7 +15,14 @@
{"Nouveaux"|b} |
{/if}
{"Nom"|b} |
- {"Description"|b} |
+
+ {if $withfeed}
+
+ {imglink action=$feed_format img=feed alt="Flux"|b accesskey=f}
+
+ {/if}
+ {"Description"|b}
+ |
{foreach from=$grouplist key=name item=grp}
diff --git a/banana/templates/banana-feed-rss2.tpl b/banana/templates/banana-feed-rss2.tpl
new file mode 100644
index 0000000..b1543b4
--- /dev/null
+++ b/banana/templates/banana-feed-rss2.tpl
@@ -0,0 +1,20 @@
+
+
+
+ {$title_prefix}{$feed->group}
+ {$language}
+ {url group=$group}
+ {$feed->description}
+ {foreach from=$feed->messages key=id item=message}
+ -
+
+ {$id}
+ {$message.link}
+
+ {$message.author}
+ {$message.date|rss_date}
+
+ {/foreach}
+
+
+{* vim:set et sw=2 sts=2 ts=2 enc=utf-8: *}
diff --git a/banana/templates/banana-thread.inc.tpl b/banana/templates/banana-thread.inc.tpl
index 9bf65fd..f8fe90b 100644
--- a/banana/templates/banana-thread.inc.tpl
+++ b/banana/templates/banana-thread.inc.tpl
@@ -27,6 +27,7 @@
{if $protocole->canSend()}
{imglink group=$group action=new img=post alt="Nouveau message"|b accesskey=p}
+ {if $feed_active}{imglink group=$group action=$feed_format img=feed alt="Flux"|b accesskey=f}{/if}
{/if}
{"Auteur"|b}
diff --git a/img/feed.gif b/img/feed.gif
new file mode 100644
index 0000000..09ecd61
Binary files /dev/null and b/img/feed.gif differ