From 79c0d52daa92b60e8eed80fa9459367c26f97122 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 25 Feb 2010 11:26:33 -0800 Subject: OStatus: save categories from the Atom entry as hashtags. --- lib/activity.php | 61 ++++++++++++++++++++++++++++++++++++++++++++- lib/distribqueuehandler.php | 12 --------- 2 files changed, 60 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/activity.php b/lib/activity.php index 3de5f62c7..e592aad6f 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -859,6 +859,7 @@ class Activity public $content; // HTML content of activity public $id; // ID of the activity public $title; // title of the activity + public $categories = array(); // list of AtomCategory objects /** * Turns a regular old Atom into a magical activity @@ -947,6 +948,14 @@ class Activity $this->summary = ActivityUtils::childContent($entry, 'summary'); $this->id = ActivityUtils::childContent($entry, 'id'); $this->content = ActivityUtils::getContent($entry); + + $catEls = $entry->getElementsByTagNameNS(self::ATOM, 'category'); + if ($catEls) { + for ($i = 0; $i < $catEls->length; $i++) { + $catEl = $catEls->item($i); + $this->categories[] = new AtomCategory($catEl); + } + } } /** @@ -1011,6 +1020,10 @@ class Activity $xs->raw($this->target->asString('activity:target')); } + foreach ($this->categories as $cat) { + $xs->raw($cat->asString()); + } + $xs->elementEnd('entry'); return $xs->getString(); @@ -1020,4 +1033,50 @@ class Activity { return ActivityUtils::child($element, $tag, $namespace); } -} \ No newline at end of file +} + +class AtomCategory +{ + public $term; + public $scheme; + public $label; + + function __construct($element=null) + { + if ($element && $element->attributes) { + $this->term = $this->extract($element, 'term'); + $this->scheme = $this->extract($element, 'scheme'); + $this->label = $this->extract($element, 'label'); + } + } + + protected function extract($element, $attrib) + { + $node = $element->attributes->getNamedItemNS(Activity::ATOM, $attrib); + if ($node) { + return trim($node->textContent); + } + $node = $element->attributes->getNamedItem($attrib); + if ($node) { + return trim($node->textContent); + } + return null; + } + + function asString() + { + $attribs = array(); + if ($this->term !== null) { + $attribs['term'] = $this->term; + } + if ($this->scheme !== null) { + $attribs['scheme'] = $this->scheme; + } + if ($this->label !== null) { + $attribs['label'] = $this->label; + } + $xs = new XMLStringer(); + $xs->element('category', $attribs); + return $xs->asString(); + } +} diff --git a/lib/distribqueuehandler.php b/lib/distribqueuehandler.php index dc183fb36..d2be7a92c 100644 --- a/lib/distribqueuehandler.php +++ b/lib/distribqueuehandler.php @@ -62,24 +62,12 @@ class DistribQueueHandler { // XXX: do we need to change this for remote users? - try { - $notice->saveTags(); - } catch (Exception $e) { - $this->logit($notice, $e); - } - try { $notice->addToInboxes(); } catch (Exception $e) { $this->logit($notice, $e); } - try { - $notice->saveUrls(); - } catch (Exception $e) { - $this->logit($notice, $e); - } - try { Event::handle('EndNoticeSave', array($notice)); // Enqueue for other handlers -- cgit v1.2.3-54-g00ecf From a8d0c8d8efca2f0ecd4203184c44c73ff11309fb Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 25 Feb 2010 11:53:39 -0800 Subject: Normalize nickname case on login; fixes failed logins where people were typing MixedCase nicknames (if browser saved this form, it would never work again until clearing the saved form data; very icky.) --- lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.php b/lib/util.php index dd8189a58..84928ec48 100644 --- a/lib/util.php +++ b/lib/util.php @@ -134,7 +134,7 @@ function common_check_user($nickname, $password) $authenticatedUser = false; if (Event::handle('StartCheckPassword', array($nickname, $password, &$authenticatedUser))) { - $user = User::staticGet('nickname', $nickname); + $user = User::staticGet('nickname', common_canonical_nickname($nickname)); if (!empty($user)) { if (!empty($password)) { // never allow login with blank password if (0 == strcmp(common_munge_password($password, $user->id), -- cgit v1.2.3-54-g00ecf From b5b5184c885e74d9532409a5962f851c4baf41c4 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 25 Feb 2010 13:02:08 -0800 Subject: OStatus: fix remote groups to work with new user_groups/local_groups split. - fix generation so we get the profile info (what's available so far) - use id instead of nickname for group join/leave forms so we can join/leave remote groups while the rest of the groups UI remains limited to local groups (plugins are responsible for making sure remote notifications and permission checks are done) - fix remote notification when joining group through OStatus's remote subscribe form --- actions/joingroup.php | 39 ++++++++++++++++------------- actions/leavegroup.php | 39 ++++++++++++++++------------- classes/User_group.php | 2 +- lib/activity.php | 7 ++++-- lib/joinform.php | 2 +- lib/leaveform.php | 2 +- lib/router.php | 3 +++ plugins/OStatus/actions/ostatussub.php | 14 ++++++++--- plugins/OStatus/classes/Ostatus_profile.php | 26 +++++++++++-------- 9 files changed, 79 insertions(+), 55 deletions(-) (limited to 'lib') diff --git a/actions/joingroup.php b/actions/joingroup.php index ba642f712..f87e5dae2 100644 --- a/actions/joingroup.php +++ b/actions/joingroup.php @@ -62,30 +62,33 @@ class JoingroupAction extends Action } $nickname_arg = $this->trimmed('nickname'); - $nickname = common_canonical_nickname($nickname_arg); - - // Permanent redirect on non-canonical nickname + $id = intval($this->arg('id')); + if ($id) { + $this->group = User_group::staticGet('id', $id); + } else if ($nickname_arg) { + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + common_redirect(common_local_url('leavegroup', $args), 301); + return false; + } - if ($nickname_arg != $nickname) { - $args = array('nickname' => $nickname); - common_redirect(common_local_url('joingroup', $args), 301); - return false; - } + $local = Local_group::staticGet('nickname', $nickname); - if (!$nickname) { - $this->clientError(_('No nickname.'), 404); - return false; - } - - $local = Local_group::staticGet('nickname', $nickname); + if (!$local) { + $this->clientError(_('No such group.'), 404); + return false; + } - if (!$local) { - $this->clientError(_('No such group.'), 404); + $this->group = User_group::staticGet('id', $local->group_id); + } else { + $this->clientError(_('No nickname or ID.'), 404); return false; } - $this->group = User_group::staticGet('id', $local->group_id); - if (!$this->group) { $this->clientError(_('No such group.'), 404); return false; diff --git a/actions/leavegroup.php b/actions/leavegroup.php index 222d4c1b4..329b5aafe 100644 --- a/actions/leavegroup.php +++ b/actions/leavegroup.php @@ -62,30 +62,33 @@ class LeavegroupAction extends Action } $nickname_arg = $this->trimmed('nickname'); - $nickname = common_canonical_nickname($nickname_arg); - - // Permanent redirect on non-canonical nickname + $id = intval($this->arg('id')); + if ($id) { + $this->group = User_group::staticGet('id', $id); + } else if ($nickname_arg) { + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + common_redirect(common_local_url('leavegroup', $args), 301); + return false; + } - if ($nickname_arg != $nickname) { - $args = array('nickname' => $nickname); - common_redirect(common_local_url('leavegroup', $args), 301); - return false; - } + $local = Local_group::staticGet('nickname', $nickname); - if (!$nickname) { - $this->clientError(_('No nickname.'), 404); - return false; - } - - $local = Local_group::staticGet('nickname', $nickname); + if (!$local) { + $this->clientError(_('No such group.'), 404); + return false; + } - if (!$local) { - $this->clientError(_('No such group.'), 404); + $this->group = User_group::staticGet('id', $local->group_id); + } else { + $this->clientError(_('No nickname or ID.'), 404); return false; } - $this->group = User_group::staticGet('id', $local->group_id); - if (!$this->group) { $this->clientError(_('No such group.'), 404); return false; diff --git a/classes/User_group.php b/classes/User_group.php index f24bef764..2a58d8895 100644 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -404,7 +404,7 @@ class User_group extends Memcached_DataObject $xs = new XMLStringer(true); $xs->elementStart('activity:subject'); - $xs->element('activity:object', null, 'http://activitystrea.ms/schema/1.0/group'); + $xs->element('activity:object-type', null, 'http://activitystrea.ms/schema/1.0/group'); $xs->element('id', null, $this->permalink()); $xs->element('title', null, $this->getBestName()); $xs->element( diff --git a/lib/activity.php b/lib/activity.php index e592aad6f..4e7ff5336 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -610,7 +610,10 @@ class ActivityObject $object->id = $profile->getUri(); $object->title = $profile->getBestName(); $object->link = $profile->profileurl; - $object->avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + if ($avatar) { + $object->avatar = $avatar->displayUrl(); + } if (isset($profile->lat) && isset($profile->lon)) { $object->geopoint = (float)$profile->lat . ' ' . (float)$profile->lon; @@ -664,7 +667,7 @@ class ActivityObject 'rel' => 'avatar', 'href' => empty($this->avatar) ? Avatar::defaultImage(AVATAR_PROFILE_SIZE) - : $this->avatar->displayUrl() + : $this->avatar ), null ); diff --git a/lib/joinform.php b/lib/joinform.php index aefb553aa..aa8bc20e2 100644 --- a/lib/joinform.php +++ b/lib/joinform.php @@ -100,7 +100,7 @@ class JoinForm extends Form function action() { return common_local_url('joingroup', - array('nickname' => $this->group->nickname)); + array('id' => $this->group->id)); } /** diff --git a/lib/leaveform.php b/lib/leaveform.php index e63d96ee8..5469b5704 100644 --- a/lib/leaveform.php +++ b/lib/leaveform.php @@ -100,7 +100,7 @@ class LeaveForm extends Form function action() { return common_local_url('leavegroup', - array('nickname' => $this->group->nickname)); + array('id' => $this->group->id)); } /** diff --git a/lib/router.php b/lib/router.php index 987d0152e..0e15d83b9 100644 --- a/lib/router.php +++ b/lib/router.php @@ -247,6 +247,9 @@ class Router $m->connect('group/:nickname/'.$v, array('action' => $v.'group'), array('nickname' => '[a-zA-Z0-9]+')); + $m->connect('group/:id/id/'.$v, + array('action' => $v.'group'), + array('id' => '[0-9]+')); } foreach (array('members', 'logo', 'rss', 'designsettings') as $n) { diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index 12832cdcf..aae22f868 100644 --- a/plugins/OStatus/actions/ostatussub.php +++ b/plugins/OStatus/actions/ostatussub.php @@ -333,10 +333,18 @@ class OStatusSubAction extends Action $group = $this->oprofile->localGroup(); if ($user->isMember($group)) { $this->showForm(_m('Already a member!')); - } elseif (Group_member::join($this->oprofile->group_id, $user->id)) { - $this->successGroup(); + return; + } + if (Event::handle('StartJoinGroup', array($group, $user))) { + $ok = Group_member::join($this->oprofile->group_id, $user->id); + if ($ok) { + Event::handle('EndJoinGroup', array($group, $user)); + $this->successGroup(); + } else { + $this->showForm(_m('Remote group join failed!')); + } } else { - $this->showForm(_m('Remote group join failed!')); + $this->showForm(_m('Remote group join aborted!')); } } else { $local = $this->oprofile->localProfile(); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index d66939399..f23017077 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -162,7 +162,7 @@ class Ostatus_profile extends Memcached_DataObject 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'gif' => 'image/gif'); - $extension = pathinfo(parse_url($avatarHref, PHP_URL_PATH), PATHINFO_EXTENSION); + $extension = pathinfo(parse_url($object->avatar, PHP_URL_PATH), PATHINFO_EXTENSION); if (isset($map[$extension])) { // @fixme this ain't used/saved yet $object->avatarType = $map[$extension]; @@ -332,6 +332,9 @@ class Ostatus_profile extends Memcached_DataObject */ public function unsubscribe() { $feedsub = FeedSub::staticGet('uri', $this->feeduri); + if (!$feedsub) { + return true; + } if ($feedsub->sub_state == 'active') { return $feedsub->unsubscribe(); } else if ($feedsub->sub_state == '' || $feedsub->sub_state == 'inactive' || $feedsub->sub_state == 'unsubscribe') { @@ -356,7 +359,7 @@ class Ostatus_profile extends Memcached_DataObject $count = $this->localProfile()->subscriberCount(); } if ($count == 0) { - common_log(LOG_INFO, "Unsubscribing from now-unused remote feed $oprofile->feeduri"); + common_log(LOG_INFO, "Unsubscribing from now-unused remote feed $this->feeduri"); $this->unsubscribe(); return true; } else { @@ -1095,8 +1098,8 @@ class Ostatus_profile extends Memcached_DataObject if ($object->type == ActivityObject::PERSON) { $profile = new Profile(); + $profile->created = common_sql_now(); self::updateProfile($profile, $object, $hints); - $profile->created = common_sql_now(); $oprofile->profile_id = $profile->insert(); if (!$oprofile->profile_id) { @@ -1104,6 +1107,7 @@ class Ostatus_profile extends Memcached_DataObject } } else { $group = new User_group(); + $group->uri = $homeuri; $group->created = common_sql_now(); self::updateGroup($group, $object, $hints); @@ -1183,19 +1187,19 @@ class Ostatus_profile extends Memcached_DataObject { $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; + if (!empty($object->link)) { + $group->mainpage = $object->link; + } else if (array_key_exists('profileurl', $hints)) { + $group->mainpage = $hints['profileurl']; + } - // @fixme homepage - // @fixme bio - // @fixme tags/categories - // @fixme location? // @todo tags from categories - // @todo lat/lon/location? + $group->description = self::getActivityObjectBio($object, $hints); + $group->location = self::getActivityObjectLocation($object, $hints); + $group->homepage = self::getActivityObjectHomepage($object, $hints); 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)); -- cgit v1.2.3-54-g00ecf From e61edb55d9c92c0c93de3a591928f4760b6cea16 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 25 Feb 2010 13:34:43 -0800 Subject: Rationalize group activity stuff --- classes/User_group.php | 46 ++++++++++++++++++++++++------------- lib/activity.php | 61 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/classes/User_group.php b/classes/User_group.php index f24bef764..7240e2703 100644 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -399,25 +399,41 @@ class User_group extends Memcached_DataObject return $xs->getString(); } + /** + * Returns an XML string fragment with group information as an + * Activity Streams element. + * + * Assumes that 'activity' namespace has been previously defined. + * + * @return string + */ function asActivitySubject() { - $xs = new XMLStringer(true); + return $this->asActivityNoun('subject'); + } - $xs->elementStart('activity:subject'); - $xs->element('activity:object', null, 'http://activitystrea.ms/schema/1.0/group'); - $xs->element('id', null, $this->permalink()); - $xs->element('title', null, $this->getBestName()); - $xs->element( - 'link', array( - 'rel' => 'avatar', - 'href' => empty($this->homepage_logo) - ? User_group::defaultLogo(AVATAR_PROFILE_SIZE) - : $this->homepage_logo - ) - ); - $xs->elementEnd('activity:subject'); + /** + * Returns an XML string fragment with group information as an + * Activity Streams noun object with the given element type. + * + * Assumes that 'activity', 'georss', and 'poco' namespace has been + * previously defined. + * + * @param string $element one of 'actor', 'subject', 'object', 'target' + * + * @return string + */ + function asActivityNoun($element) + { + $noun = ActivityObject::fromGroup($this); + return $noun->asString('activity:' . $element); + } - return $xs->getString(); + function getAvatar() + { + return empty($this->homepage_logo) + ? User_group::defaultLogo(AVATAR_PROFILE_SIZE) + : $this->homepage_logo; } static function register($fields) { diff --git a/lib/activity.php b/lib/activity.php index e592aad6f..0863cf8fa 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -223,6 +223,37 @@ class PoCo return $poco; } + function fromGroup($group) + { + if (empty($group)) { + return null; + } + + $poco = new PoCo(); + + $poco->preferredUsername = $group->nickname; + $poco->displayName = $group->getBestName(); + + $poco->note = $group->description; + + $paddy = new PoCoAddress(); + $paddy->formatted = $group->location; + $poco->address = $paddy; + + if (!empty($group->homepage)) { + array_push( + $poco->urls, + new PoCoURL( + 'homepage', + $group->homepage, + true + ) + ); + } + + return $poco; + } + function getPrimaryURL() { foreach ($this->urls as $url) { @@ -621,6 +652,21 @@ class ActivityObject return $object; } + static function fromGroup($group) + { + $object = new ActivityObject(); + + $object->type = ActivityObject::GROUP; + $object->id = $group->getUri(); + $object->title = $group->getBestName(); + $object->link = $group->getUri(); + $object->avatar = $group->getAvatar(); + + $object->poco = PoCo::fromGroup($group); + + return $object; + } + function asString($tag='activity:object') { $xs = new XMLStringer(true); @@ -656,8 +702,7 @@ class ActivityObject ); } - if ($this->type == ActivityObject::PERSON - || $this->type == ActivityObject::GROUP) { + if ($this->type == ActivityObject::PERSON) { $xs->element( 'link', array( 'type' => empty($this->avatar) ? 'image/png' : $this->avatar->mediatype, @@ -670,6 +715,18 @@ class ActivityObject ); } + // XXX: Gotta figure out mime-type! Gar. + + if ($this->type == ActivityObject::GROUP) { + $xs->element( + 'link', array( + 'rel' => 'avatar', + 'href' => $this->avatar + ), + null + ); + } + if (!empty($this->geopoint)) { $xs->element( 'georss:point', -- cgit v1.2.3-54-g00ecf