From b28266b3d6e9529c2d39d4411697803028a404e8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 5 Dec 2010 16:15:05 -0500 Subject: [PATCH] Convert Notice::asAtomEntry() to use Notice::asActivity() and Activity::asString() We had two ways to generate an activity entry from a notice; one through Notice::asAtomEntry() and one through Notice::asActivity() and Activity::asString(). The code paths had already diverged somewhat. I took the conditions that were in Notice::asAtomEntry() and made sure they were replicated in the other two functions. Then, I rewrote Notice::asAtomEntry() to use the other two functions instead. This change passes the ActivityGenerationTests unit tests, but there may be some other stuff that's not getting covered. --- classes/Notice.php | 570 ++++++++++++---------------------------- lib/activity.php | 205 +++++++++++++-- lib/activitycontext.php | 2 + lib/activitysource.php | 56 ++++ lib/atomcategory.php | 2 +- 5 files changed, 411 insertions(+), 424 deletions(-) create mode 100644 lib/activitysource.php diff --git a/classes/Notice.php b/classes/Notice.php index 4169f1a1c7..de5104fdbb 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1226,61 +1226,180 @@ class Notice extends Memcached_DataObject return $groups; } - function asActivity() + /** + * Convert a notice into an activity for export. + * + * @param User $cur Current user + * + * @return Activity activity object representing this Notice. + */ + + function asActivity($cur = null, $source = false) { - $profile = $this->getProfile(); - $act = new Activity(); + + if (Event::handle('StartNoticeAsActivity', array($this, &$act))) { + + $profile = $this->getProfile(); + + $act->actor = ActivityObject::fromProfile($profile); + $act->verb = ActivityVerb::POST; + $act->objects[] = ActivityObject::fromNotice($this); - $act->actor = ActivityObject::fromProfile($profile); - $act->verb = ActivityVerb::POST; - $act->objects[] = ActivityObject::fromNotice($this); + // XXX: should this be handled by default processing for object entry? - $act->time = strtotime($this->created); - $act->link = $this->bestUrl(); + $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); - $act->content = common_xml_safe_str($this->rendered); - $act->id = $this->uri; - $act->title = common_xml_safe_str($this->content); + // Categories - $ctx = new ActivityContext(); + $tags = $this->getTags(); - if (!empty($this->reply_to)) { - $reply = Notice::staticGet('id', $this->reply_to); - if (!empty($reply)) { - $ctx->replyToID = $reply->uri; - $ctx->replyToUrl = $reply->bestUrl(); + foreach ($tags as $tag) { + $cat = new AtomCategory(); + $cat->term = $tag; + + $act->categories[] = $cat; } - } - $ctx->location = $this->getLocation(); + // Enclosures + // XXX: use Atom Media and/or File activity objects instead - $conv = null; + $attachments = $this->attachments(); - if (!empty($this->conversation)) { - $conv = Conversation::staticGet('id', $this->conversation); - if (!empty($conv)) { - $ctx->conversation = $conv->uri; + foreach ($attachments as $attachment) { + $enclosure = $attachment->getEnclosure(); + if ($enclosure) { + $act->enclosures[] = $enclosure; + } } - } - - $reply_ids = $this->getReplies(); - - foreach ($reply_ids as $id) { - $profile = Profile::staticGet('id', $id); - if (!empty($profile)) { - $ctx->attention[] = $profile->getUri(); + + $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; + } + + // XXX: deprecated; use ActivityVerb::SHARE instead + + $repeat = null; + + if (!empty($this->repeat_of)) { + $repeat = Notice::staticGet('id', $this->repeat_of); + $ctx->forwardID = $repeat->uri; + $ctx->forwardUrl = $repeat->bestUrl(); + } + + $act->context = $ctx; + + $noticeInfoAttr = array('local_id' => $this->id); // local notice ID (useful to clients for ordering) + + $ns = $this->getSource(); + + if (!empty($ns)) { + $noticeInfoAttr['source'] = $ns->code; + if (!empty($ns->url)) { + $noticeInfoAttr['source_link'] = $ns->url; + if (!empty($ns->name)) { + $noticeInfoAttr['source'] = '' + . htmlspecialchars($ns->name) + . ''; + } + } + } + + if (!empty($cur)) { + $noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false"; + $cp = $cur->getProfile(); + $noticeInfoAttr['repeated'] = ($cp->hasRepeated($this->id)) ? "true" : "false"; + } + + if (!empty($this->repeat_of)) { + $noticeInfoAttr['repeat_of'] = $this->repeat_of; + } + + $act->extra[] = array('statusnet:notice_info', $noticeInfoAttr, null); + + if ($source) { + + $atom_feed = $profile->getAtomFeed(); + + if (!empty($atom_feed)) { + + $act->source = new ActivitySource(); + + // XXX: we should store the actual feed ID + + $act->source->id = $atom_feed; + + // XXX: we should store the actual feed title + + $act->source->title = $profile->getBestName(); + + $act->source->links['alternate'] = $profile->profileurl; + $act->source->links['self'] = $atom_feed; + + $act->source->icon = $profile->avatarUrl(AVATAR_PROFILE_SIZE); + + $notice = $profile->getCurrentNotice(); + + if (!empty($notice)) { + $act->source->updated = self::utcDate($notice->created); + } + + $user = User::staticGet('id', $profile->id); + + if (!empty($user)) { + $act->source->links['license'] = common_config('license', 'url'); + } + } + } + + if ($this->isLocal()) { + $act->selfLink = common_local_url('ApiStatusesShow', array('id' => $this->id, + 'format' => 'atom')); + $act->editLink = $act->selfLink; + } + + Event::handle('EndNoticeAsActivity', array($this, &$act)); } - - $groups = $this->getGroups(); - - foreach ($groups as $group) { - $ctx->attention[] = $group->uri; - } - - $act->context = $ctx; - + return $act; } @@ -1289,372 +1408,10 @@ class Notice extends Memcached_DataObject function asAtomEntry($namespace=false, $source=false, $author=true, $cur=null) { - $profile = $this->getProfile(); - - $xs = new XMLStringer(true); - - if ($namespace) { - $attrs = array('xmlns' => 'http://www.w3.org/2005/Atom', - 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', - 'xmlns:georss' => 'http://www.georss.org/georss', - 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', - 'xmlns:media' => 'http://purl.org/syndication/atommedia', - 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', - 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', - 'xmlns:statusnet' => 'http://status.net/schema/api/1/'); - } else { - $attrs = array(); - } - - if (Event::handle('StartActivityStart', array(&$this, &$xs, &$attrs))) { - $xs->elementStart('entry', $attrs); - Event::handle('EndActivityStart', array(&$this, &$xs, &$attrs)); - } - - if (Event::handle('StartActivitySource', array(&$this, &$xs))) { - if ($source) { - $atom_feed = $profile->getAtomFeed(); - - if (!empty($atom_feed)) { - $xs->elementStart('source'); - - // XXX: we should store the actual feed ID - - $xs->element('id', null, $atom_feed); - - // XXX: we should store the actual feed title - - $xs->element('title', null, $profile->getBestName()); - - $xs->element('link', array('rel' => 'alternate', - 'type' => 'text/html', - 'href' => $profile->profileurl)); - - $xs->element('link', array('rel' => 'self', - 'type' => 'application/atom+xml', - 'href' => $atom_feed)); - - $xs->element('icon', null, $profile->avatarUrl(AVATAR_PROFILE_SIZE)); - - $notice = $profile->getCurrentNotice(); - - if (!empty($notice)) { - $xs->element('updated', null, self::utcDate($notice->created)); - } - - $user = User::staticGet('id', $profile->id); - - if (!empty($user)) { - $xs->element('link', array('rel' => 'license', - 'href' => common_config('license', 'url'))); - } - - $xs->elementEnd('source'); - } - } - Event::handle('EndActivitySource', array(&$this, &$xs)); - } - - $title = common_xml_safe_str($this->content); - - if (Event::handle('StartActivityTitle', array(&$this, &$xs, &$title))) { - $xs->element('title', null, $title); - Event::handle('EndActivityTitle', array($this, &$xs, $title)); - } - - $atomAuthor = ''; - - if ($author) { - $atomAuthor = $profile->asAtomAuthor($cur); - } - - if (Event::handle('StartActivityAuthor', array(&$this, &$xs, &$atomAuthor))) { - if (!empty($atomAuthor)) { - $xs->raw($atomAuthor); - Event::handle('EndActivityAuthor', array(&$this, &$xs, &$atomAuthor)); - } - } - - $actor = ''; - - if ($author) { - $actor = $profile->asActivityActor(); - } - - if (Event::handle('StartActivityActor', array(&$this, &$xs, &$actor))) { - if (!empty($actor)) { - $xs->raw($actor); - Event::handle('EndActivityActor', array(&$this, &$xs, &$actor)); - } - } - - $url = $this->bestUrl(); - - if (Event::handle('StartActivityLink', array(&$this, &$xs, &$url))) { - $xs->element('link', array('rel' => 'alternate', - 'type' => 'text/html', - 'href' => $url)); - Event::handle('EndActivityLink', array(&$this, &$xs, $url)); - } - - $id = $this->uri; - - if (Event::handle('StartActivityId', array(&$this, &$xs, &$id))) { - $xs->element('id', null, $id); - Event::handle('EndActivityId', array(&$this, &$xs, $id)); - } - - $published = self::utcDate($this->created); - - if (Event::handle('StartActivityPublished', array(&$this, &$xs, &$published))) { - $xs->element('published', null, $published); - Event::handle('EndActivityPublished', array(&$this, &$xs, $published)); - } - - $updated = $published; // XXX: notices are usually immutable - - if (Event::handle('StartActivityUpdated', array(&$this, &$xs, &$updated))) { - $xs->element('updated', null, $updated); - Event::handle('EndActivityUpdated', array(&$this, &$xs, $updated)); - } - - $content = common_xml_safe_str($this->rendered); - - if (Event::handle('StartActivityContent', array(&$this, &$xs, &$content))) { - $xs->element('content', array('type' => 'html'), $content); - Event::handle('EndActivityContent', array(&$this, &$xs, $content)); - } - - // Most of our notices represent POSTing a NOTE. This is the default verb - // for activity streams, so we normally just leave it out. - - $verb = ActivityVerb::POST; - - if (Event::handle('StartActivityVerb', array(&$this, &$xs, &$verb))) { - $xs->element('activity:verb', null, $verb); - Event::handle('EndActivityVerb', array(&$this, &$xs, $verb)); - } - - // We use the default behavior for activity streams: if there's no activity:object, - // then treat the entry itself as the object. Here, you can set the type of that object, - // which is normally a NOTE. - - $type = ActivityObject::NOTE; - - if (Event::handle('StartActivityDefaultObjectType', array(&$this, &$xs, &$type))) { - $xs->element('activity:object-type', null, $type); - Event::handle('EndActivityDefaultObjectType', array(&$this, &$xs, $type)); - } - - // Since we usually use the entry itself as an object, we don't have an explicit - // object. Some extensions may want to add them (for photo, event, music, etc.). - - $objects = array(); - - if (Event::handle('StartActivityObjects', array(&$this, &$xs, &$objects))) { - foreach ($objects as $object) { - $xs->raw($object->asString()); - } - Event::handle('EndActivityObjects', array(&$this, &$xs, $objects)); - } - - $noticeInfoAttr = array('local_id' => $this->id); // local notice ID (useful to clients for ordering) - - $ns = $this->getSource(); - - if (!empty($ns)) { - $noticeInfoAttr['source'] = $ns->code; - if (!empty($ns->url)) { - $noticeInfoAttr['source_link'] = $ns->url; - if (!empty($ns->name)) { - $noticeInfoAttr['source'] = '' - . htmlspecialchars($ns->name) - . ''; - } - } - } - - if (!empty($cur)) { - $noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false"; - $profile = $cur->getProfile(); - $noticeInfoAttr['repeated'] = ($profile->hasRepeated($this->id)) ? "true" : "false"; - } - - if (!empty($this->repeat_of)) { - $noticeInfoAttr['repeat_of'] = $this->repeat_of; - } - - if (Event::handle('StartActivityNoticeInfo', array(&$this, &$xs, &$noticeInfoAttr))) { - $xs->element('statusnet:notice_info', $noticeInfoAttr, null); - Event::handle('EndActivityNoticeInfo', array(&$this, &$xs, $noticeInfoAttr)); - } - - $replyNotice = null; - - if ($this->reply_to) { - $replyNotice = Notice::staticGet('id', $this->reply_to); - } - - if (Event::handle('StartActivityInReplyTo', array(&$this, &$xs, &$replyNotice))) { - if (!empty($replyNotice)) { - $xs->element('link', array('rel' => 'related', - 'href' => $replyNotice->bestUrl())); - $xs->element('thr:in-reply-to', - array('ref' => $replyNotice->uri, - 'href' => $replyNotice->bestUrl())); - Event::handle('EndActivityInReplyTo', array(&$this, &$xs, $replyNotice)); - } - } - - $conv = null; - - if (!empty($this->conversation)) { - $conv = Conversation::staticGet('id', $this->conversation); - } - - if (Event::handle('StartActivityConversation', array(&$this, &$xs, &$conv))) { - if (!empty($conv)) { - $xs->element('link', array('rel' => 'ostatus:conversation', - 'href' => $conv->uri)); - } - Event::handle('EndActivityConversation', array(&$this, &$xs, $conv)); - } - - $replyProfiles = array(); - - $reply_ids = $this->getReplies(); - - foreach ($reply_ids as $id) { - $profile = Profile::staticGet('id', $id); - if (!empty($profile)) { - $replyProfiles[] = $profile; - } - } - - if (Event::handle('StartActivityAttentionProfiles', array(&$this, &$xs, &$replyProfiles))) { - foreach ($replyProfiles as $profile) { - $xs->element('link', array('rel' => 'ostatus:attention', - 'href' => $profile->getUri())); - $xs->element('link', array('rel' => 'mentioned', - 'href' => $profile->getUri())); - } - Event::handle('EndActivityAttentionProfiles', array(&$this, &$xs, $replyProfiles)); - } - - $groups = $this->getGroups(); - - if (Event::handle('StartActivityAttentionGroups', array(&$this, &$xs, &$groups))) { - foreach ($groups as $group) { - $xs->element('link', array('rel' => 'ostatus:attention', - 'href' => $group->permalink())); - $xs->element('link', array('rel' => 'mentioned', - 'href' => $group->permalink())); - } - Event::handle('EndActivityAttentionGroups', array(&$this, &$xs, $groups)); - } - - $repeat = null; - - if (!empty($this->repeat_of)) { - $repeat = Notice::staticGet('id', $this->repeat_of); - } - - if (Event::handle('StartActivityForward', array(&$this, &$xs, &$repeat))) { - if (!empty($repeat)) { - $xs->element('ostatus:forward', - array('ref' => $repeat->uri, - 'href' => $repeat->bestUrl())); - } - - Event::handle('EndActivityForward', array(&$this, &$xs, $repeat)); - } - - $tags = $this->getTags(); - - if (Event::handle('StartActivityCategories', array(&$this, &$xs, &$tags))) { - foreach ($tags as $tag) { - $xs->element('category', array('term' => $tag)); - } - Event::handle('EndActivityCategories', array(&$this, &$xs, $tags)); - } - - // Enclosures - - $enclosures = array(); - - $attachments = $this->attachments(); - - foreach ($attachments as $attachment) { - $enclosure = $attachment->getEnclosure(); - if ($enclosure) { - $enclosures[] = $enclosure; - } - } - - if (Event::handle('StartActivityEnclosures', array(&$this, &$xs, &$enclosures))) { - foreach ($enclosures as $enclosure) { - $attributes = array('rel' => 'enclosure', - 'href' => $enclosure->url, - 'type' => $enclosure->mimetype, - 'length' => $enclosure->size); - - if ($enclosure->title) { - $attributes['title'] = $enclosure->title; - } - - $xs->element('link', $attributes, null); - } - Event::handle('EndActivityEnclosures', array(&$this, &$xs, $enclosures)); - } - - $lat = $this->lat; - $lon = $this->lon; - - if (Event::handle('StartActivityGeo', array(&$this, &$xs, &$lat, &$lon))) { - if (!empty($lat) && !empty($lon)) { - $xs->element('georss:point', null, $lat . ' ' . $lon); - } - Event::handle('EndActivityGeo', array(&$this, &$xs, $lat, $lon)); - } - - // @fixme check this logic - - if ($this->isLocal()) { - - $selfUrl = common_local_url('ApiStatusesShow', array('id' => $this->id, - 'format' => 'atom')); - - if (Event::handle('StartActivityRelSelf', array(&$this, &$xs, &$selfUrl))) { - $xs->element('link', array('rel' => 'self', - 'type' => 'application/atom+xml', - 'href' => $selfUrl)); - Event::handle('EndActivityRelSelf', array(&$this, &$xs, $selfUrl)); - } - - if (!empty($cur) && $cur->id == $this->profile_id) { - - // note: $selfUrl may have been changed by a plugin - $relEditUrl = common_local_url('ApiStatusesShow', array('id' => $this->id, - 'format' => 'atom')); - - if (Event::handle('StartActivityRelEdit', array(&$this, &$xs, &$relEditUrl))) { - $xs->element('link', array('rel' => 'edit', - 'type' => 'application/atom+xml', - 'href' => $relEditUrl)); - Event::handle('EndActivityRelEdit', array(&$this, &$xs, $relEditUrl)); - } - } - } - - if (Event::handle('StartActivityEnd', array(&$this, &$xs))) { - $xs->elementEnd('entry'); - Event::handle('EndActivityEnd', array(&$this, &$xs)); - } - - return $xs->getString(); + $act = $this->asActivity($cur, $source); + return $act->asString($namespace, $author); } + /** * Returns an XML string fragment with a reference to a notice as an @@ -1665,6 +1422,7 @@ class Notice extends Memcached_DataObject * @param string $element one of 'subject', 'object', 'target' * @return string */ + function asActivityNoun($element) { $noun = ActivityObject::fromNotice($this); diff --git a/lib/activity.php b/lib/activity.php index e974ca991d..eb639a5dd3 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -101,6 +101,11 @@ class Activity public $categories = array(); // list of AtomCategory objects public $enclosures = array(); // list of enclosure URL references + public $extra = array(); // extra elements as array(tag, attrs, content) + public $source; // ActivitySource object representing 'home feed' + public $selfLink; // + public $editLink; // + /** * Turns a regular old Atom into a magical activity * @@ -235,6 +240,11 @@ class Activity foreach (ActivityUtils::getLinks($entry, 'enclosure') as $link) { $this->enclosures[] = $link->getAttribute('href'); } + + // From APP. Might be useful. + + $this->selfLink = ActivityUtils::getLink($entry, 'self', 'application/atom+xml'); + $this->editLink = ActivityUtils::getLink($entry, 'edit', 'application/atom+xml'); } function _fromRssItem($item, $channel) @@ -323,34 +333,76 @@ class Activity if ($namespace) { $attrs = array('xmlns' => 'http://www.w3.org/2005/Atom', + 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:georss' => 'http://www.georss.org/georss', 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', - 'xmlns:media' => 'http://purl.org/syndication/atommedia'); + 'xmlns:media' => 'http://purl.org/syndication/atommedia', + 'xmlns:statusnet' => 'http://status.net/schema/api/1/'); } else { $attrs = array(); } $xs->elementStart('entry', $attrs); - $xs->element('id', null, $this->id); - $xs->element('title', null, $this->title); - $xs->element('published', null, self::iso8601Date($this->time)); - $xs->element('content', array('type' => 'html'), $this->content); + if ($this->verb == ActivityVerb::POST && count($this->objects) == 1) { + + common_debug('Using default object entry notation.'); + + $obj = $this->objects[0]; + + $xs->element('id', null, $obj->id); + $xs->element('activity:object-type', null, $obj->type); + + if (!empty($obj->title)) { + $xs->element('title', null, $obj->title); + } else { + // XXX need a better default title + $xs->element('title', null, _('Post')); + } + + if (!empty($obj->content)) { + $xs->element('content', array('type' => 'html'), $obj->content); + } + + if (!empty($obj->summary)) { + $xs->element('summary', null, $obj->summary); + } + + if (!empty($obj->link)) { + $xs->element('link', array('rel' => 'alternate', + 'type' => 'text/html'), + $obj->link); + } + + // XXX: some object types might have other values here. + + } else { + $xs->element('id', null, $this->id); + $xs->element('title', null, $this->title); + + $xs->element('content', array('type' => 'html'), $this->content); + + if (!empty($this->summary)) { + $xs->element('summary', null, $this->summary); + } + + if (!empty($this->link)) { + $xs->element('link', array('rel' => 'alternate', + 'type' => 'text/html'), + $this->link); + } - if (!empty($this->summary)) { - $xs->element('summary', null, $this->summary); } - if (!empty($this->link)) { - $xs->element('link', array('rel' => 'alternate', - 'type' => 'text/html'), - $this->link); - } - - // XXX: add context + $xs->element('activity:verb', null, $this->verb); + $published = self::iso8601Date($this->time); + + $xs->element('published', null, $published); + $xs->element('updated', null, $published); + if ($author) { $xs->elementStart('author'); $xs->element('uri', array(), $this->actor->id); @@ -361,14 +413,61 @@ class Activity $xs->raw($this->actor->asString('activity:actor')); } - $xs->element('activity:verb', null, $this->verb); - - if (!empty($this->objects)) { + if ($this->verb != ActivityVerb::POST || count($this->objects) != 1) { foreach($this->objects as $object) { $xs->raw($object->asString()); } } + if (!empty($this->context)) { + + if (!empty($this->context->replyToID)) { + if (!empty($this->context->replyToUrl)) { + $xs->element('thr:in-reply-to', + array('ref' => $this->context->replyToID, + 'href' => $this->context->replyToUrl)); + } else { + $xs->element('thr:in-reply-to', + array('ref' => $this->context->replyToID)); + } + } + + if (!empty($this->context->replyToUrl)) { + $xs->element('link', array('rel' => 'related', + 'href' => $this->context->replyToUrl)); + } + + if (!empty($this->context->conversation)) { + $xs->element('link', array('rel' => 'ostatus:conversation', + 'href' => $this->context->conversation)); + } + + foreach ($this->context->attention as $attnURI) { + $xs->element('link', array('rel' => 'ostatus:attention', + 'href' => $attnURI)); + $xs->element('link', array('rel' => 'mentioned', + 'href' => $attnURI)); + } + + // XXX: shoulda used ActivityVerb::SHARE + + if (!empty($this->context->forwardID)) { + if (!empty($this->context->forwardUrl)) { + $xs->element('ostatus:forward', + array('ref' => $this->context->forwardID, + 'href' => $this->context->forwardUrl)); + } else { + $xs->element('ostatus:forward', + array('ref' => $this->context->forwardID)); + } + } + + if (!empty($this->context->location)) { + $loc = $this->context->location; + $xs->element('georss:point', null, $loc->lat . ' ' . $loc->lon); + } + } + if ($this->target) { $xs->raw($this->target->asString('activity:target')); } @@ -377,6 +476,78 @@ class Activity $xs->raw($cat->asString()); } + // can be either URLs or enclosure objects + + foreach ($this->enclosures as $enclosure) { + if (is_string($enclosure)) { + $xs->element('link', array('href' => $enclosure)); + } else { + $attributes = array('rel' => 'enclosure', + 'href' => $enclosure->url, + 'type' => $enclosure->mimetype, + 'length' => $enclosure->size); + if ($enclosure->title) { + $attributes['title'] = $enclosure->title; + } + $xs->element('link', array('href' => $enclosure)); + } + } + + // Info on the source feed + + if (!empty($this->source)) { + $xs->elementStart('source'); + + $xs->element('id', null, $this->source->id); + $xs->element('title', null, $this->source->title); + + if (array_key_exists('alternate', $this->source->links)) { + $xs->element('link', array('rel' => 'alternate', + 'type' => 'text/html', + 'href' => $this->source->links['alternate'])); + } + + if (array_key_exists('self', $this->source->links)) { + $xs->element('link', array('rel' => 'self', + 'type' => 'application/atom+xml', + 'href' => $this->source->links['self'])); + } + + if (array_key_exists('license', $this->source->links)) { + $xs->element('link', array('rel' => 'license', + 'href' => $this->source->links['license'])); + } + + if (!empty($this->source->icon)) { + $xs->element('icon', null, $this->source->icon); + } + + if (!empty($this->source->updated)) { + $xs->element('updated', null, $this->source->updated); + } + + $xs->elementEnd('source'); + } + + if (!empty($this->selfLink)) { + $xs->element('link', array('rel' => 'self', + 'type' => 'application/atom+xml', + 'href' => $this->selfLink)); + } + + if (!empty($this->editLink)) { + $xs->element('link', array('rel' => 'edit', + 'type' => 'application/atom+xml', + 'href' => $this->editLink)); + } + + // For throwing in extra elements; used for statusnet:notice_info + + foreach ($this->extra as $el) { + list($tag, $attrs, $content) = $el; + $xs->element($tag, $attrs, $content); + } + $xs->elementEnd('entry'); return $xs->getString(); diff --git a/lib/activitycontext.php b/lib/activitycontext.php index ff3bc9411d..fd0dfe06c1 100644 --- a/lib/activitycontext.php +++ b/lib/activitycontext.php @@ -39,6 +39,8 @@ class ActivityContext public $location; public $attention = array(); public $conversation; + public $forwardID; // deprecated, use ActivityVerb::SHARE instead + public $forwardUrl; // deprecated, use ActivityVerb::SHARE instead const THR = 'http://purl.org/syndication/thread/1.0'; const GEORSS = 'http://www.georss.org/georss'; diff --git a/lib/activitysource.php b/lib/activitysource.php new file mode 100644 index 0000000000..56266057f6 --- /dev/null +++ b/lib/activitysource.php @@ -0,0 +1,56 @@ + + * + * PHP version 5 + * + * LICENCE: 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 . + * + * @category Feed + * @package StatusNet + * @author Evan Prodromou + * @author Zach Copley + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Feed data to store in the element + * + * I wanted to use Atom10Feed but it seems more heavyweight than what's + * needed here. + * + * @category OStatus + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +class ActivitySource +{ + public $id; + public $title; + public $icon; + public $updated; + public $links; +} diff --git a/lib/atomcategory.php b/lib/atomcategory.php index 4cc3b4f4d4..9763023f75 100644 --- a/lib/atomcategory.php +++ b/lib/atomcategory.php @@ -72,6 +72,6 @@ class AtomCategory } $xs = new XMLStringer(); $xs->element('category', $attribs); - return $xs->asString(); + return $xs->getString(); } }