Merge branch 'release/0.5.17'

This commit is contained in:
Jonny Barnes 2017-06-22 17:45:57 +01:00
commit aa126d728d
44 changed files with 4879 additions and 4500 deletions

View file

@ -14,6 +14,7 @@ addons:
packages: packages:
- nginx - nginx
- realpath - realpath
- postgresql-9.6-postgis-2.3
artifacts: artifacts:
s3_region: "eu-west-1" s3_region: "eu-west-1"
paths: paths:

View file

@ -62,29 +62,29 @@ class MicropubController extends Controller
// Log the request // Log the request
Log::debug($request); Log::debug($request);
if ($tokenData->hasClaim('scope')) { if ($tokenData->hasClaim('scope')) {
if (($request->input('h') == 'entry') || ($request->input('type')[0] == 'h-entry')) { if (($request->input('h') == 'entry') || ($request->input('type.0') == 'h-entry')) {
if (stristr($tokenData->getClaim('scope'), 'create') === false) { if (stristr($tokenData->getClaim('scope'), 'create') === false) {
return $this->returnInsufficientScopeResponse(); return $this->returnInsufficientScopeResponse();
} }
$data = []; $data = [];
$data['client-id'] = $tokenData->getClaim('client_id'); $data['client-id'] = $tokenData->getClaim('client_id');
if ($request->header('Content-Type') == 'application/json') { if ($request->header('Content-Type') == 'application/json') {
if (is_string($request->input('properties.content')[0])) { if (is_string($request->input('properties.content.0'))) {
$data['content'] = $request->input('properties.content')[0]; //plaintext content $data['content'] = $request->input('properties.content.0'); //plaintext content
} }
if (is_array($request->input('properties.content')[0]) if (is_array($request->input('properties.content.0'))
&& array_key_exists('html', $request->input('properties.content')[0]) && array_key_exists('html', $request->input('properties.content.0'))
) { ) {
$data['content'] = $request->input('properties.content')[0]['html']; $data['content'] = $request->input('properties.content.0.html');
} }
$data['in-reply-to'] = $request->input('properties.in-reply-to')[0]; $data['in-reply-to'] = $request->input('properties.in-reply-to.0');
// check location is geo: string // check location is geo: string
if (is_string($request->input('properties.location.0'))) { if (is_string($request->input('properties.location.0'))) {
$data['location'] = $request->input('properties.location.0'); $data['location'] = $request->input('properties.location.0');
} }
// check location is h-card // check location is h-card
if (is_array($request->input('properties.location.0'))) { if (is_array($request->input('properties.location.0'))) {
if ($request->input('properties.location.0.type' === 'h-card')) { if ($request->input('properties.location.0.type.0' === 'h-card')) {
try { try {
$place = $this->placeService->createPlaceFromCheckin($request->input('properties.location.0')); $place = $this->placeService->createPlaceFromCheckin($request->input('properties.location.0'));
$data['checkin'] = $place->longurl; $data['checkin'] = $place->longurl;
@ -93,7 +93,7 @@ class MicropubController extends Controller
} }
} }
} }
$data['published'] = $request->input('properties.published')[0]; $data['published'] = $request->input('properties.published.0');
//create checkin place //create checkin place
if (array_key_exists('checkin', $request->input('properties'))) { if (array_key_exists('checkin', $request->input('properties'))) {
$data['swarm-url'] = $request->input('properties.syndication.0'); $data['swarm-url'] = $request->input('properties.syndication.0');
@ -374,7 +374,7 @@ class MicropubController extends Controller
//check post scope //check post scope
if ($tokenData->hasClaim('scope')) { if ($tokenData->hasClaim('scope')) {
if (stristr($token->getClaim('scope'), 'post') === false) { if (stristr($tokenData->getClaim('scope'), 'create') === false) {
return $this->returnInsufficientScopeResponse(); return $this->returnInsufficientScopeResponse();
} }
//check media valid //check media valid

View file

@ -2,16 +2,9 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Twitter; use App\Note;
use HTMLPurifier;
use App\{Note, Tag};
use GuzzleHttp\Client;
use HTMLPurifier_Config;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Jonnybarnes\IndieWeb\Numbers; use Jonnybarnes\IndieWeb\Numbers;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Cache;
use Jonnybarnes\WebmentionsParser\Authorship;
// Need to sort out Twitter and webmentions! // Need to sort out Twitter and webmentions!
@ -25,41 +18,13 @@ class NotesController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
$notes = Note::orderBy('id', 'desc')->with('webmentions', 'place', 'media')->paginate(10); $notes = Note::orderBy('id', 'desc')
foreach ($notes as $note) { ->with('place', 'media', 'client')
$replies = 0; ->withCount(['webmentions As replies' => function ($query) {
foreach ($note->webmentions as $webmention) { $query->where('type', 'in-reply-to');
if ($webmention->type == 'in-reply-to') { }])->paginate(10);
$replies++;
}
}
$note->replies = $replies;
$note->twitter = $this->checkTwitterReply($note->in_reply_to);
$note->iso8601_time = $note->updated_at->toISO8601String();
$note->human_time = $note->updated_at->diffForHumans();
if ($note->location && ($note->place === null)) {
$pieces = explode(':', $note->location);
$latlng = explode(',', $pieces[0]);
$note->latitude = trim($latlng[0]);
$note->longitude = trim($latlng[1]);
$note->address = $this->reverseGeoCode((float) trim($latlng[0]), (float) trim($latlng[1]));
}
if ($note->place !== null) {
$lnglat = explode(' ', $note->place->location);
$note->latitude = $lnglat[1];
$note->longitude = $lnglat[0];
$note->address = $note->place->name;
$note->placeLink = '/places/' . $note->place->slug;
}
/*$mediaLinks = [];
foreach ($note->media()->get() as $media) {
$mediaLinks[] = $media->url;
}*/
}
$homepage = ($request->path() == '/'); return view('notes.index', compact('notes'));
return view('notes.index', compact('notes', 'homepage'));
} }
/** /**
@ -70,90 +35,9 @@ class NotesController extends Controller
*/ */
public function show($urlId) public function show($urlId)
{ {
$numbers = new Numbers(); $note = Note::nb60($urlId)->with('webmentions')->first();
$authorship = new Authorship();
$realId = $numbers->b60tonum($urlId);
$note = Note::find($realId);
$replies = [];
$reposts = [];
$likes = [];
$carbon = new \Carbon\Carbon();
foreach ($note->webmentions as $webmention) {
/*
reply->url |
reply->photo | Author
reply->name |
reply->source
reply->date
reply->reply
repost->url | return view('notes.show', compact('note'));
repost->photo | Author
repost->name |
repost->date
repost->source
like->url |
like->photo | Author
like->name |
*/
$microformats = json_decode($webmention->mf2, true);
$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 'in-reply-to':
$content['source'] = $webmention->source;
if (isset($microformats['items'][0]['properties']['published'][0])) {
try {
$content['date'] = $carbon->parse(
$microformats['items'][0]['properties']['published'][0]
)->toDayDateTimeString();
} catch (\Exception $exception) {
$content['date'] = $webmention->updated_at->toDayDateTimeString();
}
} else {
$content['date'] = $webmention->updated_at->toDayDateTimeString();
}
$content['reply'] = $this->filterHTML(
$microformats['items'][0]['properties']['content'][0]['html']
);
$replies[] = $content;
break;
case 'repost-of':
$content['date'] = $carbon->parse(
$microformats['items'][0]['properties']['published'][0]
)->toDayDateTimeString();
$content['source'] = $webmention->source;
$reposts[] = $content;
break;
case 'like-of':
$likes[] = $content;
break;
}
}
$note->twitter = $this->checkTwitterReply($note->in_reply_to);
$note->iso8601_time = $note->updated_at->toISO8601String();
$note->human_time = $note->updated_at->diffForHumans();
if ($note->location && ($note->place === null)) {
$pieces = explode(':', $note->location);
$latlng = explode(',', $pieces[0]);
$note->latitude = trim($latlng[0]);
$note->longitude = trim($latlng[1]);
$note->address = $this->reverseGeoCode((float) trim($latlng[0]), (float) trim($latlng[1]));
}
if ($note->place !== null) {
$lnglat = explode(' ', $note->place->location);
$note->latitude = $lnglat[1];
$note->longitude = $lnglat[0];
$note->address = $note->place->name;
$note->placeLink = '/places/' . $note->place->slug;
}
return view('notes.show', compact('note', 'replies', 'reposts', 'likes'));
} }
/** /**
@ -164,12 +48,7 @@ class NotesController extends Controller
*/ */
public function redirect($decId) public function redirect($decId)
{ {
$numbers = new Numbers(); return redirect(config('app.url') . '/notes/' . (new Numbers())->numto60($decId));
$realId = $numbers->numto60($decId);
$url = config('app.url') . '/notes/' . $realId;
return redirect($url);
} }
/** /**
@ -183,180 +62,7 @@ class NotesController extends Controller
$notes = Note::whereHas('tags', function ($query) use ($tag) { $notes = Note::whereHas('tags', function ($query) use ($tag) {
$query->where('tag', $tag); $query->where('tag', $tag);
})->get(); })->get();
foreach ($notes as $note) {
$note->iso8601_time = $note->updated_at->toISO8601String();
$note->human_time = $note->updated_at->diffForHumans();
}
return view('notes.tagged', compact('notes', 'tag')); return view('notes.tagged', compact('notes', 'tag'));
} }
/**
* 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, 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;
}
/**
* Twitter!!!
*
* @param string The reply to URL
* @return string | null
*/
private function checkTwitterReply($url)
{
if ($url == null) {
return;
}
if (mb_substr($url, 0, 20, 'UTF-8') !== 'https://twitter.com/') {
return;
}
$arr = explode('/', $url);
$tweetId = end($arr);
if (Cache::has($tweetId)) {
return Cache::get($tweetId);
}
try {
$oEmbed = Twitter::getOembed([
'id' => $tweetId,
'align' => 'center',
'omit_script' => true,
'maxwidth' => 550,
]);
} catch (\Exception $e) {
return;
}
Cache::put($tweetId, $oEmbed, ($oEmbed->cache_age / 60));
return $oEmbed;
}
/**
* Filter the HTML in a reply webmention.
*
* @param string The reply HTML
* @return string The filtered HTML
*/
private function filterHTML($html)
{
$config = HTMLPurifier_Config::createDefault();
$config->set('Cache.SerializerPath', storage_path() . '/HTMLPurifier');
$config->set('HTML.TargetBlank', true);
$purifier = new HTMLPurifier($config);
return $purifier->purify($html);
}
/**
* Do a reverse geocode lookup of a `lat,lng` value.
*
* @param float The latitude
* @param float The longitude
* @return string The location HTML
*/
public function reverseGeoCode(float $latitude, float $longitude): string
{
$latlng = $latitude . ',' . $longitude;
return Cache::get($latlng, function () use ($latlng, $latitude, $longitude) {
$guzzle = new Client();
$response = $guzzle->request('GET', 'https://nominatim.openstreetmap.org/reverse', [
'query' => [
'format' => 'json',
'lat' => $latitude,
'lon' => $longitude,
'zoom' => 18,
'addressdetails' => 1,
],
'headers' => ['User-Agent' => 'jonnybarnes.uk via Guzzle, email jonny@jonnybarnes.uk'],
]);
$json = json_decode($response->getBody());
if (isset($json->address->town)) {
$address = '<span class="p-locality">'
. $json->address->town
. '</span>, <span class="p-country-name">'
. $json->address->country
. '</span>';
Cache::forever($latlng, $address);
return $address;
}
if (isset($json->address->city)) {
$address = $json->address->city . ', ' . $json->address->country;
Cache::forever($latlng, $address);
return $address;
}
if (isset($json->address->county)) {
$address = '<span class="p-region">'
. $json->address->county
. '</span>, <span class="p-country-name">'
. $json->address->country
. '</span>';
Cache::forever($latlng, $address);
return $address;
}
$adress = '<span class="p-country-name">' . $json->address->country . '</span>';
Cache::forever($latlng, $address);
return $address;
});
}
private function getGeoJson($longitude, $latitude, $title, $icon)
{
$icon = $icon ?? 'marker';
return
"{
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [$longitude, $latitude]
},
'properties': {
'title': '$title',
'icon': '$icon'
}
}]
}";
}
} }

View file

@ -0,0 +1,42 @@
<?php
namespace App\Jobs;
use App\MicropubClient;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class AddClientToDatabase implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $client_id;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(string $client_id)
{
$this->client_id = $client_id;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if (MicropubClient::where('client_url', $this->client_id)->count() == 0) {
$client = MicropubClient::create([
'client_url' => $this->client_id,
'client_name' => $this->client_id, // default client name is the URL
]);
}
}
}

View file

@ -3,16 +3,14 @@
namespace App\Jobs; namespace App\Jobs;
use Mf2; use Mf2;
use App\Note;
use App\WebMention;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use App\{Note, WebMention};
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Jonnybarnes\WebmentionsParser\Parser; use Jonnybarnes\WebmentionsParser\Parser;
use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Exception\RequestException;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use App\Exceptions\RemoteContentNotFoundException; use App\Exceptions\RemoteContentNotFoundException;
use Illuminate\Queue\{InteractsWithQueue, SerializesModels};
class ProcessWebMention implements ShouldQueue class ProcessWebMention implements ShouldQueue
{ {

View file

@ -19,4 +19,14 @@ class MicropubClient extends Model
* @var array * @var array
*/ */
protected $fillable = ['client_url', 'client_name']; protected $fillable = ['client_url', 'client_name'];
/**
* Define the relationship with notes.
*
* @return void
*/
public function notes()
{
return $this->hasMany('App\Note', 'client_id', 'client_url');
}
} }

View file

@ -2,7 +2,10 @@
namespace App; namespace App;
use Cache;
use Twitter;
use Normalizer; use Normalizer;
use GuzzleHttp\Client;
use Laravel\Scout\Searchable; use Laravel\Scout\Searchable;
use Jonnybarnes\IndieWeb\Numbers; use Jonnybarnes\IndieWeb\Numbers;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -33,6 +36,16 @@ class Note extends Model
return $this->belongsToMany('App\Tag'); return $this->belongsToMany('App\Tag');
} }
/**
* Define the relationship with clients.
*
* @var array?
*/
public function client()
{
return $this->belongsTo('App\MicropubClient', 'client_id', 'client_url');
}
/** /**
* Define the relationship with webmentions. * Define the relationship with webmentions.
* *
@ -96,17 +109,6 @@ class Note extends Model
]; ];
} }
/**
* A mutator to ensure that in-reply-to is always non-empty or null.
*
* @param string value
* @return string
*/
public function setInReplyToAttribute($value)
{
$this->attributes['in_reply_to'] = empty($value) ? null : $value;
}
/** /**
* Normalize the note to Unicode FORM C. * Normalize the note to Unicode FORM C.
* *
@ -168,6 +170,26 @@ class Note extends Model
return config('app.shorturl') . '/notes/' . $this->nb60id; return config('app.shorturl') . '/notes/' . $this->nb60id;
} }
/**
* Get the ISO8601 value for mf2.
*
* @return string
*/
public function getIso8601Attribute()
{
return $this->updated_at->toISO8601String();
}
/**
* Get the ISO8601 value for mf2.
*
* @return string
*/
public function getHumandiffAttribute()
{
return $this->updated_at->diffForHumans();
}
/** /**
* Get the pubdate value for RSS feeds. * Get the pubdate value for RSS feeds.
* *
@ -179,26 +201,85 @@ class Note extends Model
} }
/** /**
* Get the relavent client name assocaited with the client id. * Get the latitude value.
* *
* @return string|null * @return string|null
*/ */
public function getClientNameAttribute() public function getLatitudeAttribute()
{ {
if ($this->client_id == null) { if ($this->place !== null) {
$lnglat = explode(' ', $this->place->location);
return $lnglat[1];
}
if ($this->location !== null) {
$pieces = explode(':', $this->location);
$latlng = explode(',', $pieces[0]);
return trim($latlng[0]);
}
}
/**
* Get the longitude value.
*
* @return string|null
*/
public function getLongitudeAttribute()
{
if ($this->place !== null) {
$lnglat = explode(' ', $this->place->location);
return $lnglat[1];
}
if ($this->location !== null) {
$pieces = explode(':', $this->location);
$latlng = explode(',', $pieces[0]);
return trim($latlng[1]);
}
}
/**
* Get the address for a note. This is either a reverse geo-code from the
* location, or is derived from the associated place.
*
* @return string|null
*/
public function getAddressAttribute()
{
if ($this->place !== null) {
return $this->place->name;
}
if ($this->location !== null) {
return $this->reverseGeoCode((float) $this->latitude, (float) $this->longitude);
}
}
public function getTwitterAttribute()
{
if ($this->in_reply_to == null || mb_substr($this->in_reply_to, 0, 20, 'UTF-8') !== 'https://twitter.com/') {
return; return;
} }
$name = MicropubClient::where('client_url', $this->client_id)->value('client_name');
if ($name == null) {
$url = parse_url($this->client_id);
if (isset($url['path'])) {
return $url['host'] . $url['path'];
}
return $url['host']; $arr = explode('/', $url);
$tweetId = end($arr);
if (Cache::has($tweetId)) {
return Cache::get($tweetId);
} }
try {
$oEmbed = Twitter::getOembed([
'id' => $tweetId,
'align' => 'center',
'omit_script' => true,
'maxwidth' => 550,
]);
} catch (\Exception $e) {
return;
}
Cache::put($tweetId, $oEmbed, ($oEmbed->cache_age / 60));
return $name; return $oEmbed;
} }
/** /**
@ -284,4 +365,61 @@ class Note extends Model
return $text; return $text;
} }
/**
* Do a reverse geocode lookup of a `lat,lng` value.
*
* @param float The latitude
* @param float The longitude
* @return string The location HTML
*/
public function reverseGeoCode(float $latitude, float $longitude): string
{
$latlng = $latitude . ',' . $longitude;
return Cache::get($latlng, function () use ($latlng, $latitude, $longitude) {
$guzzle = new Client();
$response = $guzzle->request('GET', 'https://nominatim.openstreetmap.org/reverse', [
'query' => [
'format' => 'json',
'lat' => $latitude,
'lon' => $longitude,
'zoom' => 18,
'addressdetails' => 1,
],
'headers' => ['User-Agent' => 'jonnybarnes.uk via Guzzle, email jonny@jonnybarnes.uk'],
]);
$json = json_decode($response->getBody());
if (isset($json->address->town)) {
$address = '<span class="p-locality">'
. $json->address->town
. '</span>, <span class="p-country-name">'
. $json->address->country
. '</span>';
Cache::forever($latlng, $address);
return $address;
}
if (isset($json->address->city)) {
$address = $json->address->city . ', ' . $json->address->country;
Cache::forever($latlng, $address);
return $address;
}
if (isset($json->address->county)) {
$address = '<span class="p-region">'
. $json->address->county
. '</span>, <span class="p-country-name">'
. $json->address->country
. '</span>';
Cache::forever($latlng, $address);
return $address;
}
$adress = '<span class="p-country-name">' . $json->address->country . '</span>';
Cache::forever($latlng, $address);
return $address;
});
}
} }

View file

@ -76,19 +76,6 @@ class Place extends Model
return $query->where($field, '<=', $distance)->orderBy($field); return $query->where($field, '<=', $distance)->orderBy($field);
} }
/*
* Convert location to text.
*
* @param text $value
* @return text
*
public function getLocationAttribute($value)
{
$result = DB::select(DB::raw("SELECT ST_AsText('$value')"));
return $result[0]->st_astext;
}*/
/** /**
* Get the latitude from the `location` property. * Get the latitude from the `location` property.
* *

View file

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Services; namespace App\Services;
use App\Jobs\AddClientToDatabase;
use Lcobucci\JWT\Signer\Hmac\Sha256; use Lcobucci\JWT\Signer\Hmac\Sha256;
use App\Exceptions\InvalidTokenException; use App\Exceptions\InvalidTokenException;
use Lcobucci\JWT\{Builder, Parser, Token}; use Lcobucci\JWT\{Builder, Parser, Token};
@ -26,6 +27,7 @@ class TokenService
->set('nonce', bin2hex(random_bytes(8))) ->set('nonce', bin2hex(random_bytes(8)))
->sign($signer, config('app.key')) ->sign($signer, config('app.key'))
->getToken(); ->getToken();
dispatch(new AddClientToDatabase($data['client_id']));
return (string) $token; return (string) $token;
} }

View file

@ -2,7 +2,14 @@
namespace App; namespace App;
use Cache;
use Twitter;
use HTMLPurifier;
use Carbon\Carbon;
use HTMLPurifier_Config;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Jonnybarnes\WebmentionsParser\Authorship;
class WebMention extends Model class WebMention extends Model
{ {
@ -29,4 +36,112 @@ class WebMention extends Model
* @var array * @var array
*/ */
protected $guarded = ['id']; protected $guarded = ['id'];
/**
* Get the author of the webmention.
*
* @return array
*/
public function getAuthorAttribute()
{
$authorship = new Authorship();
$hCard = $authorship->findAuthor(json_decode($this->mf2, true));
if (array_key_exists('properties', $hCard) &&
array_key_exists('photo', $hCard['properties'])
) {
$hCard['properties']['photo'][0] = $this->createPhotoLink($hCard['properties']['photo'][0]);
}
return $hCard;
}
/**
* Get the published value for the webmention.
*
* @return string
*/
public function getPublishedAttribute()
{
$microformats = json_decode($this->mf2, true);
$carbon = new Carbon();
if (isset($microformats['items'][0]['properties']['published'][0])) {
try {
$published = $carbon->parse(
$microformats['items'][0]['properties']['published'][0]
)->toDayDateTimeString();
} catch (\Exception $exception) {
$published = $webmention->updated_at->toDayDateTimeString();
}
} else {
$published = $webmention->updated_at->toDayDateTimeString();
}
return $published;
}
/**
* Get the filteres HTML of a reply.
*
* @return strin|null
*/
public function getReplyAttribute()
{
$microformats = json_decode($this->mf2, true);
if (isset($microformats['items'][0]['properties']['content'][0]['html'])) {
return $this->filterHTML($microformats['items'][0]['properties']['content'][0]['html']);
}
}
/**
* Create the photo link.
*
* @param string
* @return string
*/
public function createPhotoLink(string $url): string
{
$url = normalize_url($url);
$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;
}
/**
* Filter the HTML in a reply webmention.
*
* @param string The reply HTML
* @return string The filtered HTML
*/
private function filterHTML($html)
{
$config = HTMLPurifier_Config::createDefault();
$config->set('Cache.SerializerPath', storage_path() . '/HTMLPurifier');
$config->set('HTML.TargetBlank', true);
$purifier = new HTMLPurifier($config);
return $purifier->purify($html);
}
} }

View file

@ -1,6 +1,10 @@
# Changelog # Changelog
## Version 0.5.16 ## Version 0.5.17 (2017-06-22)
- Lots of code tidying, especially in the notes controller
- Fix issue#53 regarding uploading photos
## Version 0.5.16 (2017-06-17)
- Allow place `slug`s to be re-generated - Allow place `slug`s to be re-generated
- Add syndication links for swarm and instagram - Add syndication links for swarm and instagram
- Move bio to its own template, next step database? - Move bio to its own template, next step database?

42
composer.lock generated
View file

@ -8,16 +8,16 @@
"packages": [ "packages": [
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
"version": "3.29.2", "version": "3.29.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aws/aws-sdk-php.git", "url": "https://github.com/aws/aws-sdk-php.git",
"reference": "1f19f74913a31fac8e98c24cef26040a16c88a33" "reference": "c60a477ad5ba1b120d4d80cbddf97fbe36573996"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/1f19f74913a31fac8e98c24cef26040a16c88a33", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c60a477ad5ba1b120d4d80cbddf97fbe36573996",
"reference": "1f19f74913a31fac8e98c24cef26040a16c88a33", "reference": "c60a477ad5ba1b120d4d80cbddf97fbe36573996",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -84,7 +84,7 @@
"s3", "s3",
"sdk" "sdk"
], ],
"time": "2017-06-09T18:57:25+00:00" "time": "2017-06-19T19:14:37+00:00"
}, },
{ {
"name": "barnabywalters/mf-cleaner", "name": "barnabywalters/mf-cleaner",
@ -1580,16 +1580,16 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v5.4.25", "version": "v5.4.27",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "6bcc9b1f542b3deed16d51f6aa1fe318ab407c2a" "reference": "66f5e1b37cbd66e730ea18850ded6dc0ad570404"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/6bcc9b1f542b3deed16d51f6aa1fe318ab407c2a", "url": "https://api.github.com/repos/laravel/framework/zipball/66f5e1b37cbd66e730ea18850ded6dc0ad570404",
"reference": "6bcc9b1f542b3deed16d51f6aa1fe318ab407c2a", "reference": "66f5e1b37cbd66e730ea18850ded6dc0ad570404",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1705,7 +1705,7 @@
"framework", "framework",
"laravel" "laravel"
], ],
"time": "2017-06-07T13:35:12+00:00" "time": "2017-06-15T19:08:25+00:00"
}, },
{ {
"name": "laravel/scout", "name": "laravel/scout",
@ -2144,16 +2144,16 @@
}, },
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "1.22.1", "version": "1.23.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Seldaek/monolog.git", "url": "https://github.com/Seldaek/monolog.git",
"reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0" "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/1e044bc4b34e91743943479f1be7a1d5eb93add0", "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0", "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2174,7 +2174,7 @@
"phpunit/phpunit-mock-objects": "2.3.0", "phpunit/phpunit-mock-objects": "2.3.0",
"ruflin/elastica": ">=0.90 <3.0", "ruflin/elastica": ">=0.90 <3.0",
"sentry/sentry": "^0.13", "sentry/sentry": "^0.13",
"swiftmailer/swiftmailer": "~5.3" "swiftmailer/swiftmailer": "^5.3|^6.0"
}, },
"suggest": { "suggest": {
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
@ -2218,7 +2218,7 @@
"logging", "logging",
"psr-3" "psr-3"
], ],
"time": "2017-03-13T07:08:03+00:00" "time": "2017-06-19T01:22:40+00:00"
}, },
{ {
"name": "mtdowling/cron-expression", "name": "mtdowling/cron-expression",
@ -2725,16 +2725,16 @@
}, },
{ {
"name": "psy/psysh", "name": "psy/psysh",
"version": "v0.8.6", "version": "v0.8.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/bobthecow/psysh.git", "url": "https://github.com/bobthecow/psysh.git",
"reference": "7028d6d525fb183d50b249b7c07598e3d386b27d" "reference": "be969b9dc89dcaefdb9a3117fa91fa38bca19f50"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/7028d6d525fb183d50b249b7c07598e3d386b27d", "url": "https://api.github.com/repos/bobthecow/psysh/zipball/be969b9dc89dcaefdb9a3117fa91fa38bca19f50",
"reference": "7028d6d525fb183d50b249b7c07598e3d386b27d", "reference": "be969b9dc89dcaefdb9a3117fa91fa38bca19f50",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2794,7 +2794,7 @@
"interactive", "interactive",
"shell" "shell"
], ],
"time": "2017-06-04T10:34:20+00:00" "time": "2017-06-20T12:51:31+00:00"
}, },
{ {
"name": "ramsey/uuid", "name": "ramsey/uuid",

View file

@ -12,7 +12,7 @@ class WebMentionsTableSeeder extends Seeder
public function run() public function run()
{ {
$webmention = App\WebMention::create([ $webmention = App\WebMention::create([
'source' => 'https://aaornpk.local/reply/1', 'source' => 'https://aaornpk.localhost/reply/1',
'target' => 'https://jonnybarnes.localhost/notes/D', 'target' => 'https://jonnybarnes.localhost/notes/D',
'commentable_id' => '13', 'commentable_id' => '13',
'commentable_type' => 'App\Note', 'commentable_type' => 'App\Note',

4430
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -6,23 +6,23 @@
"license": "CC0-1.0", "license": "CC0-1.0",
"dependencies": { "dependencies": {
"alertify.js": "^1.0.12", "alertify.js": "^1.0.12",
"mapbox-gl": "0.37.0", "mapbox-gl": "^0.38.0",
"marked": "^0.3.6", "marked": "^0.3.6",
"normalize.css": "7.0.0", "normalize.css": "^7.0.0",
"webStorage": "^1.2.2" "webStorage": "^1.2.3"
}, },
"devDependencies": { "devDependencies": {
"babel-cli": "^6.18.0", "babel-cli": "^6.18.0",
"babel-core": "^6.21.0", "babel-core": "^6.21.0",
"babel-loader": "7.0.0", "babel-loader": "^7.1.0",
"babel-preset-env": "^1.2.2", "babel-preset-env": "^1.2.2",
"babel-preset-es2015": "^6.18.0", "babel-preset-es2015": "^6.18.0",
"babel-preset-latest": "^6.16.0", "babel-preset-latest": "^6.16.0",
"babel-runtime": "^6.20.0", "babel-runtime": "^6.20.0",
"lint-staged": "^3.2.1", "lint-staged": "^4.0.0",
"pre-commit": "^1.1.3", "pre-commit": "^1.1.3",
"stylelint-config-standard": "^16.0.0", "stylelint-config-standard": "^16.0.0",
"webpack": "^2.2.0" "webpack": "^3.0.0"
}, },
"scripts": { "scripts": {
"compress": "scripts/compress", "compress": "scripts/compress",

File diff suppressed because one or more lines are too long

View file

@ -1,2 +1,2 @@
!function(modules){function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={i:moduleId,l:!1,exports:{}};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.l=!0,module.exports}var installedModules={};__webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.i=function(value){return value},__webpack_require__.d=function(exports,name,getter){__webpack_require__.o(exports,name)||Object.defineProperty(exports,name,{configurable:!1,enumerable:!0,get:getter})},__webpack_require__.n=function(module){var getter=module&&module.__esModule?function(){return module.default}:function(){return module};return __webpack_require__.d(getter,"a",getter),getter},__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)},__webpack_require__.p="",__webpack_require__(__webpack_require__.s=8)}({8:function(module,exports,__webpack_require__){"use strict";var youtubeRegex=/watch\?v=([A-Za-z0-9\-_]+)\b/,spotifyRegex=/https\:\/\/play\.spotify\.com\/(.*)\b/,notes=document.querySelectorAll(".e-content"),_iteratorNormalCompletion=!0,_didIteratorError=!1,_iteratorError=void 0;try{for(var _step,_iterator=notes[Symbol.iterator]();!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=!0){var note=_step.value,ytid=note.textContent.match(youtubeRegex);if(ytid){var ytcontainer=document.createElement("div");ytcontainer.classList.add("container");var ytiframe=document.createElement("iframe");ytiframe.classList.add("youtube"),ytiframe.setAttribute("src","https://www.youtube.com/embed/"+ytid[1]),ytiframe.setAttribute("frameborder",0),ytiframe.setAttribute("allowfullscreen","true"),ytcontainer.appendChild(ytiframe),note.appendChild(ytcontainer)}var spotifyid=note.textContent.match(spotifyRegex);if(spotifyid){var sid=spotifyid[1].replace("/",":"),siframe=document.createElement("iframe");siframe.classList.add("spotify"),siframe.setAttribute("src","https://embed.spotify.com/?uri=spotify:"+sid),siframe.setAttribute("frameborder",0),siframe.setAttribute("allowtransparency","true"),note.appendChild(siframe)}}}catch(err){_didIteratorError=!0,_iteratorError=err}finally{try{!_iteratorNormalCompletion&&_iterator.return&&_iterator.return()}finally{if(_didIteratorError)throw _iteratorError}}}}); !function(modules){function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={i:moduleId,l:!1,exports:{}};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.l=!0,module.exports}var installedModules={};__webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.d=function(exports,name,getter){__webpack_require__.o(exports,name)||Object.defineProperty(exports,name,{configurable:!1,enumerable:!0,get:getter})},__webpack_require__.n=function(module){var getter=module&&module.__esModule?function(){return module.default}:function(){return module};return __webpack_require__.d(getter,"a",getter),getter},__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)},__webpack_require__.p="",__webpack_require__(__webpack_require__.s=5)}({5:function(module,exports,__webpack_require__){"use strict";var youtubeRegex=/watch\?v=([A-Za-z0-9\-_]+)\b/,spotifyRegex=/https\:\/\/play\.spotify\.com\/(.*)\b/,notes=document.querySelectorAll(".e-content"),_iteratorNormalCompletion=!0,_didIteratorError=!1,_iteratorError=void 0;try{for(var _step,_iterator=notes[Symbol.iterator]();!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=!0){var note=_step.value,ytid=note.textContent.match(youtubeRegex);if(ytid){var ytcontainer=document.createElement("div");ytcontainer.classList.add("container");var ytiframe=document.createElement("iframe");ytiframe.classList.add("youtube"),ytiframe.setAttribute("src","https://www.youtube.com/embed/"+ytid[1]),ytiframe.setAttribute("frameborder",0),ytiframe.setAttribute("allowfullscreen","true"),ytcontainer.appendChild(ytiframe),note.appendChild(ytcontainer)}var spotifyid=note.textContent.match(spotifyRegex);if(spotifyid){var sid=spotifyid[1].replace("/",":"),siframe=document.createElement("iframe");siframe.classList.add("spotify"),siframe.setAttribute("src","https://embed.spotify.com/?uri=spotify:"+sid),siframe.setAttribute("frameborder",0),siframe.setAttribute("allowtransparency","true"),note.appendChild(siframe)}}}catch(err){_didIteratorError=!0,_iteratorError=err}finally{try{!_iteratorNormalCompletion&&_iterator.return&&_iterator.return()}finally{if(_didIteratorError)throw _iteratorError}}}});
//# sourceMappingURL=links.js.map //# sourceMappingURL=links.js.map

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more