} else if ($this->group_id && !$this->profile_id) {
return true;
} else if ($this->group_id && $this->profile_id) {
- // TRANS: Server exception.
+ // TRANS: Server exception. %s is a URI.
throw new ServerException(sprintf(_m('Invalid ostatus_profile state: both group and profile IDs set for %s.'),$this->uri));
} else {
- // TRANS: Server exception.
+ // TRANS: Server exception. %s is a URI.
throw new ServerException(sprintf(_m('Invalid ostatus_profile state: both group and profile IDs empty for %s.'),$this->uri));
}
}
* an acceptable response from the remote site.
*
* @param mixed $entry XML string, Notice, or Activity
+ * @param Profile $actor
* @return boolean success
*/
public function notifyActivity($entry, $actor)
} else if ($feed->localName == 'rss') { // @fixme check namespace
$this->processRssFeed($feed, $source);
} else {
+ // TRANS: Exception.
throw new Exception(_m('Unknown feed format.'));
}
}
$channels = $rss->getElementsByTagName('channel');
if ($channels->length == 0) {
+ // TRANS: Exception.
throw new Exception(_m('RSS feed without a channel.'));
} else if ($channels->length > 1) {
common_log(LOG_WARNING, __METHOD__ . ": more than one channel in an RSS feed");
{
$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) {
}
Event::handle('EndHandleFeedEntry', array($activity));
+ Event::handle('EndHandleFeedEntryWithProfile', array($activity, $this));
}
}
*/
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;
-
- 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 <author> info.
- common_log(LOG_WARNING, "Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}");
- } else {
- // Plain <author> without ActivityStreams actor info.
- // We'll just ignore this info for now and save the update under the feed's identity.
- }
+ $oprofile = $this->checkAuthorship($activity);
- $oprofile = $this;
+ if (empty($oprofile)) {
+ return false;
}
// It's not always an ActivityObject::NOTE, but... let's just say it is.
$sourceContent = $note->title;
} else {
// @fixme fetch from $sourceUrl?
- // TRANS: Client exception. %s is a source URL.
+ // TRANS: Client exception. %s is a source URI.
throw new ClientException(sprintf(_m('No content for notice %s.'),$sourceUri));
}
// We mark up the attachment link specially for the HTML output
// so we can fold-out the full version inline.
- // TRANS: Shown when a notice is longer than supported and/or when attachments are present.
+ // @fixme I18N this tooltip will be saved with the site's default language
+ // TRANS: Shown when a notice is longer than supported and/or when attachments are present. At runtime
+ // TRANS: this will usually be replaced with localised text from StatusNet core messages.
$showMoreText = _m('Show more');
$attachUrl = common_local_url('attachment',
array('attachment' => $attachment->id));
return self::ensureFeedURL($feedurl, $hints);
}
- // TRANS: Exception.
+ // TRANS: Exception. %s is a URL.
throw new Exception(sprintf(_m('Could not find a feed URL for profile page %s.'),$finalUrl));
}
* @return Ostatus_profile
* @throws Exception
*/
+
public static function ensureAtomFeed($feedEl, $hints)
{
- // Try to get a profile from the feed activity:subject
-
- $subject = ActivityUtils::child($feedEl, Activity::SUBJECT, Activity::SPEC);
-
- if (!empty($subject)) {
- $subjObject = new ActivityObject($subject);
- return self::ensureActivityObjectProfile($subjObject, $hints);
- }
-
- // Otherwise, try the feed author
-
- $author = ActivityUtils::child($feedEl, Activity::AUTHOR, Activity::ATOM);
-
- if (!empty($author)) {
- $authorObject = new ActivityObject($author);
- return self::ensureActivityObjectProfile($authorObject, $hints);
- }
-
- // Sheesh. Not a very nice feed! Let's try fingerpoken in the
- // entries.
-
- $entries = $feedEl->getElementsByTagNameNS(Activity::ATOM, 'entry');
-
- if (!empty($entries) && $entries->length > 0) {
-
- $entry = $entries->item(0);
-
- $actor = ActivityUtils::child($entry, Activity::ACTOR, Activity::SPEC);
-
- if (!empty($actor)) {
- $actorObject = new ActivityObject($actor);
- return self::ensureActivityObjectProfile($actorObject, $hints);
+ $author = ActivityUtils::getFeedAuthor($feedEl);
- }
-
- $author = ActivityUtils::child($entry, Activity::AUTHOR, Activity::ATOM);
-
- if (!empty($author)) {
- $authorObject = new ActivityObject($author);
- return self::ensureActivityObjectProfile($authorObject, $hints);
- }
+ if (empty($author)) {
+ // XXX: make some educated guesses here
+ // TRANS: Feed sub exception.
+ throw new FeedSubException(_m('Can\'t find enough profile '.
+ 'information to make a feed.'));
}
- // XXX: make some educated guesses here
- throw new FeedSubException(_m('Can\'t find enough profile information to make a feed.'));
+ return self::ensureActivityObjectProfile($author, $hints);
}
/**
return;
}
if (!common_valid_http_url($url)) {
+ // TRANS: Server exception. %s is a URL.
throw new ServerException(sprintf(_m("Invalid avatar URL %s."), $url));
}
}
if (!$self) {
throw new ServerException(sprintf(
+ // TRANS: Server exception. %s is a URI.
_m("Tried to update avatar for unsaved remote profile %s."),
$this->uri));
}
// @fixme this should be better encapsulated
// ripped from oauthstore.php (for old OMB client)
$temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
- if (!copy($url, $temp_filename)) {
- throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url));
- }
+ try {
+ if (!copy($url, $temp_filename)) {
+ // TRANS: Server exception. %s is a URL.
+ throw new ServerException(sprintf(_m("Unable to fetch avatar from %s."), $url));
+ }
- if ($this->isGroup()) {
- $id = $this->group_id;
- } else {
- $id = $this->profile_id;
- }
- // @fixme should we be using different ids?
- $imagefile = new ImageFile($id, $temp_filename);
- $filename = Avatar::filename($id,
- image_type_to_extension($imagefile->type),
- null,
- common_timestamp());
- rename($temp_filename, Avatar::path($filename));
+ if ($this->isGroup()) {
+ $id = $this->group_id;
+ } else {
+ $id = $this->profile_id;
+ }
+ // @fixme should we be using different ids?
+ $imagefile = new ImageFile($id, $temp_filename);
+ $filename = Avatar::filename($id,
+ image_type_to_extension($imagefile->type),
+ null,
+ common_timestamp());
+ rename($temp_filename, Avatar::path($filename));
+ } catch (Exception $e) {
+ unlink($temp_filename);
+ throw $e;
+ }
// @fixme hardcoded chmod is lame, but seems to be necessary to
// keep from accidentally saving images from command-line (queues)
// that can't be read from web server, which causes hard-to-notice
return $url;
}
}
- return common_path('plugins/OStatus/images/96px-Feed-icon.svg.png');
+
+ return Plugin::staticPath('OStatus', 'images/96px-Feed-icon.svg.png');
}
/**
$oprofile->profile_id = $profile->insert();
if (!$oprofile->profile_id) {
- // TRANS: Exception.
+ // TRANS: Server exception.
throw new ServerException(_m('Can\'t save local profile.'));
}
} else {
$oprofile->group_id = $group->insert();
if (!$oprofile->group_id) {
- // TRANS: Exception.
+ // TRANS: Server exception.
throw new ServerException(_m('Can\'t save local profile.'));
}
}
$ok = $oprofile->insert();
if (!$ok) {
- // TRANS: Exception.
+ // TRANS: Server exception.
throw new ServerException(_m('Can\'t save OStatus profile.'));
}
{
$orig = clone($profile);
- $profile->nickname = self::getActivityObjectNickname($object, $hints);
+ // Existing nickname is better than nothing.
+
+ if (!array_key_exists('nickname', $hints)) {
+ $hints['nickname'] = $profile->nickname;
+ }
+
+ $nickname = self::getActivityObjectNickname($object, $hints);
+
+ if (!empty($nickname)) {
+ $profile->nickname = $nickname;
+ }
if (!empty($object->title)) {
$profile->fullname = $object->title;
$profile->profileurl = $object->id;
}
- $profile->bio = self::getActivityObjectBio($object, $hints);
- $profile->location = self::getActivityObjectLocation($object, $hints);
- $profile->homepage = self::getActivityObjectHomepage($object, $hints);
+ $bio = self::getActivityObjectBio($object, $hints);
+
+ if (!empty($bio)) {
+ $profile->bio = $bio;
+ }
+
+ $location = self::getActivityObjectLocation($object, $hints);
+
+ if (!empty($location)) {
+ $profile->location = $location;
+ }
+
+ $homepage = self::getActivityObjectHomepage($object, $hints);
+
+ if (!empty($homepage)) {
+ $profile->homepage = $homepage;
+ }
if (!empty($object->geopoint)) {
$location = ActivityContext::locationFromPoint($object->geopoint);
}
// Try the profile url (like foo.example.com or example.com/user/foo)
-
- $profileUrl = ($object->link) ? $object->link : $hints['profileurl'];
+ if (!empty($object->link)) {
+ $profileUrl = $object->link;
+ } else if (!empty($hints['profileurl'])) {
+ $profileUrl = $hints['profileurl'];
+ }
if (!empty($profileUrl)) {
$nickname = self::nicknameFromURI($profileUrl);
protected static function nicknameFromURI($uri)
{
- preg_match('/(\w+):/', $uri, $matches);
-
- $protocol = $matches[1];
+ if (preg_match('/(\w+):/', $uri, $matches)) {
+ $protocol = $matches[1];
+ } else {
+ return null;
+ }
switch ($protocol) {
case 'acct':
if ($file_id === false) {
common_log_db_error($file, "INSERT", __FILE__);
+ // TRANS: Server exception.
throw new ServerException(_m('Could not store HTML content of long post as file.'));
}
case 'mailto':
$rest = $match[2];
$oprofile = Ostatus_profile::ensureWebfinger($rest);
+ break;
default:
- common_log("Unrecognized URI protocol for profile: $protocol ($uri)");
+ throw new ServerException("Unrecognized URI protocol for profile: $protocol ($uri)");
break;
}
+ } else {
+ throw new ServerException("No URI protocol for profile: ($uri)");
+ }
+ }
+
+ 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 <author> info.
+ common_log(LOG_WARNING, "Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}");
+ } else {
+ // Plain <author> 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;
}
}