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:
- nginx
- realpath
- postgresql-9.6-postgis-2.3
artifacts:
s3_region: "eu-west-1"
paths:

View file

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

View file

@ -2,16 +2,9 @@
namespace App\Http\Controllers;
use Twitter;
use HTMLPurifier;
use App\{Note, Tag};
use GuzzleHttp\Client;
use HTMLPurifier_Config;
use App\Note;
use Illuminate\Http\Request;
use Jonnybarnes\IndieWeb\Numbers;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Cache;
use Jonnybarnes\WebmentionsParser\Authorship;
// Need to sort out Twitter and webmentions!
@ -25,41 +18,13 @@ class NotesController extends Controller
*/
public function index(Request $request)
{
$notes = Note::orderBy('id', 'desc')->with('webmentions', 'place', 'media')->paginate(10);
foreach ($notes as $note) {
$replies = 0;
foreach ($note->webmentions as $webmention) {
if ($webmention->type == 'in-reply-to') {
$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;
}*/
}
$notes = Note::orderBy('id', 'desc')
->with('place', 'media', 'client')
->withCount(['webmentions As replies' => function ($query) {
$query->where('type', 'in-reply-to');
}])->paginate(10);
$homepage = ($request->path() == '/');
return view('notes.index', compact('notes', 'homepage'));
return view('notes.index', compact('notes'));
}
/**
@ -70,90 +35,9 @@ class NotesController extends Controller
*/
public function show($urlId)
{
$numbers = new Numbers();
$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
$note = Note::nb60($urlId)->with('webmentions')->first();
repost->url |
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'));
return view('notes.show', compact('note'));
}
/**
@ -164,12 +48,7 @@ class NotesController extends Controller
*/
public function redirect($decId)
{
$numbers = new Numbers();
$realId = $numbers->numto60($decId);
$url = config('app.url') . '/notes/' . $realId;
return redirect($url);
return redirect(config('app.url') . '/notes/' . (new Numbers())->numto60($decId));
}
/**
@ -183,180 +62,7 @@ class NotesController extends Controller
$notes = Note::whereHas('tags', function ($query) use ($tag) {
$query->where('tag', $tag);
})->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'));
}
/**
* 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;
use Mf2;
use App\Note;
use App\WebMention;
use GuzzleHttp\Client;
use App\{Note, WebMention};
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Jonnybarnes\WebmentionsParser\Parser;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Exceptions\RemoteContentNotFoundException;
use Illuminate\Queue\{InteractsWithQueue, SerializesModels};
class ProcessWebMention implements ShouldQueue
{

View file

@ -19,4 +19,14 @@ class MicropubClient extends Model
* @var array
*/
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;
use Cache;
use Twitter;
use Normalizer;
use GuzzleHttp\Client;
use Laravel\Scout\Searchable;
use Jonnybarnes\IndieWeb\Numbers;
use Illuminate\Database\Eloquent\Model;
@ -33,6 +36,16 @@ class Note extends Model
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.
*
@ -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.
*
@ -168,6 +170,26 @@ class Note extends Model
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.
*
@ -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
*/
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;
}
$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;
}
/**
* 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);
}
/*
* 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.
*

View file

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

View file

@ -2,7 +2,14 @@
namespace App;
use Cache;
use Twitter;
use HTMLPurifier;
use Carbon\Carbon;
use HTMLPurifier_Config;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Database\Eloquent\Model;
use Jonnybarnes\WebmentionsParser\Authorship;
class WebMention extends Model
{
@ -29,4 +36,112 @@ class WebMention extends Model
* @var array
*/
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
## 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
- Add syndication links for swarm and instagram
- Move bio to its own template, next step database?

42
composer.lock generated
View file

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

View file

@ -12,7 +12,7 @@ class WebMentionsTableSeeder extends Seeder
public function run()
{
$webmention = App\WebMention::create([
'source' => 'https://aaornpk.local/reply/1',
'source' => 'https://aaornpk.localhost/reply/1',
'target' => 'https://jonnybarnes.localhost/notes/D',
'commentable_id' => '13',
'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",
"dependencies": {
"alertify.js": "^1.0.12",
"mapbox-gl": "0.37.0",
"mapbox-gl": "^0.38.0",
"marked": "^0.3.6",
"normalize.css": "7.0.0",
"webStorage": "^1.2.2"
"normalize.css": "^7.0.0",
"webStorage": "^1.2.3"
},
"devDependencies": {
"babel-cli": "^6.18.0",
"babel-core": "^6.21.0",
"babel-loader": "7.0.0",
"babel-loader": "^7.1.0",
"babel-preset-env": "^1.2.2",
"babel-preset-es2015": "^6.18.0",
"babel-preset-latest": "^6.16.0",
"babel-runtime": "^6.20.0",
"lint-staged": "^3.2.1",
"lint-staged": "^4.0.0",
"pre-commit": "^1.1.3",
"stylelint-config-standard": "^16.0.0",
"webpack": "^2.2.0"
"webpack": "^3.0.0"
},
"scripts": {
"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

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

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=14)}({14:function(module,exports,__webpack_require__){"use strict";var _paq=_paq||[];_paq.push(["trackPageView"]),_paq.push(["enableLinkTracking"]),_paq.push(["setTrackerUrl","https://analytics.jmb.lv/piwik.php"]),_paq.push(["setSiteId","1"])}});
!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=16)}({16:function(module,exports,__webpack_require__){"use strict";var _paq=_paq||[];_paq.push(["trackPageView"]),_paq.push(["enableLinkTracking"]),_paq.push(["setTrackerUrl","https://analytics.jmb.lv/piwik.php"]),_paq.push(["setSiteId","1"])}});
//# sourceMappingURL=piwik.js.map

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
{"version":3,"sources":["webpack:/webpack/bootstrap 6832c5e30966f4c44816?e79d**","webpack:///piwik.js"],"names":["__webpack_require__","moduleId","installedModules","exports","module","i","l","modules","call","m","c","value","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","_paq","push"],"mappings":"mBAIA,SAAAA,oBAAAC,UAGA,GAAAC,iBAAAD,UACA,OAAAC,iBAAAD,UAAAE,QAGA,IAAAC,OAAAF,iBAAAD,WACAI,EAAAJ,SACAK,GAAA,EACAH,YAUA,OANAI,QAAAN,UAAAO,KAAAJ,OAAAD,QAAAC,OAAAA,OAAAD,QAAAH,qBAGAI,OAAAE,GAAA,EAGAF,OAAAD,QAvBA,IAAAD,oBA4BAF,oBAAAS,EAAAF,QAGAP,oBAAAU,EAAAR,iBAGAF,oBAAAK,EAAA,SAAAM,OAA2C,OAAAA,OAG3CX,oBAAAY,EAAA,SAAAT,QAAAU,KAAAC,QACAd,oBAAAe,EAAAZ,QAAAU,OACAG,OAAAC,eAAAd,QAAAU,MACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,UAMAd,oBAAAqB,EAAA,SAAAjB,QACA,IAAAU,OAAAV,QAAAA,OAAAkB,WACA,WAA2B,OAAAlB,OAAA,SAC3B,WAAiC,OAAAA,QAEjC,OADAJ,oBAAAY,EAAAE,OAAA,IAAAA,QACAA,QAIAd,oBAAAe,EAAA,SAAAQ,OAAAC,UAAsD,OAAAR,OAAAS,UAAAC,eAAAlB,KAAAe,OAAAC,WAGtDxB,oBAAA2B,EAAA,GAGA3B,oBAAAA,oBAAA4B,EAAA,mEC9DA,IAAIC,KAAOA,SAEXA,KAAKC,MAAM,kBACXD,KAAKC,MAAM,uBACXD,KAAKC,MAAM,gBAAiB,uCAC5BD,KAAKC,MAAM,YAAa","file":"public/assets/js/piwik.js.map","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 14);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 6832c5e30966f4c44816","// Piwik in its own js file to allow usage with a CSP policy\n\nvar _paq = _paq || [];\n// tracker methods like \"setCustomDimension\" should be called before \"trackPageView\"\n_paq.push(['trackPageView']);\n_paq.push(['enableLinkTracking']);\n_paq.push(['setTrackerUrl', 'https://analytics.jmb.lv/piwik.php']);\n_paq.push(['setSiteId', '1']);\n\n\n\n// WEBPACK FOOTER //\n// ./piwik.js"]}
{"version":3,"sources":["webpack:/webpack/bootstrap 9d7c9d0c3e1e7b963a9f?3a34**","webpack:///piwik.js"],"names":["__webpack_require__","moduleId","installedModules","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","default","object","property","prototype","hasOwnProperty","p","s","16","_paq","push"],"mappings":"mBAIA,SAAAA,oBAAAC,UAGA,GAAAC,iBAAAD,UACA,OAAAC,iBAAAD,UAAAE,QAGA,IAAAC,OAAAF,iBAAAD,WACAI,EAAAJ,SACAK,GAAAA,EACAH,YAUA,OANAI,QAAAN,UAAAO,KAAAJ,OAAAD,QAAAC,OAAAA,OAAAD,QAAAH,qBAGAI,OAAAE,GAAAA,EAGAF,OAAAD,QAvBA,IAAAD,oBA4BAF,oBAAAS,EAAAF,QAGAP,oBAAAU,EAAAR,iBAGAF,oBAAAW,EAAA,SAAAR,QAAAS,KAAAC,QACAb,oBAAAc,EAAAX,QAAAS,OACAG,OAAAC,eAAAb,QAAAS,MACAK,cAAAA,EACAC,YAAAA,EACAC,IAAAN,UAMAb,oBAAAoB,EAAA,SAAAhB,QACA,IAAAS,OAAAT,QAAAA,OAAAiB,WACA,WAA2B,OAAAjB,OAAAkB,SAC3B,WAAiC,OAAAlB,QAEjC,OADAJ,oBAAAW,EAAAE,OAAA,IAAAA,QACAA,QAIAb,oBAAAc,EAAA,SAAAS,OAAAC,UAAsD,OAAAT,OAAAU,UAAAC,eAAAlB,KAAAe,OAAAC,WAGtDxB,oBAAA2B,EAAA,GAGA3B,oBAAAA,oBAAA4B,EAAA,MAAAC,GAAA,SAAAzB,OAAAD,QAAAH,qBAAA,aC3DA,IAAI8B,KAAOA,SAEXA,KAAKC,MAAM,kBACXD,KAAKC,MAAM,uBACXD,KAAKC,MAAM,gBAAiB,uCAC5BD,KAAKC,MAAM,YAAa","file":"public/assets/js/piwik.js.map","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 16);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 9d7c9d0c3e1e7b963a9f","// Piwik in its own js file to allow usage with a CSP policy\n\nvar _paq = _paq || [];\n// tracker methods like \"setCustomDimension\" should be called before \"trackPageView\"\n_paq.push(['trackPageView']);\n_paq.push(['enableLinkTracking']);\n_paq.push(['setTrackerUrl', 'https://analytics.jmb.lv/piwik.php']);\n_paq.push(['setSiteId', '1']);\n\n\n\n// WEBPACK FOOTER //\n// ./piwik.js"]}

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -18,6 +18,8 @@ Notes «
{!! $notes->render() !!}
@stop
@if (Request::path() == '/')@include('templates.bio')@endif
@section('scripts')
<script defer src="/assets/js/links.js"></script>
@ -27,5 +29,3 @@ Notes «
<script defer src="/assets/prism/prism.js"></script>
<link rel="stylesheet" href="/assets/prism/prism.css">
@stop
@if ($homepage === true)@include('templates.bio')@endif

View file

@ -7,26 +7,38 @@
@section('content')
<div class="h-entry">
@include('templates.note', ['note' => $note])
@foreach($replies as $reply)
@foreach($note->webmentions->filter(function ($webmention) {
return ($webmention->type == 'in-reply-to');
}) as $reply)
<div class="u-comment h-cite">
<a class="u-author h-card mini-h-card" href="{{ $reply['url'] }}">
<img src="{{ $reply['photo'] }}" alt="" class="photo u-photo logo"> <span class="fn">{{ $reply['name'] }}</span>
</a> said at <a class="dt-published u-url" href="{{ $reply['source'] }}">{{ $reply['date'] }}</a>
<a class="u-author h-card mini-h-card" href="{{ $reply['author']['properties']['url'][0] }}">
<img src="{{ $reply['author']['properties']['photo'][0] }}" alt="" class="photo u-photo logo"> <span class="fn">{{ $reply['author']['properties']['name'][0] }}</span>
</a> said at <a class="dt-published u-url" href="{{ $reply['source'] }}">{{ $reply['published'] }}</a>
<div class="e-content p-name">
{!! $reply['reply'] !!}
</div>
</div>
@endforeach
@if(count($likes) > 0)<h1 class="notes-subtitle">Likes</h1>@endif
@foreach($likes as $like)
<a href="{{ $like['url'] }}"><img src="{{ $like['photo'] }}" alt="profile picture of {{ $like['name'] }}" class="like-photo"></a>
@if($note->webmentions->filter(function ($webmention) {
return ($webmention->type == 'like-of');
})->count() > 0)<h1 class="notes-subtitle">Likes</h1>
@foreach($note->webmentions->filter(function ($webmention) {
return ($webmention->type == 'like-of');
}) as $like)
<a href="{{ $like['author']['properties']['url'][0] }}"><img src="{{ $like['author']['properties']['photo'][0] }}" alt="profile picture of {{ $like['author']['properties']['name'][0] }}" class="like-photo"></a>
@endforeach
@if(count($reposts) > 0)<h1 class="notes-subtitle">Reposts</h1>@endif
@foreach($reposts as $repost)
<p><a class="h-card vcard mini-h-card p-author" href="{{ $repost['url'] }}">
<img src="{{ $repost['photo'] }}" alt="profile picture of {{ $repost['name'] }}" class="photo u-photo logo"> <span class="fn">{{ $repost['name'] }}</span>
</a> reposted this at <a href="{{ $repost['source'] }}">{{ $repost['date'] }}</a>.</p>
@endif
@if($note->webmentions->filter(function ($webmention) {
return ($webmention->type == 'repost-of');
})->count() > 0)<h1 class="notes-subtitle">Reposts</h1>
@foreach($note->webmentions->filter(function ($webmention) {
return ($webmention->type == 'repost-of');
}) as $repost)
<p><a class="h-card vcard mini-h-card p-author" href="{{ $repost['author']['properties']['url'][0] }}">
<img src="{{ $repost['author']['properties']['photo'][0] }}" alt="profile picture of {{ $repost['author']['properties']['name'][0] }}" class="photo u-photo logo"> <span class="fn">{{ $repost['author']['properties']['name'][0] }}</span>
</a> reposted this at <a href="{{ $repost['source'] }}">{{ $repost['published'] }}</a>.</p>
@endforeach
@endif
<!-- these empty tags are for https://brid.gys publishing service -->
<a href="https://brid.gy/publish/twitter"></a>
<a href="https://brid.gy/publish/facebook"></a>

View file

@ -1,13 +1,13 @@
@extends('master')
@section('title')
Tagged Notes «
Tagged Notes «
@stop
@section('content')
<h2>Notes tagged with <em>{{ $tag }}</em></h2>
@foreach ($notes as $note)
<div>{!! $note->note !!}
<a href="/note/{{ $note->id }}">{{ $note->human_time }}</a></div>
<a href="/note/{{ $note->id }}">{{ $note->humandiff }}</a></div>
@endforeach
@stop

View file

@ -8,7 +8,7 @@
<div class="note">
<div class="e-content p-name">
{!! $note->note !!}
@foreach($note->media()->get() as $media)
@foreach($note->media as $media)
@if($media->type == 'image')<img class="u-photo" src="{{ $media->url }}" alt="">@endif
@if($media->type == 'audio')<audio class="u-audio" src="{{ $media->url }}" controls>@endif
@if($media->type == 'video')<video class="u-video" src="{{ $media->url }}" controls>@endif
@ -17,10 +17,10 @@
</div>
<div class="note-metadata">
<div>
<a class="u-url" href="/notes/{{ $note->nb60id }}"><time class="dt-published" datetime="{{ $note->iso8601_time }}">{{ $note->human_time }}</time></a>@if($note->client_name) via <a class="client" href="{{ $note->client_id }}">{{ $note->client_name }}</a>@endif
@if($note->placeLink)in <span class="p-location h-card"><a class="p-name u-url" href="{{ $note->placeLink }}">{{ $note->address }}</a><data class="p-latitude" value="{{ $note->latitude }}"></data><data class="p-longitude" value="{{ $note->longitude }}"></data></span>
<a class="u-url" href="/notes/{{ $note->nb60id }}"><time class="dt-published" datetime="{{ $note->iso8601 }}" title="{{ $note->iso8601 }}">{{ $note->humandiff }}</time></a>@if($note->client) via <a class="client" href="{{ $note->client->client_url }}">{{ $note->client->client_name }}</a>@endif
@if($note->place)in <span class="p-location h-card"><a class="p-name u-url" href="{{ $note->place->longurl }}">{{ $note->address }}</a><data class="p-latitude" value="{{ $note->place->latitude }}"></data><data class="p-longitude" value="{{ $note->place->longitude }}"></data></span>
@elseif($note->address)in <span class="p-location h-adr">{!! $note->address !!}<data class="p-latitude" value="{{ $note->latitude }}"></data><data class="p-longitude" value="{{ $note->longitude }}"></data></span>@endif
@if($note->replies > 0) @include('templates.replies-icon'): {{ $note->replies }}@endif
@if($note->replies_count > 0) @include('templates.replies-icon'): {{ $note->replies_count }}@endif
</div>
<div class="social-links">
@if(
@ -37,10 +37,10 @@
@endif
</div>
</div>
@if ($note->placeLink)
@if ($note->place)
<div class="map"
data-latitude="{{ $note->latitude }}"
data-longitude="{{ $note->longitude }}"
data-latitude="{{ $note->place->latitude }}"
data-longitude="{{ $note->place->longitude }}"
data-name="{{ $note->place->name }}"
data-marker="{{ $note->place->icon }}"></div>
@endif

View file

@ -2,21 +2,14 @@
namespace Tests\Unit;
use Cache;
use App\WebMention;
use Tests\TestCase;
use Illuminate\Support\Facades\Cache;
use App\Http\Controllers\NotesController;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class NotesControllerTest extends TestCase
class WebMentionTest extends TestCase
{
protected $notesController;
public function __construct()
{
$this->notesController = new NotesController();
}
/**
* Test a correct profile link is formed from a generic URL.
*
@ -24,10 +17,10 @@ class NotesControllerTest extends TestCase
*/
public function test_create_photo_link_with_non_cached_image()
{
$notesController = new \App\Http\Controllers\NotesController();
$webmention = new WebMention();
$homepage = 'https://example.org/profile.png';
$expected = 'https://example.org/profile.png';
$this->assertEquals($expected, $this->notesController->createPhotoLink($homepage));
$this->assertEquals($expected, $webmention->createPhotoLink($homepage));
}
/**
@ -37,10 +30,10 @@ class NotesControllerTest extends TestCase
*/
public function test_create_photo_link_with_cached_image()
{
$notesController = new \App\Http\Controllers\NotesController();
$webmention = new WebMention();
$homepage = 'https://aaronparecki.com/profile.png';
$expected = '/assets/profile-images/aaronparecki.com/image';
$this->assertEquals($expected, $this->notesController->createPhotoLink($homepage));
$this->assertEquals($expected, $webmention->createPhotoLink($homepage));
}
/**
@ -50,10 +43,10 @@ class NotesControllerTest extends TestCase
*/
public function test_create_photo_link_with_twimg_profile_image_url()
{
$notesController = new \App\Http\Controllers\NotesController();
$webmention = new WebMention();
$twitterProfileImage = 'http://pbs.twimg.com/1234';
$expected = 'https://pbs.twimg.com/1234';
$this->assertEquals($expected, $this->notesController->createPhotoLink($twitterProfileImage));
$this->assertEquals($expected, $webmention->createPhotoLink($twitterProfileImage));
}
/**
@ -63,9 +56,10 @@ class NotesControllerTest extends TestCase
*/
public function test_create_photo_link_with_cached_twitter_url()
{
$webmention = new WebMention();
$twitterURL = 'https://twitter.com/example';
$expected = 'https://pbs.twimg.com/static_profile_link.jpg';
Cache::put($twitterURL, $expected, 1);
$this->assertEquals($expected, $this->notesController->createPhotoLink($twitterURL));
$this->assertEquals($expected, $webmention->createPhotoLink($twitterURL));
}
}

4064
yarn.lock

File diff suppressed because it is too large Load diff