Merge branch 'release/0.7.1'

This commit is contained in:
Jonny Barnes 2017-09-13 17:27:38 +01:00
commit d24f9c7680
15 changed files with 315 additions and 127 deletions

View file

@ -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'));
}
/**

View file

@ -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');
});
}
/**

View file

@ -0,0 +1,50 @@
<?php
namespace App\Services;
use App\Note;
class ActivityStreamsService
{
public function siteOwnerResponse()
{
$data = json_encode([
'@context' => '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');
}
}

View file

@ -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();
}

View file

@ -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) {

View file

@ -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

View file

@ -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",

148
composer.lock generated
View file

@ -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",

View file

@ -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);
}
}

72
package-lock.json generated
View file

@ -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",

View file

@ -12,6 +12,8 @@
<link rel="alternate" type="application/rss+xml" title="Notes RSS Feed" href="/notes/feed.rss">
<link rel="alternate" type="application/atom+xml" title="Notes Atom Feed" href="/notes/feed.atom">
<link rel="alternate" type="application/json" title="Notes JSON Feed" href="/notes/feed.json">
@isset($aslink) <link rel="alternate" type="application/activity+json" href="{{ $aslink }}">
@endisset
<link rel="openid.server" href="https://indieauth.com/openid">
<link rel="openid.delegate" href="{{ config('app.url') }}">
<link rel="authorization_endpoint" href="https://indieauth.com/auth">

View file

@ -1,23 +0,0 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ExampleTest extends DuskTestCase
{
/**
* A basic browser test example.
*
* @return void
*/
public function test_basic_example()
{
$this->browse(function (Browser $browser) {
$browser->visit('/')
->assertSee('Built with love');
});
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ActivityStreamTest extends TestCase
{
/**
* Test request to homepage returns data for site owner.
*
* @return void
*/
public function test_homepage_returns_data_for_site_owner()
{
$response = $this->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)
]
]);
}
}

View file

@ -1,21 +0,0 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}

View file

@ -1,19 +0,0 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$this->assertTrue(true);
}
}