diff options
author | Evan Prodromou <evan@status.net> | 2010-12-17 13:12:17 -0500 |
---|---|---|
committer | Evan Prodromou <evan@status.net> | 2010-12-17 13:12:17 -0500 |
commit | 044763cf06b1fb99cd8246dcbb8bb4a3e545d3ed (patch) | |
tree | 35fbd58a77a12553487fdbbb6526407162e6891b /lib/feedimporter.php | |
parent | 6469d75fb0c2d148c8947e905545da89293b3236 (diff) |
move activity importing code to two different queuehandler classes
Diffstat (limited to 'lib/feedimporter.php')
-rw-r--r-- | lib/feedimporter.php | 321 |
1 files changed, 46 insertions, 275 deletions
diff --git a/lib/feedimporter.php b/lib/feedimporter.php index 3f6ac0da4..e2c9df72f 100644 --- a/lib/feedimporter.php +++ b/lib/feedimporter.php @@ -3,7 +3,7 @@ * StatusNet - the distributed open-source microblogging tool * Copyright (C) 2010, StatusNet, Inc. * - * A class for restoring accounts + * Importer for feeds of activities * * PHP version 5 * @@ -35,13 +35,11 @@ if (!defined('STATUSNET')) { } /** - * A class for restoring accounts + * Importer for feeds of activities + * + * Takes an XML file representing a feed of activities and imports each + * activity to the user in question. * - * This is a clumsy objectification of the functions in restoreuser.php. - * - * Note that it quite illegally uses the OStatus_profile class which may - * not even exist on this server. - * * @category Account * @package StatusNet * @author Evan Prodromou <evan@status.net> @@ -50,309 +48,82 @@ if (!defined('STATUSNET')) { * @link http://status.net/ */ -class AccountRestorer +class FeedImporter extends QueueHandler { - private $_trusted = false; + /** + * Transport identifier + * + * @return string identifier for this queue handler + */ - function loadXML($xml) + public function transport() { - $dom = DOMDocument::loadXML($xml); - - if ($dom->documentElement->namespaceURI != Activity::ATOM || - $dom->documentElement->localName != 'feed') { - throw new Exception("'$filename' is not an Atom feed."); - } - - return $dom; - } - - function importActivityStream($user, $doc) - { - $feed = $doc->documentElement; - - $subjectEl = ActivityUtils::child($feed, Activity::SUBJECT, Activity::SPEC); - - if (!empty($subjectEl)) { - $subject = new ActivityObject($subjectEl); - } else { - throw new Exception("Feed doesn't have an <activity:subject> element."); - } - - if (is_null($user)) { - $user = $this->userFromSubject($subject); - } - - $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry'); - - $activities = $this->entriesToActivities($entries, $feed); - - // XXX: sort entries here - - foreach ($activities as $activity) { - try { - switch ($activity->verb) { - case ActivityVerb::FOLLOW: - $this->subscribeProfile($user, $subject, $activity); - break; - case ActivityVerb::JOIN: - $this->joinGroup($user, $activity); - break; - case ActivityVerb::POST: - $this->postNote($user, $activity); - break; - default: - throw new Exception("Unknown verb: {$activity->verb}"); - } - } catch (Exception $e) { - common_log(LOG_WARNING, $e->getMessage()); - continue; - } - } + return 'feedimp'; } - function subscribeProfile($user, $subject, $activity) + function handle($data) { - $profile = $user->getProfile(); + list($user, $xml, $trusted) = $data; - if ($activity->objects[0]->id == $subject->id) { - if (!$this->_trusted) { - throw new Exception("Skipping a pushed subscription."); - } else { - $other = $activity->actor; - $otherUser = User::staticGet('uri', $other->id); + try { + $doc = DOMDocument::loadXML($xml); - if (!empty($otherUser)) { - $otherProfile = $otherUser->getProfile(); - } else { - throw new Exception("Can't force remote user to subscribe."); - } - // XXX: don't do this for untrusted input! - Subscription::start($otherProfile, $profile); + if ($doc->documentElement->namespaceURI != Activity::ATOM || + $doc->documentElement->localName != 'feed') { + throw new ClientException(_("Not an atom feed.")); } - } else if (empty($activity->actor) - || $activity->actor->id == $subject->id) { - - $other = $activity->objects[0]; - $otherUser = User::staticGet('uri', $other->id); - if (!empty($otherUser)) { - $otherProfile = $otherUser->getProfile(); - } else { - $oprofile = Ostatus_profile::ensureActivityObjectProfile($other); - $otherProfile = $oprofile->localProfile(); - } + $feed = $doc->documentElement; - Subscription::start($profile, $otherProfile); - } else { - throw new Exception("This activity seems unrelated to our user."); - } - } + $author = ActivityUtils::getFeedAuthor($feed); - function joinGroup($user, $activity) - { - // XXX: check that actor == subject - - $uri = $activity->objects[0]->id; - - $group = User_group::staticGet('uri', $uri); - - if (empty($group)) { - $oprofile = Ostatus_profile::ensureActivityObjectProfile($activity->objects[0]); - if (!$oprofile->isGroup()) { - throw new Exception("Remote profile is not a group!"); + if (empty($author)) { + throw new ClientException(_("No author in the feed.")); } - $group = $oprofile->localGroup(); - } - assert(!empty($group)); - - if (Event::handle('StartJoinGroup', array($group, $user))) { - Group_member::join($group->id, $user->id); - Event::handle('EndJoinGroup', array($group, $user)); - } - } - - // XXX: largely cadged from Ostatus_profile::processNote() - - function postNote($user, $activity) - { - $note = $activity->objects[0]; - - $sourceUri = $note->id; - - $notice = Notice::staticGet('uri', $sourceUri); - - if (!empty($notice)) { - // This is weird. - $orig = clone($notice); - $notice->profile_id = $user->id; - $notice->update($orig); - return; - } - - // Use summary as fallback for content - - if (!empty($note->content)) { - $sourceContent = $note->content; - } else if (!empty($note->summary)) { - $sourceContent = $note->summary; - } else if (!empty($note->title)) { - $sourceContent = $note->title; - } else { - // @fixme fetch from $sourceUrl? - // @todo i18n FIXME: use sprintf and add i18n. - throw new ClientException("No content for notice {$sourceUri}."); - } - - // Get (safe!) HTML and text versions of the content - - $rendered = $this->purify($sourceContent); - $content = html_entity_decode(strip_tags($rendered), ENT_QUOTES, 'UTF-8'); - - $shortened = $user->shortenLinks($content); - - $options = array('is_local' => Notice::LOCAL_PUBLIC, - 'uri' => $sourceUri, - 'rendered' => $rendered, - 'replies' => array(), - 'groups' => array(), - 'tags' => array(), - 'urls' => array()); - - // Check for optional attributes... - - if (!empty($activity->time)) { - $options['created'] = common_sql_date($activity->time); - } - - if ($activity->context) { - // Any individual or group attn: targets? - - list($options['groups'], $options['replies']) = $this->filterAttention($activity->context->attention); - - // 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; + if (empty($user)) { + if ($trusted) { + $user = $this->userFromAuthor($author); } } - $location = $activity->context->location; + $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry'); - 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; - } - } - } + $activities = $this->entriesToActivities($entries, $feed); - // Atom categories <-> hashtags + $qm = QueueManager::get(); - foreach ($activity->categories as $cat) { - if ($cat->term) { - $term = common_canonical_tag($cat->term); - if ($term) { - $options['tags'][] = $term; - } + foreach ($activities as $activity) { + $qm->enqueue(array($user, $author, $activity, $trusted), 'actimp'); } + } catch (ClientException $ce) { + common_log(LOG_WARNING, $ce->getMessage()); + return true; + } catch (ServerException $se) { + common_log(LOG_ERR, $ce->getMessage()); + return false; + } catch (Exception $e) { + common_log(LOG_ERR, $ce->getMessage()); + return false; } - - // Atom enclosures -> attachment URLs - foreach ($activity->enclosures as $href) { - // @fixme save these locally or....? - $options['urls'][] = $href; - } - - $saved = Notice::saveNew($user->id, - $content, - 'restore', // TODO: restore the actual source - $options); - - return $saved; } - function filterAttention($attn) + function userFromAuthor($author) { - $groups = array(); - $replies = array(); - - foreach (array_unique($attn) as $recipient) { - - // Is the recipient a local user? - - $user = User::staticGet('uri', $recipient); - - if ($user) { - // @fixme sender verification, spam etc? - $replies[] = $recipient; - continue; - } - - // Is the recipient a remote group? - $oprofile = Ostatus_profile::ensureProfileURI($recipient); - - if ($oprofile) { - if (!$oprofile->isGroup()) { - // may be canonicalized or something - $replies[] = $oprofile->uri; - } - continue; - } - - // Is the recipient a local group? - // @fixme uri on user_group isn't reliable yet - // $group = User_group::staticGet('uri', $recipient); - $id = OStatusPlugin::localGroupFromUrl($recipient); - - if ($id) { - $group = User_group::staticGet('id', $id); - if ($group) { - // Deliver to all members of this local group if allowed. - $profile = $sender->localProfile(); - if ($profile->isMember($group)) { - $groups[] = $group->id; - } else { - common_log(LOG_INFO, "Skipping reply to local group {$group->nickname} as sender {$profile->id} is not a member"); - } - continue; - } else { - common_log(LOG_INFO, "Skipping reply to bogus group $recipient"); - } - } - } - - return array($groups, $replies); - } - - function userFromSubject($subject) - { - $user = User::staticGet('uri', $subject->id); + $user = User::staticGet('uri', $author->id); if (empty($user)) { $attrs = - array('nickname' => Ostatus_profile::getActivityObjectNickname($subject), - 'uri' => $subject->id); + array('nickname' => Ostatus_profile::getActivityObjectNickname($author), + 'uri' => $author->id); $user = User::register($attrs); } $profile = $user->getProfile(); - Ostatus_profile::updateProfile($profile, $subject); + Ostatus_profile::updateProfile($profile, $author); // FIXME: Update avatar return $user; } - - function purify($content) - { - $config = array('safe' => 1, - 'deny_attribute' => 'id,style,on*'); - return htmLawed($content, $config); - } } |