diff --git a/app/Http/Controllers/NotesController.php b/app/Http/Controllers/NotesController.php
index 93fecc23..dbf5adc7 100644
--- a/app/Http/Controllers/NotesController.php
+++ b/app/Http/Controllers/NotesController.php
@@ -5,26 +5,31 @@ namespace App\Http\Controllers;
use App\Note;
use Illuminate\Http\Request;
use Jonnybarnes\IndieWeb\Numbers;
+use App\Services\ActivityStreamsService;
// Need to sort out Twitter and webmentions!
class NotesController extends Controller
{
/**
- * Show all the notes.
+ * Show all the notes. This is also the homepage.
*
- * @param Illuminate\Http\Request request;
* @return \Illuminte\View\Factory view
*/
- public function index(Request $request)
+ public function index()
{
+ if (request()->wantsActivityStream()) {
+ return (new ActivityStreamsService)->siteOwnerResponse();
+ }
+
$notes = Note::orderBy('id', 'desc')
->with('place', 'media', 'client')
->withCount(['webmentions As replies' => function ($query) {
$query->where('type', 'in-reply-to');
}])->paginate(10);
+ $aslink = config('app.url');
- return view('notes.index', compact('notes'));
+ return view('notes.index', compact('notes', 'aslink'));
}
/**
@@ -37,7 +42,13 @@ class NotesController extends Controller
{
$note = Note::nb60($urlId)->with('webmentions')->firstOrFail();
- return view('notes.show', compact('note'));
+ if (request()->wantsActivityStream()) {
+ return (new ActivityStreamsService)->singleNoteResponse($note);
+ }
+
+ $aslink = $note->longurl;
+
+ return view('notes.show', compact('note', 'aslink'));
}
/**
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 88d47aec..a79e464a 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -5,6 +5,7 @@ namespace App\Providers;
use App\Tag;
use App\Note;
use Validator;
+use Illuminate\Http\Request;
use Laravel\Dusk\DuskServiceProvider;
use Illuminate\Support\ServiceProvider;
@@ -42,10 +43,15 @@ class AppServiceProvider extends ServiceProvider
$tag = Tag::firstOrCreate(['tag' => $tag]);
$tagsToAdd[] = $tag->id;
}
- if (count($tagsToAdd > 0)) {
+ if (count($tagsToAdd) > 0) {
$note->tags()->attach($tagsToAdd);
}
});
+
+ // Request AS macro
+ Request::macro('wantsActivityStream', function() {
+ return str_contains(mb_strtolower($this->header('Accept')), 'application/activity+json');
+ });
}
/**
diff --git a/app/Services/ActivityStreamsService.php b/app/Services/ActivityStreamsService.php
new file mode 100644
index 00000000..6d948ea0
--- /dev/null
+++ b/app/Services/ActivityStreamsService.php
@@ -0,0 +1,50 @@
+ 'https://www.w3.org/ns/activitystreams',
+ 'id' => config('app.url'),
+ 'type' => 'Person',
+ 'name' => config('app.display_name'),
+ 'preferredUsername' => 'jonnybarnes',
+ ]);
+
+ return response($data)->header('Content-Type', 'application/activity+json');
+ }
+
+ public function singleNoteResponse(Note $note)
+ {
+ $data = json_encode([
+ '@context' => 'https://www.w3.org/ns/activitystreams',
+ 'summary' => 'Jonny added a note to his microblog',
+ 'type' => 'Add',
+ 'published' => $note->updated_at->toW3cString(),
+ 'actor' => [
+ 'type' => 'Person',
+ 'id' => config('app.url'),
+ 'name' => config('app.display_name'),
+ 'url' => config('app.url'),
+ 'image' => [
+ 'type' => 'Link',
+ 'href' => config('app.url') . '/assets/img/jmb-bw.jpg',
+ 'mediaType' => '/image/jpeg',
+ ],
+ ],
+ 'object' => [
+ 'id' => $note->longurl,
+ 'type' => 'Note',
+ 'url' => $note->longurl,
+ 'name' => strip_tags($note->note)
+ ],
+ ]);
+
+ return response($data)->header('Content-Type', 'application/activity+json');
+ }
+}
diff --git a/app/Services/NoteService.php b/app/Services/NoteService.php
index e2b91773..a858089f 100644
--- a/app/Services/NoteService.php
+++ b/app/Services/NoteService.php
@@ -37,7 +37,7 @@ class NoteService
);
if (array_key_exists('published', $data) && empty($data['published']) === false) {
- $carbon = new \Carbon\Carbon($data['published']);
+ $carbon = carbon($data['published']);
$note->created_at = $note->updated_at = $carbon->toDateTimeString();
}
diff --git a/app/WebMention.php b/app/WebMention.php
index 3c1ee3a7..5e3d570f 100644
--- a/app/WebMention.php
+++ b/app/WebMention.php
@@ -5,7 +5,6 @@ namespace App;
use Cache;
use Twitter;
use HTMLPurifier;
-use Carbon\Carbon;
use HTMLPurifier_Config;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Database\Eloquent\Model;
@@ -63,10 +62,9 @@ class WebMention extends Model
public function getPublishedAttribute()
{
$microformats = json_decode($this->mf2, true);
- $carbon = new Carbon();
if (isset($microformats['items'][0]['properties']['published'][0])) {
try {
- $published = $carbon->parse(
+ $published = carbon()->parse(
$microformats['items'][0]['properties']['published'][0]
)->toDayDateTimeString();
} catch (\Exception $exception) {
diff --git a/changelog.md b/changelog.md
index 14f965c0..d631dbdb 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,8 @@
# Changelog
+## Version 0.7.1 (2017-09-13)
+ - Add content-negotiated AS data for homepage and single notes
+
## Version 0.7 (2017-09-08)
- Add Laravel Horizon
diff --git a/composer.json b/composer.json
index 78c96fd5..cd32e17b 100644
--- a/composer.json
+++ b/composer.json
@@ -32,6 +32,7 @@
},
"require-dev": {
"barryvdh/laravel-debugbar": "~3.0",
+ "bmitch/churn-php": "^0.2.0",
"filp/whoops": "~2.0",
"fzaninotto/faker": "~1.4",
"jakub-onderka/php-parallel-lint": "^0.9.2",
diff --git a/composer.lock b/composer.lock
index 224a0680..23c97c51 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "551d407b87a7d9ff3e65969f33605014",
+ "content-hash": "ee80722747b7215eddfb5da75063b542",
"packages": [
{
"name": "aws/aws-sdk-php",
- "version": "3.36.3",
+ "version": "3.36.4",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "7ffaee4359c161339867e565f18f6e3b7e77e44e"
+ "reference": "acb08da60a042e17b0cd9e5e111ba895c8a79f2d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7ffaee4359c161339867e565f18f6e3b7e77e44e",
- "reference": "7ffaee4359c161339867e565f18f6e3b7e77e44e",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/acb08da60a042e17b0cd9e5e111ba895c8a79f2d",
+ "reference": "acb08da60a042e17b0cd9e5e111ba895c8a79f2d",
"shasum": ""
},
"require": {
@@ -84,7 +84,7 @@
"s3",
"sdk"
],
- "time": "2017-09-07T22:30:02+00:00"
+ "time": "2017-09-08T19:50:29+00:00"
},
{
"name": "barnabywalters/mf-cleaner",
@@ -303,16 +303,16 @@
},
{
"name": "composer/ca-bundle",
- "version": "1.0.7",
+ "version": "1.0.8",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
- "reference": "b17e6153cb7f33c7e44eb59578dc12eee5dc8e12"
+ "reference": "9dd73a03951357922d8aee6cc084500de93e2343"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/ca-bundle/zipball/b17e6153cb7f33c7e44eb59578dc12eee5dc8e12",
- "reference": "b17e6153cb7f33c7e44eb59578dc12eee5dc8e12",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/9dd73a03951357922d8aee6cc084500de93e2343",
+ "reference": "9dd73a03951357922d8aee6cc084500de93e2343",
"shasum": ""
},
"require": {
@@ -358,7 +358,7 @@
"ssl",
"tls"
],
- "time": "2017-03-06T11:59:08+00:00"
+ "time": "2017-09-11T07:24:36+00:00"
},
{
"name": "cviebrock/eloquent-sluggable",
@@ -1947,16 +1947,16 @@
},
{
"name": "laravel/horizon",
- "version": "v1.0.1",
+ "version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/horizon.git",
- "reference": "856fe55c6d054dc063e71d97c0873013803d96df"
+ "reference": "ec52cd6c304eb9dfc3320e32d9e43c5ee55e7d7c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/horizon/zipball/856fe55c6d054dc063e71d97c0873013803d96df",
- "reference": "856fe55c6d054dc063e71d97c0873013803d96df",
+ "url": "https://api.github.com/repos/laravel/horizon/zipball/ec52cd6c304eb9dfc3320e32d9e43c5ee55e7d7c",
+ "reference": "ec52cd6c304eb9dfc3320e32d9e43c5ee55e7d7c",
"shasum": ""
},
"require": {
@@ -2011,7 +2011,7 @@
"laravel",
"queue"
],
- "time": "2017-09-06T19:57:45+00:00"
+ "time": "2017-09-08T16:30:09+00:00"
},
{
"name": "laravel/scout",
@@ -4400,6 +4400,67 @@
],
"time": "2017-08-29T08:54:25+00:00"
},
+ {
+ "name": "bmitch/churn-php",
+ "version": "0.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bmitch/churn-php.git",
+ "reference": "0026a7db3bebb83dc9b1cd1941e1cb15629c1dc8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bmitch/churn-php/zipball/0026a7db3bebb83dc9b1cd1941e1cb15629c1dc8",
+ "reference": "0026a7db3bebb83dc9b1cd1941e1cb15629c1dc8",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0",
+ "symfony/console": "^3.2",
+ "symfony/process": "^3.2",
+ "symfony/yaml": "^3.3",
+ "tightenco/collect": "^5.4"
+ },
+ "require-dev": {
+ "bmitch/codor": "^1.0",
+ "jakub-onderka/php-console-highlighter": "^0.3.2",
+ "jakub-onderka/php-parallel-lint": "^0.9.2",
+ "larapack/dd": "^1.1",
+ "mockery/mockery": "dev-master",
+ "phploc/phploc": "^3.0",
+ "phpmd/phpmd": "^2.5",
+ "phpunit/phpunit": "^5.7",
+ "sebastian/phpcpd": "^2.0",
+ "sensiolabs/security-checker": "^4.0",
+ "slevomat/coding-standard": "^2.0"
+ },
+ "bin": [
+ "churn"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Churn\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bill Mitchell",
+ "email": "wkmitch@gmail.com"
+ }
+ ],
+ "description": "Discover files in need of refactoring.",
+ "homepage": "https://github.com/bmitch/churn-php",
+ "keywords": [
+ "bmitch",
+ "churn-php"
+ ],
+ "time": "2017-09-02T00:42:45+00:00"
+ },
{
"name": "doctrine/instantiator",
"version": "1.1.0",
@@ -6290,6 +6351,61 @@
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2016-10-03T07:35:21+00:00"
},
+ {
+ "name": "symfony/yaml",
+ "version": "v3.3.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/1d8c2a99c80862bdc3af94c1781bf70f86bccac0",
+ "reference": "1d8c2a99c80862bdc3af94c1781bf70f86bccac0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.5.9|>=7.0.8"
+ },
+ "require-dev": {
+ "symfony/console": "~2.8|~3.0"
+ },
+ "suggest": {
+ "symfony/console": "For validating YAML files using the lint command"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Yaml Component",
+ "homepage": "https://symfony.com",
+ "time": "2017-07-29T21:54:42+00:00"
+ },
{
"name": "theseer/fdomdocument",
"version": "1.6.6",
diff --git a/helpers.php b/helpers.php
index d70efc56..7e5a0852 100644
--- a/helpers.php
+++ b/helpers.php
@@ -16,6 +16,7 @@ if (! function_exists('normalize_url')) {
$newUrl = '';
$url = parse_url($url);
$defaultSchemes = ['http' => 80, 'https' => 443];
+
if (isset($url['scheme'])) {
$url['scheme'] = strtolower($url['scheme']);
// Strip scheme default ports
@@ -27,10 +28,12 @@ if (! function_exists('normalize_url')) {
}
$newUrl .= "{$url['scheme']}://";
}
+
if (isset($url['host'])) {
$url['host'] = mb_strtolower($url['host']);
$newUrl .= $url['host'];
}
+
if (isset($url['port'])) {
$newUrl .= ":{$url['port']}";
}
@@ -63,10 +66,14 @@ if (! function_exists('normalize_url')) {
}
$url['path'] = preg_replace_callback(
array_map(
- create_function('$str', 'return "/%" . strtoupper($str) . "/x";'),
+ function ($str) {
+ return "/%" . strtoupper($str) . "/x";
+ },
$u
),
- create_function('$matches', 'return chr(hexdec($matches[0]));'),
+ function ($matches) {
+ return chr(hexdec($matches[0]));
+ },
$url['path']
);
// Remove directory index
@@ -197,3 +204,10 @@ if (! function_exists('prettyPrintJson')) {
return str_replace("\t", ' ', $result);
}
}
+
+// sourced from https://twitter.com/jrubsc/status/907776591320764416/photo/1
+if (! function_exists('carbon')) {
+ function carbon(...$args) {
+ return new Carbon\Carbon(...$args);
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index 96c10fa7..1b72344d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -29,11 +29,6 @@
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.0.0.tgz",
"integrity": "sha1-wd5CkwgUJNo6wwwjr6hQrxAZu1Q="
},
- "JSV": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
- "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
- },
"abbrev": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
@@ -3889,6 +3884,14 @@
}
}
},
+ "string_decoder": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.0.1"
+ }
+ },
"string-width": {
"version": "1.0.2",
"bundled": true,
@@ -3899,14 +3902,6 @@
"strip-ansi": "3.0.1"
}
},
- "string_decoder": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "safe-buffer": "5.0.1"
- }
- },
"stringstream": {
"version": "0.0.5",
"bundled": true,
@@ -4817,6 +4812,11 @@
}
}
},
+ "JSV": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+ "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
+ },
"kdbush": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-1.0.1.tgz",
@@ -7587,6 +7587,21 @@
"integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=",
"dev": true
},
+ "string_decoder": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz",
+ "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=",
+ "requires": {
+ "safe-buffer": "5.0.1"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
+ "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c="
+ }
+ }
+ },
"string-length": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz",
@@ -7605,21 +7620,6 @@
"strip-ansi": "3.0.1"
}
},
- "string_decoder": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz",
- "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=",
- "requires": {
- "safe-buffer": "5.0.1"
- },
- "dependencies": {
- "safe-buffer": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
- "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c="
- }
- }
- },
"stringify-object": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.2.0.tgz",
@@ -8520,14 +8520,6 @@
}
}
},
- "webStorage": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/webStorage/-/webStorage-1.2.4.tgz",
- "integrity": "sha1-/jNN8N5uLe58i9A2uxVaw115FTY=",
- "requires": {
- "gr-event-dispatcher": "1.1.1"
- }
- },
"webpack": {
"version": "3.5.6",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-3.5.6.tgz",
@@ -8790,6 +8782,14 @@
}
}
},
+ "webStorage": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/webStorage/-/webStorage-1.2.4.tgz",
+ "integrity": "sha1-/jNN8N5uLe58i9A2uxVaw115FTY=",
+ "requires": {
+ "gr-event-dispatcher": "1.1.1"
+ }
+ },
"webworkify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/webworkify/-/webworkify-1.4.0.tgz",
diff --git a/resources/views/master.blade.php b/resources/views/master.blade.php
index fbe53414..0c9855dc 100644
--- a/resources/views/master.blade.php
+++ b/resources/views/master.blade.php
@@ -12,6 +12,8 @@
+@isset($aslink)
+@endisset
diff --git a/tests/Browser/ExampleTest.php b/tests/Browser/ExampleTest.php
deleted file mode 100644
index a0099346..00000000
--- a/tests/Browser/ExampleTest.php
+++ /dev/null
@@ -1,23 +0,0 @@
-browse(function (Browser $browser) {
- $browser->visit('/')
- ->assertSee('Built with love');
- });
- }
-}
diff --git a/tests/Feature/ActivityStreamTest.php b/tests/Feature/ActivityStreamTest.php
new file mode 100644
index 00000000..5d7b2c24
--- /dev/null
+++ b/tests/Feature/ActivityStreamTest.php
@@ -0,0 +1,50 @@
+get('/', ['Accept' => 'application/activity+json']);
+ $response->assertHeader('Content-Type', 'application/activity+json');
+ $response->assertJson([
+ '@context' => 'https://www.w3.org/ns/activitystreams',
+ 'id' => config('app.url'),
+ 'type' => 'Person',
+ 'name' => config('app.display_name'),
+ ]);
+ }
+
+ /**
+ * Test request to a single note returns AS2.0 data.
+ *
+ * @return void
+ */
+ public function test_single_note_returns_as_data()
+ {
+ $note = \App\Note::find(11);
+ $response = $this->get('/notes/B', ['Accept' => 'application/activity+json']);
+ $response->assertHeader('Content-Type', 'application/activity+json');
+ $response->assertJson([
+ '@context' => 'https://www.w3.org/ns/activitystreams',
+ 'type' => 'Add',
+ 'actor' => [
+ 'type' => 'Person',
+ 'id' => config('app.url'),
+ ],
+ 'object' => [
+ 'type' => 'Note',
+ 'name' => strip_tags($note->note)
+ ]
+ ]);
+ }
+}
diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php
deleted file mode 100644
index f31e495c..00000000
--- a/tests/Feature/ExampleTest.php
+++ /dev/null
@@ -1,21 +0,0 @@
-get('/');
-
- $response->assertStatus(200);
- }
-}
diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php
deleted file mode 100644
index e9fe19c6..00000000
--- a/tests/Unit/ExampleTest.php
+++ /dev/null
@@ -1,19 +0,0 @@
-assertTrue(true);
- }
-}