From 5f9a8ca64bee5247358238c26b4f54003337d11b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Feb 2010 21:11:44 +0000 Subject: OStatus: accept webfinger addresses as well as profile URLs in the explicit remote subscribe form. --- plugins/OStatus/actions/ostatussub.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index b3569e695..12832cdcf 100644 --- a/plugins/OStatus/actions/ostatussub.php +++ b/plugins/OStatus/actions/ostatussub.php @@ -288,10 +288,15 @@ class OStatusSubAction extends Action } $this->profile_uri = $profile_uri; - // @fixme validate, normalize bla bla try { - $oprofile = Ostatus_profile::ensureProfile($this->profile_uri); - $this->oprofile = $oprofile; + if (Validate::email($this->profile_uri)) { + $this->oprofile = Ostatus_profile::ensureWebfinger($this->profile_uri); + } else if (Validate::uri($this->profile_uri)) { + $this->oprofile = Ostatus_profile::ensureProfile($this->profile_uri); + } else { + $this->error = _m("Invalid address format."); + return false; + } return true; } catch (FeedSubBadURLException $e) { $this->error = _m('Invalid URL or could not reach server.'); -- cgit v1.2.3-54-g00ecf From d6ad7332475f1cc4ab45d55fc04ef491d5f3999d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Feb 2010 21:47:14 +0000 Subject: OStatus: fixes for link/id and text extraction gets import of Buzz feeds working. --- plugins/OStatus/OStatusPlugin.php | 26 ++++++++++++++------------ plugins/OStatus/classes/Ostatus_profile.php | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 3a9e77c2a..724634924 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -287,13 +287,19 @@ class OStatusPlugin extends Plugin function onStartNoticeSourceLink($notice, &$name, &$url, &$title) { if ($notice->source == 'ostatus') { - $bits = parse_url($notice->uri); - $domain = $bits['host']; - - $name = $domain; - $url = $notice->uri; - $title = sprintf(_m("Sent from %s via OStatus"), $domain); - return false; + if ($notice->url) { + $bits = parse_url($notice->url); + $domain = $bits['host']; + if (substr($domain, 0, 4) == 'www.') { + $name = substr($domain, 4); + } else { + $name = $domain; + } + + $url = $notice->url; + $title = sprintf(_m("Sent from %s via OStatus"), $domain); + return false; + } } } @@ -509,12 +515,8 @@ class OStatusPlugin extends Plugin $oprofile = Ostatus_profile::staticGet('group_id', $group->id); if ($oprofile) { // Drop the PuSH subscription if there are no other subscribers. + $oprofile->garbageCollect(); - $members = $group->getMembers(0, 1); - if ($members->N == 0) { - common_log(LOG_INFO, "Unsubscribing from now-unused group feed $oprofile->feeduri"); - $oprofile->unsubscribe(); - } $member = Profile::staticGet($user->id); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 91b957fa2..4998809bc 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -556,17 +556,28 @@ class Ostatus_profile extends Memcached_DataObject if ($activity->object->link) { $sourceUrl = $activity->object->link; + } else if ($activity->link) { + $sourceUrl = $activity->link; } else if (preg_match('!^https?://!', $activity->object->id)) { $sourceUrl = $activity->object->id; } - // @fixme sanitize and save HTML content if available + // Get (safe!) HTML and text versions of the content - $content = $activity->object->title; + require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); + + $html = $activity->object->content; + + $purifier = new HTMLPurifier(); + + $rendered = $purifier->purify($html); + + $content = html_entity_decode(strip_tags($rendered)); $params = array('is_local' => Notice::REMOTE_OMB, 'url' => $sourceUrl, - 'uri' => $sourceUri); + 'uri' => $sourceUri, + 'rendered' => $rendered); $location = $activity->context->location; -- cgit v1.2.3-54-g00ecf From fa178a8aa7d6e5ade76eef12ac0ca49aa10f5cdc Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 23 Feb 2010 14:26:34 -0800 Subject: Add poco:displayName to Atom output for person object --- lib/activity.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/activity.php b/lib/activity.php index 04c57c561..853741a9a 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -104,6 +104,7 @@ class PoCo function __construct($profile) { $this->preferredUsername = $profile->nickname; + $this->displayName = $profile->getBestName(); $this->note = $profile->bio; $this->address = new PoCoAddress($profile->location); @@ -129,6 +130,12 @@ class PoCo $this->preferredUsername ); + $xs->element( + 'poco:displayName', + null, + $this->displayName + ); + if (!empty($this->note)) { $xs->element('poco:note', null, $this->note); } -- cgit v1.2.3-54-g00ecf From 391b45949f6fabef0427aa99d4123fe6ef5ef49d Mon Sep 17 00:00:00 2001 From: James Walker Date: Tue, 23 Feb 2010 18:25:31 -0500 Subject: adding xfn, foaf and hcard rel's to our webfinger output --- plugins/OStatus/actions/webfinger.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/plugins/OStatus/actions/webfinger.php b/plugins/OStatus/actions/webfinger.php index fbbd8d039..34336a903 100644 --- a/plugins/OStatus/actions/webfinger.php +++ b/plugins/OStatus/actions/webfinger.php @@ -65,6 +65,21 @@ class WebfingerAction extends Action 'format' => 'atom')), 'type' => 'application/atom+xml'); + // hCard + $xrd->links[] = array('rel' => 'http://microformats.org/profile/hcard', + 'type' => 'text/html', + 'href' => common_profile_url($nick)); + + // XFN + $xrd->links[] = array('rel' => 'http://gmpg.org/xfn/11', + 'type' => 'text/html', + 'href' => common_profile_url($nick)); + // FOAF + $xrd->links[] = array('rel' => 'describedby', + 'type' => 'application/rdf+xml', + 'href' => common_local_url('foaf', + array('nickname' => $nick))); + $salmon_url = common_local_url('salmon', array('id' => $this->user->id)); -- cgit v1.2.3-54-g00ecf From 25864aea9dd81ebcdb8c7386af3662eda800ccc3 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 24 Feb 2010 00:59:00 +0100 Subject: Using the default abbr class pattern for geo microformats instead of the shorthand that I've proposed at http://microformats.org/wiki/geo-brainstorming#latitude_longitude_shorthand_and_geo_link If anyone wants to pick up on where the discussion was left off or get more implementation support by other sites and software, and be recognized by parsers, I'd be happy to go back to the shorthand. Because you know, it actually makes a lot of sense. --- lib/noticelist.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/noticelist.php b/lib/noticelist.php index 837cb90fa..dcf17be08 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -438,14 +438,15 @@ class NoticeListItem extends Widget $this->out->text(_('at')); $this->out->text(' '); if (empty($url)) { - $this->out->element('span', array('class' => 'geo', + $this->out->element('abbr', array('class' => 'geo', 'title' => $latlon), $name); } else { - $this->out->element('a', array('class' => 'geo', - 'title' => $latlon, - 'href' => $url), + $this->out->elementStart('a', array('href' => $url)); + $this->out->element('abbr', array('class' => 'geo', + 'title' => $latlon), $name); + $this->out->elementEnd('a'); } $this->out->elementEnd('span'); } -- cgit v1.2.3-54-g00ecf From 2aaf8d4e308d49d072a6c43e43cb99c373deca2e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 01:09:12 +0000 Subject: Add class and (if present) id to DB_DataObject error exceptions; often they're VERRRRRY vague, and it helps to know what type of item is failing! --- classes/Memcached_DataObject.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 40576dc71..bc4c3a000 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -501,7 +501,11 @@ class Memcached_DataObject extends Safe_DataObject function raiseError($message, $type = null, $behaviour = null) { - throw new ServerException("DB_DataObject error [$type]: $message"); + $id = get_class($this); + if ($this->id) { + $id .= ':' . $this->id; + } + throw new ServerException("[$id] DB_DataObject error [$type]: $message"); } static function cacheGet($keyPart) -- cgit v1.2.3-54-g00ecf From 584b87cfe57bd2d683101194040e3563f0706536 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 01:09:52 +0000 Subject: OStatus: consolidate the low-level notice save code between Salmon and PuSH input paths. Validation etc remains at higher levels. --- plugins/OStatus/OStatusPlugin.php | 2 +- plugins/OStatus/classes/Ostatus_profile.php | 214 ++++++++++++++++------------ plugins/OStatus/lib/salmonaction.php | 50 +------ 3 files changed, 125 insertions(+), 141 deletions(-) diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 724634924..35f952935 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -314,7 +314,7 @@ class OStatusPlugin extends Plugin { $oprofile = Ostatus_profile::staticGet('feeduri', $feedsub->uri); if ($oprofile) { - $oprofile->processFeed($feed); + $oprofile->processFeed($feed, 'push'); } else { common_log(LOG_DEBUG, "No ostatus profile for incoming feed $feedsub->uri"); } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 4998809bc..6beaf0f5d 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -488,7 +488,7 @@ class Ostatus_profile extends Memcached_DataObject * * @param DOMDocument $feed */ - public function processFeed($feed) + public function processFeed($feed, $source) { $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry'); if ($entries->length == 0) { @@ -498,7 +498,7 @@ class Ostatus_profile extends Memcached_DataObject for ($i = 0; $i < $entries->length; $i++) { $entry = $entries->item($i); - $this->processEntry($entry, $feed); + $this->processEntry($entry, $feed, $source); } } @@ -508,15 +508,12 @@ class Ostatus_profile extends Memcached_DataObject * @param DOMElement $entry * @param DOMElement $feed for context */ - protected function processEntry($entry, $feed) + public function processEntry($entry, $feed, $source) { $activity = new Activity($entry, $feed); - $debug = var_export($activity, true); - common_log(LOG_DEBUG, $debug); - if ($activity->verb == ActivityVerb::POST) { - $this->processPost($activity); + $this->processPost($activity, $source); } else { common_log(LOG_INFO, "Ignoring activity with unrecognized verb $activity->verb"); } @@ -525,35 +522,47 @@ class Ostatus_profile extends Memcached_DataObject /** * Process an incoming post activity from this remote feed. * @param Activity $activity + * @param string $method 'push' or 'salmon' + * @return mixed saved Notice or false * @fixme break up this function, it's getting nasty long */ - protected function processPost($activity) + 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 { + // Individual user feeds may contain only posts from themselves. + // Authorship is validated against the profile URI on upper layers, + // through PuSH setup or Salmon signature checks. $actorUri = self::getActorProfileURI($activity); if ($actorUri == $this->uri) { // @fixme check if profile info has changed and update it } else { - // @fixme drop or reject the messages once we've got the canonical profile URI recorded sanely - common_log(LOG_INFO, "OStatus: Warning: non-group post with unexpected author: $actorUri expected $this->uri"); - //return; + common_log(LOG_WARNING, "OStatus: skipping post with bad author: got $actorUri expected $this->uri"); + return false; } $oprofile = $this; } - $sourceUri = $activity->object->id; + // The id URI will be used as a unique identifier for for the notice, + // protecting against duplicate saves. It isn't required to be a URL; + // tag: URIs for instance are found in Google Buzz feeds. + $sourceUri = $activity->object->id; $dupe = Notice::staticGet('uri', $sourceUri); - if ($dupe) { common_log(LOG_INFO, "OStatus: ignoring duplicate post: $sourceUri"); - return; + return false; } + // We'll also want to save a web link to the original notice, if provided. $sourceUrl = null; - if ($activity->object->link) { $sourceUrl = $activity->object->link; } else if ($activity->link) { @@ -563,103 +572,126 @@ class Ostatus_profile extends Memcached_DataObject } // Get (safe!) HTML and text versions of the content - - require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); - - $html = $activity->object->content; - - $purifier = new HTMLPurifier(); - - $rendered = $purifier->purify($html); - + $rendered = $this->purify($activity->object->content); $content = html_entity_decode(strip_tags($rendered)); - $params = array('is_local' => Notice::REMOTE_OMB, + $options = array('is_local' => Notice::REMOTE_OMB, 'url' => $sourceUrl, 'uri' => $sourceUri, - 'rendered' => $rendered); + 'rendered' => $rendered, + 'replies' => array(), + 'groups' => array()); - $location = $activity->context->location; + // Check for optional attributes... - if ($location) { - $params['lat'] = $location->lat; - $params['lon'] = $location->lon; - if ($location->location_id) { - $params['location_ns'] = $location->location_ns; - $params['location_id'] = $location->location_id; - } + if (!empty($activity->time)) { + $options['created'] = common_sql_date($activity->time); } - $profile = $oprofile->localProfile(); - $params['groups'] = array(); - $params['replies'] = array(); if ($activity->context) { - foreach ($activity->context->attention as $recipient) { - $roprofile = Ostatus_profile::staticGet('uri', $recipient); - if ($roprofile) { - if ($roprofile->isGroup()) { - // Deliver to local recipients of this remote group. - // @fixme sender verification? - $params['groups'][] = $roprofile->group_id; - continue; - } else { - // Delivery to remote users is the source service's job. - continue; - } - } - - $user = User::staticGet('uri', $recipient); - if ($user) { - // An @-reply directed to a local user. - // @fixme sender verification, spam etc? - $params['replies'][] = $recipient; - continue; + // Any individual or group attn: targets? + $replies = $activity->context->attention; + $options['groups'] = $this->filterReplies($oprofile, $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; } - - // @fixme we need a uri on user_group - // $group = User_group::staticGet('uri', $recipient); - $template = common_local_url('groupbyid', array('id' => '31337')); - $template = preg_quote($template, '/'); - $template = str_replace('31337', '(\d+)', $template); - common_log(LOG_DEBUG, $template); - if (preg_match("/$template/", $recipient, $matches)) { - $id = $matches[1]; - $group = User_group::staticGet('id', $id); - if ($group) { - // Deliver to all members of this local group. - // @fixme sender verification? - if ($profile->isMember($group)) { - common_log(LOG_DEBUG, "delivering to group $id $group->nickname"); - $params['groups'][] = $group->id; - } else { - common_log(LOG_DEBUG, "not delivering to group $id $group->nickname because sender $profile->nickname is not a member"); - } - continue; - } else { - common_log(LOG_DEBUG, "not delivering to missing group $id"); - } - } else { - common_log(LOG_DEBUG, "not delivering to groups for $recipient"); + } + + $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; } } } try { - $saved = Notice::saveNew($profile->id, + $saved = Notice::saveNew($oprofile->profile_id, $content, 'ostatus', - $params); + $options); + if ($saved) { + Ostatus_source::saveNew($saved, $this, $method); + } } catch (Exception $e) { - common_log(LOG_ERR, "Failed saving notice entry for $sourceUri: " . $e->getMessage()); - return; + common_log(LOG_ERR, "OStatus save of remote message $sourceUri failed: " . $e->getMessage()); + throw $e; } + common_log(LOG_INFO, "OStatus saved remote message $sourceUri as notice id $saved->id"); + return $saved; + } - // Record which feed this came through... - try { - Ostatus_source::saveNew($saved, $this, 'push'); - } catch (Exception $e) { - common_log(LOG_ERR, "Failed saving ostatus_source entry for $saved->notice_id: " . $e->getMessage()); + /** + * Clean up HTML + */ + protected function purify($html) + { + // @fixme disable caching or set a sane temp dir + require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); + $purifier = new HTMLPurifier(); + return $purifier->purify($html); + } + + /** + * Filters a list of recipient ID URIs to just those for local delivery. + * @param Ostatus_profile local profile of sender + * @param array in/out &$attention_uris set of URIs, will be pruned on output + * @return array of group IDs + */ + protected function filterReplies($sender, &$attention_uris) + { + $groups = array(); + $replies = array(); + foreach ($attention_uris 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::staticGet('uri', $recipient); + if ($oprofile) { + if ($oprofile->isGroup()) { + // Deliver to local members of this remote group. + // @fixme sender verification? + $groups[] = $oprofile->group_id; + } + continue; + } + + // Is the recipient a local group? + // @fixme we need a uri on user_group + // $group = User_group::staticGet('uri', $recipient); + $template = common_local_url('groupbyid', array('id' => '31337')); + $template = preg_quote($template, '/'); + $template = str_replace('31337', '(\d+)', $template); + if (preg_match("/$template/", $recipient, $matches)) { + $id = $matches[1]; + $group = User_group::staticGet('id', $id); + if ($group) { + // Deliver to all members of this local group if allowed. + if ($sender->localProfile()->isMember($group)) { + $groups[] = $group->id; + } + continue; + } + } } + $attention_uris = $replies; + return $groups; } /** diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php index 83cf0b8f8..9aac2ed52 100644 --- a/plugins/OStatus/lib/salmonaction.php +++ b/plugins/OStatus/lib/salmonaction.php @@ -185,54 +185,6 @@ class SalmonAction extends Action function saveNotice() { $oprofile = $this->ensureProfile(); - - // Get (safe!) HTML and text versions of the content - - require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); - - $html = $this->act->object->content; - - $purifier = new HTMLPurifier(); - - $rendered = $purifier->purify($html); - - $content = html_entity_decode(strip_tags($rendered)); - - $options = array('is_local' => Notice::REMOTE_OMB, - 'uri' => $this->act->object->id, - 'url' => $this->act->object->link, - 'rendered' => $rendered, - 'replies' => $this->act->context->attention); - - if (!empty($this->act->context->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; - } - } - - if (!empty($this->act->context->replyToID)) { - $orig = Notice::staticGet('uri', - $this->act->context->replyToID); - if (!empty($orig)) { - $options['reply_to'] = $orig->id; - } - } - - if (!empty($this->act->time)) { - $options['created'] = common_sql_date($this->act->time); - } - - $saved = Notice::saveNew($oprofile->profile_id, - $content, - 'ostatus+salmon', - $options); - - // Record that this was saved through a validated Salmon source - // @fixme actually do the signature validation! - Ostatus_source::saveNew($saved, $oprofile, 'salmon'); - return $saved; + return $oprofile->processPost($this->act, 'salmon'); } } -- cgit v1.2.3-54-g00ecf From 2e58802cc9959763f28e2f43c8e0cd0dbe7bcd8e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 02:19:13 +0000 Subject: OStatus: fix group delivery, send reply/group Salmon pings from background. --- plugins/OStatus/OStatusPlugin.php | 28 +--- plugins/OStatus/actions/groupsalmon.php | 9 +- plugins/OStatus/classes/Ostatus_profile.php | 15 +- plugins/OStatus/lib/hubdistribqueuehandler.php | 182 -------------------- plugins/OStatus/lib/ostatusqueuehandler.php | 223 +++++++++++++++++++++++++ plugins/OStatus/lib/salmonoutqueuehandler.php | 44 +++++ 6 files changed, 295 insertions(+), 206 deletions(-) delete mode 100644 plugins/OStatus/lib/hubdistribqueuehandler.php create mode 100644 plugins/OStatus/lib/ostatusqueuehandler.php create mode 100644 plugins/OStatus/lib/salmonoutqueuehandler.php diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 35f952935..9376c048d 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -78,11 +78,16 @@ class OStatusPlugin extends Plugin */ function onEndInitializeQueueManager(QueueManager $qm) { + // Prepare outgoing distributions after notice save. + $qm->connect('ostatus', 'OStatusQueueHandler'); + // Outgoing from our internal PuSH hub $qm->connect('hubverify', 'HubVerifyQueueHandler'); - $qm->connect('hubdistrib', 'HubDistribQueueHandler'); $qm->connect('hubout', 'HubOutQueueHandler'); + // Outgoing Salmon replies (when we don't need a return value) + $qm->connect('salmonout', 'SalmonOutQueueHandler'); + // Incoming from a foreign PuSH hub $qm->connect('pushinput', 'PushInputQueueHandler'); return true; @@ -93,7 +98,7 @@ class OStatusPlugin extends Plugin */ function onStartEnqueueNotice($notice, &$transports) { - $transports[] = 'hubdistrib'; + $transports[] = 'ostatus'; return true; } @@ -199,25 +204,6 @@ class OStatusPlugin extends Plugin function onEndNoticeSave($notice) { - $mentioned = $notice->getReplies(); - - foreach ($mentioned as $profile_id) { - - $oprofile = Ostatus_profile::staticGet('profile_id', $profile_id); - - if (!empty($oprofile) && !empty($oprofile->salmonuri)) { - - common_log(LOG_INFO, "Sending notice '{$notice->uri}' to remote profile '{$oprofile->uri}'."); - - // FIXME: this needs to go out in a queue handler - - $xml = ''; - $xml .= $notice->asAtomEntry(true, true); - - $salmon = new Salmon(); - $salmon->post($oprofile->salmonuri, $xml); - } - } } /** diff --git a/plugins/OStatus/actions/groupsalmon.php b/plugins/OStatus/actions/groupsalmon.php index 2e4fe9443..29377b5fa 100644 --- a/plugins/OStatus/actions/groupsalmon.php +++ b/plugins/OStatus/actions/groupsalmon.php @@ -46,6 +46,11 @@ class GroupsalmonAction extends SalmonAction $this->clientError(_('No such group.')); } + $oprofile = Ostatus_profile::staticGet('group_id', $id); + if ($oprofile) { + $this->clientError(_m("Can't accept remote posts for a remote group.")); + } + return true; } @@ -74,13 +79,13 @@ class GroupsalmonAction extends SalmonAction throw new ClientException("Not to the attention of anyone."); } else { $uri = common_local_url('groupbyid', array('id' => $this->group->id)); - if (!in_array($context->attention, $uri)) { + if (!in_array($uri, $context->attention)) { throw new ClientException("Not to the attention of this group."); } } $profile = $this->ensureProfile(); - // @fixme save the post + $this->saveNotice(); } /** diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 6beaf0f5d..82dbf773d 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -650,6 +650,7 @@ class Ostatus_profile extends Memcached_DataObject */ protected function filterReplies($sender, &$attention_uris) { + common_log(LOG_DEBUG, "Original reply recipients: " . implode(', ', $attention_uris)); $groups = array(); $replies = array(); foreach ($attention_uris as $recipient) { @@ -668,6 +669,8 @@ class Ostatus_profile extends Memcached_DataObject // Deliver to local members of this remote group. // @fixme sender verification? $groups[] = $oprofile->group_id; + } else { + common_log(LOG_DEBUG, "Skipping reply to remote profile $recipient"); } continue; } @@ -683,14 +686,24 @@ class Ostatus_profile extends Memcached_DataObject $group = User_group::staticGet('id', $id); if ($group) { // Deliver to all members of this local group if allowed. - if ($sender->localProfile()->isMember($group)) { + $profile = $sender->localProfile(); + if ($profile->isMember($group)) { $groups[] = $group->id; + } else { + common_log(LOG_DEBUG, "Skipping reply to local group $group->nickname as sender $profile->id is not a member"); } continue; + } else { + common_log(LOG_DEBUG, "Skipping reply to bogus group $recipient"); } } + + common_log(LOG_DEBUG, "Skipping reply to unrecognized profile $recipient"); + } $attention_uris = $replies; + common_log(LOG_DEBUG, "Local reply recipients: " . implode(', ', $replies)); + common_log(LOG_DEBUG, "Local group recipients: " . implode(', ', $groups)); return $groups; } diff --git a/plugins/OStatus/lib/hubdistribqueuehandler.php b/plugins/OStatus/lib/hubdistribqueuehandler.php deleted file mode 100644 index c2bd630f9..000000000 --- a/plugins/OStatus/lib/hubdistribqueuehandler.php +++ /dev/null @@ -1,182 +0,0 @@ -. - */ - -/** - * Send a PuSH subscription verification from our internal hub. - * Queue up final distribution for - * @package Hub - * @author Brion Vibber - */ -class HubDistribQueueHandler extends QueueHandler -{ - function transport() - { - return 'hubdistrib'; - } - - function handle($notice) - { - assert($notice instanceof Notice); - - $this->pushUser($notice); - foreach ($notice->getGroups() as $group) { - $this->pushGroup($notice, $group->id); - } - return true; - } - - function pushUser($notice) - { - // See if there's any PuSH subscriptions, including OStatus clients. - // @fixme handle group subscriptions as well - // http://identi.ca/api/statuses/user_timeline/1.atom - $feed = common_local_url('ApiTimelineUser', - array('id' => $notice->profile_id, - 'format' => 'atom')); - $this->pushFeed($feed, array($this, 'userFeedForNotice'), $notice); - } - - function pushGroup($notice, $group_id) - { - $feed = common_local_url('ApiTimelineGroup', - array('id' => $group_id, - 'format' => 'atom')); - $this->pushFeed($feed, array($this, 'groupFeedForNotice'), $group_id, $notice); - } - - /** - * @param string $feed URI to the feed - * @param callable $callback function to generate Atom feed update if needed - * any additional params are passed to the callback. - */ - function pushFeed($feed, $callback) - { - $hub = common_config('ostatus', 'hub'); - if ($hub) { - $this->pushFeedExternal($feed, $hub); - } - - $sub = new HubSub(); - $sub->topic = $feed; - if ($sub->find()) { - $args = array_slice(func_get_args(), 2); - $atom = call_user_func_array($callback, $args); - $this->pushFeedInternal($atom, $sub); - } else { - common_log(LOG_INFO, "No PuSH subscribers for $feed"); - } - return true; - } - - /** - * Ping external hub about this update. - * The hub will pull the feed and check for new items later. - * Not guaranteed safe in an environment with database replication. - * - * @param string $feed feed topic URI - * @param string $hub PuSH hub URI - * @fixme can consolidate pings for user & group posts - */ - function pushFeedExternal($feed, $hub) - { - $client = new HTTPClient(); - try { - $data = array('hub.mode' => 'publish', - 'hub.url' => $feed); - $response = $client->post($hub, array(), $data); - if ($response->getStatus() == 204) { - common_log(LOG_INFO, "PuSH ping to hub $hub for $feed ok"); - return true; - } else { - common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed with HTTP " . - $response->getStatus() . ': ' . - $response->getBody()); - } - } catch (Exception $e) { - common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed: " . $e->getMessage()); - return false; - } - } - - /** - * Queue up direct feed update pushes to subscribers on our internal hub. - * @param string $atom update feed, containing only new/changed items - * @param HubSub $sub open query of subscribers - */ - function pushFeedInternal($atom, $sub) - { - common_log(LOG_INFO, "Preparing $sub->N PuSH distribution(s) for $sub->topic"); - $qm = QueueManager::get(); - while ($sub->fetch()) { - $sub->distribute($atom); - } - } - - /** - * Build a single-item version of the sending user's Atom feed. - * @param Notice $notice - * @return string - */ - function userFeedForNotice($notice) - { - // @fixme this feels VERY hacky... - // should probably be a cleaner way to do it - - ob_start(); - $api = new ApiTimelineUserAction(); - $api->prepare(array('id' => $notice->profile_id, - 'format' => 'atom', - 'max_id' => $notice->id, - 'since_id' => $notice->id - 1)); - $api->showTimeline(); - $feed = ob_get_clean(); - - // ...and override the content-type back to something normal... eww! - // hope there's no other headers that got set while we weren't looking. - header('Content-Type: text/html; charset=utf-8'); - - common_log(LOG_DEBUG, $feed); - return $feed; - } - - function groupFeedForNotice($group_id, $notice) - { - // @fixme this feels VERY hacky... - // should probably be a cleaner way to do it - - ob_start(); - $api = new ApiTimelineGroupAction(); - $args = array('id' => $group_id, - 'format' => 'atom', - 'max_id' => $notice->id, - 'since_id' => $notice->id - 1); - $api->prepare($args); - $api->handle($args); - $feed = ob_get_clean(); - - // ...and override the content-type back to something normal... eww! - // hope there's no other headers that got set while we weren't looking. - header('Content-Type: text/html; charset=utf-8'); - - common_log(LOG_DEBUG, $feed); - return $feed; - } - -} - diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php new file mode 100644 index 000000000..c1e50bffa --- /dev/null +++ b/plugins/OStatus/lib/ostatusqueuehandler.php @@ -0,0 +1,223 @@ +. + */ + +/** + * Prepare PuSH and Salmon distributions for an outgoing message. + * + * @package OStatusPlugin + * @author Brion Vibber + */ +class OStatusQueueHandler extends QueueHandler +{ + function transport() + { + return 'ostatus'; + } + + function handle($notice) + { + assert($notice instanceof Notice); + + $this->notice = $notice; + $this->user = User::staticGet($notice->profile_id); + + $this->pushUser(); + + foreach ($notice->getGroups() as $group) { + $oprofile = Ostatus_profile::staticGet('group_id', $group->id); + if ($oprofile) { + $this->pingReply($oprofile); + } else { + $this->pushGroup($group->id); + } + } + + foreach ($notice->getReplies() as $profile_id) { + $oprofile = Ostatus_profile::staticGet('profile_id', $profile_id); + if ($oprofile) { + $this->pingReply($oprofile); + } + } + + return true; + } + + function pushUser() + { + if ($this->user) { + // For local posts, ping the PuSH hub to update their feed. + // http://identi.ca/api/statuses/user_timeline/1.atom + $feed = common_local_url('ApiTimelineUser', + array('id' => $this->user->id, + 'format' => 'atom')); + $this->pushFeed($feed, array($this, 'userFeedForNotice')); + } + } + + function pushGroup($group_id) + { + // For a local group, ping the PuSH hub to update its feed. + // Updates may come from either a local or a remote user. + $feed = common_local_url('ApiTimelineGroup', + array('id' => $group_id, + 'format' => 'atom')); + $this->pushFeed($feed, array($this, 'groupFeedForNotice'), $group_id); + } + + function pingReply($oprofile) + { + if ($this->user) { + if (!empty($oprofile->salmonuri)) { + // For local posts, send a Salmon ping to the mentioned + // remote user or group. + // @fixme as an optimization we can skip this if the + // remote profile is subscribed to the author. + + common_log(LOG_INFO, "Prepping to send notice '{$this->notice->uri}' to remote profile '{$oprofile->uri}'."); + + $xml = ''; + $xml .= $this->notice->asAtomEntry(true, true); + + $data = array('salmonuri' => $oprofile->salmonuri, + 'entry' => $xml); + + $qm = QueueManager::get(); + $qm->enqueue($data, 'salmonout'); + } + } + } + + /** + * @param string $feed URI to the feed + * @param callable $callback function to generate Atom feed update if needed + * any additional params are passed to the callback. + */ + function pushFeed($feed, $callback) + { + $hub = common_config('ostatus', 'hub'); + if ($hub) { + $this->pushFeedExternal($feed, $hub); + } + + $sub = new HubSub(); + $sub->topic = $feed; + if ($sub->find()) { + $args = array_slice(func_get_args(), 2); + $atom = call_user_func_array($callback, $args); + $this->pushFeedInternal($atom, $sub); + } else { + common_log(LOG_INFO, "No PuSH subscribers for $feed"); + } + return true; + } + + /** + * Ping external hub about this update. + * The hub will pull the feed and check for new items later. + * Not guaranteed safe in an environment with database replication. + * + * @param string $feed feed topic URI + * @param string $hub PuSH hub URI + * @fixme can consolidate pings for user & group posts + */ + function pushFeedExternal($feed, $hub) + { + $client = new HTTPClient(); + try { + $data = array('hub.mode' => 'publish', + 'hub.url' => $feed); + $response = $client->post($hub, array(), $data); + if ($response->getStatus() == 204) { + common_log(LOG_INFO, "PuSH ping to hub $hub for $feed ok"); + return true; + } else { + common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed with HTTP " . + $response->getStatus() . ': ' . + $response->getBody()); + } + } catch (Exception $e) { + common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed: " . $e->getMessage()); + return false; + } + } + + /** + * Queue up direct feed update pushes to subscribers on our internal hub. + * @param string $atom update feed, containing only new/changed items + * @param HubSub $sub open query of subscribers + */ + function pushFeedInternal($atom, $sub) + { + common_log(LOG_INFO, "Preparing $sub->N PuSH distribution(s) for $sub->topic"); + while ($sub->fetch()) { + $sub->distribute($atom); + } + } + + /** + * Build a single-item version of the sending user's Atom feed. + * @return string + */ + function userFeedForNotice() + { + // @fixme this feels VERY hacky... + // should probably be a cleaner way to do it + + ob_start(); + $api = new ApiTimelineUserAction(); + $api->prepare(array('id' => $this->notice->profile_id, + 'format' => 'atom', + 'max_id' => $this->notice->id, + 'since_id' => $this->notice->id - 1)); + $api->showTimeline(); + $feed = ob_get_clean(); + + // ...and override the content-type back to something normal... eww! + // hope there's no other headers that got set while we weren't looking. + header('Content-Type: text/html; charset=utf-8'); + + common_log(LOG_DEBUG, $feed); + return $feed; + } + + function groupFeedForNotice($group_id) + { + // @fixme this feels VERY hacky... + // should probably be a cleaner way to do it + + ob_start(); + $api = new ApiTimelineGroupAction(); + $args = array('id' => $group_id, + 'format' => 'atom', + 'max_id' => $this->notice->id, + 'since_id' => $this->notice->id - 1); + $api->prepare($args); + $api->handle($args); + $feed = ob_get_clean(); + + // ...and override the content-type back to something normal... eww! + // hope there's no other headers that got set while we weren't looking. + header('Content-Type: text/html; charset=utf-8'); + + common_log(LOG_DEBUG, $feed); + return $feed; + } + +} + diff --git a/plugins/OStatus/lib/salmonoutqueuehandler.php b/plugins/OStatus/lib/salmonoutqueuehandler.php new file mode 100644 index 000000000..536ff94af --- /dev/null +++ b/plugins/OStatus/lib/salmonoutqueuehandler.php @@ -0,0 +1,44 @@ +. + */ + +/** + * Send a Salmon notification in the background. + * @package OStatusPlugin + * @author Brion Vibber + */ +class SalmonOutQueueHandler extends QueueHandler +{ + function transport() + { + return 'salmonout'; + } + + function handle($data) + { + assert(is_array($data)); + assert(is_string($data['salmonuri'])); + assert(is_string($data['entry'])); + + $salmon = new Salmon(); + $salmon->post($data['salmonuri'], $data['entry']); + + // @fixme detect failure and attempt to resend + return true; + } +} -- cgit v1.2.3-54-g00ecf From 3a3af6782a82ca3512680a276b76d1d10de47d94 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 23 Feb 2010 22:35:48 -0800 Subject: Add PoCo parsing and some other fixes. --- lib/activity.php | 162 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 132 insertions(+), 30 deletions(-) diff --git a/lib/activity.php b/lib/activity.php index 853741a9a..5cbab8d5f 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -34,6 +34,7 @@ if (!defined('STATUSNET')) { class PoCoURL { + const URLS = 'urls'; const TYPE = 'type'; const VALUE = 'value'; const PRIMARY = 'primary'; @@ -55,7 +56,7 @@ class PoCoURL $xs->elementStart('poco:urls'); $xs->element('poco:type', null, $this->type); $xs->element('poco:value', null, $this->value); - if ($this->primary) { + if (!empty($this->primary)) { $xs->element('poco:primary', null, 'true'); } $xs->elementEnd('poco:urls'); @@ -70,21 +71,19 @@ class PoCoAddress public $formatted; - function __construct($formatted) - { - if (empty($formatted)) { - return null; - } - $this->formatted = $formatted; - } + // @todo Other address fields function asString() { - $xs = new XMLStringer(true); - $xs->elementStart('poco:address'); - $xs->element('poco:formatted', null, $this->formatted); - $xs->elementEnd('poco:address'); - return $xs->getString(); + if (!empty($this->formatted)) { + $xs = new XMLStringer(true); + $xs->elementStart('poco:address'); + $xs->element('poco:formatted', null, $this->formatted); + $xs->elementEnd('poco:address'); + return $xs->getString(); + } + + return null; } } @@ -92,26 +91,117 @@ class PoCo { const NS = 'http://portablecontacts.net/spec/1.0'; - const USERNAME = 'preferredUsername'; - const NOTE = 'note'; - const URLS = 'urls'; + const USERNAME = 'preferredUsername'; + const DISPLAYNAME = 'displayName'; + const NOTE = 'note'; public $preferredUsername; + public $displayName; public $note; public $address; public $urls = array(); - function __construct($profile) + function __construct($element = null) + { + if (empty($element)) { + return; + } + + $this->preferredUsername = ActivityUtils::childContent( + $element, + self::USERNAME, + self::NS + ); + + $this->displayName = ActivityUtils::childContent( + $element, + self::DISPLAYNAME, + self::NS + ); + + $this->note = ActivityUtils::childContent( + $element, + self::NOTE, + self::NS + ); + + $this->address = $this->_getAddress($element); + $this->urls = $this->_getURLs($element); + } + + private function _getURLs($element) + { + $urlEls = $element->getElementsByTagnameNS(self::NS, PoCoURL::URLS); + $urls = array(); + + foreach ($urlEls as $urlEl) { + + $type = ActivityUtils::childContent( + $urlEl, + PoCoURL::TYPE, + PoCo::NS + ); + + $value = ActivityUtils::childContent( + $urlEl, + PoCoURL::VALUE, + PoCo::NS + ); + + $primary = ActivityUtils::childContent( + $urlEl, + PoCoURL::PRIMARY, + PoCo::NS + ); + + array_push($urls, new PoCoURL($type, $value, $primary)); + } + return $urls; + } + + private function _getAddress($element) { - $this->preferredUsername = $profile->nickname; - $this->displayName = $profile->getBestName(); + $addressEl = ActivityUtils::child( + $element, + PoCoAddress::ADDRESS, + PoCo::NS + ); + + $formatted = ActivityUtils::childContent( + $addressEl, + PoCoAddress::FORMATTED, + self::NS + ); - $this->note = $profile->bio; - $this->address = new PoCoAddress($profile->location); + if (!empty($formatted)) { + $address = new PoCoAddress(); + $address->formatted = $formatted; + return $address; + } + + return null; + } + + function fromProfile($profile) + { + if (empty($profile)) { + return null; + } + + $poco = new PoCo(); + + $poco->preferredUsername = $profile->nickname; + $poco->displayName = $profile->getBestName(); + + $poco->note = $profile->bio; + + $paddy = new PoCoAddress(); + $paddy->formatted = $profile->location; + $poco->address = $paddy; if (!empty($profile->homepage)) { array_push( - $this->urls, + $poco->urls, new PoCoURL( 'homepage', $profile->homepage, @@ -119,6 +209,8 @@ class PoCo ) ); } + + return $poco; } function asString() @@ -381,6 +473,8 @@ class ActivityObject public $source; public $avatar; public $geopoint; + public $poco; + public $displayName; /** * Constructor @@ -433,7 +527,6 @@ class ActivityObject $this->link = ActivityUtils::getPermalink($element); - // XXX: grab PoCo stuff } // Some per-type attributes... @@ -441,8 +534,9 @@ class ActivityObject $this->displayName = $this->title; // @fixme we may have multiple avatars with different resolutions specified - $this->avatar = ActivityUtils::getLink($element, 'avatar'); - $this->nickname = ActivityUtils::childContent($element, PoCo::USERNAME, PoCo::NS); + $this->avatar = ActivityUtils::getLink($element, 'avatar'); + + $this->poco = new PoCo($element); } } @@ -497,7 +591,7 @@ class ActivityObject $object->geopoint = (float)$profile->lat . ' ' . (float)$profile->lon; } - $object->poco = new PoCo($profile); + $object->poco = PoCo::fromProfile($profile); return $object; } @@ -526,11 +620,19 @@ class ActivityObject } if (!empty($this->link)) { - $xs->element('link', array('rel' => 'alternate', 'type' => 'text/html'), - $this->link); + $xs->element( + 'link', + array( + 'rel' => 'alternate', + 'type' => 'text/html', + 'href' => $this->link + ), + null + ); } - if ($this->type == ActivityObject::PERSON) { + if ($this->type == ActivityObject::PERSON + || $this->type == ActivityObject::GROUP) { $xs->element( 'link', array( 'type' => empty($this->avatar) ? 'image/png' : $this->avatar->mediatype, @@ -539,7 +641,7 @@ class ActivityObject ? Avatar::defaultImage(AVATAR_PROFILE_SIZE) : $this->avatar->displayUrl() ), - '' + null ); } -- cgit v1.2.3-54-g00ecf From 618ce6a855330f424d54c3dedf10acb60f7e3001 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 23 Feb 2010 23:58:21 -0800 Subject: - Move ActivityParseTests to core - Add test for Portable Contacts stuff --- plugins/OStatus/tests/ActivityParseTests.php | 209 ----------------- tests/ActivityParseTests.php | 326 +++++++++++++++++++++++++++ 2 files changed, 326 insertions(+), 209 deletions(-) delete mode 100644 plugins/OStatus/tests/ActivityParseTests.php create mode 100644 tests/ActivityParseTests.php diff --git a/plugins/OStatus/tests/ActivityParseTests.php b/plugins/OStatus/tests/ActivityParseTests.php deleted file mode 100644 index d7305dede..000000000 --- a/plugins/OStatus/tests/ActivityParseTests.php +++ /dev/null @@ -1,209 +0,0 @@ -documentElement); - - $this->assertFalse(empty($act)); - - $this->assertEquals($act->time, 1243860840); - $this->assertEquals($act->verb, ActivityVerb::POST); - - $this->assertFalse(empty($act->object)); - $this->assertEquals($act->object->title, 'Punctuation Changeset'); - $this->assertEquals($act->object->type, 'http://versioncentral.example.org/activity/changeset'); - $this->assertEquals($act->object->summary, 'Fixing punctuation because it makes it more readable.'); - $this->assertEquals($act->object->id, 'tag:versioncentral.example.org,2009:/change/1643245'); - } - - public function testExample3() - { - global $_example3; - $dom = DOMDocument::loadXML($_example3); - - $feed = $dom->documentElement; - - $entries = $feed->getElementsByTagName('entry'); - - $entry = $entries->item(0); - - $act = new Activity($entry, $feed); - - $this->assertFalse(empty($act)); - $this->assertEquals($act->time, 1071340202); - $this->assertEquals($act->link, 'http://example.org/2003/12/13/atom03.html'); - - $this->assertEquals($act->verb, ActivityVerb::POST); - - $this->assertFalse(empty($act->actor)); - $this->assertEquals($act->actor->type, ActivityObject::PERSON); - $this->assertEquals($act->actor->title, 'John Doe'); - $this->assertEquals($act->actor->id, 'mailto:johndoe@example.com'); - - $this->assertFalse(empty($act->object)); - $this->assertEquals($act->object->type, ActivityObject::NOTE); - $this->assertEquals($act->object->id, 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'); - $this->assertEquals($act->object->title, 'Atom-Powered Robots Run Amok'); - $this->assertEquals($act->object->summary, 'Some text.'); - $this->assertEquals($act->object->link, 'http://example.org/2003/12/13/atom03.html'); - - $this->assertFalse(empty($act->context)); - - $this->assertTrue(empty($act->target)); - - $this->assertEquals($act->entry, $entry); - $this->assertEquals($act->feed, $feed); - } - - public function testExample4() - { - global $_example4; - $dom = DOMDocument::loadXML($_example4); - - $entry = $dom->documentElement; - - $act = new Activity($entry); - - $this->assertFalse(empty($act)); - $this->assertEquals(1266547958, $act->time); - $this->assertEquals('http://example.net/notice/14', $act->link); - - $this->assertFalse(empty($act->context)); - $this->assertEquals('http://example.net/notice/12', $act->context->replyToID); - $this->assertEquals('http://example.net/notice/12', $act->context->replyToUrl); - $this->assertEquals('http://example.net/conversation/11', $act->context->conversation); - $this->assertEquals(array('http://example.net/user/1'), $act->context->attention); - - $this->assertFalse(empty($act->object)); - $this->assertEquals($act->object->content, - '@evan now is the time for all good men to come to the aid of their country. #'); - - $this->assertFalse(empty($act->actor)); - } -} - -$_example1 = << - - tag:versioncentral.example.org,2009:/commit/1643245 - 2009-06-01T12:54:00Z - Geraldine committed a change to yate - Geraldine just committed a change to yate on VersionCentral - - http://activitystrea.ms/schema/1.0/post - http://versioncentral.example.org/activity/commit - - http://versioncentral.example.org/activity/changeset - tag:versioncentral.example.org,2009:/change/1643245 - Punctuation Changeset - Fixing punctuation because it makes it more readable. - - - -EXAMPLE1; - -$_example2 = << - - tag:photopanic.example.com,2008:activity01 - Geraldine posted a Photo on PhotoPanic - 2008-11-02T15:29:00Z - - - http://activitystrea.ms/schema/1.0/post - - - tag:photopanic.example.com,2008:photo01 - My Cat - 2008-11-02T15:29:00Z - - - tag:atomactivity.example.com,2008:photo - - - Geraldine's Photos - - - - - - <p>Geraldine posted a Photo on PhotoPanic</p> - <img src="/geraldine/photo1.jpg"> - - -EXAMPLE2; - -$_example3 = << - - - - Example Feed - A subtitle. - - - urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6 - 2003-12-13T18:30:02Z - - John Doe - johndoe@example.com - - - - Atom-Powered Robots Run Amok - - - - urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a - 2003-12-13T18:30:02Z - Some text. - - - -EXAMPLE3; - -$_example4 = << - - @evan now is the time for all good men to come to the aid of their country. #thetime - @evan now is the time for all good men to come to the aid of their country. #thetime - - spock - http://example.net/user/2 - - - http://activitystrea.ms/schema/1.0/person - http://example.net/user/2 - spock - - - - http://example.net/notice/14 - 2010-02-19T02:52:38+00:00 - 2010-02-19T02:52:38+00:00 - - - - - @<span class="vcard"><a href="http://example.net/user/1" class="url"><span class="fn nickname">evan</span></a></span> now is the time for all good men to come to the aid of their country. #<span class="tag"><a href="http://example.net/tag/thetime" rel="tag">thetime</a></span> - - -EXAMPLE4; diff --git a/tests/ActivityParseTests.php b/tests/ActivityParseTests.php new file mode 100644 index 000000000..5de97d2e2 --- /dev/null +++ b/tests/ActivityParseTests.php @@ -0,0 +1,326 @@ +documentElement); + + $this->assertFalse(empty($act)); + + $this->assertEquals($act->time, 1243860840); + $this->assertEquals($act->verb, ActivityVerb::POST); + + $this->assertFalse(empty($act->object)); + $this->assertEquals($act->object->title, 'Punctuation Changeset'); + $this->assertEquals($act->object->type, 'http://versioncentral.example.org/activity/changeset'); + $this->assertEquals($act->object->summary, 'Fixing punctuation because it makes it more readable.'); + $this->assertEquals($act->object->id, 'tag:versioncentral.example.org,2009:/change/1643245'); + } + + public function testExample3() + { + global $_example3; + $dom = DOMDocument::loadXML($_example3); + + $feed = $dom->documentElement; + + $entries = $feed->getElementsByTagName('entry'); + + $entry = $entries->item(0); + + $act = new Activity($entry, $feed); + + $this->assertFalse(empty($act)); + $this->assertEquals($act->time, 1071340202); + $this->assertEquals($act->link, 'http://example.org/2003/12/13/atom03.html'); + + $this->assertEquals($act->verb, ActivityVerb::POST); + + $this->assertFalse(empty($act->actor)); + $this->assertEquals($act->actor->type, ActivityObject::PERSON); + $this->assertEquals($act->actor->title, 'John Doe'); + $this->assertEquals($act->actor->id, 'mailto:johndoe@example.com'); + + $this->assertFalse(empty($act->object)); + $this->assertEquals($act->object->type, ActivityObject::NOTE); + $this->assertEquals($act->object->id, 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'); + $this->assertEquals($act->object->title, 'Atom-Powered Robots Run Amok'); + $this->assertEquals($act->object->summary, 'Some text.'); + $this->assertEquals($act->object->link, 'http://example.org/2003/12/13/atom03.html'); + + $this->assertFalse(empty($act->context)); + + $this->assertTrue(empty($act->target)); + + $this->assertEquals($act->entry, $entry); + $this->assertEquals($act->feed, $feed); + } + + public function testExample4() + { + global $_example4; + $dom = DOMDocument::loadXML($_example4); + + $entry = $dom->documentElement; + + $act = new Activity($entry); + + $this->assertFalse(empty($act)); + $this->assertEquals(1266547958, $act->time); + $this->assertEquals('http://example.net/notice/14', $act->link); + + $this->assertFalse(empty($act->context)); + $this->assertEquals('http://example.net/notice/12', $act->context->replyToID); + $this->assertEquals('http://example.net/notice/12', $act->context->replyToUrl); + $this->assertEquals('http://example.net/conversation/11', $act->context->conversation); + $this->assertEquals(array('http://example.net/user/1'), $act->context->attention); + + $this->assertFalse(empty($act->object)); + $this->assertEquals($act->object->content, + '@evan now is the time for all good men to come to the aid of their country. #'); + + $this->assertFalse(empty($act->actor)); + } + + public function testExample5() + { + global $_example5; + $dom = DOMDocument::loadXML($_example5); + + $feed = $dom->documentElement; + + // @todo Test feed elements + + $entries = $feed->getElementsByTagName('entry'); + $entry = $entries->item(0); + + $act = new Activity($entry, $feed); + + // Post + $this->assertEquals($act->verb, ActivityVerb::POST); + $this->assertFalse(empty($act->context)); + + // Actor w/Portable Contacts stuff + $this->assertFalse(empty($act->actor)); + $this->assertEquals($act->actor->type, ActivityObject::PERSON); + $this->assertEquals($act->actor->title, 'Test User'); + $this->assertEquals($act->actor->id, 'http://example.net/mysite/user/3'); + $this->assertEquals($act->actor->link, 'http://example.net/mysite/testuser'); + $this->assertEquals( + $act->actor->avatar, + 'http://example.net/mysite/avatar/3-96-20100224004207.jpeg' + ); + $this->assertEquals($act->actor->displayName, 'Test User'); + + $poco = $act->actor->poco; + $this->assertEquals($poco->preferredUsername, 'testuser'); + $this->assertEquals($poco->address->formatted, 'San Francisco, CA'); + $this->assertEquals($poco->urls[0]->type, 'homepage'); + $this->assertEquals($poco->urls[0]->value, 'http://example.com/blog.html'); + $this->assertEquals($poco->urls[0]->primary, 'true'); + } + +} + +$_example1 = << + + tag:versioncentral.example.org,2009:/commit/1643245 + 2009-06-01T12:54:00Z + Geraldine committed a change to yate + Geraldine just committed a change to yate on VersionCentral + + http://activitystrea.ms/schema/1.0/post + http://versioncentral.example.org/activity/commit + + http://versioncentral.example.org/activity/changeset + tag:versioncentral.example.org,2009:/change/1643245 + Punctuation Changeset + Fixing punctuation because it makes it more readable. + + + +EXAMPLE1; + +$_example2 = << + + tag:photopanic.example.com,2008:activity01 + Geraldine posted a Photo on PhotoPanic + 2008-11-02T15:29:00Z + + + http://activitystrea.ms/schema/1.0/post + + + tag:photopanic.example.com,2008:photo01 + My Cat + 2008-11-02T15:29:00Z + + + tag:atomactivity.example.com,2008:photo + + + Geraldine's Photos + + + + + + <p>Geraldine posted a Photo on PhotoPanic</p> + <img src="/geraldine/photo1.jpg"> + + +EXAMPLE2; + +$_example3 = << + + + + Example Feed + A subtitle. + + + urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6 + 2003-12-13T18:30:02Z + + John Doe + johndoe@example.com + + + + Atom-Powered Robots Run Amok + + + + urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + 2003-12-13T18:30:02Z + Some text. + + + +EXAMPLE3; + +$_example4 = << + + @evan now is the time for all good men to come to the aid of their country. #thetime + @evan now is the time for all good men to come to the aid of their country. #thetime + + spock + http://example.net/user/2 + + + http://activitystrea.ms/schema/1.0/person + http://example.net/user/2 + spock + + + + http://example.net/notice/14 + 2010-02-19T02:52:38+00:00 + 2010-02-19T02:52:38+00:00 + + + + + @<span class="vcard"><a href="http://example.net/user/1" class="url"><span class="fn nickname">evan</span></a></span> now is the time for all good men to come to the aid of their country. #<span class="tag"><a href="http://example.net/tag/thetime" rel="tag">thetime</a></span> + + +EXAMPLE4; + +$_example5 = << + + 3 + testuser timeline + Updates from testuser on Zach Dev! + http://example.net/mysite/avatar/3-96-20100224004207.jpeg + 2010-02-24T06:38:49+00:00 + + testuser + http://example.net/mysite/user/3 + + + + + + + + + http://activitystrea.ms/schema/1.0/person + http://example.net/mysite/user/3 + Test User + + + 37.7749295 -122.4194155 + +testuser +Test User +Just another test user. + + San Francisco, CA + + + homepage + http://example.com/blog.html + true + + + + + Hey man, is that Freedom Code?! #freedom #hippy + Hey man, is that Freedom Code?! #freedom #hippy + + testuser + http://example.net/mysite/user/3 + + + http://activitystrea.ms/schema/1.0/person + http://example.net/mysite/user/3 + Test User + + + 37.7749295 -122.4194155 + +testuser +Test User +Just another test user. + + San Francisco, CA + + + homepage + http://example.com/blog.html + true + + + + + http://example.net/mysite/notice/7 + 2010-02-24T00:53:06+00:00 + 2010-02-24T00:53:06+00:00 + + Hey man, is that Freedom Code?! #<span class="tag"><a href="http://example.net/mysite/tag/freedom" rel="tag">freedom</a></span> #<span class="tag"><a href="http://example.net/mysite/tag/hippy" rel="tag">hippy</a></span> + 37.8313160 -122.2852473 + + + +EXAMPLE5; -- cgit v1.2.3-54-g00ecf From 2466dbfc97e81144d005fa546b387c9747ce00ad Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 24 Feb 2010 14:55:35 +0100 Subject: Break long strings in tagcloud --- theme/base/css/display.css | 1 + 1 file changed, 1 insertion(+) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 380975e32..52f97f6b1 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -1490,6 +1490,7 @@ text-align:center; } .aside .tag-cloud { font-size:0.8em; +word-wrap:break-word; } .tag-cloud li { display:inline; -- cgit v1.2.3-54-g00ecf From 8e7606cc8d9003cb5d58410678c2be0f812d0ed1 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 24 Feb 2010 15:20:44 +0100 Subject: Added processing indicator for .form_remote_authorize on ostatussub page --- plugins/OStatus/js/ostatus.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/OStatus/js/ostatus.js b/plugins/OStatus/js/ostatus.js index 1fc44b21b..3637b8725 100644 --- a/plugins/OStatus/js/ostatus.js +++ b/plugins/OStatus/js/ostatus.js @@ -123,4 +123,6 @@ SN.Init.Subscribe = function() { $(document).ready(function() { SN.Init.Subscribe(); + + $('.form_remote_authorize').bind('submit', function() { $(this).addClass(SN.C.S.Processing); return true; }); }); -- cgit v1.2.3-54-g00ecf From 1f45273d5303e98430a02d4480278c24733a5be9 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 24 Feb 2010 16:35:20 +0100 Subject: Moved StatusNetInstance into SN in util.js --- js/util.js | 32 +++++++++++++++++++++++++++++++- plugins/OStatus/js/ostatus.js | 32 +++----------------------------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/js/util.js b/js/util.js index 3623337b9..78533ab73 100644 --- a/js/util.js +++ b/js/util.js @@ -54,7 +54,8 @@ var SN = { // StatusNet NoticeGeoName: 'notice_data-geo_name', NoticeDataGeo: 'notice_data-geo', NoticeDataGeoCookie: 'notice_data-geo_cookie', - NoticeDataGeoSelected: 'notice_data-geo_selected' + NoticeDataGeoSelected: 'notice_data-geo_selected', + StatusNetInstance:'StatusNetInstance' } }, @@ -670,6 +671,35 @@ var SN = { // StatusNet date.setFullYear(year, month, day); return date; + }, + + StatusNetInstance: { + Set: function(value) { + var SNI = SN.U.StatusNetInstance.Get(); + if (SNI !== null) { + value = $.extend(SNI, value); + } + + $.cookie( + SN.C.S.StatusNetInstance, + JSON.stringify(value), + { + path: '/', + expires: SN.U.GetFullYear(2029, 0, 1) + }); + }, + + Get: function() { + var cookieValue = $.cookie(SN.C.S.StatusNetInstance); + if (cookieValue !== null) { + return JSON.parse(cookieValue); + } + return null; + }, + + Delete: function() { + $.cookie(SN.C.S.StatusNetInstance, null); + } } }, diff --git a/plugins/OStatus/js/ostatus.js b/plugins/OStatus/js/ostatus.js index 3637b8725..bd29b5c0c 100644 --- a/plugins/OStatus/js/ostatus.js +++ b/plugins/OStatus/js/ostatus.js @@ -24,35 +24,9 @@ * @note Everything in here should eventually migrate over to /js/util.js's SN. */ -SN.C.S.StatusNetInstance = 'StatusNetInstance'; - -SN.U.StatusNetInstance = { - Set: function(value) { - $.cookie( - SN.C.S.StatusNetInstance, - JSON.stringify(value), - { - path: '/', - expires: SN.U.GetFullYear(2029, 0, 1) - }); - }, - - Get: function() { - var cookieValue = $.cookie(SN.C.S.StatusNetInstance); - if (cookieValue !== null) { - return JSON.parse(cookieValue); - } - return null; - }, - - Delete: function() { - $.cookie(SN.C.S.StatusNetInstance, null); - } -}; - SN.Init.OStatusCookie = function() { if (SN.U.StatusNetInstance.Get() === null) { - SN.U.StatusNetInstance.Set({profile: null}); + SN.U.StatusNetInstance.Set({RemoteProfile: null}); } }; @@ -101,10 +75,10 @@ SN.U.DialogBox = { if (form.attr('id') == 'form_ostatus_connect') { SN.Init.OStatusCookie(); - form.find('#profile').val(SN.U.StatusNetInstance.Get().profile); + form.find('#profile').val(SN.U.StatusNetInstance.Get().RemoteProfile); form.find("[type=submit]").bind('click', function() { - SN.U.StatusNetInstance.Set({profile: form.find('#profile').val()}); + SN.U.StatusNetInstance.Set({RemoteProfile: form.find('#profile').val()}); return true; }); } -- cgit v1.2.3-54-g00ecf From 959171acac5abc3716119f7d5a7918e7497fdbfd Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 24 Feb 2010 16:39:16 +0100 Subject: Added a cookie for the nickname cookie for the login page and prefill the input field. --- js/util.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/js/util.js b/js/util.js index 78533ab73..4b6c39a1d 100644 --- a/js/util.js +++ b/js/util.js @@ -737,6 +737,20 @@ var SN = { // StatusNet SN.U.NewDirectMessage(); } + }, + + Login: function() { + if (SN.U.StatusNetInstance.Get() !== null) { + var nickname = SN.U.StatusNetInstance.Get().Nickname; + if (nickname !== null) { + $('#form_login #nickname').val(nickname); + } + } + + $('#form_login').bind('submit', function() { + SN.U.StatusNetInstance.Set({Nickname: $('#form_login #nickname').val()}); + return true; + }); } } }; @@ -751,5 +765,8 @@ $(document).ready(function(){ if ($('#content .entity_actions').length > 0) { SN.Init.EntityActions(); } + if ($('#form_login').length > 0) { + SN.Init.Login(); + } }); -- cgit v1.2.3-54-g00ecf From 5cabb63e4eaf7cd3642bf7a0c4beb3fef2e1ba07 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 17:36:31 +0000 Subject: Include with actor ID and name in Activity::asString(); fixes Salmon signature on OStatus unsub pings --- lib/activity.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/activity.php b/lib/activity.php index 5cbab8d5f..fa4ae0274 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -957,11 +957,24 @@ class Activity } // XXX: add context - // XXX: add target + $xs->elementStart('author'); + $xs->element('uri', array(), $this->actor->id); + if ($this->actor->title) { + $xs->element('name', array(), $this->actor->title); + } + $xs->elementEnd('author'); $xs->raw($this->actor->asString('activity:actor')); + $xs->element('activity:verb', null, $this->verb); - $xs->raw($this->object->asString()); + + if ($this->object) { + $xs->raw($this->object->asString()); + } + + if ($this->target) { + $xs->raw($this->target->asString('activity:target')); + } $xs->elementEnd('entry'); -- cgit v1.2.3-54-g00ecf From 07214f1370547fcc64db34ce8c8a84ec70e0d5bd Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 19:06:10 +0000 Subject: OStatus: save updated profile bits when they come in over the wire; fix up nicknames --- plugins/OStatus/classes/Ostatus_profile.php | 147 +++++++++++++++++++++------- 1 file changed, 112 insertions(+), 35 deletions(-) diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 82dbf773d..9f9efb96e 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -33,6 +33,7 @@ class Ostatus_profile extends Memcached_DataObject public $feeduri; public $salmonuri; + public $avatar; // remote URL of the last avatar we saved public $created; public $modified; @@ -58,6 +59,7 @@ class Ostatus_profile extends Memcached_DataObject 'group_id' => DB_DATAOBJECT_INT, 'feeduri' => DB_DATAOBJECT_STR, 'salmonuri' => DB_DATAOBJECT_STR, + 'avatar' => DB_DATAOBJECT_STR, 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); } @@ -74,6 +76,8 @@ class Ostatus_profile extends Memcached_DataObject 255, true, 'UNI'), new ColumnDef('salmonuri', 'text', null, true), + new ColumnDef('avatar', 'text', + null, true), new ColumnDef('created', 'datetime', null, false), new ColumnDef('modified', 'datetime', @@ -543,7 +547,8 @@ class Ostatus_profile extends Memcached_DataObject // through PuSH setup or Salmon signature checks. $actorUri = self::getActorProfileURI($activity); if ($actorUri == $this->uri) { - // @fixme check if profile info has changed and update it + // Check if profile info has changed and update it + $this->updateFromActivityObject($activity->actor); } else { common_log(LOG_WARNING, "OStatus: skipping post with bad author: got $actorUri expected $this->uri"); return false; @@ -785,6 +790,11 @@ class Ostatus_profile extends Memcached_DataObject */ protected function updateAvatar($url) { + if ($url == $this->avatar) { + // We've already got this one. + return; + } + if ($this->isGroup()) { $self = $this->localGroup(); } else { @@ -816,12 +826,28 @@ class Ostatus_profile extends Memcached_DataObject common_timestamp()); rename($temp_filename, Avatar::path($filename)); $self->setOriginal($filename); + + $orig = clone($this); + $this->avatar = $url; + $this->update($orig); } - protected static function getActivityObjectAvatar($object) + /** + * Pull avatar URL from ActivityObject or profile hints + * + * @param ActivityObject $object + * @param array $hints + * @return mixed URL string or false + */ + + protected static function getActivityObjectAvatar($object, $hints=array()) { - // XXX: go poke around in the feed - return $object->avatar; + if ($object->avatar) { + return $object->avatar; + } else if (array_key_exists('avatar', $hints)) { + return $hints['avatar']; + } + return false; } /** @@ -888,7 +914,9 @@ class Ostatus_profile extends Memcached_DataObject public static function ensureActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array()) { $profile = self::getActivityObjectProfile($object); - if (!$profile) { + if ($profile) { + $profile->updateFromActivityObject($object, $hints); + } else { $profile = self::createActivityObjectProfile($object, $feeduri, $salmonuri, $hints); } return $profile; @@ -957,8 +985,6 @@ class Ostatus_profile extends Memcached_DataObject protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array()) { $homeuri = $object->id; - $nickname = self::getActivityObjectNickname($object, $hints); - $avatar = self::getActivityObjectAvatar($object); if (!$homeuri) { common_log(LOG_DEBUG, __METHOD__ . " empty actor profile URI: " . var_export($activity, true)); @@ -1002,43 +1028,19 @@ class Ostatus_profile extends Memcached_DataObject if ($object->type == ActivityObject::PERSON) { $profile = new Profile(); - $profile->nickname = $nickname; - $profile->fullname = $object->title; - if (!empty($object->link)) { - $profile->profileurl = $object->link; - } else if (array_key_exists('profileurl', $hints)) { - $profile->profileurl = $hints['profileurl']; - } - $profile->created = common_sql_now(); - - // @fixme bio - // @fixme tags/categories - // @fixme location? - // @todo tags from categories - // @todo lat/lon/location? - + self::updateProfile($profile, $object, $hints); + $profile->created = common_sql_now(); + $oprofile->profile_id = $profile->insert(); - if (!$oprofile->profile_id) { throw new ServerException("Can't save local profile"); } } else { $group = new User_group(); - $group->nickname = $nickname; - $group->fullname = $object->title; - // @fixme no canonical profileurl; using homepage instead for now - $group->homepage = $homeuri; $group->created = common_sql_now(); - - // @fixme homepage - // @fixme bio - // @fixme tags/categories - // @fixme location? - // @todo tags from categories - // @todo lat/lon/location? + self::updateGroup($group, $object, $hints); $oprofile->group_id = $group->insert(); - if (!$oprofile->group_id) { throw new ServerException("Can't save local profile"); } @@ -1047,6 +1049,7 @@ class Ostatus_profile extends Memcached_DataObject $ok = $oprofile->insert(); if ($ok) { + $avatar = self::getActivityObjectAvatar($object, $hints); if ($avatar) { $oprofile->updateAvatar($avatar); } @@ -1056,8 +1059,82 @@ class Ostatus_profile extends Memcached_DataObject } } + /** + * Save any updated profile information to our local copy. + * @param ActivityObject $object + * @param array $hints + */ + protected function updateFromActivityObject($object, $hints=array()) + { + if ($this->isGroup()) { + $group = $this->localGroup(); + self::updateGroup($group, $object, $hints); + } else { + $profile = $this->localProfile(); + self::updateProfile($profile, $object, $hints); + } + $avatar = self::getActivityObjectAvatar($object, $hints); + if ($avatar) { + $this->updateAvatar($avatar); + } + } + + protected static function updateProfile($profile, $object, $hints=array()) + { + $orig = clone($profile); + + $profile->nickname = self::getActivityObjectNickname($object, $hints); + $profile->fullname = $object->title; + if (!empty($object->link)) { + $profile->profileurl = $object->link; + } else if (array_key_exists('profileurl', $hints)) { + $profile->profileurl = $hints['profileurl']; + } + + // @fixme bio + // @fixme tags/categories + // @fixme location? + // @todo tags from categories + // @todo lat/lon/location? + + if ($profile->id) { + common_log(LOG_DEBUG, "Updating OStatus profile $profile->id from remote info $object->id: " . var_export($object, true) . var_export($hints, true)); + $profile->update($orig); + } + } + + protected static function updateGroup($group, $object, $hints=array()) + { + $orig = clone($group); + + // @fixme need to make nick unique etc *hack hack* + $group->nickname = self::getActivityObjectNickname($object, $hints); + $group->fullname = $object->title; + + // @fixme no canonical profileurl; using homepage instead for now + $group->homepage = $object->id; + + // @fixme homepage + // @fixme bio + // @fixme tags/categories + // @fixme location? + // @todo tags from categories + // @todo lat/lon/location? + + if ($group->id) { + common_log(LOG_DEBUG, "Updating OStatus group $group->id from remote info $object->id: " . var_export($object, true) . var_export($hints, true)); + $group->update($orig); + } + } + + protected static function getActivityObjectNickname($object, $hints=array()) { + if ($object->poco) { + if (!empty($object->poco->preferredUsername)) { + return common_nicknamize($object->poco->preferredUsername); + } + } if (!empty($object->nickname)) { return common_nicknamize($object->nickname); } -- cgit v1.2.3-54-g00ecf