X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Factivityobject.php;h=87eea13727e3261c8829a8459dad19b42113caab;hb=a0b9aeb43ef1b08e8dd7fe25101c515a0df53e7f;hp=13592ad32b240fb6f52362f6712519b3cf1bb8fd;hpb=cba2b1ad9cd5b2ca04eb8d31b4f4bff92f77037b;p=quix0rs-gnu-social.git diff --git a/lib/activityobject.php b/lib/activityobject.php index 13592ad32b..87eea13727 100644 --- a/lib/activityobject.php +++ b/lib/activityobject.php @@ -32,6 +32,8 @@ if (!defined('STATUSNET')) { exit(1); } +require_once(INSTALLDIR.'/lib/activitystreamjsondocument.php'); + /** * A noun-ish thing in the activity universe * @@ -70,6 +72,9 @@ class ActivityObject // ^^^^^^^^^^ tea! const ACTIVITY = 'http://activitystrea.ms/schema/1.0/activity'; const SERVICE = 'http://activitystrea.ms/schema/1.0/service'; + const IMAGE = 'http://activitystrea.ms/schema/1.0/image'; + const COLLECTION = 'http://activitystrea.ms/schema/1.0/collection'; + const APPLICATION = 'http://activitystrea.ms/schema/1.0/application'; // Atom elements we snarf @@ -111,6 +116,8 @@ class ActivityObject public $description; public $extra = array(); + public $stream; + /** * Constructor * @@ -188,15 +195,22 @@ class ActivityObject $this->type = self::PERSON; // XXX: is this fair? } - // start with - $title = ActivityUtils::childHtmlContent($element, self::TITLE); + // Start with + + $this->title = ActivityUtils::childContent($element, PoCo::DISPLAYNAME, PoCo::NS); - if (!empty($title)) { - $this->title = html_entity_decode(strip_tags($title), ENT_QUOTES, 'UTF-8'); + // try falling back to + + if (empty($this->title)) { + $title = ActivityUtils::childHtmlContent($element, self::TITLE); + + if (!empty($title)) { + $this->title = common_strip_html($title); + } } - // fall back to + // fall back to as a last resort if (empty($this->title)) { $this->title = $this->_childContent($element, self::NAME); @@ -244,10 +258,7 @@ class ActivityObject $this->content = ActivityUtils::getContent($element); // We don't like HTML in our titles, although it's technically allowed - - $title = ActivityUtils::childHtmlContent($element, self::TITLE); - - $this->title = html_entity_decode(strip_tags($title), ENT_QUOTES, 'UTF-8'); + $this->title = common_strip_html(ActivityUtils::childHtmlContent($element, self::TITLE)); $this->source = $this->_getSource($element); @@ -263,6 +274,10 @@ class ActivityObject // @todo FIXME: rationalize with Activity::_fromRssItem() private function _fromRssItem($item) { + if (empty($this->type)) { + $this->type = ActivityObject::NOTE; + } + $this->title = ActivityUtils::childContent($item, ActivityObject::TITLE, Activity::RSS); $contentEl = ActivityUtils::child($item, ActivityUtils::CONTENT, Activity::CONTENTNS); @@ -283,7 +298,7 @@ class ActivityObject if (!empty($guidEl)) { $this->id = $guidEl->textContent; - if ($guidEl->hasAttribute('isPermaLink')) { + if ($guidEl->hasAttribute('isPermaLink') && $guidEl->getAttribute('isPermaLink') != 'false') { // overwrites $this->link = $this->id; } @@ -424,129 +439,157 @@ class ActivityObject } } - static function fromNotice(Notice $notice) + static function fromGroup(User_group $group) { $object = new ActivityObject(); - if (Event::handle('StartActivityObjectFromNotice', array($notice, &$object))) { + if (Event::handle('StartActivityObjectFromGroup', array($group, &$object))) { + + $object->type = ActivityObject::GROUP; + $object->id = $group->getUri(); + $object->title = $group->getBestName(); + $object->link = $group->getUri(); + + $object->avatarLinks[] = AvatarLink::fromFilename($group->homepage_logo, + AVATAR_PROFILE_SIZE); - $object->type = (empty($notice->object_type)) ? ActivityObject::NOTE : $notice->object_type; + $object->avatarLinks[] = AvatarLink::fromFilename($group->stream_logo, + AVATAR_STREAM_SIZE); - $object->id = $notice->uri; - $object->title = $notice->content; - $object->content = $notice->rendered; - $object->link = $notice->bestUrl(); + $object->avatarLinks[] = AvatarLink::fromFilename($group->mini_logo, + AVATAR_MINI_SIZE); - Event::handle('EndActivityObjectFromNotice', array($notice, &$object)); + $object->poco = PoCo::fromGroup($group); + Event::handle('EndActivityObjectFromGroup', array($group, &$object)); } return $object; } - static function fromProfile(Profile $profile) + static function fromPeopletag($ptag) { $object = new ActivityObject(); + if (Event::handle('StartActivityObjectFromPeopletag', array($ptag, &$object))) { + $object->type = ActivityObject::_LIST; - if (Event::handle('StartActivityObjectFromProfile', array($profile, &$object))) { - $object->type = ActivityObject::PERSON; - $object->id = $profile->getUri(); - $object->title = $profile->getBestName(); - $object->link = $profile->profileurl; - - $orig = $profile->getOriginalAvatar(); + $object->id = $ptag->getUri(); + $object->title = $ptag->tag; + $object->summary = $ptag->description; + $object->link = $ptag->homeUrl(); + $object->owner = Profile::getKV('id', $ptag->tagger); + $object->poco = PoCo::fromProfile($object->owner); + Event::handle('EndActivityObjectFromPeopletag', array($ptag, &$object)); + } + return $object; + } - if (!empty($orig)) { - $object->avatarLinks[] = AvatarLink::fromAvatar($orig); - } + static function fromFile(File $file) + { + $object = new ActivityObject(); - $sizes = array( - AVATAR_PROFILE_SIZE, - AVATAR_STREAM_SIZE, - AVATAR_MINI_SIZE - ); + if (Event::handle('StartActivityObjectFromFile', array($file, &$object))) { - foreach ($sizes as $size) { - $alink = null; - $avatar = $profile->getAvatar($size); + $object->type = self::mimeTypeToObjectType($file->mimetype); + $object->id = TagURI::mint(sprintf("file:%d", $file->id)); + $object->link = common_local_url('attachment', array('attachment' => $file->id)); - if (!empty($avatar)) { - $alink = AvatarLink::fromAvatar($avatar); - } else { - $alink = new AvatarLink(); - $alink->type = 'image/png'; - $alink->height = $size; - $alink->width = $size; - $alink->url = Avatar::defaultImage($size); - - if ($size == AVATAR_PROFILE_SIZE) { - // Hack for Twitter import: we don't have a 96x96 image, - // but we do have a 73x73 image. For now, fake it with that. - $avatar = $profile->getAvatar(73); - if ($avatar) { - $alink = AvatarLink::fromAvatar($avatar); - $alink->height= $size; - $alink->width = $size; - } - } - } + if ($file->title) { + $object->title = $file->title; + } - $object->avatarLinks[] = $alink; + if ($file->date) { + $object->date = $file->date; } - if (isset($profile->lat) && isset($profile->lon)) { - $object->geopoint = (float)$profile->lat - . ' ' . (float)$profile->lon; + try { + $thumbnail = $file->getThumbnail(); + $object->thumbnail = $thumbnail; + } catch (UseFileAsThumbnailException $e) { + $object->thumbnail = null; + } catch (UnsupportedMediaException $e) { + $object->thumbnail = null; } - $object->poco = PoCo::fromProfile($profile); + switch (self::canonicalType($object->type)) { + case 'image': + $object->largerImage = $file->getUrl(); + break; + case 'video': + case 'audio': + $object->stream = $file->getUrl(); + break; + } - Event::handle('EndActivityObjectFromProfile', array($profile, &$object)); + Event::handle('EndActivityObjectFromFile', array($file, &$object)); } return $object; } - static function fromGroup($group) + static function fromNoticeSource(Notice_source $source) { $object = new ActivityObject(); + $wellKnown = array('web', 'xmpp', 'mail', 'omb', 'system', 'api', 'ostatus', + 'activity', 'feed', 'mirror', 'twitter', 'facebook'); + + if (Event::handle('StartActivityObjectFromNoticeSource', array($source, &$object))) { + $object->type = ActivityObject::APPLICATION; + + if (in_array($source->code, $wellKnown)) { + // We use one ID for all well-known StatusNet sources + $object->id = "tag:status.net,2009:notice-source:".$source->code; + } else if ($source->url) { + // They registered with an URL + $object->id = $source->url; + } else { + // Locally-registered, no URL + $object->id = TagURI::mint("notice-source:".$source->code); + } - if (Event::handle('StartActivityObjectFromGroup', array($group, &$object))) { - - $object->type = ActivityObject::GROUP; - $object->id = $group->getUri(); - $object->title = $group->getBestName(); - $object->link = $group->getUri(); - - $object->avatarLinks[] = AvatarLink::fromFilename($group->homepage_logo, - AVATAR_PROFILE_SIZE); + if ($source->url) { + $object->link = $source->url; + } - $object->avatarLinks[] = AvatarLink::fromFilename($group->stream_logo, - AVATAR_STREAM_SIZE); + if ($source->name) { + $object->title = $source->name; + } else { + $object->title = $source->code; + } - $object->avatarLinks[] = AvatarLink::fromFilename($group->mini_logo, - AVATAR_MINI_SIZE); + if ($source->created) { + $object->date = $source->created; + } + + $object->extra[] = array('status_net', array('source_code' => $source->code)); - $object->poco = PoCo::fromGroup($group); - Event::handle('EndActivityObjectFromGroup', array($group, &$object)); + Event::handle('EndActivityObjectFromNoticeSource', array($source, &$object)); } return $object; } - static function fromPeopletag($ptag) + static function fromMessage(Message $message) { $object = new ActivityObject(); - if (Event::handle('StartActivityObjectFromPeopletag', array($ptag, &$object))) { - $object->type = ActivityObject::_LIST; - $object->id = $ptag->getUri(); - $object->title = $ptag->tag; - $object->summary = $ptag->description; - $object->link = $ptag->homeUrl(); - $object->owner = Profile::staticGet('id', $ptag->tagger); - $object->poco = PoCo::fromProfile($object->owner); - Event::handle('EndActivityObjectFromPeopletag', array($ptag, &$object)); + if (Event::handle('StartActivityObjectFromMessage', array($message, &$object))) { + + $object->type = ActivityObject::NOTE; + $object->id = ($message->uri) ? $message->uri : (($message->url) ? $message->url : TagURI::mint(sprintf("message:%d", $message->id))); + $object->content = $message->rendered; + $object->date = $message->created; + + if ($message->url) { + $object->link = $message->url; + } else { + $object->link = common_local_url('showmessage', array('message' => $message->id)); + } + + $object->extra[] = array('status_net', array('message_id' => $message->id)); + + Event::handle('EndActivityObjectFromMessage', array($message, &$object)); } + return $object; } @@ -616,17 +659,16 @@ class ActivityObject if ($this->type == ActivityObject::PERSON || $this->type == ActivityObject::GROUP) { - foreach ($this->avatarLinks as $avatar) { - $xo->element( - 'link', array( - 'rel' => 'avatar', - 'type' => $avatar->type, - 'media:width' => $avatar->width, - 'media:height' => $avatar->height, - 'href' => $avatar->url - ), - null - ); + foreach ($this->avatarLinks as $alink) { + $xo->element('link', + array( + 'rel' => 'avatar', + 'type' => $alink->type, + 'media:width' => $alink->width, + 'media:height' => $alink->height, + 'href' => $alink->url, + ), + null); } } @@ -645,7 +687,7 @@ class ActivityObject // @fixme there's no way here to make a tree; elements can only contain plaintext // @fixme these may collide with JSON extensions foreach ($this->extra as $el) { - list($extraTag, $attrs, $content) = $el; + list($extraTag, $attrs, $content) = array_pad($el, 3, null); $xo->element($extraTag, $attrs, $content); } @@ -686,17 +728,22 @@ class ActivityObject // content (Add rendered version of the notice?) - // displayName - $object['displayName'] = $this->title; - // downstreamDuplicates // id - $object['id'] = $this->id; + + if ($this->id) { + $object['id'] = $this->id; + } else if ($this->link) { + $object['id'] = $this->link; + } if ($this->type == ActivityObject::PERSON || $this->type == ActivityObject::GROUP) { + // displayName + $object['displayName'] = $this->title; + // XXX: Not sure what the best avatar is to use for the // author's "image". For now, I'm using the large size. @@ -722,7 +769,11 @@ class ActivityObject $avatarMediaLinks[] = $avatar->asArray(); } - $object['avatarLinks'] = $avatarMediaLinks; // extension + if (!array_key_exists('status_net', $object)) { + $object['status_net'] = array(); + } + + $object['status_net']['avatarLinks'] = $avatarMediaLinks; // extension // image if (!empty($imgLink)) { @@ -734,13 +785,13 @@ class ActivityObject // // We can probably use the whole schema URL here but probably the // relative simple name is easier to parse - // @fixme this breaks extension URIs - $object['objectType'] = substr($this->type, strrpos($this->type, '/') + 1); + + $object['objectType'] = self::canonicalType($this->type); // summary $object['summary'] = $this->summary; - // summary + // content, usually rendered HTML $object['content'] = $this->content; // published (probably don't need. Might be useful for repeats.) @@ -749,42 +800,135 @@ class ActivityObject // TODO: upstreamDuplicates - // url (XXX: need to put the right thing here...) - $object['url'] = $this->id; + if ($this->link) { + $object['url'] = $this->link; + } /* Extensions */ // @fixme these may collide with XML extensions // @fixme multiple tags of same name will overwrite each other // @fixme text content from XML extensions will be lost + foreach ($this->extra as $e) { - list($objectName, $props, $txt) = $e; - $object[$objectName] = $props; + list($objectName, $props, $txt) = array_pad($e, 3, null); + if (!empty($objectName)) { + $parts = explode(":", $objectName); + if (count($parts) == 2 && $parts[0] == "statusnet") { + if (!array_key_exists('status_net', $object)) { + $object['status_net'] = array(); + } + $object['status_net'][$parts[1]] = $props; + } else { + $object[$objectName] = $props; + } + } } if (!empty($this->geopoint)) { - list($lat, $long) = explode(' ', $this->geopoint); + list($lat, $lon) = explode(' ', $this->geopoint); - $object['geopoint'] = array( - 'type' => 'Point', - 'coordinates' => array($lat, $long) - ); + if (!empty($lat) && !empty($lon)) { + $object['location'] = array( + 'objectType' => 'place', + 'position' => sprintf("%+02.5F%+03.5F/", $lat, $lon), + 'lat' => $lat, + 'lon' => $lon + ); + + $loc = Location::fromLatLon((float)$lat, (float)$lon); + + if ($loc) { + $name = $loc->getName(); + + if ($name) { + $object['location']['displayName'] = $name; + } + $url = $loc->getURL(); + + if ($url) { + $object['location']['url'] = $url; + } + } + } } if (!empty($this->poco)) { - $object['contact'] = array_filter($this->poco->asArray()); + $object['portablecontacts_net'] = array_filter($this->poco->asArray()); + } + + if (!empty($this->thumbnail)) { + if (is_string($this->thumbnail)) { + $object['image'] = array('url' => $this->thumbnail); + } else { + $object['image'] = array('url' => $this->thumbnail->getUrl()); + if ($this->thumbnail->width) { + $object['image']['width'] = $this->thumbnail->width; + } + if ($this->thumbnail->height) { + $object['image']['height'] = $this->thumbnail->height; + } + } } + + switch (self::canonicalType($this->type)) { + case 'image': + if (!empty($this->largerImage)) { + $object['fullImage'] = array('url' => $this->largerImage); + } + break; + case 'audio': + case 'video': + if (!empty($this->stream)) { + $object['stream'] = array('url' => $this->stream); + } + break; + } + Event::handle('EndActivityObjectOutputJson', array($this, &$object)); } return array_filter($object); } + public function getIdentifiers() { + $ids = array(); + foreach(array('id', 'link', 'url') as $id) { + if (isset($this->$id)) { + $ids[] = $this->$id; + } + } + return array_unique($ids); + } + static function canonicalType($type) { - $ns = 'http://activitystrea.ms/schema/1.0/'; - if (substr($type, 0, mb_strlen($ns)) == $ns) { - return substr($type, mb_strlen($ns)); - } else { - return $type; + return ActivityUtils::resolveUri($type, true); + } + + static function mimeTypeToObjectType($mimeType) { + $ot = null; + + // Default + + if (empty($mimeType)) { + return self::FILE; + } + + $parts = explode('/', $mimeType); + + switch ($parts[0]) { + case 'image': + $ot = self::IMAGE; + break; + case 'audio': + $ot = self::AUDIO; + break; + case 'video': + $ot = self::VIDEO; + break; + default: + $ot = self::FILE; } + + return $ot; } }