diff options
-rw-r--r-- | classes/Fave.php | 25 | ||||
-rw-r--r-- | classes/Group_member.php | 50 | ||||
-rw-r--r-- | classes/Notice.php | 58 | ||||
-rw-r--r-- | classes/Subscription.php | 26 | ||||
-rw-r--r-- | lib/activity.php | 26 | ||||
-rw-r--r-- | lib/activitycontext.php | 6 | ||||
-rw-r--r-- | lib/atomusernoticefeed.php | 4 | ||||
-rw-r--r-- | lib/useractivitystream.php | 151 | ||||
-rw-r--r-- | plugins/OStatus/OStatusPlugin.php | 47 | ||||
-rw-r--r-- | plugins/OStatus/classes/Ostatus_profile.php | 6 | ||||
-rw-r--r-- | scripts/backupuser.php | 44 | ||||
-rw-r--r-- | scripts/commandline.inc | 47 |
12 files changed, 440 insertions, 50 deletions
diff --git a/classes/Fave.php b/classes/Fave.php index ed4f56aee..f21f1b529 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -129,4 +129,29 @@ class Fave extends Memcached_DataObject return $ids; } + + function asActivity() + { + $notice = Notice::staticGet('id', $this->notice_id); + $profile = Profile::staticGet('id', $this->user_id); + + $act = new Activity(); + + $act->verb = ActivityVerb::FAVORITE; + $act->id = TagURI::mint('favor:%d:%d:%s', + $profile->id, + $notice->id, + common_date_iso8601($this->modified)); + + $act->time = strtotime($this->modified); + $act->title = _("Favor"); + $act->content = sprintf(_("%s marked notice %s as a favorite."), + $profile->getBestName(), + $notice->uri); + + $act->actor = ActivityObject::fromProfile($profile); + $act->objects[] = ActivityObject::fromNotice($notice); + + return $act; + } } diff --git a/classes/Group_member.php b/classes/Group_member.php index 2239461be..939a9cde7 100644 --- a/classes/Group_member.php +++ b/classes/Group_member.php @@ -65,4 +65,54 @@ class Group_member extends Memcached_DataObject return true; } + + function getMember() + { + $member = Profile::staticGet('id', $this->profile_id); + + if (empty($member)) { + throw new Exception("Profile ID {$this->profile_id} invalid."); + } + + return $member; + } + + function getGroup() + { + $group = User_group::staticGet('id', $this->group_id); + + if (empty($group)) { + throw new Exception("Group ID {$this->group_id} invalid."); + } + + return $group; + } + + function asActivity() + { + $member = $this->getMember(); + $group = $this->getGroup(); + + $act = new Activity(); + + $act->id = TagURI::mint('join:%d:%d:%s', + $member->id, + $group->id, + common_date_iso8601($this->created)); + + $act->actor = ActivityObject::fromProfile($member); + $act->verb = ActivityVerb::JOIN; + $act->objects[] = ActivityObject::fromGroup($group); + + $act->time = strtotime($this->created); + $act->title = _("Join"); + + // TRANS: Success message for subscribe to group attempt through OStatus. + // TRANS: %1$s is the member name, %2$s is the subscribed group's name. + $act->content = sprintf(_('%1$s has joined group %2$s.'), + $member->getBestName(), + $group->getBestName()); + + return $act; + } } diff --git a/classes/Notice.php b/classes/Notice.php index 13b322828..4f23e3500 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1219,6 +1219,64 @@ class Notice extends Memcached_DataObject return $groups; } + function asActivity() + { + $profile = $this->getProfile(); + + $act = new Activity(); + + $act->actor = ActivityObject::fromProfile($profile); + $act->verb = ActivityVerb::POST; + $act->objects[] = ActivityObject::fromNotice($this); + + $act->time = strtotime($this->created); + $act->link = $this->bestUrl(); + + $act->content = common_xml_safe_str($this->rendered); + $act->id = $this->uri; + $act->title = common_xml_safe_str($this->content); + + $ctx = new ActivityContext(); + + if (!empty($this->reply_to)) { + $reply = Notice::staticGet('id', $this->reply_to); + if (!empty($reply)) { + $ctx->replyToID = $reply->uri; + $ctx->replyToUrl = $reply->bestUrl(); + } + } + + $ctx->location = $this->getLocation(); + + $conv = null; + + if (!empty($this->conversation)) { + $conv = Conversation::staticGet('id', $this->conversation); + if (!empty($conv)) { + $ctx->conversation = $conv->uri; + } + } + + $reply_ids = $this->getReplies(); + + foreach ($reply_ids as $id) { + $profile = Profile::staticGet('id', $id); + if (!empty($profile)) { + $ctx->attention[] = $profile->getUri(); + } + } + + $groups = $this->getGroups(); + + foreach ($groups as $group) { + $ctx->attention[] = $group->uri; + } + + $act->context = $ctx; + + return $act; + } + // This has gotten way too long. Needs to be sliced up into functional bits // or ideally exported to a utility class. diff --git a/classes/Subscription.php b/classes/Subscription.php index 0225ed4df..1287499fa 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -235,4 +235,30 @@ class Subscription extends Memcached_DataObject 'subscribed' => $other->id)); return (empty($sub)) ? false : true; } + + function asActivity() + { + $subscriber = Profile::staticGet('id', $this->subscriber); + $subscribed = Profile::staticGet('id', $this->subscribed); + + $act = new Activity(); + + $act->verb = ActivityVerb::FOLLOW; + + $act->id = TagURI::mint('follow:%d:%d:%s', + $subscriber->id, + $subscribed->id, + common_date_iso8601($this->created)); + + $act->time = strtotime($this->created); + $act->title = _("Follow"); + $act->content = sprintf(_("%s is now following %s."), + $subscriber->getBestName(), + $subscribed->getBestName()); + + $act->actor = ActivityObject::fromProfile($subscriber); + $act->objects[] = ActivityObject::fromProfile($subscribed); + + return $act; + } } diff --git a/lib/activity.php b/lib/activity.php index 09a4daee8..f19f10e34 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -319,7 +319,7 @@ class Activity return null; } - function asString($namespace=false) + function asString($namespace=false, $author=true) { $xs = new XMLStringer(true); @@ -338,7 +338,7 @@ class Activity $xs->element('id', null, $this->id); $xs->element('title', null, $this->title); - $xs->element('published', null, common_date_iso8601($this->time)); + $xs->element('published', null, self::iso8601Date($this->time)); $xs->element('content', array('type' => 'html'), $this->content); if (!empty($this->summary)) { @@ -353,13 +353,15 @@ class Activity // XXX: add context - $xs->elementStart('author'); - $xs->element('uri', array(), $this->actor->id); - if ($this->actor->title) { - $xs->element('name', array(), $this->actor->title); + if ($author) { + $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->elementEnd('author'); - $xs->raw($this->actor->asString('activity:actor')); $xs->element('activity:verb', null, $this->verb); @@ -386,4 +388,12 @@ class Activity { return ActivityUtils::child($element, $tag, $namespace); } + + static function iso8601Date($tm) + { + $dateStr = date('d F Y H:i:s', $tm); + $d = new DateTime($dateStr, new DateTimeZone('UTC')); + $d->setTimezone(new DateTimeZone(common_timezone())); + return $d->format('c'); + } } diff --git a/lib/activitycontext.php b/lib/activitycontext.php index cc02a0195..ff3bc9411 100644 --- a/lib/activitycontext.php +++ b/lib/activitycontext.php @@ -54,8 +54,12 @@ class ActivityContext const MENTIONED = 'mentioned'; const CONVERSATION = 'ostatus:conversation'; - function __construct($element) + function __construct($element = null) { + if (empty($element)) { + return; + } + $replyToEl = ActivityUtils::child($element, self::INREPLYTO, self::THR); if (!empty($replyToEl)) { diff --git a/lib/atomusernoticefeed.php b/lib/atomusernoticefeed.php index 785db4915..34a187017 100644 --- a/lib/atomusernoticefeed.php +++ b/lib/atomusernoticefeed.php @@ -44,7 +44,7 @@ if (!defined('STATUSNET')) */ class AtomUserNoticeFeed extends AtomNoticeFeed { - private $user; + protected $user; /** * Constructor @@ -90,7 +90,7 @@ class AtomUserNoticeFeed extends AtomNoticeFeed array('nickname' => $user->nickname) ) ); - + $self = common_local_url('ApiTimelineUser', array('id' => $user->id, 'format' => 'atom')); diff --git a/lib/useractivitystream.php b/lib/useractivitystream.php new file mode 100644 index 000000000..0fc315e26 --- /dev/null +++ b/lib/useractivitystream.php @@ -0,0 +1,151 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010 StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * Class for activity streams + * + * Includes faves, notices, and subscriptions. + * + * We extend atomusernoticefeed since it does some nice setup for us. + * + */ + +class UserActivityStream extends AtomUserNoticeFeed +{ + function __construct($user, $indent = true) + { + parent::__construct($user, null, $indent); + + $subscriptions = $this->getSubscriptions(); + $subscribers = $this->getSubscribers(); + $groups = $this->getGroups(); + $faves = $this->getFaves(); + $notices = $this->getNotices(); + + $objs = array_merge($subscriptions, $subscribers, $groups, $faves, $notices); + + // Sort by create date + + usort($objs, 'UserActivityStream::compareObject'); + + foreach ($objs as $obj) { + $act = $obj->asActivity(); + // Only show the author sub-element if it's different from default user + $str = $act->asString(false, ($act->actor->id != $this->user->uri)); + $this->addEntryRaw($str); + } + } + + function compareObject($a, $b) + { + $ac = strtotime((empty($a->created)) ? $a->modified : $a->created); + $bc = strtotime((empty($b->created)) ? $b->modified : $b->created); + + return (($ac == $bc) ? 0 : (($ac < $bc) ? 1 : -1)); + } + + function getSubscriptions() + { + $subs = array(); + + $sub = new Subscription(); + + $sub->subscriber = $this->user->id; + + if ($sub->find()) { + while ($sub->fetch()) { + if ($sub->subscribed != $this->user->id) { + $subs[] = clone($sub); + } + } + } + + return $subs; + } + + function getSubscribers() + { + $subs = array(); + + $sub = new Subscription(); + + $sub->subscribed = $this->user->id; + + if ($sub->find()) { + while ($sub->fetch()) { + if ($sub->subscriber != $this->user->id) { + $subs[] = clone($sub); + } + } + } + + return $subs; + } + + function getFaves() + { + $faves = array(); + + $fave = new Fave(); + + $fave->user_id = $this->user->id; + + if ($fave->find()) { + while ($fave->fetch()) { + $faves[] = clone($fave); + } + } + + return $faves; + } + + function getNotices() + { + $notices = array(); + + $notice = new Notice(); + + $notice->profile_id = $this->user->id; + + if ($notice->find()) { + while ($notice->fetch()) { + $notices[] = clone($notice); + } + } + + return $notices; + } + + function getGroups() + { + $groups = array(); + + $gm = new Group_member(); + + $gm->profile_id = $this->user->id; + + if ($gm->find()) { + while ($gm->fetch()) { + $groups[] = clone($gm); + } + } + + return $groups; + } +} diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 2dddfcbe2..6cd935c9e 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -557,25 +557,10 @@ class OStatusPlugin extends Plugin return true; } - $act = new Activity(); - - $act->verb = ActivityVerb::FOLLOW; + $sub = Subscription::pkeyGet(array('subscriber' => $subscriber->id, + 'subscribed' => $other->id)); - $act->id = TagURI::mint('follow:%d:%d:%s', - $subscriber->id, - $other->id, - common_date_iso8601(time())); - - $act->time = time(); - $act->title = _m("Follow"); - // TRANS: Success message for subscribe to user attempt through OStatus. - // TRANS: %1$s is the subscriber name, %2$s is the subscribed user's name. - $act->content = sprintf(_m('%1$s is now following %2$s.'), - $subscriber->getBestName(), - $other->getBestName()); - - $act->actor = ActivityObject::fromProfile($subscriber); - $act->object = ActivityObject::fromProfile($other); + $act = $sub->asActivity(); $oprofile->notifyActivity($act, $subscriber); @@ -651,6 +636,9 @@ class OStatusPlugin extends Plugin throw new Exception(_m('Could not set up remote group membership.')); } + // NOTE: we don't use Group_member::asActivity() since that record + // has not yet been created. + $member = Profile::staticGet($user->id); $act = new Activity(); @@ -748,24 +736,15 @@ class OStatusPlugin extends Plugin return true; } - $act = new Activity(); - - $act->verb = ActivityVerb::FAVORITE; - $act->id = TagURI::mint('favor:%d:%d:%s', - $profile->id, - $notice->id, - common_date_iso8601(time())); + $fav = Fave::pkeyGet(array('user_id' => $user->id, + 'notice_id' => $notice->id)); - $act->time = time(); - $act->title = _m('Favor'); - // TRANS: Success message for adding a favorite notice through OStatus. - // TRANS: %1$s is the favoring user's name, %2$s is URI to the favored notice. - $act->content = sprintf(_m('%1$s marked notice %2$s as a favorite.'), - $profile->getBestName(), - $notice->uri); + if (empty($fav)) { + // That's weird. + return true; + } - $act->actor = ActivityObject::fromProfile($profile); - $act->object = ActivityObject::fromNotice($notice); + $act = $fav->asActivity(); $oprofile->notifyActivity($act, $profile); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 402d1967f..047435f66 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1088,7 +1088,7 @@ class Ostatus_profile extends Memcached_DataObject * @return mixed URL string or false */ - protected static function getActivityObjectAvatar($object, $hints=array()) + public static function getActivityObjectAvatar($object, $hints=array()) { if ($object->avatarLinks) { $best = false; @@ -1390,7 +1390,7 @@ class Ostatus_profile extends Memcached_DataObject } } - protected static function updateProfile($profile, $object, $hints=array()) + public static function updateProfile($profile, $object, $hints=array()) { $orig = clone($profile); @@ -1518,7 +1518,7 @@ class Ostatus_profile extends Memcached_DataObject return $bio; } - protected static function getActivityObjectNickname($object, $hints=array()) + public static function getActivityObjectNickname($object, $hints=array()) { if ($object->poco) { if (!empty($object->poco->preferredUsername)) { diff --git a/scripts/backupuser.php b/scripts/backupuser.php new file mode 100644 index 000000000..49fc1cefd --- /dev/null +++ b/scripts/backupuser.php @@ -0,0 +1,44 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010 StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'i:n:f:'; +$longoptions = array('id=', 'nickname=', 'file='); + +$helptext = <<<END_OF_EXPORTACTIVITYSTREAM_HELP +exportactivitystream.php [options] +Export a StatusNet user history to a file + + -i --id ID of user to export + -n --nickname nickname of the user to export + -f --file file to export to (default STDOUT) + +END_OF_EXPORTACTIVITYSTREAM_HELP; + +require_once INSTALLDIR.'/scripts/commandline.inc'; + +try { + $user = getUser(); + $actstr = new UserActivityStream($user); + print $actstr->getString(); +} catch (Exception $e) { + print $e->getMessage()."\n"; + exit(1); +} diff --git a/scripts/commandline.inc b/scripts/commandline.inc index a29f58844..9390890ef 100644 --- a/scripts/commandline.inc +++ b/scripts/commandline.inc @@ -178,6 +178,10 @@ function get_option_value($opt, $alt=null) return null; } +class NoUserArgumentException extends Exception +{ +} + function getUser() { $user = null; @@ -195,9 +199,48 @@ function getUser() throw new Exception("Can't find user with nickname '$nickname'"); } } else { - show_help(); - exit(1); + throw new NoUserArgumentException("No user argument specified."); } return $user; } + +/** "Printf not quiet" */ + +function printfnq() +{ + if (have_option('q', 'quiet')) { + return null; + } + + $cargs = func_num_args(); + + if ($cargs == 0) { + return 0; + } + + $args = func_get_args(); + $format = array_shift($args); + + return vprintf($format, $args); +} + +/** "Print when verbose" */ + +function printfv() +{ + if (!have_option('v', 'verbose')) { + return null; + } + + $cargs = func_num_args(); + + if ($cargs == 0) { + return 0; + } + + $args = func_get_args(); + $format = array_shift($args); + + return vprintf($format, $args); +}
\ No newline at end of file |