forked from GNUsocial/gnu-social
Update activity streams JSON to match spec
Squashed commit of the following: commit 0722450267a1d0f4bdc2853f52a85b850329db73 Author: Zach Copley <zach@status.net> Date: Thu Aug 25 09:58:29 2011 -0700 Updgrade activity object json commit 882ba1dceaba8a0b3ec3513760aa09f68e41f270 Author: Zach Copley <zach@status.net> Date: Wed Aug 24 16:30:07 2011 -0700 Update to the JSON activity serialization document commit 121e441b314b93e184711c3dcc79ada69d429eba Author: Zach Copley <zach@status.net> Date: Wed Aug 24 15:08:06 2011 -0700 Output application/json instead of application/stream+json (at least for now) commit e045e214bffe5e0ddeb0a42555d440b75ae4edde Author: Zach Copley <zach@status.net> Date: Wed Aug 24 15:06:40 2011 -0700 Update to use latest property names from the JSON activity spec
This commit is contained in:
parent
94503a50fd
commit
297d603feb
@ -267,15 +267,14 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction
|
|||||||
break;
|
break;
|
||||||
case 'as':
|
case 'as':
|
||||||
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
|
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
|
||||||
$doc = new ActivityStreamJSONDocument($this->auth_user);
|
$doc = new ActivityStreamJSONDocument($this->auth_user, $title);
|
||||||
$doc->setTitle($title);
|
|
||||||
$doc->addLink($link, 'alternate', 'text/html');
|
$doc->addLink($link, 'alternate', 'text/html');
|
||||||
$doc->addItemsFromNotices($this->notices);
|
$doc->addItemsFromNotices($this->notices);
|
||||||
$this->raw($doc->asString());
|
$this->raw($doc->asString());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// TRANS: Client error displayed when coming across a non-supported API method.
|
// TRANS: Client error displayed when coming across a non-supported API method.
|
||||||
$this->clientError(_('API method not found.'), $code = 404);
|
$this->clientError(_('API method not found.'), 404);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -362,15 +362,16 @@ class Activity
|
|||||||
// actor
|
// actor
|
||||||
$activity['actor'] = $this->actor->asArray();
|
$activity['actor'] = $this->actor->asArray();
|
||||||
|
|
||||||
// body
|
// content
|
||||||
$activity['body'] = $this->content;
|
$activity['content'] = $this->content;
|
||||||
|
|
||||||
// generator <-- We could use this when we know a notice is created
|
// generator <-- We could use this when we know a notice is created
|
||||||
// locally. Or if we know the upstream Generator.
|
// locally. Or if we know the upstream Generator.
|
||||||
|
|
||||||
// icon <-- I've decided to use the posting user's stream avatar here
|
// icon <-- possibly a mini object representing verb?
|
||||||
// for now (also included in the avatarLinks extension)
|
|
||||||
|
|
||||||
|
// id
|
||||||
|
$activity['id'] = $this->id;
|
||||||
|
|
||||||
// object
|
// object
|
||||||
if ($this->verb == ActivityVerb::POST && count($this->objects) == 1) {
|
if ($this->verb == ActivityVerb::POST && count($this->objects) == 1) {
|
||||||
@ -399,9 +400,9 @@ class Activity
|
|||||||
|
|
||||||
// Instead of adding enclosures as an extension to JSON
|
// Instead of adding enclosures as an extension to JSON
|
||||||
// Activities, it seems like we should be using the
|
// Activities, it seems like we should be using the
|
||||||
// attachedObjects property of ActivityObject
|
// attachements property of ActivityObject
|
||||||
|
|
||||||
$attachedObjects = array();
|
$attachments = array();
|
||||||
|
|
||||||
// XXX: OK, this is kinda cheating. We should probably figure out
|
// XXX: OK, this is kinda cheating. We should probably figure out
|
||||||
// what kind of objects these are based on mime-type and then
|
// what kind of objects these are based on mime-type and then
|
||||||
@ -413,11 +414,11 @@ class Activity
|
|||||||
|
|
||||||
if (is_string($enclosure)) {
|
if (is_string($enclosure)) {
|
||||||
|
|
||||||
$attachedObjects[]['id'] = $enclosure;
|
$attachments[]['id'] = $enclosure;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$attachedObjects[]['id'] = $enclosure->url;
|
$attachments[]['id'] = $enclosure->url;
|
||||||
|
|
||||||
$mediaLink = new ActivityStreamsMediaLink(
|
$mediaLink = new ActivityStreamsMediaLink(
|
||||||
$enclosure->url,
|
$enclosure->url,
|
||||||
@ -427,16 +428,16 @@ class Activity
|
|||||||
// XXX: Add 'size' as an extension to MediaLink?
|
// XXX: Add 'size' as an extension to MediaLink?
|
||||||
);
|
);
|
||||||
|
|
||||||
$attachedObjects[]['mediaLink'] = $mediaLink->asArray(); // extension
|
$attachments[]['mediaLink'] = $mediaLink->asArray(); // extension
|
||||||
|
|
||||||
if ($enclosure->title) {
|
if ($enclosure->title) {
|
||||||
$attachedObjects[]['displayName'] = $enclosure->title;
|
$attachments[]['displayName'] = $enclosure->title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($attachedObjects)) {
|
if (!empty($attachments)) {
|
||||||
$activity['object']['attachedObjects'] = $attachedObjects;
|
$activity['object']['attachments'] = $attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -452,7 +453,8 @@ class Activity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$activity['postedTime'] = self::iso8601Date($this->time); // Change to exactly be RFC3339?
|
// published
|
||||||
|
$activity['published'] = self::iso8601Date($this->time);
|
||||||
|
|
||||||
// provider
|
// provider
|
||||||
$provider = array(
|
$provider = array(
|
||||||
@ -471,8 +473,8 @@ class Activity
|
|||||||
// title
|
// title
|
||||||
$activity['title'] = $this->title;
|
$activity['title'] = $this->title;
|
||||||
|
|
||||||
// updatedTime <-- Should we use this to indicate the time we received
|
// updated <-- Optional. Should we use this to indicate the time we r
|
||||||
// a remote notice? Probably not.
|
// eceived a remote notice? Probably not.
|
||||||
|
|
||||||
// verb
|
// verb
|
||||||
//
|
//
|
||||||
@ -480,6 +482,9 @@ class Activity
|
|||||||
// relative simple name is easier to parse
|
// relative simple name is easier to parse
|
||||||
$activity['verb'] = substr($this->verb, strrpos($this->verb, '/') + 1);
|
$activity['verb'] = substr($this->verb, strrpos($this->verb, '/') + 1);
|
||||||
|
|
||||||
|
// url
|
||||||
|
$activity['url'] = $this->id;
|
||||||
|
|
||||||
/* Purely extensions hereafter */
|
/* Purely extensions hereafter */
|
||||||
|
|
||||||
$tags = array();
|
$tags = array();
|
||||||
|
@ -679,16 +679,19 @@ class ActivityObject
|
|||||||
$object = array();
|
$object = array();
|
||||||
|
|
||||||
if (Event::handle('StartActivityObjectOutputJson', array($this, &$object))) {
|
if (Event::handle('StartActivityObjectOutputJson', array($this, &$object))) {
|
||||||
// XXX: attachedObjects are added by Activity
|
// XXX: attachments are added by Activity
|
||||||
|
|
||||||
|
// author (Add object for author? Could be useful for repeats.)
|
||||||
|
|
||||||
|
// content (Add rendered version of the notice?)
|
||||||
|
|
||||||
// displayName
|
// displayName
|
||||||
$object['displayName'] = $this->title;
|
$object['displayName'] = $this->title;
|
||||||
|
|
||||||
// TODO: downstreamDuplicates
|
// downstreamDuplicates
|
||||||
|
|
||||||
// embedCode (used for video)
|
|
||||||
|
|
||||||
// id
|
// id
|
||||||
|
$object['id'] = $this->id;
|
||||||
//
|
//
|
||||||
// XXX: Should we use URL here? or a crazy tag URI?
|
// XXX: Should we use URL here? or a crazy tag URI?
|
||||||
$object['id'] = $this->id;
|
$object['id'] = $this->id;
|
||||||
@ -736,9 +739,13 @@ class ActivityObject
|
|||||||
// @fixme this breaks extension URIs
|
// @fixme this breaks extension URIs
|
||||||
$object['type'] = substr($this->type, strrpos($this->type, '/') + 1);
|
$object['type'] = substr($this->type, strrpos($this->type, '/') + 1);
|
||||||
|
|
||||||
|
// published (probably don't need. Might be useful for repeats.)
|
||||||
|
|
||||||
// summary
|
// summary
|
||||||
$object['summary'] = $this->summary;
|
$object['summary'] = $this->summary;
|
||||||
|
|
||||||
|
// udpated (probably don't need this)
|
||||||
|
|
||||||
// TODO: upstreamDuplicates
|
// TODO: upstreamDuplicates
|
||||||
|
|
||||||
// url (XXX: need to put the right thing here...)
|
// url (XXX: need to put the right thing here...)
|
||||||
@ -753,8 +760,6 @@ class ActivityObject
|
|||||||
$object[$objectName] = $props;
|
$object[$objectName] = $props;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GeoJSON
|
|
||||||
|
|
||||||
if (!empty($this->geopoint)) {
|
if (!empty($this->geopoint)) {
|
||||||
|
|
||||||
list($lat, $long) = explode(' ', $this->geopoint);
|
list($lat, $long) = explode(' ', $this->geopoint);
|
||||||
@ -766,7 +771,7 @@ class ActivityObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($this->poco)) {
|
if (!empty($this->poco)) {
|
||||||
$object['contact'] = $this->poco->asArray();
|
$object['contact'] = array_filter($this->poco->asArray());
|
||||||
}
|
}
|
||||||
Event::handle('EndActivityObjectOutputJson', array($this, &$object));
|
Event::handle('EndActivityObjectOutputJson', array($this, &$object));
|
||||||
}
|
}
|
||||||
|
@ -41,15 +41,29 @@ if (!defined('STATUSNET'))
|
|||||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
* @link http://status.net/
|
* @link http://status.net/
|
||||||
*/
|
*/
|
||||||
class ActivityStreamJSONDocument
|
class ActivityStreamJSONDocument extends JSONActivityCollection
|
||||||
{
|
{
|
||||||
const CONTENT_TYPE = 'application/stream+json; charset=utf-8';
|
// Note: Lot of AS folks think the content type should be:
|
||||||
|
// 'application/stream+json; charset=utf-8', but this is more
|
||||||
|
// useful at the moment, because some programs actually understand
|
||||||
|
// it.
|
||||||
|
const CONTENT_TYPE = 'application/json; charset=utf-8';
|
||||||
|
|
||||||
/* Top level array representing the document */
|
/* Top level array representing the document */
|
||||||
protected $doc = array();
|
protected $doc = array();
|
||||||
|
|
||||||
/* The current authenticated user */
|
/* The current authenticated user */
|
||||||
protected $cur = null;
|
protected $cur;
|
||||||
|
|
||||||
|
/* Title of the document */
|
||||||
|
protected $title;
|
||||||
|
|
||||||
|
/* Links associated with this document */
|
||||||
|
protected $links;
|
||||||
|
|
||||||
|
/* Count of items in this document */
|
||||||
|
// XXX This is cryptically referred to in the spec: "The Stream serialization MAY contain a count property."
|
||||||
|
protected $count;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -57,20 +71,26 @@ class ActivityStreamJSONDocument
|
|||||||
* @param User $cur the current authenticated user
|
* @param User $cur the current authenticated user
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function __construct($cur = null)
|
function __construct($cur = null, $title = null, $items = null, $links = null, $url = null)
|
||||||
{
|
{
|
||||||
|
parent::__construct($items, $url);
|
||||||
|
|
||||||
$this->cur = $cur;
|
$this->cur = $cur;
|
||||||
|
|
||||||
/* Title of the JSON document */
|
/* Title of the JSON document */
|
||||||
$this->doc['title'] = null;
|
$this->title = $title;
|
||||||
|
|
||||||
/* Array of activity items */
|
if (!empty($items)) {
|
||||||
$this->doc['items'] = array();
|
$this->count = count($this->items);
|
||||||
|
}
|
||||||
|
|
||||||
/* Array of links associated with the document */
|
/* Array of links associated with the document */
|
||||||
$this->doc['links'] = array();
|
$this->links = empty($links) ? array() : $items;
|
||||||
|
|
||||||
|
/* URL of a document, this document? containing a list of all the items in the stream */
|
||||||
|
if (!empty($this->url)) {
|
||||||
|
$this->url = $this->url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,9 +101,15 @@ class ActivityStreamJSONDocument
|
|||||||
|
|
||||||
function setTitle($title)
|
function setTitle($title)
|
||||||
{
|
{
|
||||||
$this->doc['title'] = $title;
|
$this->title = $title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setUrl($url)
|
||||||
|
{
|
||||||
|
$this->url = $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add more than one Item to the document
|
* Add more than one Item to the document
|
||||||
*
|
*
|
||||||
@ -116,7 +142,8 @@ class ActivityStreamJSONDocument
|
|||||||
|
|
||||||
$act = $notice->asActivity($cur);
|
$act = $notice->asActivity($cur);
|
||||||
$act->extra[] = $notice->noticeInfo($cur);
|
$act->extra[] = $notice->noticeInfo($cur);
|
||||||
array_push($this->doc['items'], $act->asArray());
|
array_push($this->items, $act->asArray());
|
||||||
|
$this->count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,7 +155,7 @@ class ActivityStreamJSONDocument
|
|||||||
function addLink($url = null, $rel = null, $mediaType = null)
|
function addLink($url = null, $rel = null, $mediaType = null)
|
||||||
{
|
{
|
||||||
$link = new ActivityStreamsLink($url, $rel, $mediaType);
|
$link = new ActivityStreamsLink($url, $rel, $mediaType);
|
||||||
$this->doc['links'][] = $link->asArray();
|
array_push($this->links, $link->asArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -138,7 +165,13 @@ class ActivityStreamJSONDocument
|
|||||||
*/
|
*/
|
||||||
function asString()
|
function asString()
|
||||||
{
|
{
|
||||||
return json_encode(array_filter($this->doc));
|
$this->doc['generator'] = 'StatusNet ' . STATUSNET_VERSION; // extension
|
||||||
|
$this->doc['title'] = $this->title;
|
||||||
|
$this->doc['url'] = $this->url;
|
||||||
|
$this->doc['count'] = $this->count;
|
||||||
|
$this->doc['items'] = $this->items;
|
||||||
|
$this->doc['links'] = $this->links; // extension
|
||||||
|
return json_encode(array_filter($this->doc)); // filter out empty elements
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -161,8 +194,8 @@ class ActivityStreamsMediaLink extends ActivityStreamsLink
|
|||||||
$url = null,
|
$url = null,
|
||||||
$width = null,
|
$width = null,
|
||||||
$height = null,
|
$height = null,
|
||||||
$mediaType = null,
|
$mediaType = null, // extension
|
||||||
$rel = null,
|
$rel = null, // extension
|
||||||
$duration = null
|
$duration = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -183,6 +216,49 @@ class ActivityStreamsMediaLink extends ActivityStreamsLink
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Collection primarily as the root of an Activity Streams doc but can be used as the value
|
||||||
|
* of extension properties in a variety of situations.
|
||||||
|
*
|
||||||
|
* A valid Collection object serialization MUST contain at least the url or items properties.
|
||||||
|
*/
|
||||||
|
class JSONActivityCollection {
|
||||||
|
|
||||||
|
/* Non-negative integer specifying the total number of activities within the stream */
|
||||||
|
protected $totalItems;
|
||||||
|
|
||||||
|
/* An array containing a listing of Objects of any object type */
|
||||||
|
protected $items;
|
||||||
|
|
||||||
|
/* IRI referencing a JSON document containing the full listing of objects in the collection */
|
||||||
|
protected $url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param array $items array of activity items
|
||||||
|
* @param string $url url of a doc list all the objs in the collection
|
||||||
|
* @param int $totalItems total number of items in the collection
|
||||||
|
*/
|
||||||
|
function __construct($items = null, $url = null)
|
||||||
|
{
|
||||||
|
$this->items = empty($items) ? array() : $items;
|
||||||
|
$this->totalItems = count($items);
|
||||||
|
$this->url = $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total number of items in the collection
|
||||||
|
*
|
||||||
|
* @return int total the total
|
||||||
|
*/
|
||||||
|
public function getTotalItems()
|
||||||
|
{
|
||||||
|
$this->totalItems = count($items);
|
||||||
|
return $this->totalItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for representing links in JSON Activities
|
* A class for representing links in JSON Activities
|
||||||
*
|
*
|
||||||
@ -216,3 +292,4 @@ class ActivityStreamsLink
|
|||||||
return array_filter($this->linkDict);
|
return array_filter($this->linkDict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user