diff options
Diffstat (limited to 'plugins/OStatus/lib')
-rw-r--r-- | plugins/OStatus/lib/activity.php | 330 |
1 files changed, 319 insertions, 11 deletions
diff --git a/plugins/OStatus/lib/activity.php b/plugins/OStatus/lib/activity.php index 36e227913..048efda2c 100644 --- a/plugins/OStatus/lib/activity.php +++ b/plugins/OStatus/lib/activity.php @@ -31,7 +31,75 @@ if (!defined('STATUSNET')) { exit(1); } -class ActivityNoun +/** + * Utilities for turning DOMish things into Activityish things + * + * Some common functions that I didn't have the bandwidth to try to factor + * into some kind of reasonable superclass, so just dumped here. Might + * be useful to have an ActivityObject parent class or something. + * + * @category OStatus + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +class ActivityUtils +{ + const ATOM = 'http://www.w3.org/2005/Atom'; + + const LINK = 'link'; + const REL = 'rel'; + const TYPE = 'type'; + const HREF = 'href'; + + /** + * Get the permalink for an Activity object + * + * @param DOMElement $element A DOM element + * + * @return string related link, if any + */ + + static function getLink($element) + { + $links = $element->getElementsByTagnameNS(self::ATOM, self::LINK); + + foreach ($links as $link) { + + $rel = $link->getAttribute(self::REL); + $type = $link->getAttribute(self::TYPE); + + if ($rel == 'alternate' && $type == 'text/html') { + return $link->getAttribute(self::HREF); + } + } + + return null; + } +} + +/** + * A noun-ish thing in the activity universe + * + * The activity streams spec talks about activity objects, while also having + * a tag activity:object, which is in fact an activity object. Aaaaaah! + * + * This is just a thing in the activity universe. Can be the subject, object, + * or indirect object (target!) of an activity verb. Rotten name, and I'm + * propagating it. *sigh* + * + * @category OStatus + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +class ActivityObject { const ARTICLE = 'http://activitystrea.ms/schema/1.0/article'; const BLOGENTRY = 'http://activitystrea.ms/schema/1.0/blog-entry'; @@ -47,19 +115,114 @@ class ActivityNoun const PERSON = 'http://activitystrea.ms/schema/1.0/person'; const GROUP = 'http://activitystrea.ms/schema/1.0/group'; const PLACE = 'http://activitystrea.ms/schema/1.0/place'; - const COMMENT = 'http://activitystrea.ms/schema/1.0/comment'; // tea + const COMMENT = 'http://activitystrea.ms/schema/1.0/comment'; + // ^^^^^^^^^^ tea! + + // Atom elements we snarf + + const TITLE = 'title'; + const SUMMARY = 'summary'; + const CONTENT = 'content'; + const ID = 'id'; + const SOURCE = 'source'; + + const NAME = 'name'; + const URI = 'uri'; + const EMAIL = 'email'; public $type; public $id; public $title; public $summary; public $content; + public $link; + public $source; + + /** + * Constructor + * + * This probably needs to be refactored + * to generate a local class (ActivityPerson, ActivityFile, ...) + * based on the object type. + * + * @param DOMElement $element DOM thing to turn into an Activity thing + */ + + function __construct($element) + { + $this->source = $element; + + if ($element->tagName == 'author') { + + $this->type = self::PERSON; // XXX: is this fair? + $this->title = $this->_childContent($element, self::NAME); + $this->id = $this->_childContent($element, self::URI); + + if (empty($this->id)) { + $email = $this->_childContent($element, self::EMAIL); + if (!empty($email)) { + // XXX: acct: ? + $this->id = 'mailto:'.$email; + } + } + + } else { + + $this->type = $this->_childContent($element, Activity::OBJECTTYPE, + Activity::SPEC); + + if (empty($this->type)) { + $this->type = ActivityObject::NOTE; + } + + $this->id = $this->_childContent($element, self::ID); + $this->title = $this->_childContent($element, self::TITLE); + $this->summary = $this->_childContent($element, self::SUMMARY); + $this->content = $this->_childContent($element, self::CONTENT); + $this->source = $this->_childContent($element, self::SOURCE); + + $this->link = ActivityUtils::getLink($element); + + // XXX: grab PoCo stuff + } + } + + /** + * Grab the text content of a DOM element child of the current element + * + * @param DOMElement $element Element whose children we examine + * @param string $tag Tag to look up + * @param string $namespace Namespace to use, defaults to Atom + * + * @return string content of the child + */ + + private function _childContent($element, $tag, $namespace=Activity::ATOM) + { + $els = $element->getElementsByTagnameNS($namespace, $tag); + + if (empty($els) || $els->length == 0) { + return null; + } else { + $el = $els->item(0); + return $el->textContent; + } + } } -class Activity -{ - const NAMESPACE = 'http://activitystrea.ms/schema/1.0/'; +/** + * Utility class to hold a bunch of constant defining default verb types + * + * @category OStatus + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ +class ActivityVerb +{ const POST = 'http://activitystrea.ms/schema/1.0/post'; const SHARE = 'http://activitystrea.ms/schema/1.0/share'; const SAVE = 'http://activitystrea.ms/schema/1.0/save'; @@ -69,17 +232,162 @@ class Activity const FRIEND = 'http://activitystrea.ms/schema/1.0/make-friend'; const JOIN = 'http://activitystrea.ms/schema/1.0/join'; const TAG = 'http://activitystrea.ms/schema/1.0/tag'; +} + +/** + * An activity in the ActivityStrea.ms world + * + * An activity is kind of like a sentence: someone did something + * to something else. + * + * 'someone' is the 'actor'; 'did something' is the verb; + * 'something else' is the object. + * + * @category OStatus + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +class Activity +{ + const SPEC = 'http://activitystrea.ms/spec/1.0/'; + const SCHEMA = 'http://activitystrea.ms/schema/1.0/'; + + const VERB = 'verb'; + const OBJECT = 'object'; + const ACTOR = 'actor'; + const SUBJECT = 'subject'; + const OBJECTTYPE = 'object-type'; + const CONTEXT = 'context'; + const TARGET = 'target'; + + const ATOM = 'http://www.w3.org/2005/Atom'; + + const AUTHOR = 'author'; + const PUBLISHED = 'published'; + const UPDATED = 'updated'; - public $actor; // an ActivityNoun - public $verb; // a string (the URL) - public $object; // an ActivityNoun - public $target; // an ActivityNoun + public $actor; // an ActivityObject + public $verb; // a string (the URL) + public $object; // an ActivityObject + public $target; // an ActivityObject + public $context; // an ActivityObject + public $time; // Time of the activity + public $link; // an ActivityObject + public $entry; // the source entry + public $feed; // the source feed - static function fromAtomEntry($domEntry) + /** + * Turns a regular old Atom <entry> into a magical activity + * + * @param DOMElement $entry Atom entry to poke at + * @param DOMElement $feed Atom feed, for context + */ + + function __construct($entry, $feed = null) { + $this->entry = $entry; + $this->feed = $feed; + + $pubEl = $this->_child($entry, self::PUBLISHED, self::ATOM); + + if (!empty($pubEl)) { + $this->time = strtotime($pubEl->textContent); + } else { + // XXX technically an error; being liberal. Good idea...? + $updateEl = $this->_child($entry, self::UPDATED, self::ATOM); + if (!empty($updateEl)) { + $this->time = strtotime($updateEl->textContent); + } else { + $this->time = null; + } + } + + $this->link = ActivityUtils::getLink($entry); + + $verbEl = $this->_child($entry, self::VERB); + + if (!empty($verbEl)) { + $this->verb = trim($verbEl->textContent); + } else { + $this->verb = ActivityVerb::POST; + // XXX: do other implied stuff here + } + + $objectEl = $this->_child($entry, self::OBJECT); + + if (!empty($objectEl)) { + $this->object = new ActivityObject($objectEl); + } else { + $this->object = new ActivityObject($entry); + } + + $actorEl = $this->_child($entry, self::ACTOR); + + if (!empty($actorEl)) { + + $this->actor = new ActivityObject($actorEl); + + } else if (!empty($feed) && + $subjectEl = $this->_child($feed, self::SUBJECT)) { + + $this->actor = new ActivityObject($subjectEl); + + } else if ($authorEl = $this->_child($entry, self::AUTHOR, self::ATOM)) { + + $this->actor = new ActivityObject($authorEl); + + } else if (!empty($feed) && $authorEl = $this->_child($feed, self::AUTHOR, + self::ATOM)) { + + $this->actor = new ActivityObject($authorEl); + } + + $contextEl = $this->_child($entry, self::CONTEXT); + + if (!empty($contextEl)) { + $this->context = new ActivityObject($contextEl); + } + + $targetEl = $this->_child($entry, self::TARGET); + + if (!empty($targetEl)) { + $this->target = new ActivityObject($targetEl); + } } + /** + * Returns an Atom <entry> based on this activity + * + * @return DOMElement Atom entry + */ + function toAtomEntry() { + return null; } -} + + /** + * Gets the first child element with the given tag + * + * @param DOMElement $element element to pick at + * @param string $tag tag to look for + * @param string $namespace Namespace to look under + * + * @return DOMElement found element or null + */ + + private function _child($element, $tag, $namespace=self::SPEC) + { + $els = $element->getElementsByTagnameNS($namespace, $tag); + + if (empty($els) || $els->length == 0) { + return null; + } else { + return $els->item(0); + } + } +}
\ No newline at end of file |