From 67bde86f7c434eee8d5d9d05c651bcb2260927f8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 23 Dec 2010 09:42:42 -0800 Subject: [PATCH] accept bookmarks over PuSH --- plugins/Bookmark/BookmarkPlugin.php | 105 ++++++++++++++++++++ plugins/Bookmark/Notice_bookmark.php | 3 +- plugins/OStatus/classes/Ostatus_profile.php | 75 ++++++++------ 3 files changed, 152 insertions(+), 31 deletions(-) diff --git a/plugins/Bookmark/BookmarkPlugin.php b/plugins/Bookmark/BookmarkPlugin.php index 080daac59b..e18ea25eaa 100644 --- a/plugins/Bookmark/BookmarkPlugin.php +++ b/plugins/Bookmark/BookmarkPlugin.php @@ -276,11 +276,17 @@ class BookmarkPlugin extends Plugin function onStartActivityObjectFromNotice($notice, &$object) { + common_log(LOG_INFO, + "Checking {$notice->uri} to see if it's a bookmark."); + $nb = Notice_bookmark::staticGet('notice_id', $notice->id); if (!empty($nb)) { + common_log(LOG_INFO, + "Formatting notice {$notice->uri} as a bookmark."); + $object->id = $notice->uri; $object->type = ActivityObject::BOOKMARK; $object->title = $nb->title; @@ -387,5 +393,104 @@ class BookmarkPlugin extends Plugin return true; } + + /** + * Handle a posted bookmark from PuSH + * + * @param Activity $activity activity to handle + * @param Ostatus_profile $oprofile Profile for the feed + * + * @return boolean hook value + */ + + function onStartHandleFeedEntryWithProfile($activity, $oprofile) { + + common_log(LOG_INFO, "BookmarkPlugin called for new feed entry."); + + if ($activity->verb == ActivityVerb::POST && + $activity->objects[0]->type == ActivityObject::BOOKMARK) { + + common_log(LOG_INFO, "Importing activity {$activity->id} as a bookmark."); + + $author = $oprofile->checkAuthorship($activity); + + if (empty($author)) { + throw new ClientException(_('Can\'t get author for activity.')); + } + + self::_postRemoteBookmark($author, + $activity); + + return false; + } + + return true; + } + + static private function _postRemoteBookmark(Ostatus_profile $author, Activity $activity) + { + $bookmark = $activity->objects[0]; + + $relLinkEls = ActivityUtils::getLinks($bookmark->element, 'related'); + + if (count($relLinkEls) < 1) { + throw new ClientException(_('Expected exactly 1 link rel=related in a Bookmark.')); + } + + if (count($relLinkEls) > 1) { + common_log(LOG_WARNING, "Got too many link rel=related in a Bookmark."); + } + + $linkEl = $relLinkEls[0]; + + $url = $linkEl->getAttribute('href'); + + $tags = array(); + + foreach ($activity->categories as $category) { + $tags[] = common_canonical_tag($category->term); + } + + $options = array('uri' => $bookmark->id, + 'url' => $bookmark->link, + 'created' => common_sql_time($activity->time), + 'is_local' => Notice::REMOTE_OMB, + 'source' => 'ostatus'); + + // Fill in location if available + + $location = $activity->context->location; + + if ($location) { + $options['lat'] = $location->lat; + $options['lon'] = $location->lon; + if ($location->location_id) { + $options['location_ns'] = $location->location_ns; + $options['location_id'] = $location->location_id; + } + } + + $replies = $activity->context->attention; + $options['groups'] = $author->filterReplies($author, $replies); + $options['replies'] = $replies; + + // Maintain direct reply associations + // @fixme what about conversation ID? + + if (!empty($activity->context->replyToID)) { + $orig = Notice::staticGet('uri', + $activity->context->replyToID); + if (!empty($orig)) { + $options['reply_to'] = $orig->id; + } + } + + Notice_bookmark::saveNew($author->localProfile(), + $bookmark->title, + $url, + $tags, + $bookmark->summary, + $options); + } } diff --git a/plugins/Bookmark/Notice_bookmark.php b/plugins/Bookmark/Notice_bookmark.php index 38e2890abe..3a7d0ed742 100644 --- a/plugins/Bookmark/Notice_bookmark.php +++ b/plugins/Bookmark/Notice_bookmark.php @@ -238,7 +238,8 @@ class Notice_bookmark extends Memcached_DataObject $saved = Notice::saveNew($profile->id, $content, - 'web', + array_key_exists('source', $options) ? + $options['source'] : 'web', $options); if (!empty($saved)) { diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 77cf57a670..9c0f014fc6 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -457,7 +457,8 @@ class Ostatus_profile extends Memcached_DataObject { $activity = new Activity($entry, $feed); - if (Event::handle('StartHandleFeedEntry', array($activity))) { + if (Event::handle('StartHandleFeedEntryWithProfile', array($activity, $this)) && + Event::handle('StartHandleFeedEntry', array($activity))) { // @todo process all activity objects switch ($activity->objects[0]->type) { @@ -479,6 +480,7 @@ class Ostatus_profile extends Memcached_DataObject } Event::handle('EndHandleFeedEntry', array($activity)); + Event::handle('EndHandleFeedEntryWithProfile', array($activity, $this)); } } @@ -491,36 +493,10 @@ class Ostatus_profile extends Memcached_DataObject */ public function processPost($activity, $method) { - if ($this->isGroup()) { - // A group feed will contain posts from multiple authors. - // @fixme validate these profiles in some way! - $oprofile = self::ensureActorProfile($activity); - if ($oprofile->isGroup()) { - // Groups can't post notices in StatusNet. - common_log(LOG_WARNING, "OStatus: skipping post with group listed as author: $oprofile->uri in feed from $this->uri"); - return false; - } - } else { - $actor = $activity->actor; + $oprofile = $this->checkAuthorship($activity); - if (empty($actor)) { - // OK here! assume the default - } else if ($actor->id == $this->uri || $actor->link == $this->uri) { - $this->updateFromActivityObject($actor); - } else if ($actor->id) { - // We have an ActivityStreams actor with an explicit ID that doesn't match the feed owner. - // This isn't what we expect from mainline OStatus person feeds! - // Group feeds go down another path, with different validation... - // Most likely this is a plain ol' blog feed of some kind which - // doesn't match our expectations. We'll take the entry, but ignore - // the info. - common_log(LOG_WARNING, "Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}"); - } else { - // Plain without ActivityStreams actor info. - // We'll just ignore this info for now and save the update under the feed's identity. - } - - $oprofile = $this; + if (empty($oprofile)) { + return false; } // It's not always an ActivityObject::NOTE, but... let's just say it is. @@ -1810,6 +1786,45 @@ class Ostatus_profile extends Memcached_DataObject } return $oprofile; } + + function checkAuthorship($activity) + { + if ($this->isGroup()) { + // A group feed will contain posts from multiple authors. + // @fixme validate these profiles in some way! + $oprofile = self::ensureActorProfile($activity); + if ($oprofile->isGroup()) { + // Groups can't post notices in StatusNet. + common_log(LOG_WARNING, + "OStatus: skipping post with group listed as author: ". + "$oprofile->uri in feed from $this->uri"); + return false; + } + } else { + $actor = $activity->actor; + + if (empty($actor)) { + // OK here! assume the default + } else if ($actor->id == $this->uri || $actor->link == $this->uri) { + $this->updateFromActivityObject($actor); + } else if ($actor->id) { + // We have an ActivityStreams actor with an explicit ID that doesn't match the feed owner. + // This isn't what we expect from mainline OStatus person feeds! + // Group feeds go down another path, with different validation... + // Most likely this is a plain ol' blog feed of some kind which + // doesn't match our expectations. We'll take the entry, but ignore + // the info. + common_log(LOG_WARNING, "Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}"); + } else { + // Plain without ActivityStreams actor info. + // We'll just ignore this info for now and save the update under the feed's identity. + } + + $oprofile = $this; + } + + return $oprofile; + } } /**