OStatus: prep work for sending notifications on sub/unsub/join/leave/favorite/unfavorite via Salmon; needs to be completed and hooked up once feed gen is fixed.

This commit is contained in:
Brion Vibber 2010-02-12 18:54:48 +00:00
parent b57e3dfae2
commit b39047d95b
2 changed files with 184 additions and 3 deletions

View File

@ -755,6 +755,14 @@ class Profile extends Memcached_DataObject
return !empty($notice);
}
/**
* Returns an XML string fragment with limited profile information
* as an Atom <author> element.
*
* Assumes that Atom has been previously set up as the base namespace.
*
* @return string
*/
function asAtomAuthor()
{
$xs = new XMLStringer(true);
@ -767,11 +775,33 @@ class Profile extends Memcached_DataObject
return $xs->getString();
}
/**
* Returns an XML string fragment with profile information as an
* Activity Streams <activity:actor> element.
*
* Assumes that 'activity' namespace has been previously defined.
*
* @return string
*/
function asActivityActor()
{
return $this->asActivityNoun('actor');
}
/**
* Returns an XML string fragment with profile information as an
* Activity Streams noun object with the given element type.
*
* Assumes that 'activity' namespace has been previously defined.
*
* @param string $element one of 'actor', 'subject', 'object', 'target'
* @return string
*/
function asActivityNoun($element)
{
$xs = new XMLStringer(true);
$xs->elementStart('activity:actor');
$xs->elementStart('activity:' . $element);
$xs->element(
'activity:object-type',
null,
@ -799,7 +829,7 @@ class Profile extends Memcached_DataObject
''
);
$xs->elementEnd('activity:actor');
$xs->elementEnd('activity:' . $element);
return $xs->getString();
}

View File

@ -299,6 +299,71 @@ class Ostatus_profile extends Memcached_DataObject
}
}
/**
* Returns an XML string fragment with profile information as an
* Activity Streams noun object with the given element type.
*
* Assumes that 'activity' namespace has been previously defined.
*
* @param string $element one of 'actor', 'subject', 'object', 'target'
* @return string
*/
function asActivityNoun($element)
{
$xs = new XMLStringer(true);
$avatarHref = Avatar::defaultImage(AVATAR_PROFILE_SIZE);
$avatarType = 'image/png';
if ($this->isGroup()) {
$type = 'http://activitystrea.ms/schema/1.0/group';
$self = $this->localGroup();
// @fixme put a standard getAvatar() interface on groups too
if ($self->homepage_logo) {
$avatarHref = $self->homepage_logo;
$map = array('png' => 'image/png',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif');
$extension = pathinfo(parse_url($avatarHref, PHP_URL_PATH), PATHINFO_EXTENSION);
if (isset($map[$extension])) {
$avatarType = $map[$extension];
}
}
} else {
$type = 'http://activitystrea.ms/schema/1.0/person';
$self = $this->localProfile();
$avatar = $self->getAvatar(AVATAR_PROFILE_SIZE);
if ($avatar) {
$avatarHref = $avatar->
$avatarType = $avatar->mediatype;
}
}
$xs->elementStart('activity:' . $element);
$xs->element(
'activity:object-type',
null,
$type
);
$xs->element(
'id',
null,
$this->homeuri); // ?
$xs->element('title', null, $self->getBestName());
$xs->element(
'link', array(
'type' => $avatarType,
'href' => $avatarHref
),
''
);
$xs->elementEnd('activity:' . $element);
return $xs->getString();
}
/**
* Damn dirty hack!
*/
@ -397,7 +462,7 @@ class Ostatus_profile extends Memcached_DataObject
}
/**
* Send an unsubscription request to the hub for this feed.
* Send a PuSH unsubscription request to the hub for this feed.
* The hub will later send us a confirmation POST to /main/push/callback.
*
* @return bool true on success, false on failure
@ -406,6 +471,92 @@ class Ostatus_profile extends Memcached_DataObject
return $this->subscribe('unsubscribe');
}
/**
* Send an Activity Streams notification to the remote Salmon endpoint,
* if so configured.
*
* @param Profile $actor
* @param $verb eg Activity::SUBSCRIBE or Activity::JOIN
* @param $object object of the action; if null, the remote entity itself is assumed
*/
public function notify(Profile $actor, $verb, $object=null)
{
if ($object == null) {
$object = $this;
}
if ($this->salmonuri) {
$text = 'update'; // @fixme
$id = 'tag:' . common_config('site', 'server') .
':' . $verb .
':' . $actor->id .
':' . time(); // @fixme
$entry = new Atom10Entry();
$entry->elementStart('entry');
$entry->element('id', null, $id);
$entry->element('title', null, $text);
$entry->element('summary', null, $text);
$entry->element('published', null, common_date_w3dtf());
$entry->element('activity:verb', null, $verb);
$entry->raw($profile->asAtomAuthor());
$entry->raw($profile->asActivityActor());
$entry->raw($object->asActivityNoun('object'));
$entry->elmentEnd('entry');
$feed = $this->atomFeed($actor);
$feed->initFeed();
$feed->addEntry($entry);
$feed->renderEntries();
$feed->endFeed();
$xml = $feed->getString();
common_log(LOG_INFO, "Posting to Salmon endpoint $salmon: $xml");
$salmon = new Salmon(); // ?
$salmon->post($this->salmonuri, $xml);
}
}
function getBestName()
{
if ($this->isGroup()) {
return $this->localGroup()->getBestName();
} else {
return $this->localProfile()->getBestName();
}
}
function atomFeed($actor)
{
$feed = new Atom10Feed();
// @fixme should these be set up somewhere else?
$feed->addNamespace('activity', 'http://activitystrea.ms/spec/1.0/');
$feed->addNamesapce('thr', 'http://purl.org/syndication/thread/1.0');
$feed->addNamespace('georss', 'http://www.georss.org/georss');
$feed->addNamespace('ostatus', 'http://ostatus.org/schema/1.0');
$taguribase = common_config('integration', 'taguri');
$feed->setId("tag:{$taguribase}:UserTimeline:{$actor->id}"); // ???
$feed->setTitle($actor->getBestName() . ' timeline'); // @fixme
$feed->setUpdated(time());
$feed->setPublished(time());
$feed->addLink(common_url('ApiTimelineUser',
array('id' => $actor->id,
'type' => 'atom')),
array('rel' => 'self',
'type' => 'application/atom+xml'));
$feed->addLink(common_url('userbyid',
array('id' => $actor->id)),
array('rel' => 'alternate',
'type' => 'text/html'));
return $feed;
}
/**
* Read and post notices for updates from the feed.
* Currently assumes that all items in the feed are new,