Merge branch 'develop' of github.com:jonnybarnes/jonnybarnes.uk into develop
This commit is contained in:
commit
3be0a6c3a7
64 changed files with 1162 additions and 259 deletions
|
|
@ -16,6 +16,7 @@ addons:
|
|||
- nginx
|
||||
- realpath
|
||||
- postgresql-9.6-postgis-2.3
|
||||
- imagemagick
|
||||
artifacts:
|
||||
s3_region: "eu-west-1"
|
||||
paths:
|
||||
|
|
@ -33,8 +34,10 @@ env:
|
|||
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
|
||||
before_install:
|
||||
- printf "\n" | pecl install imagick
|
||||
- cp .env.travis .env
|
||||
- echo 'error_log = "/tmp/php.error.log"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
|
||||
- psql -U travis -c 'create database travis_ci_test'
|
||||
|
|
|
|||
20
app/Http/Controllers/LikesController.php
Normal file
20
app/Http/Controllers/LikesController.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Like;
|
||||
|
||||
class LikesController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$likes = Like::latest()->paginate(20);
|
||||
|
||||
return view('likes.index', compact('likes'));
|
||||
}
|
||||
|
||||
public function show(Like $like)
|
||||
{
|
||||
return view('likes.show', compact('like'));
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,14 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Storage;
|
||||
use Monolog\Logger;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use App\{Media, Note, Place};
|
||||
use App\Jobs\ProcessImage;
|
||||
use App\Services\LikeService;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use App\{Like, Media, Note, Place};
|
||||
use Intervention\Image\ImageManager;
|
||||
use Illuminate\Http\{Request, Response};
|
||||
use App\Exceptions\InvalidTokenException;
|
||||
use Phaza\LaravelPostgis\Geometries\Point;
|
||||
|
|
@ -70,6 +74,14 @@ class MicropubController extends Controller
|
|||
if (stristr($tokenData->getClaim('scope'), 'create') === false) {
|
||||
return $this->returnInsufficientScopeResponse();
|
||||
}
|
||||
if ($request->has('properties.like-of') || $request->has('like-of')) {
|
||||
$like = (new LikeService())->createLike($request);
|
||||
|
||||
return response()->json([
|
||||
'response' => 'created',
|
||||
'location' => config('app.url') . "/likes/$like->id",
|
||||
], 201)->header('Location', config('app.url') . "/likes/$like->id");
|
||||
}
|
||||
$data = [];
|
||||
$data['client-id'] = $tokenData->getClaim('client_id');
|
||||
if ($request->header('Content-Type') == 'application/json') {
|
||||
|
|
@ -90,7 +102,9 @@ class MicropubController extends Controller
|
|||
if (is_array($request->input('properties.location.0'))) {
|
||||
if ($request->input('properties.location.0.type.0' === 'h-card')) {
|
||||
try {
|
||||
$place = $this->placeService->createPlaceFromCheckin($request->input('properties.location.0'));
|
||||
$place = $this->placeService->createPlaceFromCheckin(
|
||||
$request->input('properties.location.0')
|
||||
);
|
||||
$data['checkin'] = $place->longurl;
|
||||
} catch (\Exception $e) {
|
||||
//
|
||||
|
|
@ -102,7 +116,9 @@ class MicropubController extends Controller
|
|||
if (array_key_exists('checkin', $request->input('properties'))) {
|
||||
$data['swarm-url'] = $request->input('properties.syndication.0');
|
||||
try {
|
||||
$place = $this->placeService->createPlaceFromCheckin($request->input('properties.checkin.0'));
|
||||
$place = $this->placeService->createPlaceFromCheckin(
|
||||
$request->input('properties.checkin.0')
|
||||
);
|
||||
$data['checkin'] = $place->longurl;
|
||||
} catch (\Exception $e) {
|
||||
$data['checkin'] = null;
|
||||
|
|
@ -395,8 +411,11 @@ class MicropubController extends Controller
|
|||
'error_description' => 'A problem occured handling your request',
|
||||
], 500);
|
||||
}
|
||||
|
||||
$size = $request->file('file')->getClientSize();
|
||||
Storage::disk('local')->put($filename, $request->file('file')->openFile()->fread($size));
|
||||
try {
|
||||
$path = $request->file('file')->storeAs('media', $filename, 's3');
|
||||
Storage::disk('s3')->put('media/' . $filename, $request->file('file')->openFile()->fread($size));
|
||||
} catch (Exception $e) { // which exception?
|
||||
return response()->json([
|
||||
'response' => 'error',
|
||||
|
|
@ -404,12 +423,25 @@ class MicropubController extends Controller
|
|||
'error_description' => 'Unable to save media to S3',
|
||||
], 503);
|
||||
}
|
||||
|
||||
$manager = app()->make(ImageManager::class);
|
||||
try {
|
||||
$image = $manager->make($request->file('file'));
|
||||
$width = $image->width();
|
||||
} catch (\Intervention\Image\Exception\NotReadableException $exception) {
|
||||
// not an image
|
||||
$width = null;
|
||||
}
|
||||
|
||||
$media = new Media();
|
||||
$media->token = $request->bearerToken();
|
||||
$media->path = $path;
|
||||
$media->path = 'media/' . $filename;
|
||||
$media->type = $this->getFileTypeFromMimeType($request->file('file')->getMimeType());
|
||||
$media->image_widths = $width;
|
||||
$media->save();
|
||||
|
||||
dispatch(new ProcessImage($filename));
|
||||
|
||||
return response()->json([
|
||||
'response' => 'created',
|
||||
'location' => $media->url,
|
||||
|
|
|
|||
|
|
@ -5,20 +5,24 @@ namespace App\Http\Controllers;
|
|||
use App\Note;
|
||||
use Illuminate\Http\Request;
|
||||
use Jonnybarnes\IndieWeb\Numbers;
|
||||
use App\Services\ActivityStreamsService;
|
||||
|
||||
// Need to sort out Twitter and webmentions!
|
||||
|
||||
class NotesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show all the notes.
|
||||
* Show all the notes. This is also the homepage.
|
||||
*
|
||||
* @param Illuminate\Http\Request request;
|
||||
* @return \Illuminte\View\Factory view
|
||||
*/
|
||||
public function index(Request $request)
|
||||
public function index()
|
||||
{
|
||||
$notes = Note::orderBy('id', 'desc')
|
||||
if (request()->wantsActivityStream()) {
|
||||
return (new ActivityStreamsService)->siteOwnerResponse();
|
||||
}
|
||||
|
||||
$notes = Note::latest()
|
||||
->with('place', 'media', 'client')
|
||||
->withCount(['webmentions As replies' => function ($query) {
|
||||
$query->where('type', 'in-reply-to');
|
||||
|
|
@ -37,6 +41,10 @@ class NotesController extends Controller
|
|||
{
|
||||
$note = Note::nb60($urlId)->with('webmentions')->firstOrFail();
|
||||
|
||||
if (request()->wantsActivityStream()) {
|
||||
return (new ActivityStreamsService)->singleNoteResponse($note);
|
||||
}
|
||||
|
||||
return view('notes.show', compact('note'));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class Kernel extends HttpKernel
|
|||
\App\Http\Middleware\LinkHeadersMiddleware::class,
|
||||
//\App\Http\Middleware\DevTokenMiddleware::class,
|
||||
\App\Http\Middleware\LocalhostSessionMiddleware::class,
|
||||
\App\Http\Middleware\ActivityStreamLinks::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
|
|
|
|||
28
app/Http/Middleware/ActivityStreamLinks.php
Normal file
28
app/Http/Middleware/ActivityStreamLinks.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
|
||||
class ActivityStreamLinks
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$response = $next($request);
|
||||
if ($request->path() === '/') {
|
||||
$response->header('Link', '<' . config('app.url') . '>; rel="application/activity+json"', false);
|
||||
}
|
||||
if ($request->is('notes/*')) {
|
||||
$response->header('Link', '<' . $request->url() . '>; rel="application/activity+json"', false);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
68
app/Jobs/ProcessImage.php
Normal file
68
app/Jobs/ProcessImage.php
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Storage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Intervention\Image\Exception\NotReadableException;
|
||||
|
||||
class ProcessImage implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(ImageManager $manager)
|
||||
{
|
||||
//open file
|
||||
try {
|
||||
$image = $manager->make(storage_path('app') . '/' . $this->filename);
|
||||
} catch (NotReadableException $exception) {
|
||||
// not an image; delete file and end job
|
||||
unlink(storage_path('app') . '/' . $this->filename);
|
||||
|
||||
return;
|
||||
}
|
||||
//create smaller versions if necessary
|
||||
if ($image->width() >= 1000) {
|
||||
$filenameParts = explode('.', $this->filename);
|
||||
$extension = array_pop($filenameParts);
|
||||
// the following acheives this data flow
|
||||
// foo.bar.png => ['foo', 'bar', 'png'] => ['foo', 'bar'] => foo.bar
|
||||
$basename = ltrim(array_reduce($filenameParts, function ($carry, $item) {
|
||||
return $carry . '.' . $item;
|
||||
}, ''), '.');
|
||||
$medium = $image->resize(1000, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
});
|
||||
Storage::disk('s3')->put('media/'. $basename . '-medium.' . $extension, (string) $medium->encode());
|
||||
$small = $image->resize(500, null, function ($constraint) {
|
||||
$constraint->aspectRatio();
|
||||
});
|
||||
Storage::disk('s3')->put('media/' . $basename . '-small.' . $extension, (string) $small->encode());
|
||||
}
|
||||
|
||||
// now we can delete the locally saved image
|
||||
unlink(storage_path('app') . '/' . $this->filename);
|
||||
}
|
||||
}
|
||||
59
app/Jobs/ProcessLike.php
Normal file
59
app/Jobs/ProcessLike.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Like;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Jonnybarnes\WebmentionsParser\Authorship;
|
||||
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
|
||||
|
||||
class ProcessLike implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $like;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Like $like)
|
||||
{
|
||||
$this->like = $like;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Client $client, Authorship $authorship)
|
||||
{
|
||||
$response = $client->request('GET', $this->like->url);
|
||||
$mf2 = \Mf2\parse((string) $response->getBody(), $this->like->url);
|
||||
if (array_has($mf2, 'items.0.properties.content')) {
|
||||
$this->like->content = $mf2['items'][0]['properties']['content'][0]['html'];
|
||||
}
|
||||
|
||||
try {
|
||||
$author = $authorship->findAuthor($mf2);
|
||||
if (is_array($author)) {
|
||||
$this->like->author_name = $author['name'];
|
||||
$this->like->author_url = $author['url'];
|
||||
}
|
||||
if (is_string($author) && $author !== '') {
|
||||
$this->like->author_name = $author;
|
||||
}
|
||||
} catch (AuthorshipParserException $exception) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->like->save();
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ class SyndicateToFacebook implements ShouldQueue
|
|||
'form_params' => [
|
||||
'source' => $this->note->longurl,
|
||||
'target' => 'https://brid.gy/publish/facebook',
|
||||
'bridgy_omit_link' => 'maybe',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class SyndicateToTwitter implements ShouldQueue
|
|||
'form_params' => [
|
||||
'source' => $this->note->longurl,
|
||||
'target' => 'https://brid.gy/publish/twitter',
|
||||
'bridgy_omit_link' => 'maybe',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
|
|
|||
44
app/Like.php
Normal file
44
app/Like.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Mf2;
|
||||
use HTMLPurifier;
|
||||
use HTMLPurifier_Config;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Like extends Model
|
||||
{
|
||||
protected $fillable = ['url'];
|
||||
|
||||
public function setUrlAttribute($value)
|
||||
{
|
||||
$this->attributes['url'] = normalize_url($value);
|
||||
}
|
||||
|
||||
public function setAuthorUrlAttribute($value)
|
||||
{
|
||||
$this->attributes['author_url'] = normalize_url($value);
|
||||
}
|
||||
|
||||
public function getContentAttribute($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
$mf2 = Mf2\parse($value, $this->url);
|
||||
|
||||
return $this->filterHTML($mf2['items'][0]['properties']['content'][0]['html']);
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -41,4 +41,40 @@ class Media extends Model
|
|||
|
||||
return config('filesystems.disks.s3.url') . '/' . $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL for the medium size of an S3 image file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMediumurlAttribute()
|
||||
{
|
||||
$filenameParts = explode('.', $this->path);
|
||||
$extension = array_pop($filenameParts);
|
||||
// the following acheives this data flow
|
||||
// foo.bar.png => ['foo', 'bar', 'png'] => ['foo', 'bar'] => foo.bar
|
||||
$basename = ltrim(array_reduce($filenameParts, function ($carry, $item) {
|
||||
return $carry . '.' . $item;
|
||||
}, ''), '.');
|
||||
|
||||
return config('filesystems.disks.s3.url') . '/' . $basename . '-medium.' . $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL for the small size of an S3 image file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSmallurlAttribute()
|
||||
{
|
||||
$filenameParts = explode('.', $this->path);
|
||||
$extension = array_pop($filenameParts);
|
||||
// the following acheives this data flow
|
||||
// foo.bar.png => ['foo', 'bar', 'png'] => ['foo', 'bar'] => foo.bar
|
||||
$basename = ltrim(array_reduce($filenameParts, function ($carry, $item) {
|
||||
return $carry . '.' . $item;
|
||||
}, ''), '.');
|
||||
|
||||
return config('filesystems.disks.s3.url') . '/' . $basename . '-small.' . $extension;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -280,8 +280,8 @@ class Note extends Model
|
|||
try {
|
||||
$oEmbed = Twitter::getOembed([
|
||||
'id' => $tweetId,
|
||||
'dnt' => true,
|
||||
'align' => 'center',
|
||||
'omit_script' => true,
|
||||
'maxwidth' => 550,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
|
|
@ -368,7 +368,8 @@ class Note extends Model
|
|||
return $matches[0];
|
||||
}
|
||||
if ($contact->facebook) {
|
||||
return '<a class="u-category h-card" href="https://facebook.com/' . $contact->facebook . '">' . $contact->name . '</a>';
|
||||
return '<a class="u-category h-card" href="https://facebook.com/'
|
||||
. $contact->facebook . '">' . $contact->name . '</a>';
|
||||
}
|
||||
|
||||
return $contact->name;
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ class Place extends Model
|
|||
public function scopeNear(Builder $query, Point $point, $distance = 1000)
|
||||
{
|
||||
$field = DB::raw(
|
||||
sprintf("ST_Distance(%s.location, ST_GeogFromText('%s'))",
|
||||
sprintf(
|
||||
"ST_Distance(%s.location, ST_GeogFromText('%s'))",
|
||||
$this->getTable(),
|
||||
$point->toWKT()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Providers;
|
|||
use App\Tag;
|
||||
use App\Note;
|
||||
use Validator;
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Dusk\DuskServiceProvider;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
|
|
@ -42,10 +43,20 @@ class AppServiceProvider extends ServiceProvider
|
|||
$tag = Tag::firstOrCreate(['tag' => $tag]);
|
||||
$tagsToAdd[] = $tag->id;
|
||||
}
|
||||
if (count($tagsToAdd > 0)) {
|
||||
if (count($tagsToAdd) > 0) {
|
||||
$note->tags()->attach($tagsToAdd);
|
||||
}
|
||||
});
|
||||
|
||||
// Request AS macro
|
||||
Request::macro('wantsActivityStream', function () {
|
||||
return str_contains(mb_strtolower($this->header('Accept')), 'application/activity+json');
|
||||
});
|
||||
|
||||
// configure Intervention/Image
|
||||
$this->app->bind('Intervention\Image\ImageManager', function () {
|
||||
return new \Intervention\Image\ImageManager(['driver' => config('image.driver')]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
50
app/Services/ActivityStreamsService.php
Normal file
50
app/Services/ActivityStreamsService.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Note;
|
||||
|
||||
class ActivityStreamsService
|
||||
{
|
||||
public function siteOwnerResponse()
|
||||
{
|
||||
$data = json_encode([
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => config('app.url'),
|
||||
'type' => 'Person',
|
||||
'name' => config('user.displayname'),
|
||||
'preferredUsername' => config('user.username'),
|
||||
]);
|
||||
|
||||
return response($data)->header('Content-Type', 'application/activity+json');
|
||||
}
|
||||
|
||||
public function singleNoteResponse(Note $note)
|
||||
{
|
||||
$data = json_encode([
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'summary' => strtok(config('user.displayname'), ' ') . ' added a note to their microblog',
|
||||
'type' => 'Add',
|
||||
'published' => $note->updated_at->toW3cString(),
|
||||
'actor' => [
|
||||
'type' => 'Person',
|
||||
'id' => config('app.url'),
|
||||
'name' => config('app.display_name'),
|
||||
'url' => config('app.url'),
|
||||
'image' => [
|
||||
'type' => 'Link',
|
||||
'href' => config('app.url') . '/assets/img/profile.jpg',
|
||||
'mediaType' => '/image/jpeg',
|
||||
],
|
||||
],
|
||||
'object' => [
|
||||
'id' => $note->longurl,
|
||||
'type' => 'Note',
|
||||
'url' => $note->longurl,
|
||||
'name' => strip_tags($note->note),
|
||||
],
|
||||
]);
|
||||
|
||||
return response($data)->header('Content-Type', 'application/activity+json');
|
||||
}
|
||||
}
|
||||
37
app/Services/LikeService.php
Normal file
37
app/Services/LikeService.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Like;
|
||||
use App\Jobs\ProcessLike;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LikeService
|
||||
{
|
||||
/**
|
||||
* Create a new Like.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function createLike(Request $request): Like
|
||||
{
|
||||
if ($request->header('Content-Type') == 'application/json') {
|
||||
//micropub request
|
||||
$url = normalize_url($request->input('properties.like-of.0'));
|
||||
}
|
||||
if (
|
||||
($request->header('Content-Type') == 'x-www-url-formencoded')
|
||||
||
|
||||
($request->header('Content-Type') == 'multipart/form-data')
|
||||
) {
|
||||
$url = normalize_url($request->input('like-of'));
|
||||
}
|
||||
|
||||
$like = Like::create(['url' => $url]);
|
||||
ProcessLike::dispatch($like);
|
||||
|
||||
return $like;
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ class NoteService
|
|||
);
|
||||
|
||||
if (array_key_exists('published', $data) && empty($data['published']) === false) {
|
||||
$carbon = new \Carbon\Carbon($data['published']);
|
||||
$carbon = carbon($data['published']);
|
||||
$note->created_at = $note->updated_at = $carbon->toDateTimeString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ namespace App;
|
|||
use Cache;
|
||||
use Twitter;
|
||||
use HTMLPurifier;
|
||||
use Carbon\Carbon;
|
||||
use HTMLPurifier_Config;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
|
@ -63,10 +62,9 @@ class WebMention extends Model
|
|||
public function getPublishedAttribute()
|
||||
{
|
||||
$microformats = json_decode($this->mf2, true);
|
||||
$carbon = new Carbon();
|
||||
if (isset($microformats['items'][0]['properties']['published'][0])) {
|
||||
try {
|
||||
$published = $carbon->parse(
|
||||
$published = carbon()->parse(
|
||||
$microformats['items'][0]['properties']['published'][0]
|
||||
)->toDayDateTimeString();
|
||||
} catch (\Exception $exception) {
|
||||
|
|
|
|||
21
changelog.md
21
changelog.md
|
|
@ -1,5 +1,26 @@
|
|||
# Changelog
|
||||
|
||||
## Version {next}
|
||||
- Add support for `likes` (issue#69)
|
||||
- Only included links on truncated syndicated notes https://brid.gy/about#omit-link
|
||||
|
||||
## Version 0.8.1 (2017-09-16)
|
||||
- Order notes by latest (issue#70)
|
||||
- AcitivtyStream support is now indicated with HTTP Link headers
|
||||
|
||||
## Version 0.8 (2017-09-16)
|
||||
- Improve embedding of tweets (issue#66)
|
||||
- Allow for “responsive” images (issue#62)
|
||||
|
||||
## Version 0.7.3 (2017-09-13)
|
||||
- Fix a test
|
||||
|
||||
## Version 0.7.2 (2017-09-13)
|
||||
- Small AS2.0 improvements
|
||||
|
||||
## Version 0.7.1 (2017-09-13)
|
||||
- Add content-negotiated AS data for homepage and single notes
|
||||
|
||||
## Version 0.7 (2017-09-08)
|
||||
- Add Laravel Horizon
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue