]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
OStatus cleanup...
authorBrion Vibber <brion@pobox.com>
Mon, 8 Feb 2010 22:06:36 +0000 (14:06 -0800)
committerBrion Vibber <brion@pobox.com>
Mon, 8 Feb 2010 22:58:12 +0000 (14:58 -0800)
* Treat linkless feed posts as status updates; drop the "New post:" prefix and quotes on them.
* Use stable user IDs for atom/rss2 feed links instead of unstable nicknames
* Pull Atom feed preferentially when subscribing -- can now put the remote user's profile page straight into the feed subscription form and get to the right place.
* Clean up naming for push endpoints

actions/showstream.php
classes/Notice.php
lib/util.php
plugins/OStatus/OStatusPlugin.php
plugins/OStatus/actions/feedsubcallback.php [deleted file]
plugins/OStatus/actions/hub.php [deleted file]
plugins/OStatus/actions/pushcallback.php [new file with mode: 0644]
plugins/OStatus/actions/pushhub.php [new file with mode: 0644]
plugins/OStatus/classes/Feedinfo.php
plugins/OStatus/lib/feeddiscovery.php
plugins/OStatus/lib/feedmunger.php

index 07cc68b765cb216e86025f52b2fc4d2390230f0a..f9407e35a1f7890189b817c39dd8900c2c6a1f33 100644 (file)
@@ -131,14 +131,14 @@ class ShowstreamAction extends ProfileAction
                      new Feed(Feed::RSS2,
                               common_local_url('ApiTimelineUser',
                                                array(
-                                                    'id' => $this->user->nickname,
+                                                    'id' => $this->user->id,
                                                     'format' => 'rss')),
                               sprintf(_('Notice feed for %s (RSS 2.0)'),
                                       $this->user->nickname)),
                      new Feed(Feed::ATOM,
                               common_local_url('ApiTimelineUser',
                                                array(
-                                                    'id' => $this->user->nickname,
+                                                    'id' => $this->user->id,
                                                     'format' => 'atom')),
                               sprintf(_('Notice feed for %s (Atom)'),
                                       $this->user->nickname)),
index f9f38635797f44456610cb973baf2611bdebd3a3..fca1c599ce39830993f8304397a11cefb8470799 100644 (file)
@@ -1176,6 +1176,10 @@ class Notice extends Memcached_DataObject
         // Figure out who that is.
 
         $sender = Profile::staticGet('id', $profile_id);
+        if (empty($sender)) {
+            return null;
+        }
+
         $recipient = common_relative_profile($sender, $nickname, common_sql_now());
 
         if (empty($recipient)) {
index f0f262dc5e032da708d85a84b6ade00ce07db749..00c21aeb2144f61a4daf3aea44a9cc38b2527560 100644 (file)
@@ -665,6 +665,9 @@ function common_valid_profile_tag($str)
 function common_at_link($sender_id, $nickname)
 {
     $sender = Profile::staticGet($sender_id);
+    if (!$sender) {
+        return $nickname;
+    }
     $recipient = common_relative_profile($sender, common_canonical_nickname($nickname));
     if ($recipient) {
         $user = User::staticGet('id', $recipient->id);
index 94191211213a30365a448bd3f85b68716b1c70eb..4e8b892c6b1ed04022a9b11f9378d051f8b652dd 100644 (file)
@@ -53,10 +53,10 @@ class OStatusPlugin extends Plugin
      */
     function onRouterInitialized($m)
     {
-        $m->connect('push/hub', array('action' => 'hub'));
+        $m->connect('main/push/hub', array('action' => 'pushhub'));
 
-        $m->connect('feedsub/callback/:feed',
-                    array('action' => 'feedsubcallback'),
+        $m->connect('main/push/callback/:feed',
+                    array('action' => 'pushcallback'),
                     array('feed' => '[0-9]+'));
         $m->connect('settings/feedsub',
                     array('action' => 'feedsubsettings'));
@@ -97,7 +97,7 @@ class OStatusPlugin extends Plugin
                 // Canonical form of id in URL?
                 // Updates will be handled for our internal PuSH hub.
                 $action->element('link', array('rel' => 'hub',
-                                               'href' => common_local_url('hub')));
+                                               'href' => common_local_url('pushhub')));
             }
         }
         return true;
diff --git a/plugins/OStatus/actions/feedsubcallback.php b/plugins/OStatus/actions/feedsubcallback.php
deleted file mode 100644 (file)
index c57ea5b..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * @package FeedSubPlugin
- * @maintainer Brion Vibber <brion@status.net>
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-
-class FeedSubCallbackAction extends Action
-{
-    function handle()
-    {
-        parent::handle();
-        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
-            $this->handlePost();
-        } else {
-            $this->handleGet();
-        }
-    }
-    
-    /**
-     * Handler for POST content updates from the hub
-     */
-    function handlePost()
-    {
-        $feedid = $this->arg('feed');
-        common_log(LOG_INFO, "POST for feed id $feedid");
-        if (!$feedid) {
-            throw new ServerException('Empty or invalid feed id', 400);
-        }
-
-        $feedinfo = Feedinfo::staticGet('id', $feedid);
-        if (!$feedinfo) {
-            throw new ServerException('Unknown feed id ' . $feedid, 400);
-        }
-
-        $hmac = '';
-        if (isset($_SERVER['HTTP_X_HUB_SIGNATURE'])) {
-            $hmac = $_SERVER['HTTP_X_HUB_SIGNATURE'];
-        }
-
-        $post = file_get_contents('php://input');
-        $feedinfo->postUpdates($post, $hmac);
-    }
-    
-    /**
-     * Handler for GET verification requests from the hub
-     */
-    function handleGet()
-    {
-        $mode = $this->arg('hub_mode');
-        $topic = $this->arg('hub_topic');
-        $challenge = $this->arg('hub_challenge');
-        $lease_seconds = $this->arg('hub_lease_seconds');
-        $verify_token = $this->arg('hub_verify_token');
-        
-        if ($mode != 'subscribe' && $mode != 'unsubscribe') {
-            common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with mode \"$mode\"");
-            throw new ServerException("Bogus hub callback: bad mode", 404);
-        }
-        
-        $feedinfo = Feedinfo::staticGet('feeduri', $topic);
-        if (!$feedinfo) {
-            common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback for unknown feed $topic");
-            throw new ServerException("Bogus hub callback: unknown feed", 404);
-        }
-
-        # Can't currently set the token in our sub api
-        #if ($feedinfo->verify_token !== $verify_token) {
-        #    common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with bad token \"$verify_token\" for feed $topic");
-        #    throw new ServerError("Bogus hub callback: bad token", 404);
-        #}
-        
-        // OK!
-        common_log(LOG_INFO, __METHOD__ . ': sub confirmed');
-        $feedinfo->sub_start = common_sql_date(time());
-        if ($lease_seconds > 0) {
-            $feedinfo->sub_end = common_sql_date(time() + $lease_seconds);
-        } else {
-            $feedinfo->sub_end = null;
-        }
-        $feedinfo->update();
-        
-        print $challenge;
-    }
-}
diff --git a/plugins/OStatus/actions/hub.php b/plugins/OStatus/actions/hub.php
deleted file mode 100644 (file)
index 5caf4b4..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * Integrated PuSH hub; lets us only ping them what need it.
- * @package Hub
- * @maintainer Brion Vibber <brion@status.net>
- */
-
-/**
-
-
-Things to consider...
-* should we purge incomplete subscriptions that never get a verification pingback?
-* when can we send subscription renewal checks?
-    - at next send time probably ok
-* when can we handle trimming of subscriptions?
-    - at next send time probably ok
-* should we keep a fail count?
-
-*/
-
-
-class HubAction extends Action
-{
-    function arg($arg, $def=null)
-    {
-        // PHP converts '.'s in incoming var names to '_'s.
-        // It also merges multiple values, which'll break hub.verify and hub.topic for publishing
-        // @fixme handle multiple args
-        $arg = str_replace('.', '_', $arg);
-        return parent::arg($arg, $def);
-    }
-
-    function prepare($args)
-    {
-        StatusNet::setApi(true); // reduce exception reports to aid in debugging
-        return parent::prepare($args);
-    }
-
-    function handle()
-    {
-        $mode = $this->trimmed('hub.mode');
-        switch ($mode) {
-        case "subscribe":
-            $this->subscribe();
-            break;
-        case "unsubscribe":
-            $this->unsubscribe();
-            break;
-        case "publish":
-            throw new ServerException("Publishing outside feeds not supported.", 400);
-        default:
-            throw new ServerException("Unrecognized mode '$mode'.", 400);
-        }
-    }
-
-    /**
-     * Process a PuSH feed subscription request.
-     *
-     * HTTP return codes:
-     *   202 Accepted - request saved and awaiting verification
-     *   204 No Content - already subscribed
-     *   403 Forbidden - rejecting this (not specifically spec'd)
-     */
-    function subscribe()
-    {
-        $feed = $this->argUrl('hub.topic');
-        $callback = $this->argUrl('hub.callback');
-
-        common_log(LOG_DEBUG, __METHOD__ . ": checking sub'd to $feed $callback");
-        if ($this->getSub($feed, $callback)) {
-            // Already subscribed; return 204 per spec.
-            header('HTTP/1.1 204 No Content');
-            common_log(LOG_DEBUG, __METHOD__ . ': already subscribed');
-            return;
-        }
-
-        common_log(LOG_DEBUG, __METHOD__ . ': setting up');
-        $sub = new HubSub();
-        $sub->topic = $feed;
-        $sub->callback = $callback;
-        $sub->secret = $this->arg('hub.secret', null);
-        $sub->setLease(intval($this->arg('hub.lease_seconds')));
-
-        // @fixme check for feeds we don't manage
-        // @fixme check the verification mode, might want a return immediately?
-
-        common_log(LOG_DEBUG, __METHOD__ . ': inserting');
-        $ok = $sub->insert();
-        
-        if (!$ok) {
-            throw new ServerException("Failed to save subscription record", 500);
-        }
-
-        // @fixme check errors ;)
-
-        $data = array('sub' => $sub, 'mode' => 'subscribe');
-        $qm = QueueManager::get();
-        $qm->enqueue($data, 'hubverify');
-        
-        header('HTTP/1.1 202 Accepted');
-        common_log(LOG_DEBUG, __METHOD__ . ': done');
-    }
-
-    /**
-     * Process a PuSH feed unsubscription request.
-     *
-     * HTTP return codes:
-     *   202 Accepted - request saved and awaiting verification
-     *   204 No Content - already subscribed
-     *   400 Bad Request - invalid params or rejected feed
-     */
-    function unsubscribe()
-    {
-        $feed = $this->argUrl('hub.topic');
-        $callback = $this->argUrl('hub.callback');
-        $sub = $this->getSub($feed, $callback);
-        
-        if ($sub) {
-            if ($sub->verify('unsubscribe')) {
-                $sub->delete();
-                common_log(LOG_INFO, "PuSH unsubscribed $feed for $callback");
-            } else {
-                throw new ServerException("Failed PuSH unsubscription: verification failed! $feed for $callback");
-            }
-        } else {
-            throw new ServerException("Failed PuSH unsubscription: not subscribed! $feed for $callback");
-        }
-    }
-
-    /**
-     * Grab and validate a URL from POST parameters.
-     * @throws ServerException for malformed or non-http/https URLs
-     */
-    protected function argUrl($arg)
-    {
-        $url = $this->arg($arg);
-        $params = array('domain_check' => false, // otherwise breaks my local tests :P
-                        'allowed_schemes' => array('http', 'https'));
-        if (Validate::uri($url, $params)) {
-            return $url;
-        } else {
-            throw new ServerException("Invalid URL passed for $arg: '$url'", 400);
-        }
-    }
-
-    /**
-     * Get HubSub subscription record for a given feed & subscriber.
-     *
-     * @param string $feed
-     * @param string $callback
-     * @return mixed HubSub or false
-     */
-    protected function getSub($feed, $callback)
-    {
-        return HubSub::staticGet($feed, $callback);
-    }
-}
-
diff --git a/plugins/OStatus/actions/pushcallback.php b/plugins/OStatus/actions/pushcallback.php
new file mode 100644 (file)
index 0000000..a5e02e0
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @package FeedSubPlugin
+ * @maintainer Brion Vibber <brion@status.net>
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+
+class PushCallbackAction extends Action
+{
+    function handle()
+    {
+        parent::handle();
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $this->handlePost();
+        } else {
+            $this->handleGet();
+        }
+    }
+    
+    /**
+     * Handler for POST content updates from the hub
+     */
+    function handlePost()
+    {
+        $feedid = $this->arg('feed');
+        common_log(LOG_INFO, "POST for feed id $feedid");
+        if (!$feedid) {
+            throw new ServerException('Empty or invalid feed id', 400);
+        }
+
+        $feedinfo = Feedinfo::staticGet('id', $feedid);
+        if (!$feedinfo) {
+            throw new ServerException('Unknown feed id ' . $feedid, 400);
+        }
+
+        $hmac = '';
+        if (isset($_SERVER['HTTP_X_HUB_SIGNATURE'])) {
+            $hmac = $_SERVER['HTTP_X_HUB_SIGNATURE'];
+        }
+
+        $post = file_get_contents('php://input');
+        $feedinfo->postUpdates($post, $hmac);
+    }
+    
+    /**
+     * Handler for GET verification requests from the hub
+     */
+    function handleGet()
+    {
+        $mode = $this->arg('hub_mode');
+        $topic = $this->arg('hub_topic');
+        $challenge = $this->arg('hub_challenge');
+        $lease_seconds = $this->arg('hub_lease_seconds');
+        $verify_token = $this->arg('hub_verify_token');
+        
+        if ($mode != 'subscribe' && $mode != 'unsubscribe') {
+            common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with mode \"$mode\"");
+            throw new ServerException("Bogus hub callback: bad mode", 404);
+        }
+        
+        $feedinfo = Feedinfo::staticGet('feeduri', $topic);
+        if (!$feedinfo) {
+            common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback for unknown feed $topic");
+            throw new ServerException("Bogus hub callback: unknown feed", 404);
+        }
+
+        # Can't currently set the token in our sub api
+        #if ($feedinfo->verify_token !== $verify_token) {
+        #    common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with bad token \"$verify_token\" for feed $topic");
+        #    throw new ServerError("Bogus hub callback: bad token", 404);
+        #}
+        
+        // OK!
+        common_log(LOG_INFO, __METHOD__ . ': sub confirmed');
+        $feedinfo->sub_start = common_sql_date(time());
+        if ($lease_seconds > 0) {
+            $feedinfo->sub_end = common_sql_date(time() + $lease_seconds);
+        } else {
+            $feedinfo->sub_end = null;
+        }
+        $feedinfo->update();
+        
+        print $challenge;
+    }
+}
diff --git a/plugins/OStatus/actions/pushhub.php b/plugins/OStatus/actions/pushhub.php
new file mode 100644 (file)
index 0000000..901c18f
--- /dev/null
@@ -0,0 +1,176 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Integrated PuSH hub; lets us only ping them what need it.
+ * @package Hub
+ * @maintainer Brion Vibber <brion@status.net>
+ */
+
+/**
+
+
+Things to consider...
+* should we purge incomplete subscriptions that never get a verification pingback?
+* when can we send subscription renewal checks?
+    - at next send time probably ok
+* when can we handle trimming of subscriptions?
+    - at next send time probably ok
+* should we keep a fail count?
+
+*/
+
+
+class PushHubAction extends Action
+{
+    function arg($arg, $def=null)
+    {
+        // PHP converts '.'s in incoming var names to '_'s.
+        // It also merges multiple values, which'll break hub.verify and hub.topic for publishing
+        // @fixme handle multiple args
+        $arg = str_replace('.', '_', $arg);
+        return parent::arg($arg, $def);
+    }
+
+    function prepare($args)
+    {
+        StatusNet::setApi(true); // reduce exception reports to aid in debugging
+        return parent::prepare($args);
+    }
+
+    function handle()
+    {
+        $mode = $this->trimmed('hub.mode');
+        switch ($mode) {
+        case "subscribe":
+            $this->subscribe();
+            break;
+        case "unsubscribe":
+            $this->unsubscribe();
+            break;
+        case "publish":
+            throw new ServerException("Publishing outside feeds not supported.", 400);
+        default:
+            throw new ServerException("Unrecognized mode '$mode'.", 400);
+        }
+    }
+
+    /**
+     * Process a PuSH feed subscription request.
+     *
+     * HTTP return codes:
+     *   202 Accepted - request saved and awaiting verification
+     *   204 No Content - already subscribed
+     *   403 Forbidden - rejecting this (not specifically spec'd)
+     */
+    function subscribe()
+    {
+        $feed = $this->argUrl('hub.topic');
+        $callback = $this->argUrl('hub.callback');
+
+        common_log(LOG_DEBUG, __METHOD__ . ": checking sub'd to $feed $callback");
+        if ($this->getSub($feed, $callback)) {
+            // Already subscribed; return 204 per spec.
+            header('HTTP/1.1 204 No Content');
+            common_log(LOG_DEBUG, __METHOD__ . ': already subscribed');
+            return;
+        }
+
+        common_log(LOG_DEBUG, __METHOD__ . ': setting up');
+        $sub = new HubSub();
+        $sub->topic = $feed;
+        $sub->callback = $callback;
+        $sub->secret = $this->arg('hub.secret', null);
+        $sub->setLease(intval($this->arg('hub.lease_seconds')));
+
+        // @fixme check for feeds we don't manage
+        // @fixme check the verification mode, might want a return immediately?
+
+        common_log(LOG_DEBUG, __METHOD__ . ': inserting');
+        $ok = $sub->insert();
+        
+        if (!$ok) {
+            throw new ServerException("Failed to save subscription record", 500);
+        }
+
+        // @fixme check errors ;)
+
+        $data = array('sub' => $sub, 'mode' => 'subscribe');
+        $qm = QueueManager::get();
+        $qm->enqueue($data, 'hubverify');
+        
+        header('HTTP/1.1 202 Accepted');
+        common_log(LOG_DEBUG, __METHOD__ . ': done');
+    }
+
+    /**
+     * Process a PuSH feed unsubscription request.
+     *
+     * HTTP return codes:
+     *   202 Accepted - request saved and awaiting verification
+     *   204 No Content - already subscribed
+     *   400 Bad Request - invalid params or rejected feed
+     */
+    function unsubscribe()
+    {
+        $feed = $this->argUrl('hub.topic');
+        $callback = $this->argUrl('hub.callback');
+        $sub = $this->getSub($feed, $callback);
+        
+        if ($sub) {
+            if ($sub->verify('unsubscribe')) {
+                $sub->delete();
+                common_log(LOG_INFO, "PuSH unsubscribed $feed for $callback");
+            } else {
+                throw new ServerException("Failed PuSH unsubscription: verification failed! $feed for $callback");
+            }
+        } else {
+            throw new ServerException("Failed PuSH unsubscription: not subscribed! $feed for $callback");
+        }
+    }
+
+    /**
+     * Grab and validate a URL from POST parameters.
+     * @throws ServerException for malformed or non-http/https URLs
+     */
+    protected function argUrl($arg)
+    {
+        $url = $this->arg($arg);
+        $params = array('domain_check' => false, // otherwise breaks my local tests :P
+                        'allowed_schemes' => array('http', 'https'));
+        if (Validate::uri($url, $params)) {
+            return $url;
+        } else {
+            throw new ServerException("Invalid URL passed for $arg: '$url'", 400);
+        }
+    }
+
+    /**
+     * Get HubSub subscription record for a given feed & subscriber.
+     *
+     * @param string $feed
+     * @param string $callback
+     * @return mixed HubSub or false
+     */
+    protected function getSub($feed, $callback)
+    {
+        return HubSub::staticGet($feed, $callback);
+    }
+}
+
index f29d08cb0322bdf88fc212eb009ff0e35c8a4c3c..107faf01258c97c67f22be78848e86dda6a7edfb 100644 (file)
@@ -248,7 +248,7 @@ class Feedinfo extends Memcached_DataObject
         #$this->verify_token = $token;
         #$this->update(); // @fixme
         try {
-            $callback = common_local_url('feedsubcallback', array('feed' => $this->id));
+            $callback = common_local_url('pushcallback', array('feed' => $this->id));
             $headers = array('Content-Type: application/x-www-form-urlencoded');
             $post = array('hub.mode' => 'subscribe',
                           'hub.callback' => $callback,
index 9bc7892fb2db0e641e7f75b4af45fb8e9424bcce..39985fc90278012dc15f9cc77382f13ad96e554e 100644 (file)
@@ -168,7 +168,13 @@ class FeedDiscovery
         }
 
         // Ok... now on to the links!
+        // Types listed in order of priority -- we'll prefer Atom if available.
         // @fixme merge with the munger link checks
+        $feeds = array(
+            'application/atom+xml' => false,
+            'application/rss+xml' => false,
+        );
+        
         $nodes = $dom->getElementsByTagName('link');
         for ($i = 0; $i < $nodes->length; $i++) {
             $node = $nodes->item($i);
@@ -181,17 +187,21 @@ class FeedDiscovery
                     $type = trim($type->value);
                     $href = trim($href->value);
 
-                    $feedTypes = array(
-                        'application/rss+xml',
-                        'application/atom+xml',
-                    );
-                    if (trim($rel) == 'alternate' && in_array($type, $feedTypes)) {
-                        return $this->resolveURI($href, $base);
+                    if (trim($rel) == 'alternate' && array_key_exists($type, $feeds) && empty($feeds[$type])) {
+                        // Save the first feed found of each type...
+                        $feeds[$type] = $this->resolveURI($href, $base);
                     }
                 }
             }
         }
 
+        // Return the highest-priority feed found
+        foreach ($feeds as $type => $url) {
+            if ($url) {
+                return $url;
+            }
+        }
+
         return false;
     }
 
index eeb8d2df39d2b371f19d80c581ff441825c18aea..94801770254ac8bb875a7535e4077a2d1ff65e9c 100644 (file)
@@ -235,34 +235,45 @@ class FeedMunger
      */
     function noticeFromEntry($entry)
     {
+        $max = Notice::maxContent();
+        $ellipsis = "\xe2\x80\xa6"; // U+2026 HORIZONTAL ELLIPSIS
         $title = $entry->title;
         $link = $entry->link;
-        
+
         // @todo We can get <category> entries like this:
         // $cats = $entry->getCategory('category', array(0, true));
         // but it feels like an awful hack. If it's accessible cleanly,
         // try adding #hashtags from the categories/tags on a post.
-        
-        // @todo Should we force a language here?
-        $format = _m('New post: "%1$s" %2$s');
+
         $title = $entry->title;
         $link = $this->getAltLink($entry);
-        $out = sprintf($format, $title, $link);
-        
-        // Trim link if needed...
-        $max = Notice::maxContent();
-        if (mb_strlen($out) > $max) {
-            $link = common_shorten_url($link);
+        if ($link) {
+            // Blog post or such...
+            // @todo Should we force a language here?
+            $format = _m('New post: "%1$s" %2$s');
             $out = sprintf($format, $title, $link);
-        }
 
-        // Trim title if needed...
-        if (mb_strlen($out) > $max) {
-            $ellipsis = "\xe2\x80\xa6"; // U+2026 HORIZONTAL ELLIPSIS
-            $used = mb_strlen($out) - mb_strlen($title);
-            $available = $max - $used - mb_strlen($ellipsis);
-            $title = mb_substr($title, 0, $available) . $ellipsis;
-            $out = sprintf($format, $title, $link);
+            // Trim link if needed...
+            if (mb_strlen($out) > $max) {
+                $link = common_shorten_url($link);
+                $out = sprintf($format, $title, $link);
+            }
+
+            // Trim title if needed...
+            if (mb_strlen($out) > $max) {
+                $used = mb_strlen($out) - mb_strlen($title);
+                $available = $max - $used - mb_strlen($ellipsis);
+                $title = mb_substr($title, 0, $available) . $ellipsis;
+                $out = sprintf($format, $title, $link);
+            }
+        } else {
+            // No link? Consider a bare status update.
+            if (mb_strlen($title) > $max) {
+                $available = $max - mb_strlen($ellipsis);
+                $out = mb_substr($title, 0, $available) . $ellipsis;
+            } else {
+                $out = $title;
+            }
         }
         
         return $out;