@ -0,0 +1,179 @@ | |||||
<?php | |||||
namespace p3k\XRay\Formats; | |||||
use DateTime; | |||||
use \p3k\XRay\PostType; | |||||
class ActivityStreams extends Format { | |||||
public static function is_as2_json($document) { | |||||
if(is_array($document) && isset($document['@context'])) { | |||||
if(is_string($document['@context']) && $document['@context'] == 'https://www.w3.org/ns/activitystreams') | |||||
return true; | |||||
if(is_array($document['@context']) && in_array('https://www.w3.org/ns/activitystreams', $document['@context'])) | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
public static function matches_host($url) { | |||||
return true; | |||||
} | |||||
public static function matches($url) { | |||||
return true; | |||||
} | |||||
public static function parse($as2, $url, $http, $opts=[]) { | |||||
if(!isset($as2['type'])) | |||||
return false; | |||||
switch($as2['type']) { | |||||
case 'Person': | |||||
return self::parseAsHCard($as2, $url, $http, $opts); | |||||
case 'Note': | |||||
return self::parseAsHEntry($as2, $url, $http, $opts); | |||||
} | |||||
$result = [ | |||||
'data' => [ | |||||
'type' => 'unknown', | |||||
], | |||||
'url' => $url, | |||||
]; | |||||
return $result; | |||||
} | |||||
private static function parseAsHEntry($as2, $url, $http, $opts) { | |||||
$data = [ | |||||
'type' => 'entry' | |||||
]; | |||||
$refs = []; | |||||
if(isset($as2['url'])) | |||||
$data['url'] = $as2['url']; | |||||
elseif(isset($as2['id'])) | |||||
$data['url'] = $as2['id']; | |||||
if(isset($as2['published'])) { | |||||
try { | |||||
$date = new DateTime($as2['published']); | |||||
$data['published'] = $date->format('c'); | |||||
} catch(\Exception $e){} | |||||
} | |||||
if(isset($as2['content'])) { | |||||
$html = trim(self::sanitizeHTML($as2['content'])); | |||||
$text = trim(self::stripHTML($html)); | |||||
$data['content'] = [ | |||||
'text' => $text | |||||
]; | |||||
if($html && $text && $text != $html) { | |||||
$data['content']['html'] = $html; | |||||
} | |||||
} | |||||
if(isset($as2['tag']) && is_array($as2['tag'])) { | |||||
$emoji = []; | |||||
$category = []; | |||||
foreach($as2['tag'] as $tag) { | |||||
if(is_array($tag) && isset($tag['name']) && isset($tag['type']) && $tag['type'] == 'Hashtag') | |||||
$category[] = trim($tag['name'], '#'); | |||||
if(is_array($tag) && isset($tag['type']) && $tag['type'] == 'Emoji' && isset($tag['icon']['url'])) { | |||||
$emoji[$tag['name']] = $tag['icon']['url']; | |||||
} | |||||
} | |||||
if(count($category)) | |||||
$data['category'] = $category; | |||||
if(count($emoji) && isset($data['content']['html'])) { | |||||
foreach($emoji as $code=>$img) { | |||||
$data['content']['html'] = str_replace($code, '<img src="'.$img.'" alt="'.$code.'" title="'.$code.'" height="24" class="xray-custom-emoji">', $data['content']['html']); | |||||
} | |||||
} | |||||
} | |||||
if(isset($as2['inReplyTo'])) { | |||||
$data['in-reply-to'] = [$as2['inReplyTo']]; | |||||
} | |||||
// Photos and Videos | |||||
if(isset($as2['attachment'])) { | |||||
$photos = []; | |||||
$videos = []; | |||||
foreach($as2['attachment'] as $attachment) { | |||||
if(strpos($attachment['mediaType'], 'image/') !== false) { | |||||
$photos[] = $attachment['url']; | |||||
} | |||||
if(strpos($attachment['mediaType'], 'video/') !== false) { | |||||
$videos[] = $attachment['url']; | |||||
} | |||||
} | |||||
if(count($photos)) | |||||
$data['photo'] = $photos; | |||||
if(count($videos)) | |||||
$data['video'] = $videos; | |||||
} | |||||
// Fetch the author info, which requires an HTTP request | |||||
if(isset($as2['attributedTo']) && is_string($as2['attributedTo'])) { | |||||
$authorResponse = $http->get($as2['attributedTo'], ['Accept: application/activity+json,application/json']); | |||||
if($authorResponse && !empty($authorResponse['body'])) { | |||||
$authorProfile = json_decode($authorResponse['body'], true); | |||||
$author = self::parseAsHCard($authorProfile, $as2['attributedTo'], $http, $opts); | |||||
if($author && !empty($author['data'])) | |||||
$data['author'] = $author['data']; | |||||
} | |||||
} | |||||
$data['post-type'] = PostType::discover($data); | |||||
$response = [ | |||||
'data' => $data, | |||||
]; | |||||
if(count($refs)) { | |||||
$response['data']['refs'] = $refs; | |||||
} | |||||
return $response; | |||||
} | |||||
private static function parseAsHCard($as2, $url, $http, $opts) { | |||||
$data = [ | |||||
'type' => 'card', | |||||
'name' => null, | |||||
'url' => null, | |||||
'photo' => null | |||||
]; | |||||
if(!empty($as2['name'])) | |||||
$data['name'] = $as2['name']; | |||||
elseif(isset($as2['preferredUsername'])) | |||||
$data['name'] = $as2['preferredUsername']; | |||||
if(isset($as2['preferredUsername'])) | |||||
$data['nickname'] = $as2['preferredUsername']; | |||||
if(isset($as2['url'])) | |||||
$data['url'] = $as2['url']; | |||||
if(isset($as2['icon']) && isset($as2['icon']['url'])) | |||||
$data['photo'] = $as2['icon']['url']; | |||||
// TODO: featured image for h-cards? | |||||
// if(isset($as2['image']) && isset($as2['image']['url'])) | |||||
// $data['featured'] = $as2['image']['url']; | |||||
$response = [ | |||||
'data' => $data | |||||
]; | |||||
return $response; | |||||
} | |||||
} |
@ -0,0 +1,142 @@ | |||||
<?php | |||||
use Symfony\Component\HttpFoundation\Request; | |||||
use Symfony\Component\HttpFoundation\Response; | |||||
class ActivityStreamsTest extends PHPUnit_Framework_TestCase { | |||||
private $http; | |||||
public function setUp() { | |||||
$this->client = new Parse(); | |||||
$this->client->http = new p3k\HTTP\Test(dirname(__FILE__).'/data/'); | |||||
$this->client->mc = null; | |||||
} | |||||
private function parse($params) { | |||||
$request = new Request($params); | |||||
$response = new Response(); | |||||
return $this->client->parse($request, $response); | |||||
} | |||||
public function testAuthorProfile() { | |||||
$url = 'http://activitystreams.example/aaronpk'; | |||||
$response = $this->parse(['url' => $url]); | |||||
$body = $response->getContent(); | |||||
$this->assertEquals(200, $response->getStatusCode()); | |||||
$data = json_decode($body, true); | |||||
$this->assertEquals('activity+json', $data['source-format']); | |||||
$this->assertEquals('card', $data['data']['type']); | |||||
$this->assertEquals('aaronpk', $data['data']['name']); | |||||
$this->assertEquals('https://aaronparecki.com/images/profile.jpg', $data['data']['photo']); | |||||
$this->assertEquals('https://aaronparecki.com/', $data['data']['url']); | |||||
} | |||||
public function testNoteWithTags() { | |||||
$url = 'http://activitystreams.example/note.json'; | |||||
$response = $this->parse(['url' => $url]); | |||||
$body = $response->getContent(); | |||||
$this->assertEquals(200, $response->getStatusCode()); | |||||
$data = json_decode($body, true); | |||||
$this->assertEquals('activity+json', $data['source-format']); | |||||
$this->assertEquals('note', $data['data']['post-type']); | |||||
$this->assertEquals($url, $data['data']['url']); | |||||
$this->assertEquals('2018-07-12T13:02:04-07:00', $data['data']['published']); | |||||
$this->assertEquals('This is the text content of an ActivityStreams note', $data['data']['content']['text']); | |||||
$this->assertArrayNotHasKey('html', $data['data']['content']); | |||||
$this->assertSame(['activitystreams'], $data['data']['category']); | |||||
$this->assertEquals('aaronpk', $data['data']['author']['name']); | |||||
$this->assertEquals('https://aaronparecki.com/images/profile.jpg', $data['data']['author']['photo']); | |||||
$this->assertEquals('https://aaronparecki.com/', $data['data']['author']['url']); | |||||
} | |||||
public function testPhoto() { | |||||
$url = 'http://activitystreams.example/photo.json'; | |||||
$response = $this->parse(['url' => $url]); | |||||
$body = $response->getContent(); | |||||
$this->assertEquals(200, $response->getStatusCode()); | |||||
$data = json_decode($body, true); | |||||
$this->assertEquals('activity+json', $data['source-format']); | |||||
$this->assertEquals($url, $data['data']['url']); | |||||
$this->assertEquals('photo', $data['data']['post-type']); | |||||
$this->assertEquals('2018-07-12T13:02:04-07:00', $data['data']['published']); | |||||
$this->assertEquals('This is the text content of an ActivityStreams photo', $data['data']['content']['text']); | |||||
$this->assertArrayNotHasKey('html', $data['data']['content']); | |||||
$this->assertSame(['activitystreams'], $data['data']['category']); | |||||
$this->assertSame(['https://aaronparecki.com/2018/06/28/26/photo.jpg'], $data['data']['photo']); | |||||
} | |||||
public function testVideo() { | |||||
$url = 'http://activitystreams.example/video.json'; | |||||
$response = $this->parse(['url' => $url]); | |||||
$body = $response->getContent(); | |||||
$this->assertEquals(200, $response->getStatusCode()); | |||||
$data = json_decode($body, true); | |||||
$this->assertEquals('activity+json', $data['source-format']); | |||||
$this->assertEquals('video', $data['data']['post-type']); | |||||
$this->assertEquals('2018-07-12T13:02:04-07:00', $data['data']['published']); | |||||
$this->assertSame(['https://aaronparecki.com/2018/07/21/19/video.mp4'], $data['data']['video']); | |||||
} | |||||
public function testReply() { | |||||
$url = 'http://activitystreams.example/reply.json'; | |||||
$response = $this->parse(['url' => $url]); | |||||
$body = $response->getContent(); | |||||
$this->assertEquals(200, $response->getStatusCode()); | |||||
$data = json_decode($body, true); | |||||
$this->assertEquals('activity+json', $data['source-format']); | |||||
$this->assertEquals('reply', $data['data']['post-type']); | |||||
$this->assertEquals('2018-07-12T13:02:04-07:00', $data['data']['published']); | |||||
$this->assertArrayNotHasKey('category', $data['data']); // should not include the person-tag | |||||
// For now, don't fetch the reply context | |||||
$this->assertEquals(['http://activitystreams.example/note.json'], $data['data']['in-reply-to']); | |||||
} | |||||
public function testCustomEmoji() { | |||||
$url = 'http://activitystreams.example/custom-emoji.json'; | |||||
$response = $this->parse(['url' => $url]); | |||||
$body = $response->getContent(); | |||||
$this->assertEquals(200, $response->getStatusCode()); | |||||
$data = json_decode($body, true); | |||||
$this->assertEquals('activity+json', $data['source-format']); | |||||
$this->assertEquals('note', $data['data']['post-type']); | |||||
$this->assertEquals("https://mastodon.social/@Gargron/100465999501820229", $data['data']['url']); | |||||
$this->assertEquals('2018-07-30T22:24:54+00:00', $data['data']['published']); | |||||
$this->assertEquals(':yikes:', $data['data']['content']['text']); | |||||
$this->assertEquals('<p><img src="https://files.mastodon.social/custom_emojis/images/000/031/275/original/yikes.png" alt=":yikes:" title=":yikes:" height="24" class="xray-custom-emoji"></p>', $data['data']['content']['html']); | |||||
$this->assertEquals('Eugen', $data['data']['author']['name']); | |||||
$this->assertEquals('Gargron', $data['data']['author']['nickname']); | |||||
$this->assertEquals('https://files.mastodon.social/accounts/avatars/000/000/001/original/eb9e00274b135808.png', $data['data']['author']['photo']); | |||||
$this->assertEquals('https://mastodon.social/@Gargron', $data['data']['author']['url']); | |||||
} | |||||
public function testRelAlternatePriority() { | |||||
$url = 'http://source.example.com/rel-alternate-as2'; | |||||
$response = $this->parse(['url' => $url]); | |||||
$body = $response->getContent(); | |||||
$this->assertEquals(200, $response->getStatusCode()); | |||||
$data = json_decode($body, true); | |||||
$this->assertEquals('activity+json', $data['source-format']); | |||||
$this->assertEquals('http://activitystreams.example/note.json', $data['parsed-url']); | |||||
$this->assertEquals('http://source.example.com/rel-alternate-as2', $data['url']); | |||||
$this->assertEquals('note', $data['data']['post-type']); | |||||
$this->assertEquals('2018-07-12T13:02:04-07:00', $data['data']['published']); | |||||
$this->assertEquals('This is the text content of an ActivityStreams note', $data['data']['content']['text']); | |||||
$this->assertArrayNotHasKey('html', $data['data']['content']); | |||||
$this->assertSame(['activitystreams'], $data['data']['category']); | |||||
} | |||||
} |
@ -0,0 +1,7 @@ | |||||
HTTP/1.1 200 OK | |||||
Server: Apache | |||||
Date: Wed, 30 Jul 2018 03:29:14 GMT | |||||
Content-Type: application/activity+json | |||||
Connection: keep-alive | |||||
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":{"@id":"as:movedTo","@type":"@id"},"Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji","focalPoint":{"@container":"@list","@id":"toot:focalPoint"},"featured":{"@id":"toot:featured","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value"}],"id":"https://mastodon.social/users/Gargron","type":"Person","following":"https://mastodon.social/users/Gargron/following","followers":"https://mastodon.social/users/Gargron/followers","inbox":"https://mastodon.social/users/Gargron/inbox","outbox":"https://mastodon.social/users/Gargron/outbox","featured":"https://mastodon.social/users/Gargron/collections/featured","preferredUsername":"Gargron","name":"Eugen","summary":"\u003cp\u003eDeveloper of Mastodon. 25\u003c/p\u003e","url":"https://mastodon.social/@Gargron","manuallyApprovesFollowers":false,"publicKey":{"id":"https://mastodon.social/users/Gargron#main-key","owner":"https://mastodon.social/users/Gargron","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n"},"tag":[],"attachment":[{"type":"PropertyValue","name":"Patreon","value":"\u003ca href=\"https://www.patreon.com/mastodon\" rel=\"me nofollow noopener\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003epatreon.com/mastodon\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"},{"type":"PropertyValue","name":"E-mail","value":"eugen@zeonfederated.com"}],"endpoints":{"sharedInbox":"https://mastodon.social/inbox"},"icon":{"type":"Image","mediaType":"image/png","url":"https://files.mastodon.social/accounts/avatars/000/000/001/original/eb9e00274b135808.png"},"image":{"type":"Image","mediaType":"image/jpeg","url":"https://files.mastodon.social/accounts/headers/000/000/001/original/998815725e9554b0.jpg"}} |
@ -0,0 +1,33 @@ | |||||
HTTP/1.1 200 OK | |||||
Server: Apache | |||||
Date: Wed, 30 Jul 2018 03:29:14 GMT | |||||
Content-Type: application/activity+json | |||||
Connection: keep-alive | |||||
{ | |||||
"@context": [ | |||||
"https://www.w3.org/ns/activitystreams", | |||||
"https://w3id.org/security/v1" | |||||
], | |||||
"id": "https://aaronparecki.com/aaronpk", | |||||
"type": "Person", | |||||
"preferredUsername": "aaronpk", | |||||
"url": "https://aaronparecki.com/", | |||||
"icon": { | |||||
"type": "Image", | |||||
"mediaType": "image/jpeg", | |||||
"url": "https://aaronparecki.com/images/profile.jpg" | |||||
}, | |||||
"image": { | |||||
"type": "Image", | |||||
"mediaType": "image/jpeg", | |||||
"url": "https://aaronparecki.com/images/cover-photo.jpg" | |||||
}, | |||||
"inbox": "https://aaronparecki.com/activitypub/inbox", | |||||
"outbox": "https://aaronparecki.com/activitypub/outbox", | |||||
"publicKey": { | |||||
"id": "https://aaronparecki.com/aaronpk#key", | |||||
"owner": "https://aaronparecki.com/aaronpk", | |||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzlRsHgaSwaoB/AOLxjsU\nBDGDZJileQyg2ZmEhWBMLaqNMJH0EqnraQRHfnHz7Yp32zjYCj2NObaX7ySd3uUJ\nzoEyhmfx122WQupOPclGYBC7mbXXhiheDb3ItPn9TxCOveC8d5d2Bm6vyCq4m31x\nBH0jOImL3AscLNwhEdYIHvweXuIqaat50O6yrgJUadJBvw0hyPVFvwiak1dKA2Su\nHCxsgLpxasEoByJMNy1COG8AvR+SuSvwJXZ2DeDS98Ji9EbeaKl6F2mJGJC/Fe2q\nz0t3mVll8Zs4bdnraw6pcnmFNzOv0SziunJoKjQ5IgZDmWHQY4EgEybxa9SEOZ/s\ntQIDAQAB\n-----END PUBLIC KEY-----\n" | |||||
} | |||||
} |
@ -0,0 +1,7 @@ | |||||
HTTP/1.1 200 OK | |||||
Server: Apache | |||||
Date: Wed, 30 Jul 2018 03:29:14 GMT | |||||
Content-Type: application/activity+json | |||||
Connection: keep-alive | |||||
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":{"@id":"as:movedTo","@type":"@id"},"Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji","focalPoint":{"@container":"@list","@id":"toot:focalPoint"},"featured":{"@id":"toot:featured","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value"}],"id":"https://mastodon.social/users/Gargron/statuses/100465999501820229","type":"Note","summary":null,"inReplyTo":null,"published":"2018-07-30T22:24:54Z","url":"https://mastodon.social/@Gargron/100465999501820229","attributedTo":"https://activitystreams.example/Gargron","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://mastodon.social/users/Gargron/followers"],"sensitive":false,"atomUri":"https://mastodon.social/users/Gargron/statuses/100465999501820229","inReplyToAtomUri":null,"conversation":"tag:mastodon.social,2018-07-30:objectId=44299858:objectType=Conversation","content":"\u003cp\u003e:yikes:\u003c/p\u003e","contentMap":{"en":"\u003cp\u003e:yikes:\u003c/p\u003e"},"attachment":[],"tag":[{"id":"https://mastodon.social/emojis/31275","type":"Emoji","name":":yikes:","updated":"2018-07-15T17:28:20Z","icon":{"type":"Image","mediaType":"image/png","url":"https://files.mastodon.social/custom_emojis/images/000/031/275/original/yikes.png"}}]} |
@ -0,0 +1,24 @@ | |||||
HTTP/1.1 200 OK | |||||
Server: Apache | |||||
Date: Wed, 30 Jul 2018 03:29:14 GMT | |||||
Content-Type: application/activity+json | |||||
Connection: keep-alive | |||||
{ | |||||
"@context": "https://www.w3.org/ns/activitystreams", | |||||
"id": "http://activitystreams.example/note.json", | |||||
"type": "Note", | |||||
"published": "2018-07-12T13:02:04-07:00", | |||||
"attributedTo": "https://activitystreams.example/aaronpk", | |||||
"content": "This is the text content of an ActivityStreams note", | |||||
"to": [ | |||||
"https://www.w3.org/ns/activitystreams#Public" | |||||
], | |||||
"tag": [ | |||||
{ | |||||
"id": "https://aaronparecki.com/tag/activitystreams", | |||||
"name": "#activitystreams", | |||||
"type": "Hashtag" | |||||
} | |||||
] | |||||
} |
@ -0,0 +1,32 @@ | |||||
HTTP/1.1 200 OK | |||||
Server: Apache | |||||
Date: Wed, 30 Jul 2018 03:29:14 GMT | |||||
Content-Type: application/activity+json | |||||
Connection: keep-alive | |||||
{ | |||||
"@context": "https://www.w3.org/ns/activitystreams", | |||||
"id": "http://activitystreams.example/photo.json", | |||||
"type": "Note", | |||||
"published": "2018-07-12T13:02:04-07:00", | |||||
"attributedTo": "https://activitystreams.example/aaronpk", | |||||
"content": "This is the text content of an ActivityStreams photo", | |||||
"to": [ | |||||
"https://www.w3.org/ns/activitystreams#Public" | |||||
], | |||||
"tag": [ | |||||
{ | |||||
"type": "Hashtag", | |||||
"id": "https://aaronparecki.com/tag/activitystreams", | |||||
"name": "#activitystreams" | |||||
} | |||||
], | |||||
"attachment": [ | |||||
{ | |||||
"type": "Image", | |||||
"mediaType": "image/jpeg", | |||||
"url": "https://aaronparecki.com/2018/06/28/26/photo.jpg", | |||||
"name": null | |||||
} | |||||
] | |||||
} |
@ -0,0 +1,25 @@ | |||||
HTTP/1.1 200 OK | |||||
Server: Apache | |||||
Date: Wed, 30 Jul 2018 03:29:14 GMT | |||||
Content-Type: application/activity+json | |||||
Connection: keep-alive | |||||
{ | |||||
"@context": "https://www.w3.org/ns/activitystreams", | |||||
"id": "http://activitystreams.example/reply.json", | |||||
"type": "Note", | |||||
"published": "2018-07-12T13:02:04-07:00", | |||||
"attributedTo": "https://activitystreams.example/aaronpk", | |||||
"content": "<a href=\"http://activitystreams.example/aaronpk\">@aaronpk</a> This is a reply", | |||||
"inReplyTo": "http://activitystreams.example/note.json", | |||||
"to": [ | |||||
"https://www.w3.org/ns/activitystreams#Public" | |||||
], | |||||
"tag": [ | |||||
{ | |||||
"type": "Mention", | |||||
"href": "http://activitystreams.example/aaronpk", | |||||
"name": "@aaronpk@activitystreams.example" | |||||
} | |||||
] | |||||
} |
@ -0,0 +1,31 @@ | |||||
HTTP/1.1 200 OK | |||||
Server: Apache | |||||
Date: Wed, 30 Jul 2018 03:29:14 GMT | |||||
Content-Type: application/activity+json | |||||
Connection: keep-alive | |||||
{ | |||||
"@context": "https://www.w3.org/ns/activitystreams", | |||||
"id": "http://activitystreams.example/video.json", | |||||
"type": "Note", | |||||
"published": "2018-07-12T13:02:04-07:00", | |||||
"attributedTo": "https://activitystreams.example/aaronpk", | |||||
"content": "This is the text content of an ActivityStreams photo", | |||||
"to": [ | |||||
"https://www.w3.org/ns/activitystreams#Public" | |||||
], | |||||
"tag": [ | |||||
{ | |||||
"id": "https://aaronparecki.com/tag/activitystreams", | |||||
"name": "#activitystreams" | |||||
} | |||||
], | |||||
"attachment": [ | |||||
{ | |||||
"type": "Document", | |||||
"mediaType": "video/mp4", | |||||
"url": "https://aaronparecki.com/2018/07/21/19/video.mp4", | |||||
"name": null | |||||
} | |||||
] | |||||
} |
@ -0,0 +1,19 @@ | |||||
HTTP/1.1 200 OK | |||||
Server: Apache | |||||
Date: Wed, 09 Dec 2015 03:29:14 GMT | |||||
Content-Type: text/html | |||||
Connection: keep-alive | |||||
<!doctype html> | |||||
<html> | |||||
<head> | |||||
<meta charset="utf-8"> | |||||
<title>Test</title> | |||||
<link rel="alternate" type="application/activity+json" href="http://activitystreams.example/note.json" /> | |||||
</head> | |||||
<body> | |||||
<div class="h-entry"> | |||||
<p class="p-content">This should not be the content from XRay</p> | |||||
</div> | |||||
</body> | |||||
</html> |
@ -0,0 +1,20 @@ | |||||
HTTP/1.1 200 OK | |||||
Server: Apache | |||||
Date: Wed, 09 Dec 2015 03:29:14 GMT | |||||
Content-Type: text/html | |||||
Connection: keep-alive | |||||
<!doctype html> | |||||
<html> | |||||
<head> | |||||
<meta charset="utf-8"> | |||||
<title>Test</title> | |||||
<link rel="alternate" type="application/activity+json" href="http://activitystreams.example/note.json" /> | |||||
<link rel="alternate" type="application/mf2+json" href="http://source.example.com/rel-alternate-priority.json" /> | |||||
</head> | |||||
<body> | |||||
<div class="h-entry"> | |||||
<p class="p-content">This should not be the content from XRay</p> | |||||
</div> | |||||
</body> | |||||
</html> |