summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--classes/Fave.php25
-rw-r--r--classes/Group_member.php50
-rw-r--r--classes/Notice.php58
-rw-r--r--classes/Subscription.php26
-rw-r--r--lib/activity.php26
-rw-r--r--lib/activitycontext.php6
-rw-r--r--lib/atomusernoticefeed.php4
-rw-r--r--lib/useractivitystream.php151
-rw-r--r--plugins/OStatus/OStatusPlugin.php47
-rw-r--r--plugins/OStatus/classes/Ostatus_profile.php6
-rw-r--r--scripts/backupuser.php44
-rw-r--r--scripts/commandline.inc47
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