From 75cb5cf454c8f698546dacebf081b5ec0ba3b283 Mon Sep 17 00:00:00 2001
From: Jonny Barnes
Date: Thu, 21 Jul 2016 11:12:01 +0100
Subject: [PATCH 1/7] Updated .lock
---
composer.lock | 45 +++++++++++++++++++++++----------------------
1 file changed, 23 insertions(+), 22 deletions(-)
diff --git a/composer.lock b/composer.lock
index da7115b0..cacb990a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -59,20 +59,20 @@
},
{
"name": "aws/aws-sdk-php",
- "version": "3.18.28",
+ "version": "3.18.31",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "c75d3ba185d5db6998124fa1a99a63e5d529b247"
+ "reference": "dad0b7db5fa8f3c7a3805efb2a1e86a50f11fe8b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c75d3ba185d5db6998124fa1a99a63e5d529b247",
- "reference": "c75d3ba185d5db6998124fa1a99a63e5d529b247",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/dad0b7db5fa8f3c7a3805efb2a1e86a50f11fe8b",
+ "reference": "dad0b7db5fa8f3c7a3805efb2a1e86a50f11fe8b",
"shasum": ""
},
"require": {
- "guzzlehttp/guzzle": "~5.3|~6.0.1|~6.1",
+ "guzzlehttp/guzzle": "^5.3.1|^6.2.1",
"guzzlehttp/promises": "~1.0",
"guzzlehttp/psr7": "~1.3.1",
"mtdowling/jmespath.php": "~2.2",
@@ -135,7 +135,7 @@
"s3",
"sdk"
],
- "time": "2016-07-13 20:34:06"
+ "time": "2016-07-19 17:25:45"
},
{
"name": "barnabywalters/mf-cleaner",
@@ -385,16 +385,16 @@
},
{
"name": "ezyang/htmlpurifier",
- "version": "v4.7.0",
+ "version": "v4.8.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
- "reference": "ae1828d955112356f7677c465f94f7deb7d27a40"
+ "reference": "d0c392f77d2f2a3dcf7fcb79e2a1e2b8804e75b2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/ae1828d955112356f7677c465f94f7deb7d27a40",
- "reference": "ae1828d955112356f7677c465f94f7deb7d27a40",
+ "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/d0c392f77d2f2a3dcf7fcb79e2a1e2b8804e75b2",
+ "reference": "d0c392f77d2f2a3dcf7fcb79e2a1e2b8804e75b2",
"shasum": ""
},
"require": {
@@ -425,7 +425,7 @@
"keywords": [
"html"
],
- "time": "2015-08-05 01:03:42"
+ "time": "2016-07-16 12:58:58"
},
{
"name": "geo-io/interface",
@@ -1134,16 +1134,16 @@
},
{
"name": "laravel/framework",
- "version": "v5.2.39",
+ "version": "5.2.41",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "c2a77050269b4e03bd9a735a9f24e573a7598b8a"
+ "reference": "29ba2e310cfeb42ab6545bcd81ff4c2ec1f6b5c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/c2a77050269b4e03bd9a735a9f24e573a7598b8a",
- "reference": "c2a77050269b4e03bd9a735a9f24e573a7598b8a",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/29ba2e310cfeb42ab6545bcd81ff4c2ec1f6b5c2",
+ "reference": "29ba2e310cfeb42ab6545bcd81ff4c2ec1f6b5c2",
"shasum": ""
},
"require": {
@@ -1200,7 +1200,8 @@
"illuminate/support": "self.version",
"illuminate/translation": "self.version",
"illuminate/validation": "self.version",
- "illuminate/view": "self.version"
+ "illuminate/view": "self.version",
+ "tightenco/collect": "self.version"
},
"require-dev": {
"aws/aws-sdk-php": "~3.0",
@@ -1259,7 +1260,7 @@
"framework",
"laravel"
],
- "time": "2016-06-17 19:25:12"
+ "time": "2016-07-20 13:13:06"
},
{
"name": "lcobucci/jwt",
@@ -4454,16 +4455,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "5.4.6",
+ "version": "5.4.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "2f1fc94b77ea6418bd6a06c64a1dac0645fbce59"
+ "reference": "6c8a756c17a1a92a066c99860eb57922e8b723da"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2f1fc94b77ea6418bd6a06c64a1dac0645fbce59",
- "reference": "2f1fc94b77ea6418bd6a06c64a1dac0645fbce59",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6c8a756c17a1a92a066c99860eb57922e8b723da",
+ "reference": "6c8a756c17a1a92a066c99860eb57922e8b723da",
"shasum": ""
},
"require": {
@@ -4528,7 +4529,7 @@
"testing",
"xunit"
],
- "time": "2016-06-16 06:01:15"
+ "time": "2016-07-21 06:55:27"
},
{
"name": "phpunit/phpunit-mock-objects",
From 558548380003ec4135a9cfe4a9b468c3203db699 Mon Sep 17 00:00:00 2001
From: Jonny Barnes
Date: Fri, 29 Jul 2016 09:55:27 +0100
Subject: [PATCH 2/7] Start work on better webmention support
---
.../Controllers/WebMentionsController.php | 5 ++-
app/Jobs/ProcessWebMention.php | 33 ++++++++-----------
2 files changed, 16 insertions(+), 22 deletions(-)
diff --git a/app/Http/Controllers/WebMentionsController.php b/app/Http/Controllers/WebMentionsController.php
index fc254058..ad0a59fe 100644
--- a/app/Http/Controllers/WebMentionsController.php
+++ b/app/Http/Controllers/WebMentionsController.php
@@ -28,7 +28,7 @@ class WebMentionsController extends Controller
}
//next check the $target is valid
- $path = parse_url($request->input('target'))['path'];
+ $path = parse_url($request->input('target'), PHP_URL_PATH);
$pathParts = explode('/', $path);
switch ($pathParts[1]) {
@@ -36,9 +36,8 @@ class WebMentionsController extends Controller
//we have a note
$noteId = $pathParts[2];
$numbers = new Numbers();
- $realId = $numbers->b60tonum($noteId);
try {
- $note = Note::findOrFail($realId);
+ $note = Note::findOrFail($numbers->b60tonum($noteId));
$this->dispatch(new ProcessWebMention($note, $request->input('source')));
} catch (ModelNotFoundException $e) {
return new Response('This note doesn’t exist.', 400);
diff --git a/app/Jobs/ProcessWebMention.php b/app/Jobs/ProcessWebMention.php
index b2593427..d9d73eb4 100644
--- a/app/Jobs/ProcessWebMention.php
+++ b/app/Jobs/ProcessWebMention.php
@@ -2,8 +2,8 @@
namespace App\Jobs;
+use Mf2;
use App\Note;
-use Mf2\parse;
use HTMLPurifier;
use App\WebMention;
use GuzzleHttp\Client;
@@ -11,6 +11,7 @@ use HTMLPurifier_Config;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Jonnybarnes\WebmentionsParser\Parser;
+use GuzzleHttp\Exception\RequestException;
use Illuminate\Contracts\Queue\ShouldQueue;
class ProcessWebMention extends Job implements ShouldQueue
@@ -44,7 +45,11 @@ class ProcessWebMention extends Job implements ShouldQueue
$sourceURL = parse_url($this->source);
$baseURL = $sourceURL['scheme'] . '://' . $sourceURL['host'];
$remoteContent = $this->getRemoteContent($this->source);
- $microformats = $this->parseHTML($remoteContent, $baseURL);
+ if ($remoteContent === null) {
+ return false;
+ }
+ $mf2Parser = new Mf2\Parser($remoteContent, $baseURL, true);
+ $microformats = $mf2Parser->parse();
$count = WebMention::where('source', '=', $this->source)->count();
if ($count > 0) {
//we already have a webmention from this source
@@ -140,14 +145,18 @@ class ProcessWebMention extends Job implements ShouldQueue
/**
* Retreive the remote content from a URL, and caches the result.
*
- * @param string The URL to retreive content from
- * @return string The HTML from the URL
+ * @param string The URL to retreive content from
+ * @return string|null The HTML from the URL (or null if error)
*/
private function getRemoteContent($url)
{
$client = new Client();
- $response = $client->get($url);
+ try {
+ $response = $client->request('GET', $url);
+ } catch(RequestException $e) {
+ return;
+ }
$html = (string) $response->getBody();
$path = storage_path() . '/HTML/' . $this->createFilenameFromURL($url);
$this->fileForceContents($path, $html);
@@ -189,20 +198,6 @@ class ProcessWebMention extends Job implements ShouldQueue
file_put_contents("$dir/$name", $contents);
}
- /**
- * A wrapper function for php-mf2’s parse method.
- *
- * @param string The HTML to parse
- * @param string The base URL to resolve relative URLs in the HTML against
- * @return array The porcessed microformats
- */
- private function parseHTML($html, $baseurl)
- {
- $microformats = \Mf2\parse((string) $html, $baseurl);
-
- return $microformats;
- }
-
/**
* Save a profile image to the local cache.
*
From c81eddd12cc7885a267ac60df00f4bc1f9fa16ca Mon Sep 17 00:00:00 2001
From: Jonny Barnes
Date: Fri, 29 Jul 2016 10:00:05 +0100
Subject: [PATCH 3/7] Update .lock
---
composer.lock | 71 ++++++++++++++++++++++++---------------------------
1 file changed, 33 insertions(+), 38 deletions(-)
diff --git a/composer.lock b/composer.lock
index cacb990a..36372915 100644
--- a/composer.lock
+++ b/composer.lock
@@ -59,16 +59,16 @@
},
{
"name": "aws/aws-sdk-php",
- "version": "3.18.31",
+ "version": "3.18.35",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "dad0b7db5fa8f3c7a3805efb2a1e86a50f11fe8b"
+ "reference": "8133c4ce336c8cac97218546f896f491bb6ffd7e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/dad0b7db5fa8f3c7a3805efb2a1e86a50f11fe8b",
- "reference": "dad0b7db5fa8f3c7a3805efb2a1e86a50f11fe8b",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/8133c4ce336c8cac97218546f896f491bb6ffd7e",
+ "reference": "8133c4ce336c8cac97218546f896f491bb6ffd7e",
"shasum": ""
},
"require": {
@@ -135,7 +135,7 @@
"s3",
"sdk"
],
- "time": "2016-07-19 17:25:45"
+ "time": "2016-07-29 01:07:59"
},
{
"name": "barnabywalters/mf-cleaner",
@@ -429,27 +429,22 @@
},
{
"name": "geo-io/interface",
- "version": "v1.0.0",
+ "version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/geo-io/interface.git",
- "reference": "cdbb55801e3f8d5485227c2031cc7a3c16ccd06a"
+ "reference": "cf46fe7b013de20ab8b601238c7d91b480810644"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/geo-io/interface/zipball/cdbb55801e3f8d5485227c2031cc7a3c16ccd06a",
- "reference": "cdbb55801e3f8d5485227c2031cc7a3c16ccd06a",
+ "url": "https://api.github.com/repos/geo-io/interface/zipball/cf46fe7b013de20ab8b601238c7d91b480810644",
+ "reference": "cf46fe7b013de20ab8b601238c7d91b480810644",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
- }
- },
"autoload": {
"psr-4": {
"GeoIO\\": "src/"
@@ -471,7 +466,7 @@
"geometry",
"io"
],
- "time": "2015-04-17 18:52:52"
+ "time": "2016-07-28 07:17:02"
},
{
"name": "geo-io/wkb-parser",
@@ -1264,16 +1259,16 @@
},
{
"name": "lcobucci/jwt",
- "version": "3.1.1",
+ "version": "3.1.2",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
- "reference": "afea8e682e911a21574fd8519321b32522fa25b5"
+ "reference": "9a8db2cd42346f96993928ff6a6c22563f555ab1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/lcobucci/jwt/zipball/afea8e682e911a21574fd8519321b32522fa25b5",
- "reference": "afea8e682e911a21574fd8519321b32522fa25b5",
+ "url": "https://api.github.com/repos/lcobucci/jwt/zipball/9a8db2cd42346f96993928ff6a6c22563f555ab1",
+ "reference": "9a8db2cd42346f96993928ff6a6c22563f555ab1",
"shasum": ""
},
"require": {
@@ -1281,7 +1276,7 @@
"php": ">=5.5"
},
"require-dev": {
- "mdanter/ecc": "~0.3",
+ "mdanter/ecc": "~0.3.1",
"mikey179/vfsstream": "~1.5",
"phpmd/phpmd": "~2.2",
"phpunit/php-invoker": "~1.1",
@@ -1318,7 +1313,7 @@
"JWS",
"jwt"
],
- "time": "2016-03-24 22:46:13"
+ "time": "2016-07-27 11:09:37"
},
{
"name": "league/commonmark",
@@ -3682,16 +3677,16 @@
},
{
"name": "filp/whoops",
- "version": "2.1.2",
+ "version": "2.1.3",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
- "reference": "d13505b240a6f580bc75ba591da30299d6cb0eec"
+ "reference": "8828aaa2178e0a19325522e2a45282ff0a14649b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/filp/whoops/zipball/d13505b240a6f580bc75ba591da30299d6cb0eec",
- "reference": "d13505b240a6f580bc75ba591da30299d6cb0eec",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/8828aaa2178e0a19325522e2a45282ff0a14649b",
+ "reference": "8828aaa2178e0a19325522e2a45282ff0a14649b",
"shasum": ""
},
"require": {
@@ -3738,7 +3733,7 @@
"whoops",
"zf2"
],
- "time": "2016-04-07 06:16:25"
+ "time": "2016-05-06 18:25:35"
},
{
"name": "fzaninotto/faker",
@@ -4211,16 +4206,16 @@
},
{
"name": "phpunit/php-code-coverage",
- "version": "4.0.0",
+ "version": "4.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "900370c81280cc0d942ffbc5912d80464eaee7e9"
+ "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/900370c81280cc0d942ffbc5912d80464eaee7e9",
- "reference": "900370c81280cc0d942ffbc5912d80464eaee7e9",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3",
+ "reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3",
"shasum": ""
},
"require": {
@@ -4229,7 +4224,7 @@
"phpunit/php-text-template": "~1.2",
"phpunit/php-token-stream": "^1.4.2",
"sebastian/code-unit-reverse-lookup": "~1.0",
- "sebastian/environment": "^1.3.2",
+ "sebastian/environment": "^1.3.2 || ^2.0",
"sebastian/version": "~1.0|~2.0"
},
"require-dev": {
@@ -4270,7 +4265,7 @@
"testing",
"xunit"
],
- "time": "2016-06-03 05:03:56"
+ "time": "2016-07-26 14:39:29"
},
{
"name": "phpunit/php-file-iterator",
@@ -4455,16 +4450,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "5.4.7",
+ "version": "5.4.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "6c8a756c17a1a92a066c99860eb57922e8b723da"
+ "reference": "3132365e1430c091f208e120b8845d39c25f20e6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6c8a756c17a1a92a066c99860eb57922e8b723da",
- "reference": "6c8a756c17a1a92a066c99860eb57922e8b723da",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3132365e1430c091f208e120b8845d39c25f20e6",
+ "reference": "3132365e1430c091f208e120b8845d39c25f20e6",
"shasum": ""
},
"require": {
@@ -4476,7 +4471,7 @@
"myclabs/deep-copy": "~1.3",
"php": "^5.6 || ^7.0",
"phpspec/prophecy": "^1.3.1",
- "phpunit/php-code-coverage": "^4.0",
+ "phpunit/php-code-coverage": "^4.0.1",
"phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "^1.0.6",
@@ -4529,7 +4524,7 @@
"testing",
"xunit"
],
- "time": "2016-07-21 06:55:27"
+ "time": "2016-07-26 14:48:00"
},
{
"name": "phpunit/phpunit-mock-objects",
From bb165479429bc6c01f7fd85cacce23734cdefb74 Mon Sep 17 00:00:00 2001
From: Jonny Barnes
Date: Fri, 29 Jul 2016 10:48:05 +0100
Subject: [PATCH 4/7] Add a missing space (PSR-2)
---
app/Jobs/ProcessWebMention.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/Jobs/ProcessWebMention.php b/app/Jobs/ProcessWebMention.php
index d9d73eb4..324fee6a 100644
--- a/app/Jobs/ProcessWebMention.php
+++ b/app/Jobs/ProcessWebMention.php
@@ -154,7 +154,7 @@ class ProcessWebMention extends Job implements ShouldQueue
try {
$response = $client->request('GET', $url);
- } catch(RequestException $e) {
+ } catch (RequestException $e) {
return;
}
$html = (string) $response->getBody();
From 84c7969a4eb71aad37cbff80f8b47f22f366c0ef Mon Sep 17 00:00:00 2001
From: Jonny Barnes
Date: Fri, 29 Jul 2016 14:22:49 +0100
Subject: [PATCH 5/7] Add an mf2 column to webemtnions, type `jsonb`
---
changelog.md | 4 +++
..._jsonb_mf2_column_to_webmentions_table.php | 33 +++++++++++++++++++
2 files changed, 37 insertions(+)
create mode 100644 database/migrations/2016_07_29_113150_add_jsonb_mf2_column_to_webmentions_table.php
diff --git a/changelog.md b/changelog.md
index f6b62544..4b8f3b2f 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,9 @@
# Changelog
+## Version {next}
+ - Adding jsonb column to store webmentions’ mf2.
+ * As of L5.2 this needs a custom command to drop NOT NULL from content, L5.3 should allow a fix for this
+
## Version 0.0.8.5 (2016-07-18)
- Set the size of the textarea in a form better
- Update to latest Guzzle to fix CVE-2016-5385
diff --git a/database/migrations/2016_07_29_113150_add_jsonb_mf2_column_to_webmentions_table.php b/database/migrations/2016_07_29_113150_add_jsonb_mf2_column_to_webmentions_table.php
new file mode 100644
index 00000000..04ad6f0a
--- /dev/null
+++ b/database/migrations/2016_07_29_113150_add_jsonb_mf2_column_to_webmentions_table.php
@@ -0,0 +1,33 @@
+jsonb('mf2')->nullable();
+ $table->index(['mf2']);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('webmentions', function (Blueprint $table) {
+ $table->dropIndex(['mf2']);
+ $table->dropColumn('mf2');
+ });
+ }
+}
From a9f089098c8e57d2685eb8af32f456d0999bafc7 Mon Sep 17 00:00:00 2001
From: Jonny Barnes
Date: Wed, 3 Aug 2016 16:08:30 +0100
Subject: [PATCH 6/7] Work on webmwtion code refactoring
---
app/Http/Controllers/NotesController.php | 98 +++++++----
app/Jobs/ProcessWebMention.php | 212 ++++++++---------------
app/Jobs/SaveProfileImage.php | 67 +++++++
composer.lock | 116 ++++++-------
resources/views/singlenote.blade.php | 4 +-
tests/NotesTest.php | 25 +--
tests/ProcessWebMentionTest.php | 29 ++++
tests/WebMentionsTest.php | 92 ++++++++++
8 files changed, 395 insertions(+), 248 deletions(-)
create mode 100644 app/Jobs/SaveProfileImage.php
create mode 100644 tests/ProcessWebMentionTest.php
create mode 100644 tests/WebMentionsTest.php
diff --git a/app/Http/Controllers/NotesController.php b/app/Http/Controllers/NotesController.php
index 1d6125fe..590ff654 100644
--- a/app/Http/Controllers/NotesController.php
+++ b/app/Http/Controllers/NotesController.php
@@ -7,6 +7,8 @@ use Twitter;
use App\Tag;
use App\Note;
use Jonnybarnes\IndieWeb\Numbers;
+use Illuminate\Filesystem\Filesystem;
+use Jonnybarnes\WebmentionsParser\Authorship;
// Need to sort out Twitter and webmentions!
@@ -23,8 +25,8 @@ class NotesController extends Controller
foreach ($notes as $note) {
$replies = 0;
foreach ($note->webmentions as $webmention) {
- if ($webmention->type == 'reply') {
- $replies = $replies + 1;
+ if ($webmention->type == 'in-reply-to') {
+ $replies++;
}
}
$note->replies = $replies;
@@ -67,31 +69,51 @@ class NotesController extends Controller
public function singleNote($urlId)
{
$numbers = new Numbers();
+ $authorship = new Authorship();
$realId = $numbers->b60tonum($urlId);
$note = Note::find($realId);
$replies = [];
$reposts = [];
$likes = [];
foreach ($note->webmentions as $webmention) {
+ /*
+ reply->url |
+ reply->photo | Author
+ reply->name |
+ reply->source
+ reply->date
+ reply->reply
+
+ repost->url |
+ repost->photo | Author
+ repost->name |
+ repost->date
+ repost->source
+
+ like->url |
+ like->photo | Author
+ like->name |
+ */
+ $microformats = json_decode($webmention->mf2);
+ $authorHCard = $authorship->findAuthor($microformats);
+ $content['url'] = $authorHCard['properties']['url'][0];
+ $content['photo'] = $this->createPhotoLink($authorHCard['properties']['photo'][0]);
+ $content['name'] = $authorHCard['properties']['name'][0];
switch ($webmention->type) {
- case 'reply':
- $content = unserialize($webmention->content);
- $content['source'] = $this->bridgyReply($webmention->source);
- $content['photo'] = $this->createPhotoLink($content['photo']);
+ case 'in-reply-to':
+ $content['source'] = $webmention->source;
$content['date'] = $carbon->parse($content['date'])->toDayDateTimeString();
+ $content['reply'] = $microformats['items'][0]['properties']['content'][0]['html_purified'];
$replies[] = $content;
break;
- case 'repost':
- $content = unserialize($webmention->content);
- $content['photo'] = $this->createPhotoLink($content['photo']);
+ case 'repost-of':
$content['date'] = $carbon->parse($content['date'])->toDayDateTimeString();
+ $content['source'] = $webmention->source;
$reposts[] = $content;
break;
- case 'like':
- $content = unserialize($webmention->content);
- $content['photo'] = $this->createPhotoLink($content['photo']);
+ case 'like-of':
$likes[] = $content;
break;
}
@@ -164,41 +186,43 @@ class NotesController extends Controller
return view('taggednotes', ['notes' => $notes, 'tag' => $tag]);
}
- /**
- * Swap a brid.gy URL shim-ing a twitter reply to a real twitter link.
- *
- * @param string
- * @return string
- */
- public function bridgyReply($source)
- {
- $url = $source;
- if (mb_substr($source, 0, 28, 'UTF-8') == 'https://brid-gy.appspot.com/') {
- $parts = explode('/', $source);
- $tweetId = array_pop($parts);
- if ($tweetId) {
- $url = 'https://twitter.com/_/status/' . $tweetId;
- }
- }
-
- return $url;
- }
-
/**
* Create the photo link.
*
+ * We shall leave twitter.com and twimg.com links as they are. Then we shall
+ * check for local copies, if that fails leave the link as is.
+ *
* @param string
* @return string
*/
public function createPhotoLink($url)
{
- $host = parse_url($url)['host'];
- if ($host != 'twitter.com' && $host != 'pbs.twimg.com') {
- return '/assets/profile-images/' . $host . '/image';
- }
- if (mb_substr($url, 0, 20) == 'http://pbs.twimg.com') {
+ $host = parse_url($url, PHP_URL_HOST);
+ if ($host == 'pbs.twimg.com') {
+ //make sure we use HTTPS, we know twitter supports it
return str_replace('http://', 'https://', $url);
}
+ if ($host == 'twitter.com') {
+ if (Cache::has($url)) {
+ return Cache::get($url);
+ }
+ $username = parse_url($url, PHP_URL_PATH);
+ try {
+ $info = Twitter::getUsers(['screen_name' => $username]);
+ $profile_image = $info->profile_image_url_https;
+ Cache::put($url, $profile_image, 10080); //1 week
+ } catch (Exception $e) {
+ return $url; //not sure here
+ }
+
+ return $profile_image;
+ }
+ $filesystem = new Filesystem();
+ if ($filesystem->exists(public_path() . '/assets/profile-images/' . $host . '/image')) {
+ return '/assets/profile-images/' . $host . '/image';
+ }
+
+ return $url;
}
/**
diff --git a/app/Jobs/ProcessWebMention.php b/app/Jobs/ProcessWebMention.php
index 324fee6a..380ffb94 100644
--- a/app/Jobs/ProcessWebMention.php
+++ b/app/Jobs/ProcessWebMention.php
@@ -13,13 +13,16 @@ use Illuminate\Queue\InteractsWithQueue;
use Jonnybarnes\WebmentionsParser\Parser;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use App\Exceptions\RemoteContentNotFoundException;
class ProcessWebMention extends Job implements ShouldQueue
{
- use InteractsWithQueue, SerializesModels;
+ use InteractsWithQueue, SerializesModels, DispatchesJobs;
protected $note;
protected $source;
+ protected $guzzle;
/**
* Create a new job instance.
@@ -28,10 +31,11 @@ class ProcessWebMention extends Job implements ShouldQueue
* @param string $source
* @return void
*/
- public function __construct(Note $note, $source)
+ public function __construct(Note $note, $source, Client $guzzle = null)
{
$this->note = $note;
$this->source = $source;
+ $this->guzzle = $guzzle ?? new Client();
}
/**
@@ -46,100 +50,60 @@ class ProcessWebMention extends Job implements ShouldQueue
$baseURL = $sourceURL['scheme'] . '://' . $sourceURL['host'];
$remoteContent = $this->getRemoteContent($this->source);
if ($remoteContent === null) {
- return false;
+ throw new RemoteContentNotFoundException;
}
- $mf2Parser = new Mf2\Parser($remoteContent, $baseURL, true);
- $microformats = $mf2Parser->parse();
- $count = WebMention::where('source', '=', $this->source)->count();
- if ($count > 0) {
- //we already have a webmention from this source
- $webmentions = WebMention::where('source', '=', $this->source)->get();
- foreach ($webmentions as $webmention) {
- //now check it still 'mentions' this target
- //we switch for each type of mention (reply/like/repost)
- switch ($webmention->type) {
- case 'reply':
- if ($parser->checkInReplyTo($microformats, $note->longurl) == false) {
- //it doesn't so delete
- $webmention->delete();
+ $microformats = Mf2\parse($remoteContent, $baseURL);
+ $webmentions = WebMention::where('source', $this->source)->get();
+ foreach ($webmentions as $webmention) {
+ //check webmention still references target
+ //we try each type of mention (reply/like/repost)
+ if ($webmention->type == 'in-reply-to') {
+ if ($parser->checkInReplyTo($microformats, $this->note->longurl) == false) {
+ //it doesn't so delete
+ $webmention->delete();
- return true;
- }
- //webmenion is still a reply, so update content
- $content = $parser->replyContent($microformats);
- $this->saveImage($content);
- $content['reply'] = $this->filterHTML($content['reply']);
- $content = serialize($content);
- $webmention->content = $content;
- $webmention->save();
+ return;
+ }
+ //webmenion is still a reply, so update content
+ $microformats = $this->filterHTML($microformats);
+ $this->dispatch(new SaveProfileImage($microformats));
+ $webmention->mf2 = json_encode($microformats);
+ $webmention->save();
- return true;
- break;
- case 'like':
- if ($parser->checkLikeOf($microformats, $note->longurl) == false) {
- //it doesn't so delete
- $webmention->delete();
+ return;
+ }
+ if ($webmention->type == 'like-of') {
+ if ($parser->checkLikeOf($microformats, $note->longurl) == false) {
+ //it doesn't so delete
+ $webmention->delete();
- return true;
- } //note we don't need to do anything if it still is a like
- break;
- case 'repost':
- if ($parser->checkRepostOf($microformats, $note->longurl) == false) {
- //it doesn't so delete
- $webmention->delete();
+ return;
+ } //note we don't need to do anything if it still is a like
+ }
+ if ($webmention->type == 'repost-of') {
+ if ($parser->checkRepostOf($microformats, $note->longurl) == false) {
+ //it doesn't so delete
+ $webmention->delete();
+
+ return;
+ } //again, we don't need to do anything if it still is a repost
+ }
+ }//foreach
- return true;
- } //again, we don't need to do anything if it still is a repost
- break;
- }//switch
- }//foreach
- }//if
//no wemention in db so create new one
$webmention = new WebMention();
- //check it is in fact a reply
- if ($parser->checkInReplyTo($microformats, $note->longurl)) {
- $content = $parser->replyContent($microformats);
- $this->saveImage($content);
- $content['reply'] = $this->filterHTML($content['reply']);
- $content = serialize($content);
- $webmention->source = $this->source;
- $webmention->target = $note->longurl;
- $webmention->commentable_id = $this->note->id;
- $webmention->commentable_type = 'App\Note';
- $webmention->type = 'reply';
- $webmention->content = $content;
- $webmention->save();
+ $type = $parser->getMentionType($microformats); //throw error here?
+ $this->dispatch(new SaveProfileImage($microformats));
+ $microformats = $this->filterHTML($microformats);
+ $webmention->source = $this->source;
+ $webmention->target = $this->note->longurl;
+ $webmention->commentable_id = $this->note->id;
+ $webmention->commentable_type = 'App\Note';
+ $webmention->type = $type;
+ $webmention->mf2 = json_encode($microformats);
+ $webmention->save();
- return true;
- } elseif ($parser->checkLikeOf($microformats, $note->longurl)) {
- //it is a like
- $content = $parser->likeContent($microformats);
- $this->saveImage($content);
- $content = serialize($content);
- $webmention->source = $this->source;
- $webmention->target = $note->longurl;
- $webmention->commentable_id = $this->note->id;
- $webmention->commentable_type = 'App\Note';
- $webmention->type = 'like';
- $webmention->content = $content;
- $webmention->save();
-
- return true;
- } elseif ($parser->checkRepostOf($microformats, $note->longurl)) {
- //it is a repost
- $content = $parser->repostContent($microformats);
- $this->saveImage($content);
- $content = serialize($content);
- $webmention->source = $this->source;
- $webmention->target = $note->longurl;
- $webmention->commentable_id = $this->note->id;
- $webmention->commentable_type = 'App\Note';
- $webmention->type = 'repost';
- $webmention->content = $content;
- $webmention->save();
-
- return true;
- }
+ return;
}
/**
@@ -150,16 +114,20 @@ class ProcessWebMention extends Job implements ShouldQueue
*/
private function getRemoteContent($url)
{
- $client = new Client();
-
try {
- $response = $client->request('GET', $url);
+ $response = $this->guzzle->request('GET', $url);
} catch (RequestException $e) {
return;
}
$html = (string) $response->getBody();
$path = storage_path() . '/HTML/' . $this->createFilenameFromURL($url);
- $this->fileForceContents($path, $html);
+ $parts = explode('/', $path);
+ $name = array_pop($parts);
+ $dir = implode('/', $parts);
+ if (! is_dir($dir)) {
+ mkdir($dir, 0755, true);
+ }
+ file_put_contents("$dir/$name", $html);
return $html;
}
@@ -182,65 +150,29 @@ class ProcessWebMention extends Job implements ShouldQueue
}
/**
- * Save a file, and create any necessary folders.
+ * Filter the HTML in a reply webmention.
*
- * @param string The directory to save to
- * @param binary The file to save
+ * @param array The unfiltered microformats
+ * @return array The filtered microformats
*/
- private function fileForceContents($dir, $contents)
+ private function filterHTML($microformats)
{
- $parts = explode('/', $dir);
- $name = array_pop($parts);
- $dir = implode('/', $parts);
- if (! is_dir($dir)) {
- mkdir($dir, 0755, true);
+ if (isset($microformats['items'][0]['properties']['content'][0]['html'])) {
+ $microformats['items'][0]['properties']['content'][0]['html_purified'] = $this->useHTMLPurifier(
+ $microformats['items'][0]['properties']['content'][0]['html']
+ );
}
- file_put_contents("$dir/$name", $contents);
+
+ return $microformats;
}
/**
- * Save a profile image to the local cache.
- *
- * @param array source content
- * @return bool wether image was saved or not (we don’t save twitter profiles)
- */
- public function saveImage(array $content)
- {
- $photo = $content['photo'];
- $home = $content['url'];
- //dont save pbs.twimg.com links
- if (parse_url($photo)['host'] != 'pbs.twimg.com'
- && parse_url($photo)['host'] != 'twitter.com') {
- $client = new Client();
- try {
- $response = $client->get($photo);
- $image = $response->getBody(true);
- $path = public_path() . '/assets/profile-images/' . parse_url($home)['host'] . '/image';
- $this->fileForceContents($path, $image);
- } catch (Exception $e) {
- // we are openning and reading the default image so that
- // fileForceContent work
- $default = public_path() . '/assets/profile-images/default-image';
- $handle = fopen($default, 'rb');
- $image = fread($handle, filesize($default));
- fclose($handle);
- $path = public_path() . '/assets/profile-images/' . parse_url($home)['host'] . '/image';
- $this->fileForceContents($path, $image);
- }
-
- return true;
- }
-
- return false;
- }
-
- /**
- * Purify HTML received from a webmention.
+ * Set up and use HTMLPurifer on some HTML.
*
* @param string The HTML to be processed
* @return string The processed HTML
*/
- public function filterHTML($html)
+ private function useHTMLPurifier($html)
{
$config = HTMLPurifier_Config::createDefault();
$config->set('Cache.SerializerPath', storage_path() . '/HTMLPurifier');
diff --git a/app/Jobs/SaveProfileImage.php b/app/Jobs/SaveProfileImage.php
new file mode 100644
index 00000000..5600fe21
--- /dev/null
+++ b/app/Jobs/SaveProfileImage.php
@@ -0,0 +1,67 @@
+microformats = $microformats;
+ }
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle(Authorship $authorship)
+ {
+ try {
+ $author = $authorship->findAuthor($microformats);
+ } catch (AuthorshipParserException $e) {
+ return;
+ }
+ $photo = $author['properties'][0]['photo'][0];
+ $home = $author['properties'][0]['url'][0];
+ //dont save pbs.twimg.com links
+ if (parse_url($photo, PHP_URL_HOST) != 'pbs.twimg.com'
+ && parse_url($photo, PHP_URL_HOST) != 'twitter.com') {
+ $client = new Client();
+ try {
+ $response = $client->get($photo);
+ $image = $response->getBody(true);
+ } catch (RequestException $e) {
+ // we are openning and reading the default image so that
+ $default = public_path() . '/assets/profile-images/default-image';
+ $handle = fopen($default, 'rb');
+ $image = fread($handle, filesize($default));
+ fclose($handle);
+ }
+ $path = public_path() . '/assets/profile-images/' . parse_url($home, PHP_URL_HOST) . '/image';
+ $parts = explode('/', $path);
+ $name = array_pop($parts);
+ $dir = implode('/', $parts);
+ if (! is_dir($dir)) {
+ mkdir($dir, 0755, true);
+ }
+ file_put_contents("$dir/$name", $image);
+ }
+ }
+}
diff --git a/composer.lock b/composer.lock
index 36372915..92da6d7a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1672,16 +1672,16 @@
},
{
"name": "monolog/monolog",
- "version": "1.20.0",
+ "version": "1.21.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
- "reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037"
+ "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/55841909e2bcde01b5318c35f2b74f8ecc86e037",
- "reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f42fbdfd53e306bda545845e4dbfd3e72edb4952",
+ "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952",
"shasum": ""
},
"require": {
@@ -1746,7 +1746,7 @@
"logging",
"psr-3"
],
- "time": "2016-07-02 14:02:10"
+ "time": "2016-07-29 03:23:52"
},
{
"name": "mtdowling/cron-expression",
@@ -2652,16 +2652,16 @@
},
{
"name": "symfony/console",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "a7abb7153f6d1da47f87ec50274844e246b09d9f"
+ "reference": "926061e74229e935d3c5b4e9ba87237316c6693f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/a7abb7153f6d1da47f87ec50274844e246b09d9f",
- "reference": "a7abb7153f6d1da47f87ec50274844e246b09d9f",
+ "url": "https://api.github.com/repos/symfony/console/zipball/926061e74229e935d3c5b4e9ba87237316c6693f",
+ "reference": "926061e74229e935d3c5b4e9ba87237316c6693f",
"shasum": ""
},
"require": {
@@ -2708,20 +2708,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
- "time": "2016-06-29 07:02:21"
+ "time": "2016-07-30 07:22:48"
},
{
"name": "symfony/debug",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
- "reference": "c54bc3539c3b87e86799533801e8ae0e971d78c2"
+ "reference": "697c527acd9ea1b2d3efac34d9806bf255278b0a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/debug/zipball/c54bc3539c3b87e86799533801e8ae0e971d78c2",
- "reference": "c54bc3539c3b87e86799533801e8ae0e971d78c2",
+ "url": "https://api.github.com/repos/symfony/debug/zipball/697c527acd9ea1b2d3efac34d9806bf255278b0a",
+ "reference": "697c527acd9ea1b2d3efac34d9806bf255278b0a",
"shasum": ""
},
"require": {
@@ -2765,20 +2765,20 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
- "time": "2016-06-29 05:40:00"
+ "time": "2016-07-30 07:22:48"
},
{
"name": "symfony/event-dispatcher",
- "version": "v3.1.2",
+ "version": "v3.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "7f9839ede2070f53e7e2f0849b9bd14748c434c5"
+ "reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7f9839ede2070f53e7e2f0849b9bd14748c434c5",
- "reference": "7f9839ede2070f53e7e2f0849b9bd14748c434c5",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c0c00c80b3a69132c4e55c3e7db32b4a387615e5",
+ "reference": "c0c00c80b3a69132c4e55c3e7db32b4a387615e5",
"shasum": ""
},
"require": {
@@ -2825,11 +2825,11 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2016-06-29 05:41:56"
+ "time": "2016-07-19 10:45:57"
},
{
"name": "symfony/finder",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
@@ -2878,16 +2878,16 @@
},
{
"name": "symfony/http-foundation",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "1341139f906d295baa4f4abd55293d07e25a065a"
+ "reference": "49ba00f8ede742169cb6b70abe33243f4d673f82"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1341139f906d295baa4f4abd55293d07e25a065a",
- "reference": "1341139f906d295baa4f4abd55293d07e25a065a",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/49ba00f8ede742169cb6b70abe33243f4d673f82",
+ "reference": "49ba00f8ede742169cb6b70abe33243f4d673f82",
"shasum": ""
},
"require": {
@@ -2927,20 +2927,20 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
- "time": "2016-06-29 07:02:21"
+ "time": "2016-07-17 13:54:30"
},
{
"name": "symfony/http-kernel",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "177b63b2d50b63fa6d82ea41359ed9928cc7a1fb"
+ "reference": "d97ba4425e36e79c794e7d14ff36f00f081b37b3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/177b63b2d50b63fa6d82ea41359ed9928cc7a1fb",
- "reference": "177b63b2d50b63fa6d82ea41359ed9928cc7a1fb",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d97ba4425e36e79c794e7d14ff36f00f081b37b3",
+ "reference": "d97ba4425e36e79c794e7d14ff36f00f081b37b3",
"shasum": ""
},
"require": {
@@ -3009,7 +3009,7 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com",
- "time": "2016-06-30 16:30:17"
+ "time": "2016-07-30 09:10:37"
},
{
"name": "symfony/polyfill-mbstring",
@@ -3180,16 +3180,16 @@
},
{
"name": "symfony/process",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "d7cde1f9d94d87060204f863779389b61c382eeb"
+ "reference": "768debc5996f599c4372b322d9061dba2a4bf505"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/d7cde1f9d94d87060204f863779389b61c382eeb",
- "reference": "d7cde1f9d94d87060204f863779389b61c382eeb",
+ "url": "https://api.github.com/repos/symfony/process/zipball/768debc5996f599c4372b322d9061dba2a4bf505",
+ "reference": "768debc5996f599c4372b322d9061dba2a4bf505",
"shasum": ""
},
"require": {
@@ -3225,11 +3225,11 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
- "time": "2016-06-29 05:40:00"
+ "time": "2016-07-28 11:13:34"
},
{
"name": "symfony/routing",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
@@ -3304,16 +3304,16 @@
},
{
"name": "symfony/translation",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "6bf844e1ee3c820c012386c10427a5c67bbefec8"
+ "reference": "eee6c664853fd0576f21ae25725cfffeafe83f26"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/6bf844e1ee3c820c012386c10427a5c67bbefec8",
- "reference": "6bf844e1ee3c820c012386c10427a5c67bbefec8",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/eee6c664853fd0576f21ae25725cfffeafe83f26",
+ "reference": "eee6c664853fd0576f21ae25725cfffeafe83f26",
"shasum": ""
},
"require": {
@@ -3364,20 +3364,20 @@
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
- "time": "2016-06-29 05:40:00"
+ "time": "2016-07-30 07:22:48"
},
{
"name": "symfony/var-dumper",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "2f046e9a9d571f22cc8b26783564876713b06579"
+ "reference": "1f7e071aafc6676fcb6e3f0497f87c2397247377"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2f046e9a9d571f22cc8b26783564876713b06579",
- "reference": "2f046e9a9d571f22cc8b26783564876713b06579",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1f7e071aafc6676fcb6e3f0497f87c2397247377",
+ "reference": "1f7e071aafc6676fcb6e3f0497f87c2397247377",
"shasum": ""
},
"require": {
@@ -3427,7 +3427,7 @@
"debug",
"dump"
],
- "time": "2016-06-29 05:40:00"
+ "time": "2016-07-26 08:03:56"
},
{
"name": "themattharris/tmhoauth",
@@ -5100,7 +5100,7 @@
},
{
"name": "symfony/css-selector",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
@@ -5153,16 +5153,16 @@
},
{
"name": "symfony/dom-crawler",
- "version": "v3.0.8",
+ "version": "v3.0.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
- "reference": "62769e3409006b937bb333b29da8df9a8b262975"
+ "reference": "dff8fecf1f56990d88058e3a1885c2a5f1b8e970"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/62769e3409006b937bb333b29da8df9a8b262975",
- "reference": "62769e3409006b937bb333b29da8df9a8b262975",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/dff8fecf1f56990d88058e3a1885c2a5f1b8e970",
+ "reference": "dff8fecf1f56990d88058e3a1885c2a5f1b8e970",
"shasum": ""
},
"require": {
@@ -5205,20 +5205,20 @@
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
- "time": "2016-06-29 05:40:00"
+ "time": "2016-07-30 07:22:48"
},
{
"name": "symfony/yaml",
- "version": "v3.1.2",
+ "version": "v3.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de"
+ "reference": "1819adf2066880c7967df7180f4f662b6f0567ac"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/2884c26ce4c1d61aebf423a8b912950fe7c764de",
- "reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/1819adf2066880c7967df7180f4f662b6f0567ac",
+ "reference": "1819adf2066880c7967df7180f4f662b6f0567ac",
"shasum": ""
},
"require": {
@@ -5254,7 +5254,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2016-06-29 05:41:56"
+ "time": "2016-07-17 14:02:08"
},
{
"name": "webmozart/assert",
diff --git a/resources/views/singlenote.blade.php b/resources/views/singlenote.blade.php
index d564616e..9c5f6525 100644
--- a/resources/views/singlenote.blade.php
+++ b/resources/views/singlenote.blade.php
@@ -20,13 +20,13 @@
@if(count($likes) > 0)Likes
@endif
@foreach($likes as $like)
-
+
@endforeach
@if(count($reposts) > 0)Reposts
@endif
@foreach($reposts as $repost)
{{ $repost['name'] }}
- reposted this at {{ $repost['date'] }}.
+ reposted this at {{ $repost['date'] }}.
@endforeach
@stop
diff --git a/tests/NotesTest.php b/tests/NotesTest.php
index 233704e3..c1ac954d 100644
--- a/tests/NotesTest.php
+++ b/tests/NotesTest.php
@@ -2,6 +2,7 @@
namespace App\Tests;
+use Cache;
use TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
@@ -131,15 +132,15 @@ class NotesTest extends TestCase
}
/**
- * Test the bridgy url shim method.
+ * Test a correct profile link is formed from a generic URL.
*
* @return void
*/
- public function testBridgy()
+ public function testCreatePhotoLinkWithNonCachedImage()
{
- $url = 'https://brid-gy.appspot.com/comment/twitter/jonnybarnes/497778866816299008/497781260937203712';
- $expected = 'https://twitter.com/_/status/497781260937203712';
- $this->assertEquals($expected, $this->notesController->bridgyReply($url));
+ $homepage = 'https://example.org/profile.png';
+ $expected = 'https://example.org/profile.png';
+ $this->assertEquals($expected, $this->notesController->createPhotoLink($homepage));
}
/**
@@ -147,10 +148,10 @@ class NotesTest extends TestCase
*
* @return void
*/
- public function testCreatePhotoLinkWithGenericURL()
+ public function testCreatePhotoLinkWithCachedImage()
{
- $homepage = 'https://example.org';
- $expected = '/assets/profile-images/example.org/image';
+ $homepage = 'https://aaronparecki.com/profile.png';
+ $expected = '/assets/profile-images/aaronparecki.com/image';
$this->assertEquals($expected, $this->notesController->createPhotoLink($homepage));
}
@@ -159,7 +160,7 @@ class NotesTest extends TestCase
*
* @return void
*/
- public function testCreatePhotoLinkWithTwitterProfileImageURL()
+ public function testCreatePhotoLinkWithTwimgProfileImageURL()
{
$twitterProfileImage = 'http://pbs.twimg.com/1234';
$expected = 'https://pbs.twimg.com/1234';
@@ -171,9 +172,11 @@ class NotesTest extends TestCase
*
* @return void
*/
- public function testCreatePhotoLinkWithTwitterURL()
+ public function testCreatePhotoLinkWithCachedTwitterURL()
{
$twitterURL = 'https://twitter.com/example';
- $this->assertNull($this->notesController->createPhotoLink($twitterURL));
+ $expected = 'https://pbs.twimg.com/static_profile_link.jpg';
+ Cache::put($twitterURL, $expected, 1);
+ $this->assertEquals($expected, $this->notesController->createPhotoLink($twitterURL));
}
}
diff --git a/tests/ProcessWebMentionTest.php b/tests/ProcessWebMentionTest.php
new file mode 100644
index 00000000..77e04ba0
--- /dev/null
+++ b/tests/ProcessWebMentionTest.php
@@ -0,0 +1,29 @@
+appurl = config('app.url');
+ }
+
+ /**
+ * A basic test.
+ *
+ * @return void
+ */
+ public function testExample()
+ {
+
+ }
+}
diff --git a/tests/WebMentionsTest.php b/tests/WebMentionsTest.php
new file mode 100644
index 00000000..a0bf502c
--- /dev/null
+++ b/tests/WebMentionsTest.php
@@ -0,0 +1,92 @@
+appurl = config('app.url');
+ }
+
+ /**
+ * Test webmentions without source and target are rejected.
+ *
+ * @return void
+ */
+ public function testWebmentionsWithoutSourceAndTargetAreRejected()
+ {
+ $this->call('POST', $this->appurl . '/webmention', ['source' => 'https://example.org/post/123']);
+ $this->assertResponseStatus(400)
+ ->see('You need both the target and source parameters');
+ }
+
+ /**
+ * Test invalid target gets a 400 response.
+ *
+ * @return void
+ */
+ public function testInvalidTargetReturns400Response()
+ {
+ $this->call('POST', $this->appurl . '/webmention', [
+ 'source' => 'https://example.org/post/123',
+ 'target' => $this->appurl . '/invalid/target'
+ ]);
+ $this->assertResponseStatus(400)
+ ->see('Invalid request');
+ }
+
+ /**
+ * Test blog target gets a 501 response.
+ *
+ * @return void
+ */
+ public function testBlogpostTargetReturns501Response()
+ {
+ $this->call('POST', $this->appurl . '/webmention', [
+ 'source' => 'https://example.org/post/123',
+ 'target' => $this->appurl . '/blog/target'
+ ]);
+ $this->assertResponseStatus(501)
+ ->see('I don’t accept webmentions for blog posts yet.');
+ }
+
+ /**
+ * Test that a non-existant note gives a 400 response.
+ *
+ * @return void
+ */
+ public function testNonexistantNoteReturns400Response()
+ {
+ $this->call('POST', $this->appurl . '/webmention', [
+ 'source' => 'https://example.org/post/123',
+ 'target' => $this->appurl . '/notes/ZZZZZ'
+ ]);
+ $this->assertResponseStatus(400)
+ ->see('This note doesn’t exist.');
+ }
+
+ /**
+ * Test a legit webmention triggers the ProcessWebMention job.
+ *
+ * @return void
+ */
+ public function testLegitimateWebmnetionTriggersProcessWebMentionJob()
+ {
+ $this->expectsJobs(\App\Jobs\ProcessWebMention::class);
+ $this->call('POST', $this->appurl . '/webmention', [
+ 'source' => 'https://example.org/post/123',
+ 'target' => $this->appurl . '/notes/B'
+ ]);
+ $this->assertResponseStatus(202)
+ ->see('Webmention received, it will be processed shortly');
+ }
+}
From 94ec2ec25404eb632b9afab7a6d2c66cd694cbe0 Mon Sep 17 00:00:00 2001
From: Jonny Barnes
Date: Mon, 5 Sep 2016 23:39:54 +0100
Subject: [PATCH 7/7] Should now be able to send webmentions to
webmention.rocks
---
app/Jobs/SendWebMentions.php | 40 +++++++++++++++++++++++++-----------
changelog.md | 2 ++
composer.lock | 15 +++++++-------
3 files changed, 38 insertions(+), 19 deletions(-)
diff --git a/app/Jobs/SendWebMentions.php b/app/Jobs/SendWebMentions.php
index 4cfbaafc..4caf6c7d 100644
--- a/app/Jobs/SendWebMentions.php
+++ b/app/Jobs/SendWebMentions.php
@@ -20,9 +20,10 @@ class SendWebMentions extends Job implements ShouldQueue
* @param Note $note
* @return void
*/
- public function __construct(Note $note)
+ public function __construct(Note $note, Client $guzzle = null)
{
$this->note = $note;
+ $this->guzzle = $guzzle ?? new Client();
}
/**
@@ -31,16 +32,16 @@ class SendWebMentions extends Job implements ShouldQueue
* @param \GuzzleHttp\Client $guzzle
* @return void
*/
- public function handle(Client $guzzle)
+ public function handle()
{
//grab the URLs
$urlsInReplyTo = explode(' ', $this->note->in_reply_to);
$urlsNote = $this->getLinks($this->note->note);
$urls = array_filter(array_merge($urlsInReplyTo, $urlsNote)); //filter out none URLs
foreach ($urls as $url) {
- $endpoint = $this->discoverWebmentionEndpoint($url, $guzzle);
+ $endpoint = $this->discoverWebmentionEndpoint($url, $this->guzzle);
if ($endpoint) {
- $guzzle->post($endpoint, [
+ $this->guzzle->post($endpoint, [
'form_params' => [
'source' => $this->note->longurl,
'target' => $url,
@@ -73,8 +74,8 @@ class SendWebMentions extends Job implements ShouldQueue
//check HTTP Headers for webmention endpoint
$links = \GuzzleHttp\Psr7\parse_header($response->getHeader('Link'));
foreach ($links as $link) {
- if ($link['rel'] == 'webmention') {
- return trim($link[0], '<>');
+ if (mb_stristr($link['rel'], 'webmention')) {
+ return $this->resolveUri($link[0], $url);
}
}
@@ -89,11 +90,7 @@ class SendWebMentions extends Job implements ShouldQueue
$endpoint = $rels[0]['http://webmention.org/'][0];
}
if ($endpoint) {
- if (filter_var($endpoint, FILTER_VALIDATE_URL)) {
- return $endpoint;
- }
- //it must be a relative url, so resolve with php-mf2
- return $mf2->resolveUrl($endpoint);
+ return $this->resolveUri($endpoint, $url);
}
return false;
@@ -105,7 +102,7 @@ class SendWebMentions extends Job implements ShouldQueue
* @param string $html
* @return array $urls
*/
- private function getLinks($html)
+ public function getLinks($html)
{
$urls = [];
$dom = new \DOMDocument();
@@ -117,4 +114,23 @@ class SendWebMentions extends Job implements ShouldQueue
return $urls;
}
+
+ /**
+ * Resolve a URI if necessary.
+ *
+ * @param string $url
+ * @param string $base
+ * @return string
+ */
+ public function resolveUri(string $url, string $base): string
+ {
+ $endpoint = \GuzzleHttp\Psr7\uri_for($url);
+ if ($endpoint->getScheme() !== null) {
+ return (string) $endpoint;
+ }
+ return (string) \GuzzleHttp\Psr7\Uri::resolve(
+ \GuzzleHttp\Psr7\uri_for($base),
+ $endpoint
+ );
+ }
}
diff --git a/changelog.md b/changelog.md
index 4b8f3b2f..788e181e 100644
--- a/changelog.md
+++ b/changelog.md
@@ -3,6 +3,8 @@
## Version {next}
- Adding jsonb column to store webmentions’ mf2.
* As of L5.2 this needs a custom command to drop NOT NULL from content, L5.3 should allow a fix for this
+ - Refactor receiving webmention code
+ - Refactor sending webmention code to pass webmention.rocks
## Version 0.0.8.5 (2016-07-18)
- Set the size of the textarea in a form better
diff --git a/composer.lock b/composer.lock
index 92da6d7a..f7578a1f 100644
--- a/composer.lock
+++ b/composer.lock
@@ -182,12 +182,12 @@
"version": "0.16",
"source": {
"type": "git",
- "url": "https://github.com/Bosnadev/Database.git",
+ "url": "https://github.com/bosnadev/database.git",
"reference": "c2748d118415d30ce69b792448689285d01ffdb9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Bosnadev/Database/zipball/c2748d118415d30ce69b792448689285d01ffdb9",
+ "url": "https://api.github.com/repos/bosnadev/database/zipball/c2748d118415d30ce69b792448689285d01ffdb9",
"reference": "c2748d118415d30ce69b792448689285d01ffdb9",
"shasum": ""
},
@@ -2154,16 +2154,16 @@
},
{
"name": "psr/http-message",
- "version": "1.0",
+ "version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
- "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298"
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
- "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
@@ -2191,6 +2191,7 @@
}
],
"description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
@@ -2199,7 +2200,7 @@
"request",
"response"
],
- "time": "2015-05-04 20:22:00"
+ "time": "2016-08-06 14:39:51"
},
{
"name": "psr/log",