X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Factivity.php;h=e352baf63956880d7e8fc21c1b58736ffaaec42b;hb=0b53b6768e03932f4beec6b6655763e6ecedc36d;hp=802d09304a7c3750e8296d3cad062373fae08b24;hpb=963e7576f21ba8e2adbec9b60ec52fb4d44b008e;p=quix0rs-gnu-social.git diff --git a/lib/activity.php b/lib/activity.php index 802d09304a..e352baf639 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -135,6 +135,9 @@ class Activity } else if ($entry->namespaceURI == Activity::RSS && $entry->localName == 'item') { $this->_fromRssItem($entry, $feed); + } else if ($entry->namespaceURI == Activity::SPEC && + $entry->localName == 'object') { + $this->_fromAtomEntry($entry, $feed); } else { // Low level exception. No need for i18n. throw new Exception("Unknown DOM element: {$entry->namespaceURI} {$entry->localName}"); @@ -168,14 +171,22 @@ class Activity // XXX: do other implied stuff here } - $objectEls = $entry->getElementsByTagNameNS(self::SPEC, self::OBJECT); + // get immediate object children + + $objectEls = ActivityUtils::children($entry, self::OBJECT, self::SPEC); - if ($objectEls->length > 0) { - for ($i = 0; $i < $objectEls->length; $i++) { - $objectEl = $objectEls->item($i); - $this->objects[] = new ActivityObject($objectEl); + if (count($objectEls) > 0) { + foreach ($objectEls as $objectEl) { + // Special case for embedded activities + $objectType = ActivityUtils::childContent($objectEl, self::OBJECTTYPE, self::SPEC); + if (!empty($objectType) && $objectType == ActivityObject::ACTIVITY) { + $this->objects[] = new Activity($objectEl); + } else { + $this->objects[] = new ActivityObject($objectEl); + } } } else { + // XXX: really? $this->objects[] = new ActivityObject($entry); } @@ -205,18 +216,19 @@ class Activity // the surrounding feed. $this->actor = new ActivityObject($authorEl); - } else if (!empty($feed) && $authorEl = $this->_child($feed, self::AUTHOR, - self::ATOM)) { - - // If there's no on the entry, it's safe to assume - // the containing feed's authorship info applies. - $this->actor = new ActivityObject($authorEl); } else if (!empty($feed) && $subjectEl = $this->_child($feed, self::SUBJECT)) { // Feed subject is used for things like groups. // Should actually possibly not be interpreted as an actor...? $this->actor = new ActivityObject($subjectEl); + + } else if (!empty($feed) && $authorEl = $this->_child($feed, self::AUTHOR, + self::ATOM)) { + + // If there's no on the entry, it's safe to assume + // the containing feed's authorship info applies. + $this->actor = new ActivityObject($authorEl); } $contextEl = $this->_child($entry, self::CONTEXT); @@ -336,6 +348,192 @@ class Activity return null; } + /** + * Returns an array based on this activity suitable + * for encoding as a JSON object + * + * @return array $activity + */ + + function asArray() + { + $activity = array(); + + // actor + $activity['actor'] = $this->actor->asArray(); + + // content + $activity['content'] = $this->content; + + // generator <-- We could use this when we know a notice is created + // locally. Or if we know the upstream Generator. + + // icon <-- possibly a mini object representing verb? + + // id + $activity['id'] = $this->id; + + // object + if ($this->verb == ActivityVerb::POST && count($this->objects) == 1) { + $activity['object'] = $this->objects[0]->asArray(); + + // Context stuff. For now I'm just sticking most of it + // in a property called "context" + + if (!empty($this->context)) { + + if (!empty($this->context->location)) { + $loc = $this->context->location; + + // GeoJSON + + $activity['geopoint'] = array( + 'type' => 'Point', + 'coordinates' => array($loc->lat, $loc->lon), + 'deprecated' => true, + ); + + $activity['location'] = array( + 'objectType' => 'place', + 'position' => sprintf("%+02.5F%+03.5F/", $loc->lat, $loc->lon), + 'lat' => $loc->lat, + 'lon' => $loc->lon + ); + + $name = $loc->getName(); + + if ($name) { + $activity['location']['displayName'] = $name; + } + + $url = $loc->getURL(); + + if ($url) { + $activity['location']['url'] = $url; + } + } + + $activity['to'] = $this->context->getToArray(); + $activity['context'] = $this->context->asArray(); + } + + // Instead of adding enclosures as an extension to JSON + // Activities, it seems like we should be using the + // attachements property of ActivityObject + + $attachments = array(); + + // XXX: OK, this is kinda cheating. We should probably figure out + // what kind of objects these are based on mime-type and then + // create specific object types. Right now this rely on + // duck-typing. Also, we should include an embed code for + // video attachments. + + foreach ($this->enclosures as $enclosure) { + + if (is_string($enclosure)) { + + $attachments[]['id'] = $enclosure; + + } else { + + $attachments[]['id'] = $enclosure->url; + + $mediaLink = new ActivityStreamsMediaLink( + $enclosure->url, + null, + null, + $enclosure->mimetype + // XXX: Add 'size' as an extension to MediaLink? + ); + + $attachments[]['mediaLink'] = $mediaLink->asArray(); // extension + + if ($enclosure->title) { + $attachments[]['displayName'] = $enclosure->title; + } + } + } + + if (!empty($attachments)) { + $activity['object']['attachments'] = $attachments; + } + + } else { + $activity['object'] = array(); + foreach($this->objects as $object) { + $oa = $object->asArray(); + if ($object instanceof Activity) { + // throw in a type + // XXX: hackety-hack + $oa['objectType'] = 'activity'; + } + $activity['object'][] = $oa; + } + } + + // published + $activity['published'] = self::iso8601Date($this->time); + + // provider + $provider = array( + 'objectType' => 'service', + 'displayName' => common_config('site', 'name'), + 'url' => common_root_url() + ); + + $activity['provider'] = $provider; + + // target + if (!empty($this->target)) { + $activity['target'] = $this->target->asArray(); + } + + // title + $activity['title'] = $this->title; + + // updated <-- Optional. Should we use this to indicate the time we r + // eceived a remote notice? Probably not. + + // verb + // + // We can probably use the whole schema URL here but probably the + // relative simple name is easier to parse + $activity['verb'] = substr($this->verb, strrpos($this->verb, '/') + 1); + + // url + $activity['url'] = $this->id; + + /* Purely extensions hereafter */ + + if ($activity['verb'] == 'post') { + $tags = array(); + foreach ($this->categories as $cat) { + if (mb_strlen($cat->term) > 0) { + // Couldn't figure out which object type to use, so... + $tags[] = array('objectType' => 'http://activityschema.org/object/hashtag', + 'displayName' => $cat->term); + } + } + if (count($tags) > 0) { + $activity['object']['tags'] = $tags; + } + } + + // XXX: a bit of a hack... Since JSON isn't namespaced we probably + // shouldn't be using 'statusnet:notice_info', but this will work + // for the moment. + + foreach ($this->extra as $e) { + list($objectName, $props, $txt) = $e; + if (!empty($objectName)) { + $activity[$objectName] = $props; + } + } + + return array_filter($activity); + } + function asString($namespace=false, $author=true, $source=false) { $xs = new XMLStringer(true); @@ -343,7 +541,7 @@ class Activity return $xs->getString(); } - function outputTo($xs, $namespace=false, $author=true, $source=false) + function outputTo($xs, $namespace=false, $author=true, $source=false, $tag='entry') { if ($namespace) { $attrs = array('xmlns' => 'http://www.w3.org/2005/Atom', @@ -358,9 +556,13 @@ class Activity $attrs = array(); } - $xs->elementStart('entry', $attrs); + $xs->elementStart($tag, $attrs); - if ($this->verb == ActivityVerb::POST && count($this->objects) == 1) { + if ($tag != 'entry') { + $xs->element('activity:object-type', null, ActivityObject::ACTIVITY); + } + + if ($this->verb == ActivityVerb::POST && count($this->objects) == 1 && $tag == 'entry') { $obj = $this->objects[0]; $obj->outputTo($xs, null); @@ -370,11 +572,11 @@ class Activity $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'), @@ -386,17 +588,21 @@ class Activity $xs->element('activity:verb', null, $this->verb); $published = self::iso8601Date($this->time); - + $xs->element('published', null, $published); $xs->element('updated', null, $published); - + if ($author) { $this->actor->outputTo($xs, 'author'); } - if ($this->verb != ActivityVerb::POST || count($this->objects) != 1) { + if ($this->verb != ActivityVerb::POST || count($this->objects) != 1 || $tag != 'entry') { foreach($this->objects as $object) { - $object->outputTo($xs, 'activity:object'); + if ($object instanceof Activity) { + $object->outputTo($xs, false, true, true, 'activity:object'); + } else { + $object->outputTo($xs, 'activity:object'); + } } } @@ -458,7 +664,7 @@ class Activity } // can be either URLs or enclosure objects - + foreach ($this->enclosures as $enclosure) { if (is_string($enclosure)) { $xs->element('link', array('rel' => 'enclosure', @@ -479,7 +685,7 @@ class Activity if ($source && !empty($this->source)) { $xs->elementStart('source'); - + $xs->element('id', null, $this->source->id); $xs->element('title', null, $this->source->title); @@ -488,7 +694,7 @@ class Activity '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', @@ -507,7 +713,7 @@ class Activity if (!empty($this->source->updated)) { $xs->element('updated', null, $this->source->updated); } - + $xs->elementEnd('source'); } @@ -524,13 +730,13 @@ class Activity } // 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'); + $xs->elementEnd($tag); return; } @@ -540,11 +746,18 @@ class Activity return ActivityUtils::child($element, $tag, $namespace); } + /** + * For consistency, we'll always output UTC rather than local time. + * Note that clients *should* accept any timezone we give them as long + * as it's properly formatted. + * + * @param int $tm Unix timestamp + * @return string + */ 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'); } }