Merge branch 'release/0.15.4'

This commit is contained in:
Jonny Barnes 2018-01-15 15:48:19 +00:00
commit 85c8bb7085
84 changed files with 976 additions and 462 deletions

View file

@ -12,3 +12,6 @@ SESSION_DRIVER=array
QUEUE_DRIVER=sync
SCOUT_DRIVER=pgsql
DISPLAY_NAME='Travis Test'
USER_NAME=travis

View file

@ -34,7 +34,6 @@ env:
- setup=basic
php:
- 7.1
- 7.2
before_install:

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use App\Models\WebMention;
@ -61,7 +63,7 @@ class ParseCachedWebMentions extends Command
* @param string
* @return string
*/
private function URLFromFilename($filepath)
private function URLFromFilename(string $filepath): string
{
$dir = mb_substr($filepath, mb_strlen(storage_path() . '/HTML/'));
$url = str_replace(['http/', 'https/'], ['http://', 'https://'], $dir);

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use App\Models\WebMention;

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use Illuminate\Console\Command;
@ -49,7 +51,7 @@ class SecurityCheck extends Command
*
* @return mixed
*/
public function handle()
public function handle(): int
{
$alerts = $this->securityChecker->check(base_path() . '/composer.lock');
if (count($alerts) === 0) {

View file

@ -1,19 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Admin;
use App\Models\Article;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
class ArticlesController extends Controller
{
/**
* List the articles that can be edited.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function index()
public function index(): View
{
$posts = Article::select('id', 'title', 'published')->orderBy('id', 'desc')->get();
@ -23,9 +27,9 @@ class ArticlesController extends Controller
/**
* Show the new article form.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function create()
public function create(): View
{
$message = session('message');
@ -35,23 +39,22 @@ class ArticlesController extends Controller
/**
* Process an incoming request for a new article and save it.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(): RedirectResponse
{
//if a `.md` is attached use that for the main content.
if ($request->hasFile('article')) {
$file = $request->file('article')->openFile();
if (request()->hasFile('article')) {
$file = request()->file('article')->openFile();
$content = $file->fread($file->getSize());
}
$main = $content ?? $request->input('main');
$main = $content ?? request()->input('main');
$article = Article::create(
[
'url' => $request->input('url'),
'title' => $request->input('title'),
'url' => request()->input('url'),
'title' => request()->input('title'),
'main' => $main,
'published' => $request->input('published') ?? 0,
'published' => request()->input('published') ?? 0,
]
);
@ -61,10 +64,10 @@ class ArticlesController extends Controller
/**
* Show the edit form for an existing article.
*
* @param string The article id
* @return \Illuminate\View\Factory view
* @param int $articleId
* @return \Illuminate\View\View
*/
public function edit($articleId)
public function edit(int $articleId): View
{
$post = Article::select(
'title',
@ -79,17 +82,16 @@ class ArticlesController extends Controller
/**
* Process an incoming request to edit an article.
*
* @param \Illuminate\Http\Request $request
* @param string
* @return \Illuminate|View\Factory view
* @param int $articleId
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, $articleId)
public function update(int $articleId): RedirectResponse
{
$article = Article::find($articleId);
$article->title = $request->input('title');
$article->url = $request->input('url');
$article->main = $request->input('main');
$article->published = $request->input('published') ?? 0;
$article->title = request()->input('title');
$article->url = request()->input('url');
$article->main = request()->input('main');
$article->published = request()->input('published') ?? 0;
$article->save();
return redirect('/admin/blog');
@ -98,10 +100,10 @@ class ArticlesController extends Controller
/**
* Process a request to delete an aricle.
*
* @param string The article id
* @return \Illuminate\View\Factory view
* @param int $articleId
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($articleId)
public function destroy(int $articleId): RedirectResponse
{
Article::where('id', $articleId)->delete();

View file

@ -1,19 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Admin;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Models\MicropubClient;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
class ClientsController extends Controller
{
/**
* Show a list of known clients.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function index()
public function index(): View
{
$clients = MicropubClient::all();
@ -23,9 +27,9 @@ class ClientsController extends Controller
/**
* Show form to add a client name.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function create()
public function create(): View
{
return view('admin.clients.create');
}
@ -33,14 +37,13 @@ class ClientsController extends Controller
/**
* Process the request to adda new client name.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(): RedirectResponse
{
MicropubClient::create([
'client_url' => $request->input('client_url'),
'client_name' => $request->input('client_name'),
'client_url' => request()->input('client_url'),
'client_name' => request()->input('client_name'),
]);
return redirect('/admin/clients');
@ -49,10 +52,10 @@ class ClientsController extends Controller
/**
* Show a form to edit a client name.
*
* @param string The client id
* @return \Illuminate\View\Factory view
* @param int $clientId
* @return \Illuminate\View\View
*/
public function edit($clientId)
public function edit(int $clientId): View
{
$client = MicropubClient::findOrFail($clientId);
@ -66,15 +69,14 @@ class ClientsController extends Controller
/**
* Process the request to edit a client name.
*
* @param string The client id
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
* @param int $clientId
* @return \Illuminate\Http\RedirectResponse
*/
public function update($clientId, Request $request)
public function update(int $clientId): RedirectResponse
{
$client = MicropubClient::findOrFail($clientId);
$client->client_url = $request->input('client_url');
$client->client_name = $request->input('client_name');
$client->client_url = request()->input('client_url');
$client->client_name = request()->input('client_name');
$client->save();
return redirect('/admin/clients');
@ -83,10 +85,10 @@ class ClientsController extends Controller
/**
* Process a request to delete a client.
*
* @param string The client id
* @return redirect
* @param int $clientId
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($clientId)
public function destroy(int $clientId): RedirectResponse
{
MicropubClient::where('id', $clientId)->delete();

View file

@ -1,21 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Admin;
use GuzzleHttp\Client;
use App\Models\Contact;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Http\RedirectResponse;
class ContactsController extends Controller
{
/**
* List the currect contacts that can be edited.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function index()
public function index(): View
{
$contacts = Contact::all();
@ -25,9 +29,9 @@ class ContactsController extends Controller
/**
* Display the form to add a new contact.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function create()
public function create(): View
{
return view('admin.contacts.create');
}
@ -35,17 +39,16 @@ class ContactsController extends Controller
/**
* Process the request to add a new contact.
*
* @param \Illuminate\Http|request $request
* @return \Illuminate\View\Factory view
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(): RedirectResponse
{
$contact = new Contact();
$contact->name = $request->input('name');
$contact->nick = $request->input('nick');
$contact->homepage = $request->input('homepage');
$contact->twitter = $request->input('twitter');
$contact->facebook = $request->input('facebook');
$contact->name = request()->input('name');
$contact->nick = request()->input('nick');
$contact->homepage = request()->input('homepage');
$contact->twitter = request()->input('twitter');
$contact->facebook = request()->input('facebook');
$contact->save();
return redirect('/admin/contacts');
@ -54,10 +57,10 @@ class ContactsController extends Controller
/**
* Show the form to edit an existing contact.
*
* @param string The contact id
* @return \Illuminate\View\Factory view
* @param int $contactId
* @return \Illuminate\View\View
*/
public function edit($contactId)
public function edit(int $contactId): View
{
$contact = Contact::findOrFail($contactId);
@ -69,28 +72,27 @@ class ContactsController extends Controller
*
* @todo Allow saving profile pictures for people without homepages
*
* @param string The contact id
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
* @param int $contactId
* @return \Illuminate\Http\RedirectResponse
*/
public function update($contactId, Request $request)
public function update(int $contactId): RedirectResponse
{
$contact = Contact::findOrFail($contactId);
$contact->name = $request->input('name');
$contact->nick = $request->input('nick');
$contact->homepage = $request->input('homepage');
$contact->twitter = $request->input('twitter');
$contact->facebook = $request->input('facebook');
$contact->name = request()->input('name');
$contact->nick = request()->input('nick');
$contact->homepage = request()->input('homepage');
$contact->twitter = request()->input('twitter');
$contact->facebook = request()->input('facebook');
$contact->save();
if ($request->hasFile('avatar') && ($request->input('homepage') != '')) {
$dir = parse_url($request->input('homepage'), PHP_URL_HOST);
if (request()->hasFile('avatar') && (request()->input('homepage') != '')) {
$dir = parse_url(request()->input('homepage'), PHP_URL_HOST);
$destination = public_path() . '/assets/profile-images/' . $dir;
$filesystem = new Filesystem();
if ($filesystem->isDirectory($destination) === false) {
$filesystem->makeDirectory($destination);
}
$request->file('avatar')->move($destination, 'image');
request()->file('avatar')->move($destination, 'image');
}
return redirect('/admin/contacts');
@ -99,10 +101,10 @@ class ContactsController extends Controller
/**
* Process the request to delete a contact.
*
* @param string The contact id
* @return \Illuminate\View\Factory view
* @param int $contactId
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($contactId)
public function destroy(int $contactId): RedirectResponse
{
$contact = Contact::findOrFail($contactId);
$contact->delete();
@ -116,16 +118,16 @@ class ContactsController extends Controller
* This method attempts to find the microformat marked-up profile image
* from a given homepage and save it accordingly
*
* @param string The contact id
* @return \Illuminate\View\Factory view
* @param int $contactId
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function getAvatar($contactId)
public function getAvatar(int $contactId)
{
// Initialising
$avatarURL = null;
$avatar = null;
$contact = Contact::findOrFail($contactId);
if (mb_strlen($contact->homepage !== null) !== 0) {
if ($contact->homepage !== null && mb_strlen($contact->homepage) !== 0) {
$client = resolve(Client::class);
try {
$response = $client->get($contact->homepage);

View file

@ -1,12 +1,20 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Admin;
use Illuminate\View\View;
use App\Http\Controllers\Controller;
class HomeController extends Controller
{
public function welcome()
/**
* Show the homepage of the admin CP.
*
* @return \Illuminate\View\View
*/
public function welcome(): View
{
return view('admin.welcome', ['name' => config('admin.user')]);
}

View file

@ -12,6 +12,11 @@ use Illuminate\Http\RedirectResponse;
class LikesController extends Controller
{
/**
* List the likes that can be edited.
*
* @return \Illuminate\View\View
*/
public function index(): View
{
$likes = Like::all();
@ -19,11 +24,21 @@ class LikesController extends Controller
return view('admin.likes.index', compact('likes'));
}
/**
* Show the form to make a new like.
*
* @return \Illuminate\View\View
*/
public function create(): View
{
return view('admin.likes.create');
}
/**
* Process a request to make a new like.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(): RedirectResponse
{
$like = Like::create([
@ -34,6 +49,12 @@ class LikesController extends Controller
return redirect('/admin/likes');
}
/**
* Display the form to edit a specific like.
*
* @param int $likeId
* @return \Illuminate\View\View
*/
public function edit(int $likeId): View
{
$like = Like::findOrFail($likeId);
@ -44,6 +65,12 @@ class LikesController extends Controller
]);
}
/**
* Process a request to edit a like.
*
* @param int $likeId
* @return \Illuminate\Http\RedirectResponse
*/
public function update(int $likeId): RedirectResponse
{
$like = Like::findOrFail($likeId);
@ -54,6 +81,12 @@ class LikesController extends Controller
return redirect('/admin/likes');
}
/**
* Process the request to delete a like.
*
* @param int $likeId
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(int $likeId): RedirectResponse
{
Like::where('id', $likeId)->delete();

View file

@ -1,20 +1,24 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Admin;
use App\Models\Note;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Jobs\SendWebMentions;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
class NotesController extends Controller
{
/**
* List the notes that can be edited.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function index()
public function index(): View
{
$notes = Note::select('id', 'note')->orderBy('id', 'desc')->get();
foreach ($notes as $note) {
@ -27,9 +31,9 @@ class NotesController extends Controller
/**
* Show the form to make a new note.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function create()
public function create(): View
{
return view('admin.notes.create');
}
@ -37,14 +41,13 @@ class NotesController extends Controller
/**
* Process a request to make a new note.
*
* @param Illuminate\Http\Request $request
* @todo Sort this mess out
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(): RedirectResponse
{
Note::create([
'in-reply-to' => $request->input('in-reply-to'),
'note' => $request->input('content'),
'in-reply-to' => request()->input('in-reply-to'),
'note' => request()->input('content'),
]);
return redirect('/admin/notes');
@ -53,10 +56,10 @@ class NotesController extends Controller
/**
* Display the form to edit a specific note.
*
* @param string The note id
* @return \Illuminate\View\Factory view
* @param int $noteId
* @return \Illuminate\View\View
*/
public function edit($noteId)
public function edit(int $noteId): View
{
$note = Note::find($noteId);
$note->originalNote = $note->getOriginal('note');
@ -68,18 +71,18 @@ class NotesController extends Controller
* Process a request to edit a note. Easy since this can only be done
* from the admin CP.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
* @param int $noteId
* @return \Illuminate\Http\RedirectResponse
*/
public function update($noteId, Request $request)
public function update(int $noteId): RedirectResponse
{
//update note data
$note = Note::findOrFail($noteId);
$note->note = $request->input('content');
$note->in_reply_to = $request->input('in-reply-to');
$note->note = request()->input('content');
$note->in_reply_to = request()->input('in-reply-to');
$note->save();
if ($request->input('webmentions')) {
if (request()->input('webmentions')) {
dispatch(new SendWebMentions($note));
}
@ -89,12 +92,12 @@ class NotesController extends Controller
/**
* Delete the note.
*
* @param int id
* @return view
* @param int $noteId
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy($id)
public function destroy(int $noteId): RedirectResponse
{
$note = Note::findOrFail($id);
$note = Note::findOrFail($noteId);
$note->delete();
return redirect('/admin/notes');

View file

@ -1,11 +1,15 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Admin;
use App\Models\Place;
use Illuminate\View\View;
use Illuminate\Http\Request;
use App\Services\PlaceService;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Phaza\LaravelPostgis\Geometries\Point;
class PlacesController extends Controller
@ -20,9 +24,9 @@ class PlacesController extends Controller
/**
* List the places that can be edited.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function index()
public function index(): View
{
$places = Place::all();
@ -32,9 +36,9 @@ class PlacesController extends Controller
/**
* Show the form to make a new place.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function create()
public function create(): View
{
return view('admin.places.create');
}
@ -42,12 +46,11 @@ class PlacesController extends Controller
/**
* Process a request to make a new place.
*
* @param Illuminate\Http\Request $request
* @return Illuminate\View\Factory view
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
public function store(): RedirectResponse
{
$data = $request->only(['name', 'description', 'latitude', 'longitude']);
$data = request()->only(['name', 'description', 'latitude', 'longitude']);
$place = $this->placeService->createPlace($data);
return redirect('/admin/places');
@ -56,10 +59,10 @@ class PlacesController extends Controller
/**
* Display the form to edit a specific place.
*
* @param string The place id
* @return \Illuminate\View\Factory view
* @param int $placeId
* @return \Illuminate\View\View
*/
public function edit($placeId)
public function edit(int $placeId): View
{
$place = Place::findOrFail($placeId);
@ -69,17 +72,19 @@ class PlacesController extends Controller
/**
* Process a request to edit a place.
*
* @param string The place id
* @param Illuminate\Http\Request $request
* @return Illuminate\View\Factory view
* @param int $placeId
* @return \Illuminate\Http\RedirectResponse
*/
public function update($placeId, Request $request)
public function update(int $placeId): RedirectResponse
{
$place = Place::findOrFail($placeId);
$place->name = $request->name;
$place->description = $request->description;
$place->location = new Point((float) $request->latitude, (float) $request->longitude);
$place->icon = $request->icon;
$place->name = request()->input('name');
$place->description = request()->input('description');
$place->location = new Point(
(float) request()->input('latitude'),
(float) request()->input('longitude')
);
$place->icon = request()->input('icon');
$place->save();
return redirect('/admin/places');
@ -88,10 +93,10 @@ class PlacesController extends Controller
/**
* List the places we can merge with the current place.
*
* @param string Place id
* @return Illuminate\View\Factory view
* @param int $placeId
* @return \Illuminate\View\View
*/
public function mergeIndex($placeId)
public function mergeIndex(int $placeId): View
{
$first = Place::find($placeId);
$results = Place::near(new Point($first->latitude, $first->longitude))->get();
@ -105,27 +110,39 @@ class PlacesController extends Controller
return view('admin.places.merge.index', compact('first', 'places'));
}
public function mergeEdit($place1_id, $place2_id)
/**
* Show a form for merging two specific places.
*
* @param int $placeId1
* @param int $placeId2
* @return \Illuminate\View\View
*/
public function mergeEdit(int $placeId1, int $placeId2): View
{
$place1 = Place::find($place1_id);
$place2 = Place::find($place2_id);
$place1 = Place::find($placeId1);
$place2 = Place::find($placeId2);
return view('admin.places.merge.edit', compact('place1', 'place2'));
}
public function mergeStore(Request $request)
/**
* Process the request to merge two places.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function mergeStore(): RedirectResponse
{
$place1 = Place::find($request->input('place1'));
$place2 = Place::find($request->input('place2'));
$place1 = Place::find(request()->input('place1'));
$place2 = Place::find(request()->input('place2'));
if ($request->input('delete') === '1') {
if (request()->input('delete') === '1') {
foreach ($place1->notes as $note) {
$note->place()->dissociate();
$note->place()->associate($place2->id);
}
$place1->delete();
}
if ($request->input('delete') === '2') {
if (request()->input('delete') === '2') {
foreach ($place2->notes as $note) {
$note->place()->dissociate();
$note->place()->associate($place1->id);

View file

@ -1,21 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Article;
use Illuminate\View\View;
use Jonnybarnes\IndieWeb\Numbers;
use Illuminate\Http\RedirectResponse;
class ArticlesController extends Controller
{
/**
* Show all articles (with pagination).
*
* @return \Illuminate\View\Factory view
* @param int $year
* @param int $month
* @return \Illuminate\View\View
*/
public function index($year = null, $month = null)
public function index(int $year = null, int $month = null): View
{
$articles = Article::where('published', '1')
->date((int) $year, (int) $month)
->date($year, $month)
->orderBy('updated_at', 'desc')
->simplePaginate(5);
@ -25,9 +31,12 @@ class ArticlesController extends Controller
/**
* Show a single article.
*
* @return \Illuminate\View\Factory view
* @param int $year
* @param int $month
* @param string $slug
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function show($year, $month, $slug)
public function show(int $year, int $month, string $slug)
{
$article = Article::where('titleurl', $slug)->firstOrFail();
if ($article->updated_at->year != $year || $article->updated_at->month != $month) {
@ -44,12 +53,12 @@ class ArticlesController extends Controller
* We only have the ID, work out post title, year and month
* and redirect to it.
*
* @return \Illuminte\Routing\RedirectResponse redirect
* @param int $idFromUrl
* @return \Illuminte\Http\RedirectResponse
*/
public function onlyIdInUrl($inURLId)
public function onlyIdInUrl(int $idFromUrl): RedirectResponse
{
$numbers = new Numbers();
$realId = $numbers->b60tonum($inURLId);
$realId = resolve(Numbers::class)->b60tonum($idFromUrl);
$article = Article::findOrFail($realId);
return redirect($article->link);

View file

@ -1,12 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
class AuthController extends Controller
{
public function showLogin()
/**
* Show the login form.
*
* @return \Illuminate\View\View
*/
public function showLogin(): View
{
return view('login');
}
@ -15,14 +24,13 @@ class AuthController extends Controller
* Log in a user, set a sesion variable, check credentials against
* the .env file.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\RedirectResponse redirect
* @return \Illuminate\Http\RedirectResponse
*/
public function login(Request $request)
public function login(): RedirectResponse
{
if ($request->input('username') === config('admin.user')
if (request()->input('username') === config('admin.user')
&&
$request->input('password') === config('admin.pass')
request()->input('password') === config('admin.pass')
) {
session(['loggedin' => true]);

View file

@ -1,19 +1,33 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Bookmark;
use Illuminate\View\View;
class BookmarksController extends Controller
{
public function index()
/**
* Show the most recent bookmarks.
*
* @return \Illuminate\View\View
*/
public function index(): View
{
$bookmarks = Bookmark::latest()->with('tags')->withCount('tags')->paginate(10);
return view('bookmarks.index', compact('bookmarks'));
}
public function show(Bookmark $bookmark)
/**
* Show a single bookmark.
*
* @param \App\Models\Bookmark $bookmark
* @return \Illuminate\View\View
*/
public function show(Bookmark $bookmark): View
{
$bookmark->loadMissing('tags');

View file

@ -1,8 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Contact;
use Illuminate\View\View;
use Illuminate\Filesystem\Filesystem;
class ContactsController extends Controller
@ -10,9 +13,9 @@ class ContactsController extends Controller
/**
* Show all the contacts.
*
* @return \Illuminate\View\Factory view
* @return \Illuminate\View\View
*/
public function index()
public function index(): View
{
$filesystem = new Filesystem();
$contacts = Contact::all();
@ -31,9 +34,12 @@ class ContactsController extends Controller
/**
* Show a single contact.
*
* @return \Illuminate\View\Factory view
* @todo Use implicit model binding.
*
* @param string $nick The nickname associated with contact
* @return \Illuminate\View\View
*/
public function show($nick)
public function show(string $nick): View
{
$filesystem = new Filesystem();
$contact = Contact::where('nick', '=', $nick)->firstOrFail();

View file

@ -1,7 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use App\Models\{Article, Note};
class FeedsController extends Controller
@ -11,7 +14,7 @@ class FeedsController extends Controller
*
* @return \Illuminate\Http\Response
*/
public function blogRss()
public function blogRss(): Response
{
$articles = Article::where('published', '1')->latest('updated_at')->take(20)->get();
$buildDate = $articles->first()->updated_at->toRssString();
@ -26,7 +29,7 @@ class FeedsController extends Controller
*
* @return \Illuminate\Http\Response
*/
public function blogAtom()
public function blogAtom(): Response
{
$articles = Article::where('published', '1')->latest('updated_at')->take(20)->get();
@ -40,7 +43,7 @@ class FeedsController extends Controller
*
* @return \Illuminate\Http\Response
*/
public function notesRss()
public function notesRss(): Response
{
$notes = Note::latest()->take(20)->get();
$buildDate = $notes->first()->updated_at->toRssString();
@ -55,7 +58,7 @@ class FeedsController extends Controller
*
* @return \Illuminate\Http\Response
*/
public function notesAtom()
public function notesAtom(): Response
{
$notes = Note::latest()->take(20)->get();
@ -64,10 +67,12 @@ class FeedsController extends Controller
->header('Content-Type', 'application/atom+xml; charset=utf-8');
}
/** @todo sort out return type for json responses */
/**
* Returns the blog JSON feed.
*
* @return \Illuminate\Http\response
* @return \Illuminate\Http\JsonResponse
*/
public function blogJson()
{
@ -100,7 +105,7 @@ class FeedsController extends Controller
/**
* Returns the notes JSON feed.
*
* @return \Illuminate\Http\response
* @return \Illuminate\Http\JsonResponse
*/
public function notesJson()
{

View file

@ -1,19 +1,33 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Like;
use Illuminate\View\View;
class LikesController extends Controller
{
public function index()
/**
* Show the latest likes.
*
* @return \Illuminate\View\View
*/
public function index(): View
{
$likes = Like::latest()->paginate(20);
return view('likes.index', compact('likes'));
}
public function show(Like $like)
/**
* Show a single like.
*
* @param \App\Models\Like $like
* @return \Illuminate\View\View
*/
public function show(Like $like): View
{
return view('likes.show', compact('like'));
}

View file

@ -1,11 +1,14 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Monolog\Logger;
use Ramsey\Uuid\Uuid;
use App\Jobs\ProcessMedia;
use App\Services\TokenService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\UploadedFile;
use Monolog\Handler\StreamHandler;
use Intervention\Image\ImageManager;
@ -40,12 +43,11 @@ class MicropubController extends Controller
* This function receives an API request, verifies the authenticity
* then passes over the info to the relavent Service class.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function post()
public function post(): JsonResponse
{
try {
info(request()->input('access_token'));
$tokenData = $this->tokenService->validateToken(request()->input('access_token'));
} catch (InvalidTokenException $e) {
return $this->invalidTokenResponse();
@ -96,14 +98,16 @@ class MicropubController extends Controller
}
/**
* Respond to a GET request to the micropub endpoint.
*
* A GET request has been made to `api/post` with an accompanying
* token, here we check wether the token is valid and respond
* appropriately. Further if the request has the query parameter
* synidicate-to we respond with the known syndication endpoints.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function get()
public function get(): JsonResponse
{
try {
$tokenData = $this->tokenService->validateToken(request()->bearerToken());
@ -124,7 +128,7 @@ class MicropubController extends Controller
]);
}
if (substr(request()->input('q'), 0, 4) === 'geo:') {
if (request()->has('q') && substr(request()->input('q'), 0, 4) === 'geo:') {
preg_match_all(
'/([0-9\.\-]+)/',
request()->input('q'),
@ -153,9 +157,9 @@ class MicropubController extends Controller
/**
* Process a media item posted to the media endpoint.
*
* @return Illuminate\Http\Response
* @return Illuminate\Http\JsonResponse
*/
public function media()
public function media(): JsonResponse
{
try {
$tokenData = $this->tokenService->validateToken(request()->bearerToken());
@ -210,10 +214,10 @@ class MicropubController extends Controller
/**
* Get the file type from the mimetype of the uploaded file.
*
* @param string The mimetype
* @return string The type
* @param string $mimetype
* @return string
*/
private function getFileTypeFromMimeType($mimetype)
private function getFileTypeFromMimeType(string $mimetype): string
{
//try known images
$imageMimeTypes = [
@ -252,6 +256,11 @@ class MicropubController extends Controller
return 'download';
}
/**
* Determine the client id from the access token sent with the request.
*
* @return string
*/
private function getClientId(): string
{
return resolve(TokenService::class)
@ -259,6 +268,11 @@ class MicropubController extends Controller
->getClaim('client_id');
}
/**
* Save the details of the micropub request to a log file.
*
* @param array $request This is the info from request()->all()
*/
private function logMicropubRequest(array $request)
{
$logger = new Logger('micropub');
@ -266,7 +280,13 @@ class MicropubController extends Controller
$logger->debug('MicropubLog', $request);
}
private function saveFile(UploadedFile $file)
/**
* Save an uploaded file to the local disk.
*
* @param \Illuminate\Http\UploadedFele $file
* @return string $filename
*/
private function saveFile(UploadedFile $file): string
{
$filename = Uuid::uuid4() . '.' . $file->extension();
Storage::disk('local')->put($filename, $file);
@ -274,6 +294,11 @@ class MicropubController extends Controller
return $filename;
}
/**
* Generate a response to be returned when the token has insufficient scope.
*
* @return \Illuminate\Http\JsonRepsonse
*/
private function insufficientScopeResponse()
{
return response()->json([
@ -283,6 +308,11 @@ class MicropubController extends Controller
], 401);
}
/**
* Generate a response to be returned when the token is invalid.
*
* @return \Illuminate\Http\JsonRepsonse
*/
private function invalidTokenResponse()
{
return response()->json([
@ -292,6 +322,11 @@ class MicropubController extends Controller
], 400);
}
/**
* Generate a response to be returned when the token has no scope.
*
* @return \Illuminate\Http\JsonRepsonse
*/
private function tokenHasNoScopeResponse()
{
return response()->json([

View file

@ -1,10 +1,14 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Note;
use Illuminate\View\View;
use Illuminate\Http\Request;
use Jonnybarnes\IndieWeb\Numbers;
use Illuminate\Http\RedirectResponse;
use App\Services\ActivityStreamsService;
// Need to sort out Twitter and webmentions!
@ -14,7 +18,7 @@ class NotesController extends Controller
/**
* Show all the notes. This is also the homepage.
*
* @return \Illuminte\View\Factory view
* @return \Illuminate\View\View|\Illuminate\Http\JsonResponse
*/
public function index()
{
@ -34,10 +38,10 @@ class NotesController extends Controller
/**
* Show a single note.
*
* @param string The id of the note
* @return \Illuminate\View\Factory view
* @param string $urlId The id of the note
* @return \Illuminate\View\View|\Illuminate\Http\JsonResponse
*/
public function show($urlId)
public function show(string $urlId)
{
$note = Note::nb60($urlId)->with('webmentions')->firstOrFail();
@ -51,10 +55,10 @@ class NotesController extends Controller
/**
* Redirect /note/{decID} to /notes/{nb60id}.
*
* @param string The decimal id of he note
* @return \Illuminate\Routing\RedirectResponse redirect
* @param int $decId The decimal id of the note
* @return \Illuminate\Http\RedirectResponse
*/
public function redirect($decId)
public function redirect(int $decId): RedirectResponse
{
return redirect(config('app.url') . '/notes/' . (new Numbers())->numto60($decId));
}
@ -62,10 +66,10 @@ class NotesController extends Controller
/**
* Show all notes tagged with {tag}.
*
* @param string The tag
* @return \Illuminate\View\Factory view
* @param string $tag
* @return \Illuminate\View\View
*/
public function tagged($tag)
public function tagged(string $tag): View
{
$notes = Note::whereHas('tags', function ($query) use ($tag) {
$query->where('tag', $tag);

View file

@ -1,17 +1,20 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Place;
use Illuminate\View\View;
class PlacesController extends Controller
{
/**
* Display a listing of the resource.
* Show all the places.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\View\View
*/
public function index()
public function index(): View
{
$places = Place::all();
@ -19,12 +22,12 @@ class PlacesController extends Controller
}
/**
* Display the specified resource.
* Show a specific place.
*
* @param string $slug
* @return \Illuminate\Http\Response
* @return \Illuminate\View\View
*/
public function show($slug)
public function show(string $slug): View
{
$place = Place::where('slug', '=', $slug)->firstOrFail();

View file

@ -1,15 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Note;
use Illuminate\Http\Request;
use Illuminate\View\View;
class SearchController extends Controller
{
public function search(Request $request)
/**
* Display search results.
*
* @return \Illuminate\View\View
*/
public function search(): View
{
$notes = Note::search($request->terms)->paginate(10);
$notes = Note::search(request()->input('terms'))->paginate(10);
return view('search', compact('notes'));
}

View file

@ -1,9 +1,16 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
class SessionStoreController extends Controller
{
/**
* Save the selected colour scheme in the session.
*
* @return \Illuminate\Http\JsonResponse
*/
public function saveColour()
{
$css = request()->input('css');

View file

@ -1,7 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
class ShortURLsController extends Controller
{
/*
@ -16,9 +20,9 @@ class ShortURLsController extends Controller
/**
* Redirect from '/' to the long url.
*
* @return \Illuminate\Routing\RedirectResponse redirect
* @return \Illuminate\Http\RedirectResponse
*/
public function baseURL()
public function baseURL(): RedirectResponse
{
return redirect(config('app.url'));
}
@ -26,9 +30,9 @@ class ShortURLsController extends Controller
/**
* Redirect from '/@' to a twitter profile.
*
* @return \Illuminate\Routing\RedirectResponse redirect
* @return \Illuminate\Http\RedirectResponse
*/
public function twitter()
public function twitter(): RedirectResponse
{
return redirect('https://twitter.com/jonnybarnes');
}
@ -36,9 +40,9 @@ class ShortURLsController extends Controller
/**
* Redirect from '/+' to a Google+ profile.
*
* @return \Illuminate\Routing\RedirectResponse redirect
* @return \Illuminate\Http\RedirectResponse
*/
public function googlePlus()
public function googlePlus(): RedirectResponse
{
return redirect('https://plus.google.com/u/0/117317270900655269082/about');
}
@ -49,9 +53,9 @@ class ShortURLsController extends Controller
*
* @param string Post type
* @param string Post ID
* @return \Illuminate\Routing\Redirector redirect
* @return \Illuminate\Http\RedirectResponse
*/
public function expandType($type, $postId)
public function expandType(string $type, string $postId): RedirectResponse
{
if ($type == 't') {
$type = 'notes';

View file

@ -1,9 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use IndieAuth\Client;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use App\Services\TokenService;
class TokenEndpointController extends Controller
@ -21,9 +23,8 @@ class TokenEndpointController extends Controller
/**
* Inject the dependencies.
*
* @param \IndieAuth\Client $client
* @param \App\Services\TokenService $tokenService
* @return void
* @param \IndieAuth\Client $client
* @param \App\Services\TokenService $tokenService
*/
public function __construct(
Client $client,
@ -36,30 +37,29 @@ class TokenEndpointController extends Controller
/**
* If the user has authd via the IndieAuth protocol, issue a valid token.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function create(Request $request)
public function create(): Response
{
$authorizationEndpoint = $this->client->discoverAuthorizationEndpoint(normalize_url($request->input('me')));
$authorizationEndpoint = $this->client->discoverAuthorizationEndpoint(normalize_url(request()->input('me')));
if ($authorizationEndpoint) {
$auth = $this->client->verifyIndieAuthCode(
$authorizationEndpoint,
$request->input('code'),
$request->input('me'),
$request->input('redirect_uri'),
$request->input('client_id')
request()->input('code'),
request()->input('me'),
request()->input('redirect_uri'),
request()->input('client_id')
);
if (array_key_exists('me', $auth)) {
$scope = $auth['scope'] ?? '';
$tokenData = [
'me' => $request->input('me'),
'client_id' => $request->input('client_id'),
'me' => request()->input('me'),
'client_id' => request()->input('client_id'),
'scope' => $scope,
];
$token = $this->tokenService->getNewToken($tokenData);
$content = http_build_query([
'me' => $request->input('me'),
'me' => request()->input('me'),
'scope' => $scope,
'access_token' => $token,
]);

View file

@ -1,9 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Note;
use Illuminate\Http\Request;
use Illuminate\View\View;
use Illuminate\Http\Response;
use App\Jobs\ProcessWebMention;
use Jonnybarnes\IndieWeb\Numbers;
@ -11,7 +13,15 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
class WebMentionsController extends Controller
{
public function get()
/**
* Response to a GET request to the webmention endpoint.
*
* This is probably someone looking for information about what
* webmentions are, or about my particular implementation.
*
* @return \Illuminate\View\View
*/
public function get(): View
{
return view('webmention-endpoint');
}
@ -19,47 +29,45 @@ class WebMentionsController extends Controller
/**
* Receive and process a webmention.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Respone
*/
public function receive(Request $request)
public function receive(): Response
{
//first we trivially reject requets that lack all required inputs
if (($request->has('target') !== true) || ($request->has('source') !== true)) {
return new Response(
if ((request()->has('target') !== true) || (request()->has('source') !== true)) {
return response(
'You need both the target and source parameters',
400
);
}
//next check the $target is valid
$path = parse_url($request->input('target'), PHP_URL_PATH);
$path = parse_url(request()->input('target'), PHP_URL_PATH);
$pathParts = explode('/', $path);
if ($pathParts[1] == 'notes') {
//we have a note
$noteId = $pathParts[2];
$numbers = new Numbers();
try {
$note = Note::findOrFail($numbers->b60tonum($noteId));
dispatch(new ProcessWebMention($note, $request->input('source')));
$note = Note::findOrFail(resolve(Numbers::class)->b60tonum($noteId));
dispatch(new ProcessWebMention($note, request()->input('source')));
} catch (ModelNotFoundException $e) {
return new Response('This note doesnt exist.', 400);
return response('This note doesnt exist.', 400);
}
return new Response(
return response(
'Webmention received, it will be processed shortly',
202
);
}
if ($pathParts[1] == 'blog') {
return new Response(
return response(
'I dont accept webmentions for blog posts yet.',
501
);
}
return new Response(
return response(
'Invalid request',
400
);

View file

@ -1,8 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ActivityStreamLinks
{
@ -13,7 +16,7 @@ class ActivityStreamLinks
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
$response = $next($request);
if ($request->path() === '/') {

View file

@ -1,8 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class LocalhostSessionMiddleware
{
@ -15,7 +18,7 @@ class LocalhostSessionMiddleware
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
if (config('app.env') !== 'production') {
session(['me' => config('app.url')]);

View file

@ -1,8 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class MyAuthMiddleware
{
@ -13,7 +16,7 @@ class MyAuthMiddleware
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
if ($request->session()->has('loggedin') !== true) {
//theyre not logged in, so send them to login form

View file

@ -1,8 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class VerifyMicropubToken
{
@ -13,7 +16,7 @@ class VerifyMicropubToken
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
if ($request->input('access_token')) {
return $next($request);

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use Illuminate\Bus\Queueable;
@ -18,7 +20,7 @@ class AddClientToDatabase implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param string $client_id
*/
public function __construct(string $client_id)
{

View file

@ -1,9 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use GuzzleHttp\Client;
use Illuminate\Bus\Queueable;
use Illuminate\FileSystem\FileSystem;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -22,7 +25,7 @@ class DownloadWebMention implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param string $source
*/
public function __construct(string $source)
{
@ -32,7 +35,7 @@ class DownloadWebMention implements ShouldQueue
/**
* Execute the job.
*
* @return void
* @param \GuzzleHttp\Client $guzzle
*/
public function handle(Client $guzzle)
{
@ -40,7 +43,7 @@ class DownloadWebMention implements ShouldQueue
//4XX and 5XX responses should get Guzzle to throw an exception,
//Laravel should catch and retry these automatically.
if ($response->getStatusCode() == '200') {
$filesystem = new \Illuminate\FileSystem\FileSystem();
$filesystem = new FileSystem();
$filename = storage_path('HTML') . '/' . $this->createFilenameFromURL($this->source);
//backup file first
$filenameBackup = $filename . '.' . date('Y-m-d') . '.backup';

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use App\Models\Bookmark;
@ -20,7 +22,7 @@ class ProcessBookmark implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param \App\Models\Bookmark $bookmark
*/
public function __construct(Bookmark $bookmark)
{

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use App\Models\Like;
@ -23,7 +25,7 @@ class ProcessLike implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param \App\Models\Like $like
*/
public function __construct(Like $like)
{
@ -33,9 +35,11 @@ class ProcessLike implements ShouldQueue
/**
* Execute the job.
*
* @return void
* @param \GuzzleHttp\Client $client
* @param \Jonnybarnes\WebmentionsParser\Authorship $authorship
* @return int
*/
public function handle(Client $client, Authorship $authorship)
public function handle(Client $client, Authorship $authorship): int
{
if ($this->isTweet($this->like->url)) {
$tweet = Twitter::getOembed(['url' => $this->like->url]);
@ -83,8 +87,16 @@ class ProcessLike implements ShouldQueue
}
$this->like->save();
return 0;
}
/**
* Determine if a given URL is that of a Tweet.
*
* @param string $url
* @return bool
*/
private function isTweet(string $url): bool
{
$host = parse_url($url, PHP_URL_HOST);

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use Illuminate\Bus\Queueable;
@ -20,7 +22,7 @@ class ProcessMedia implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param string $filename
*/
public function __construct(string $filename)
{
@ -30,7 +32,7 @@ class ProcessMedia implements ShouldQueue
/**
* Execute the job.
*
* @return void
* @param \Intervention\Image\ImageManager $manager
*/
public function handle(ImageManager $manager)
{

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use Mf2;
@ -22,9 +24,8 @@ class ProcessWebMention implements ShouldQueue
/**
* Create a new job instance.
*
* @param \App\Note $note
* @param string $source
* @return void
* @param \App\Note $note
* @param string $source
*/
public function __construct(Note $note, $source)
{
@ -37,7 +38,6 @@ class ProcessWebMention implements ShouldQueue
*
* @param \Jonnybarnes\WebmentionsParser\Parser $parser
* @param \GuzzleHttp\Client $guzzle
* @return void
*/
public function handle(Parser $parser, Client $guzzle)
{
@ -102,7 +102,6 @@ class ProcessWebMention implements ShouldQueue
*
* @param string $html
* @param string $url
* @return string|null
*/
private function saveRemoteContent($html, $url)
{

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use GuzzleHttp\Client;
@ -20,9 +22,9 @@ class SaveProfileImage implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param array $microformats
*/
public function __construct($microformats)
public function __construct(array $microformats)
{
$this->microformats = $microformats;
}
@ -30,7 +32,7 @@ class SaveProfileImage implements ShouldQueue
/**
* Execute the job.
*
* @return void
* @param \Jonnybarnes\WebmentionsParser\Authorship $authorship
*/
public function handle(Authorship $authorship)
{

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use App\Models\Note;
@ -18,8 +20,7 @@ class SendWebMentions implements ShouldQueue
/**
* Create the job instance, inject dependencies.
*
* @param Note $note
* @return void
* @param Note $note
*/
public function __construct(Note $note)
{
@ -34,7 +35,9 @@ class SendWebMentions implements ShouldQueue
public function handle()
{
//grab the URLs
$urlsInReplyTo = explode(' ', $this->note->in_reply_to);
$inReplyTo = $this->note->in_reply_to ?? '';
// above so explode doesnt complain about null being passed in
$urlsInReplyTo = explode(' ', $inReplyTo);
$urlsNote = $this->getLinks($this->note->note);
$urls = array_filter(array_merge($urlsInReplyTo, $urlsNote)); //filter out none URLs
foreach ($urls as $url) {
@ -54,10 +57,10 @@ class SendWebMentions implements ShouldQueue
/**
* Discover if a URL has a webmention endpoint.
*
* @param string The URL
* @return string The webmention endpoint URL
* @param string $url
* @return string|null
*/
public function discoverWebmentionEndpoint($url)
public function discoverWebmentionEndpoint(string $url)
{
//lets not send webmentions to myself
if (parse_url($url, PHP_URL_HOST) == config('app.longurl')) {
@ -97,8 +100,8 @@ class SendWebMentions implements ShouldQueue
/**
* Get the URLs from a note.
*
* @param string $html
* @return array $urls
* @param string $html
* @return array $urls
*/
public function getLinks($html)
{
@ -119,8 +122,8 @@ class SendWebMentions implements ShouldQueue
/**
* Resolve a URI if necessary.
*
* @param string $url
* @param string $base
* @param string $url
* @param string $base The base of the URL
* @return string
*/
public function resolveUri(string $url, string $base): string

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use GuzzleHttp\Client;
@ -19,7 +21,7 @@ class SyndicateBookmarkToFacebook implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param \App\Models\Bookmark $bookmark
*/
public function __construct(Bookmark $bookmark)
{
@ -29,7 +31,7 @@ class SyndicateBookmarkToFacebook implements ShouldQueue
/**
* Execute the job.
*
* @return void
* @param \GuzzleHttp\Client $guzzle
*/
public function handle(Client $guzzle)
{

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use GuzzleHttp\Client;
@ -19,7 +21,7 @@ class SyndicateBookmarkToTwitter implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param \App\Models\Bookmark $bookmark
*/
public function __construct(Bookmark $bookmark)
{
@ -29,7 +31,7 @@ class SyndicateBookmarkToTwitter implements ShouldQueue
/**
* Execute the job.
*
* @return void
* @param \GuzzleHttp\Client $guzzle
*/
public function handle(Client $guzzle)
{

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use App\Models\Note;
@ -18,7 +20,7 @@ class SyndicateNoteToFacebook implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param \App\Models\Note $note
*/
public function __construct(Note $note)
{
@ -28,7 +30,7 @@ class SyndicateNoteToFacebook implements ShouldQueue
/**
* Execute the job.
*
* @return void
* @param \GuzzleHttp\Client $guzzle
*/
public function handle(Client $guzzle)
{

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Jobs;
use App\Models\Note;
@ -18,7 +20,7 @@ class SyndicateNoteToTwitter implements ShouldQueue
/**
* Create a new job instance.
*
* @return void
* @param \App\Models\Note $note
*/
public function __construct(Note $note)
{
@ -28,8 +30,7 @@ class SyndicateNoteToTwitter implements ShouldQueue
/**
* Execute the job.
*
* @param \GuzzleHttp\Client $guzzle
* @return void
* @param \GuzzleHttp\Client $guzzle
*/
public function handle(Client $guzzle)
{

View file

@ -1,8 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Cviebrock\EloquentSluggable\Sluggable;
use League\CommonMark\CommonMarkConverter;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -31,7 +34,7 @@ class Article extends Model
*
* @return array
*/
public function sluggable()
public function sluggable(): array
{
return [
'titleurl' => [
@ -52,7 +55,7 @@ class Article extends Model
*
* @return string
*/
public function getHtmlAttribute()
public function getHtmlAttribute(): string
{
$markdown = new CommonMarkConverter();
$html = $markdown->convertToHtml($this->main);
@ -70,7 +73,7 @@ class Article extends Model
*
* @return string
*/
public function getW3cTimeAttribute()
public function getW3cTimeAttribute(): string
{
return $this->updated_at->toW3CString();
}
@ -80,7 +83,7 @@ class Article extends Model
*
* @return string
*/
public function getTooltipTimeAttribute()
public function getTooltipTimeAttribute(): string
{
return $this->updated_at->toRFC850String();
}
@ -90,7 +93,7 @@ class Article extends Model
*
* @return string
*/
public function getHumanTimeAttribute()
public function getHumanTimeAttribute(): string
{
return $this->updated_at->diffForHumans();
}
@ -100,7 +103,7 @@ class Article extends Model
*
* @return string
*/
public function getPubdateAttribute()
public function getPubdateAttribute(): string
{
return $this->updated_at->toRSSString();
}
@ -110,7 +113,7 @@ class Article extends Model
*
* @return string
*/
public function getLinkAttribute()
public function getLinkAttribute(): string
{
return '/blog/' . $this->updated_at->year . '/' . $this->updated_at->format('m') . '/' . $this->titleurl;
}
@ -120,7 +123,7 @@ class Article extends Model
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeDate($query, int $year = null, int $month = null)
public function scopeDate($query, int $year = null, int $month = null): Builder
{
if ($year == null) {
return $query;

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
@ -24,6 +26,8 @@ class Bookmark extends Model
/**
* The tags that belong to the bookmark.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
@ -32,8 +36,10 @@ class Bookmark extends Model
/**
* The full url of a bookmark.
*
* @return string
*/
public function getLongurlAttribute()
public function getLongurlAttribute(): string
{
return config('app.url') . '/bookmarks/' . $this->id;
}

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Mf2;
@ -11,17 +13,33 @@ class Like extends Model
{
protected $fillable = ['url'];
public function setUrlAttribute($value)
/**
* Normalize the URL of a Like.
*
* @param string $value The provided URL
*/
public function setUrlAttribute(string $value)
{
$this->attributes['url'] = normalize_url($value);
}
public function setAuthorUrlAttribute($value)
/**
* Normalize the URL of the author of the like.
*
* @param string $value The authors url
*/
public function setAuthorUrlAttribute(?string $value)
{
$this->attributes['author_url'] = normalize_url($value);
}
public function getContentAttribute($value)
/**
* If the content contains HTML, filter it.
*
* @param string $value The content of the like
* @return string|null
*/
public function getContentAttribute(?string $value): ?string
{
if ($value === null) {
return null;
@ -38,7 +56,13 @@ class Like extends Model
return $value;
}
public function filterHTML($html)
/**
* Filter some HTML with HTMLPurifier.
*
* @param string $html
* @return string
*/
private function filterHTML(string $html): string
{
$config = HTMLPurifier_Config::createDefault();
$config->set('Cache.SerializerPath', storage_path() . '/HTMLPurifier');

View file

@ -1,8 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Media extends Model
{
@ -22,8 +25,10 @@ class Media extends Model
/**
* Get the note that owns this media.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function note()
public function note(): BelongsTo
{
return $this->belongsTo('App\Models\Note');
}
@ -33,7 +38,7 @@ class Media extends Model
*
* @return string
*/
public function getUrlAttribute()
public function getUrlAttribute(): string
{
if (starts_with($this->path, 'https://')) {
return $this->path;
@ -47,7 +52,7 @@ class Media extends Model
*
* @return string
*/
public function getMediumurlAttribute()
public function getMediumurlAttribute(): string
{
$basename = $this->getBasename($this->path);
$extension = $this->getExtension($this->path);
@ -60,7 +65,7 @@ class Media extends Model
*
* @return string
*/
public function getSmallurlAttribute()
public function getSmallurlAttribute(): string
{
$basename = $this->getBasename($this->path);
$extension = $this->getExtension($this->path);
@ -68,7 +73,13 @@ class Media extends Model
return config('filesystems.disks.s3.url') . '/' . $basename . '-small.' . $extension;
}
public function getBasename($path)
/**
* Give the real part of a filename, i.e. strip the file extension.
*
* @param string $path
* @return string
*/
public function getBasename(string $path): string
{
// the following achieves this data flow
// foo.bar.png => ['foo', 'bar', 'png'] => ['foo', 'bar'] => foo.bar
@ -81,7 +92,13 @@ class Media extends Model
return $basename;
}
public function getExtension($path)
/**
* Get the extension from a given filename.
*
* @param string $path
* @return string
*/
public function getExtension(string $path): string
{
$parts = explode('.', $path);

View file

@ -1,8 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class MicropubClient extends Model
{
@ -23,9 +26,9 @@ class MicropubClient extends Model
/**
* Define the relationship with notes.
*
* @return void
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function notes()
public function notes(): HasMany
{
return $this->hasMany('App\Models\Note', 'client_id', 'client_url');
}

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Cache;
@ -14,6 +16,7 @@ use League\CommonMark\Environment;
use League\CommonMark\HtmlRenderer;
use Illuminate\Database\Eloquent\Model;
use Jonnybarnes\EmojiA11y\EmojiModifier;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletes;
use Jonnybarnes\CommonmarkLinkify\LinkifyExtension;
@ -29,8 +32,16 @@ class Note extends Model
*/
private const USERNAMES_REGEX = '/\[.*?\](*SKIP)(*F)|@(\w+)/';
/**
* This variable is used to keep track of contacts in a note.
*/
protected $contacts;
/**
* Set our contacts variable to null.
*
* @param array $attributes
*/
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
@ -65,7 +76,7 @@ class Note extends Model
/**
* Define the relationship with tags.
*
* @var array
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
@ -75,7 +86,7 @@ class Note extends Model
/**
* Define the relationship with clients.
*
* @var array?
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function client()
{
@ -85,7 +96,7 @@ class Note extends Model
/**
* Define the relationship with webmentions.
*
* @var array
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function webmentions()
{
@ -93,9 +104,9 @@ class Note extends Model
}
/**
* Definte the relationship with places.
* Define the relationship with places.
*
* @var array
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function place()
{
@ -105,7 +116,7 @@ class Note extends Model
/**
* Define the relationship with media.
*
* @return void
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function media()
{
@ -117,7 +128,7 @@ class Note extends Model
*
* @return array
*/
public function toSearchableArray()
public function toSearchableArray(): array
{
return [
'note' => $this->note,
@ -127,30 +138,36 @@ class Note extends Model
/**
* Normalize the note to Unicode FORM C.
*
* @param string $value
* @return string
* @param string|null $value
*/
public function setNoteAttribute($value)
public function setNoteAttribute(?string $value)
{
$normalized = normalizer_normalize($value, Normalizer::FORM_C);
if ($normalized === '') { //we dont want to save empty strings to the db
$normalized = null;
if ($value !== null) {
$normalized = normalizer_normalize($value, Normalizer::FORM_C);
if ($normalized === '') { //we dont want to save empty strings to the db
$normalized = null;
}
$this->attributes['note'] = $normalized;
}
$this->attributes['note'] = $normalized;
}
/**
* Pre-process notes for web-view.
*
* @param string
* @return string
* @param string|null $value
* @return string|null
*/
public function getNoteAttribute($value)
public function getNoteAttribute(?string $value): ?string
{
if ($value === null && $this->place !== null) {
$value = '📍: <a href="' . $this->place->longurl . '">' . $this->place->name . '</a>';
}
// if $value is still null, just return null
if ($value === null) {
return null;
}
$hcards = $this->makeHCards($value);
$hashtags = $this->autoLinkHashtag($hcards);
$html = $this->convertMarkdown($hashtags);
@ -164,11 +181,10 @@ class Note extends Model
*
* @return string
*/
public function getNb60idAttribute()
public function getNb60idAttribute(): string
{
$numbers = new Numbers();
return $numbers->numto60($this->id);
// we cast to string because sometimes the nb60id is an “int”
return (string) resolve(Numbers::class)->numto60($this->id);
}
/**
@ -176,7 +192,7 @@ class Note extends Model
*
* @return string
*/
public function getLongurlAttribute()
public function getLongurlAttribute(): string
{
return config('app.url') . '/notes/' . $this->nb60id;
}
@ -186,7 +202,7 @@ class Note extends Model
*
* @return string
*/
public function getShorturlAttribute()
public function getShorturlAttribute(): string
{
return config('app.shorturl') . '/notes/' . $this->nb60id;
}
@ -196,7 +212,7 @@ class Note extends Model
*
* @return string
*/
public function getIso8601Attribute()
public function getIso8601Attribute(): string
{
return $this->updated_at->toISO8601String();
}
@ -206,7 +222,7 @@ class Note extends Model
*
* @return string
*/
public function getHumandiffAttribute()
public function getHumandiffAttribute(): string
{
return $this->updated_at->diffForHumans();
}
@ -216,7 +232,7 @@ class Note extends Model
*
* @return string
*/
public function getPubdateAttribute()
public function getPubdateAttribute(): string
{
return $this->updated_at->toRSSString();
}
@ -224,9 +240,9 @@ class Note extends Model
/**
* Get the latitude value.
*
* @return string|null
* @return float|null
*/
public function getLatitudeAttribute()
public function getLatitudeAttribute(): ?float
{
if ($this->place !== null) {
return $this->place->location->getLat();
@ -235,16 +251,18 @@ class Note extends Model
$pieces = explode(':', $this->location);
$latlng = explode(',', $pieces[0]);
return trim($latlng[0]);
return (float) trim($latlng[0]);
}
return null;
}
/**
* Get the longitude value.
*
* @return string|null
* @return float|null
*/
public function getLongitudeAttribute()
public function getLongitudeAttribute(): ?float
{
if ($this->place !== null) {
return $this->place->location->getLng();
@ -253,8 +271,10 @@ class Note extends Model
$pieces = explode(':', $this->location);
$latlng = explode(',', $pieces[0]);
return trim($latlng[1]);
return (float) trim($latlng[1]);
}
return null;
}
/**
@ -263,7 +283,7 @@ class Note extends Model
*
* @return string|null
*/
public function getAddressAttribute()
public function getAddressAttribute(): ?string
{
if ($this->place !== null) {
return $this->place->name;
@ -271,12 +291,19 @@ class Note extends Model
if ($this->location !== null) {
return $this->reverseGeoCode((float) $this->latitude, (float) $this->longitude);
}
return null;
}
public function getTwitterAttribute()
/**
* Get the OEmbed html for a tweet the note is a reply to.
*
* @return object|null
*/
public function getTwitterAttribute(): ?object
{
if ($this->in_reply_to == null || mb_substr($this->in_reply_to, 0, 20, 'UTF-8') !== 'https://twitter.com/') {
return;
return null;
}
$tweetId = basename($this->in_reply_to);
@ -292,7 +319,7 @@ class Note extends Model
'maxwidth' => 512,
]);
} catch (\Exception $e) {
return;
return null;
}
Cache::put($tweetId, $oEmbed, ($oEmbed->cache_age / 60));
@ -301,20 +328,24 @@ class Note extends Model
/**
* Show a specific form of the note for twitter.
*
* That is we swap the contacts names for their known Twitter handles.
*
* @return string|null
*/
public function getTwitterContentAttribute()
public function getTwitterContentAttribute(): ?string
{
if ($this->contacts === null) {
return;
return null;
}
if (count($this->contacts) === 0) {
return;
return null;
}
if (count(array_unique(array_values($this->contacts))) === 1
&& array_unique(array_values($this->contacts))[0] === null) {
return;
return null;
}
// swap in twitter usernames
@ -338,15 +369,22 @@ class Note extends Model
return $this->convertMarkdown($swapped);
}
public function getFacebookContentAttribute()
/**
* Show a specific form of the note for facebook.
*
* That is we swap the contacts names for their known Facebook usernames.
*
* @return string|null
*/
public function getFacebookContentAttribute(): ?string
{
if (count($this->contacts) === 0) {
return;
return null;
}
if (count(array_unique(array_values($this->contacts))) === 1
&& array_unique(array_values($this->contacts))[0] === null) {
return;
return null;
}
// swap in facebook usernames
@ -374,27 +412,27 @@ class Note extends Model
/**
* Scope a query to select a note via a NewBase60 id.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $nb60id
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $nb60id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeNb60($query, $nb60id)
public function scopeNb60(Builder $query, string $nb60id): Builder
{
$numbers = new Numbers();
return $query->where('id', $numbers->b60tonum($nb60id));
return $query->where('id', resolve(Numbers::class)->b60tonum($nb60id));
}
/**
* Swap contacts nicks for a full mf2 h-card.
*
* Take note that this method does two things, given @username (NOT [@username](URL)!)
* we try to create a fancy hcard from our contact info. If this is not possible
* due to lack of contact info, we assume @username is a twitter handle and link it
* as such.
*
* @param string The notes text
* @param string $text
* @return string
*/
private function makeHCards($text)
private function makeHCards(string $text): string
{
$this->getContacts();
@ -424,6 +462,9 @@ class Note extends Model
return $hcards;
}
/**
* Get the value of the `contacts` property.
*/
public function getContacts()
{
if ($this->contacts === null) {
@ -431,6 +472,9 @@ class Note extends Model
}
}
/**
* Process the note and save the contacts to the `contacts` property.
*/
public function setContacts()
{
$contacts = [];
@ -446,14 +490,16 @@ class Note extends Model
}
/**
* Turn text hashtags to full HTML links.
*
* Given a string and section, finds all hashtags matching
* `#[\-_a-zA-Z0-9]+` and wraps them in an `a` element with
* `rel=tag` set and a `href` of 'section/tagged/' + tagname without the #.
*
* @param string The note
* @param string $note
* @return string
*/
public function autoLinkHashtag($text)
public function autoLinkHashtag(string $note): string
{
return preg_replace_callback(
'/#([^\s]*)\b/',
@ -462,25 +508,31 @@ class Note extends Model
. Tag::normalize($matches[1]) . '">#'
. $matches[1] . '</a>';
},
$text
$note
);
}
private function convertMarkdown($text)
/**
* Pass a note through the commonmark library.
*
* @param string $note
* @return string
*/
private function convertMarkdown(string $note): string
{
$environment = Environment::createCommonMarkEnvironment();
$environment->addExtension(new LinkifyExtension());
$converter = new Converter(new DocParser($environment), new HtmlRenderer($environment));
return $converter->convertToHtml($text);
return $converter->convertToHtml($note);
}
/**
* Do a reverse geocode lookup of a `lat,lng` value.
*
* @param float The latitude
* @param float The longitude
* @return string The location HTML
* @param float $latitude
* @param float $longitude
* @return string
*/
public function reverseGeoCode(float $latitude, float $longitude): string
{
@ -498,7 +550,7 @@ class Note extends Model
],
'headers' => ['User-Agent' => 'jonnybarnes.uk via Guzzle, email jonny@jonnybarnes.uk'],
]);
$json = json_decode($response->getBody());
$json = json_decode((string) $response->getBody());
if (isset($json->address->town)) {
$address = '<span class="p-locality">'
. $json->address->town

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Support\Facades\DB;
@ -36,7 +38,7 @@ class Place extends Model
*
* @return array
*/
public function sluggable()
public function sluggable(): array
{
return [
'slug' => [
@ -49,7 +51,7 @@ class Place extends Model
/**
* Define the relationship with Notes.
*
* @var array
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function notes()
{
@ -59,12 +61,12 @@ class Place extends Model
/**
* Select places near a given location.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param Point $point
* @param int Distance
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Phaza\LaravelPostgis\Geometries\Point $point
* @param int $distance
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeNear(Builder $query, Point $point, $distance = 1000)
public function scopeNear(Builder $query, Point $point, $distance = 1000): Builder
{
$field = DB::raw(
sprintf(
@ -77,7 +79,14 @@ class Place extends Model
return $query->where($field, '<=', $distance)->orderBy($field);
}
public function scopeWhereExternalURL(Builder $query, string $url)
/**
* Select places based on a URL.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $url
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeWhereExternalURL(Builder $query, string $url): Builder
{
return $query->where('external_urls', '@>', json_encode([
$this->getType($url) => $url,
@ -87,21 +96,21 @@ class Place extends Model
/**
* Get the latitude from the `location` property.
*
* @return string latitude
* @return float
*/
public function getLatitudeAttribute()
public function getLatitudeAttribute(): float
{
return explode(' ', $this->location)[1];
return $this->location->getLat();
}
/**
* Get the longitude from the `location` property.
*
* @return string longitude
* @return float
*/
public function getLongitudeAttribute()
public function getLongitudeAttribute(): float
{
return explode(' ', $this->location)[0];
return $this->location->getLng();
}
/**
@ -109,7 +118,7 @@ class Place extends Model
*
* @return string
*/
public function getLongurlAttribute()
public function getLongurlAttribute(): string
{
return config('app.url') . '/places/' . $this->slug;
}
@ -119,7 +128,7 @@ class Place extends Model
*
* @return string
*/
public function getShorturlAttribute()
public function getShorturlAttribute(): string
{
return config('app.shorturl') . '/places/' . $this->slug;
}
@ -129,12 +138,17 @@ class Place extends Model
*
* @return string
*/
public function getUriAttribute()
public function getUriAttribute(): string
{
return $this->longurl;
}
public function setExternalUrlsAttribute($url)
/**
* Dealing with a jsonb column, so we check input first.
*
* @param string|null $url
*/
public function setExternalUrlsAttribute(?string $url)
{
if ($url === null) {
return;
@ -148,7 +162,13 @@ class Place extends Model
$this->attributes['external_urls'] = json_encode($already);
}
private function getType(string $url): ?string
/**
* Given a URL, see if it is one of our known types.
*
* @param string $url
* @return string
*/
private function getType(string $url): string
{
$host = parse_url($url, PHP_URL_HOST);
if (ends_with($host, 'foursquare.com') === true) {

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
@ -14,9 +16,9 @@ class Tag extends Model
protected $guarded = ['id'];
/**
* Define the relationship with tags.
* Define the relationship with notes.
*
* @var array
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function notes()
{
@ -25,6 +27,8 @@ class Tag extends Model
/**
* The bookmarks that belong to the tag.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function bookmarks()
{
@ -32,11 +36,11 @@ class Tag extends Model
}
/**
* Normalize tags so theyre lowercase and fancy diatrics are removed.
* When creating a Tag model instance, invoke the nomralize method on the tag.
*
* @param string
* @param string $value
*/
public function setTagAttribute($value)
public function setTagAttribute(string $value)
{
$this->attributes['tag'] = $this->normalize($value);
}
@ -45,9 +49,10 @@ class Tag extends Model
* This method actually normalizes a tag. That means lowercase-ing and
* removing fancy diatric characters.
*
* @param string
* @param string $tag
* @return string
*/
public static function normalize($tag)
public static function normalize(string $tag): string
{
return mb_strtolower(
preg_replace(

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Notifications\Notifiable;

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Cache;
@ -29,7 +31,7 @@ class WebMention extends Model
/**
* Define the relationship.
*
* @var array
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function commentable()
{
@ -41,7 +43,7 @@ class WebMention extends Model
*
* @return array
*/
public function getAuthorAttribute()
public function getAuthorAttribute(): array
{
$authorship = new Authorship();
$hCard = $authorship->findAuthor(json_decode($this->mf2, true));
@ -57,11 +59,12 @@ class WebMention extends Model
/**
* Get the published value for the webmention.
*
* @return string
* @return string|null
*/
public function getPublishedAttribute()
public function getPublishedAttribute(): ?string
{
$microformats = json_decode($this->mf2, true);
$mf2 = $this->mf2 ?? '';
$microformats = json_decode($mf2, true);
if (isset($microformats['items'][0]['properties']['published'][0])) {
try {
$published = carbon()->parse(
@ -82,12 +85,17 @@ class WebMention extends Model
*
* @return string|null
*/
public function getReplyAttribute()
public function getReplyAttribute(): ?string
{
if ($this->mf2 === null) {
return null;
}
$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']);
}
return null;
}
/**
@ -126,10 +134,10 @@ class WebMention extends Model
/**
* Filter the HTML in a reply webmention.
*
* @param string The reply HTML
* @return string The filtered HTML
* @param string $html
* @return string
*/
private function filterHTML($html)
private function filterHTML(string $html): string
{
$config = HTMLPurifier_Config::createDefault();
$config->set('Cache.SerializerPath', storage_path() . '/HTMLPurifier');

View file

@ -1,8 +1,11 @@
<?php
declare(strict_types=1);
namespace App\Observers;
use App\Models\{Note, Tag};
use Illuminate\Support\Collection;
class NoteObserver
{
@ -10,11 +13,14 @@ class NoteObserver
* Listen to the Note created event.
*
* @param \App\Note $note
* @return void
*/
public function created(Note $note)
{
$tags = $this->getTagsFromNote($note->getAttributes()['note'] ?? null);
$text = array_get($note->getAttributes(), 'note');
if ($text === null) {
return;
}
$tags = $this->getTagsFromNote($text);
if (count($tags) === 0) {
return;
@ -31,11 +37,15 @@ class NoteObserver
* Listen to the Note updated event.
*
* @param \App\Note $Note
* @return void
*/
public function updated(Note $note)
{
$tags = $this->getTagsFromNote($note->getAttributes()['note']);
$text = array_get($note->getAttributes(), 'note');
if ($text === null) {
return;
}
$tags = $this->getTagsFromNote($text);
if (count($tags) === 0) {
return;
}
@ -53,14 +63,19 @@ class NoteObserver
* Listen to the Note deleting event.
*
* @param \App\Note $note
* @return void
*/
public function deleting(Note $note)
{
$note->tags()->detach();
}
public function getTagsFromNote($note)
/**
* Retrieve the tags from a notes text, tag for form #tag.
*
* @param string $note
* @return \Illuminate\Support\Collection
*/
private function getTagsFromNote(string $note): Collection
{
preg_match_all('/#([^\s<>]+)\b/', $note, $tags);

View file

@ -1,11 +1,18 @@
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\Note;
class ActivityStreamsService
{
/**
* Return the relevant data to an AS2.0 request to the root path.
*
* @return \Illuminate\Http\Response
*/
public function siteOwnerResponse()
{
$data = json_encode([
@ -19,6 +26,12 @@ class ActivityStreamsService
return response($data)->header('Content-Type', 'application/activity+json');
}
/**
* Return the relevant data to an AS2.0 request for a particular note.
*
* @param \App\Models\Note $note
* @return \Illuminate\Http\Response
*/
public function singleNoteResponse(Note $note)
{
$data = json_encode([

View file

@ -20,7 +20,7 @@ class BookmarkService
/**
* Create a new Bookmark.
*
* @param array $request
* @param array $request Data from request()->all()
* @return Bookmark $bookmark
*/
public function createBookmark(array $request): Bookmark
@ -84,6 +84,12 @@ class BookmarkService
return $bookmark;
}
/**
* Given a URL, use browsershot to save an image of the page.
*
* @param string $url
* @return string The uuid for the screenshot
*/
public function saveScreenshot(string $url): string
{
$browsershot = new Browsershot();
@ -99,6 +105,12 @@ class BookmarkService
return $uuid->toString();
}
/**
* Given a URL, attempt to save it to the Internet Archive.
*
* @param string $url
* @return string
*/
public function getArchiveLink(string $url): string
{
$client = resolve(Client::class);

View file

@ -1,12 +1,20 @@
<?php
declare(strict_types=1);
namespace App\Services\Micropub;
use App\Services\PlaceService;
class HCardService
{
public function process(array $request)
/**
* Create a Place from h-card data, return the URL.
*
* @param array $request Data from request()->all()
* @return string
*/
public function process(array $request): string
{
$data = [];
if (array_get($request, 'properties.name')) {

View file

@ -1,12 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Services\Micropub;
use App\Services\{BookmarkService, LikeService, NoteService};
class HEntryService
{
public function process(array $request, string $client = null)
/**
* Create the relavent model from some h-entry data.
*
* @param array $request Data from request()->all()
* @param string|null $client The micropub client that made the request
* @return string|null
*/
public function process(array $request, ?string $client = null): ?string
{
if (array_get($request, 'properties.like-of') || array_get($request, 'like-of')) {
$like = resolve(LikeService::class)->createLike($request);

View file

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace App\Services\Micropub;
use App\Models\{Media, Note};
@ -7,6 +9,12 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
class UpdateService
{
/**
* Process a micropub request to update an entry.
*
* @param array $request Data from request()->all()
* @return \Illuminate\Http\JsonResponse
*/
public function process(array $request)
{
$urlPath = parse_url(array_get($request, 'url'), PHP_URL_PATH);

View file

@ -12,11 +12,11 @@ class NoteService
/**
* Create a new note.
*
* @param array $request
* @param string $client
* @return \App\Note $note
* @param array $request Data from request()->all()
* @param string $client
* @return \App\Note
*/
public function createNote(array $request, string $client = null): Note
public function createNote(array $request, ?string $client = null): Note
{
$note = Note::create(
[
@ -60,6 +60,12 @@ class NoteService
return $note;
}
/**
* Get the content from the request to create a new note.
*
* @param array $request Data from request()->all()
* @return string|null
*/
private function getContent(array $request): ?string
{
if (array_get($request, 'properties.content.0.html')) {
@ -72,6 +78,12 @@ class NoteService
return array_get($request, 'content');
}
/**
* Get the in-reply-to from the request to create a new note.
*
* @param array $request Data from request()->all()
* @return string|null
*/
private function getInReplyTo(array $request): ?string
{
if (array_get($request, 'properties.in-reply-to.0')) {
@ -81,6 +93,12 @@ class NoteService
return array_get($request, 'in-reply-to');
}
/**
* Get the published time from the request to create a new note.
*
* @param array $request Data from request()->all()
* @return string|null
*/
private function getPublished(array $request): ?string
{
if (array_get($request, 'properties.published.0')) {
@ -94,6 +112,12 @@ class NoteService
return null;
}
/**
* Get the location data from the request to create a new note.
*
* @param array $request Data from request()->all()
* @return string|null
*/
private function getLocation(array $request): ?string
{
$location = array_get($request, 'properties.location.0') ?? array_get($request, 'location');
@ -110,6 +134,12 @@ class NoteService
return null;
}
/**
* Get the checkin data from the request to create a new note. This will be a Place.
*
* @param array $request Data from request()->all()
* @return \App\Models\Place|null
*/
private function getCheckin(array $request): ?Place
{
if (array_get($request, 'properties.location.0.type.0') === 'h-card') {
@ -149,6 +179,12 @@ class NoteService
return null;
}
/**
* Get the Swarm URL from the syndication data in the request to create a new note.
*
* @param array $request Data from request()->all()
* @return string|null
*/
private function getSwarmUrl(array $request): ?string
{
if (stristr(array_get($request, 'properties.syndication.0', ''), 'swarmapp')) {
@ -158,6 +194,12 @@ class NoteService
return null;
}
/**
* Get the syndication targets from the request to create a new note.
*
* @param array $request Data from request()->all()
* @return array
*/
private function getSyndicationTargets(array $request): array
{
$syndication = [];
@ -187,6 +229,12 @@ class NoteService
return $syndication;
}
/**
* Get the media URLs from the request to create a new note.
*
* @param array $request Data from request()->all()
* @return array
*/
private function getMedia(array $request): array
{
$media = [];
@ -211,6 +259,12 @@ class NoteService
return $media;
}
/**
* Get the Instagram photo URL from the request to create a new note.
*
* @param array $request Data from request()->all()
* @return string|null
*/
private function getInstagramUrl(array $request): ?string
{
if (starts_with(array_get($request, 'properties.syndication.0'), 'https://www.instagram.com')) {

View file

@ -1,5 +1,9 @@
# Changelog
## Version 0.15.4
- Improve code-base by liberal use of `strict_types`
- Added some basic CSS text styling
## Version 0.15.3 (2018-01-12)
- Improve `likes`, including adding a new section in the admin cp
- Add the ability to POSSE the like of a Tweet

44
composer.lock generated
View file

@ -8,16 +8,16 @@
"packages": [
{
"name": "aws/aws-sdk-php",
"version": "3.48.12",
"version": "3.48.13",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "c7f3148d537db877e9b4b63308d61f52eaa253bc"
"reference": "e083a4dae6f460df1d02dcd827cf547592f5dc29"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c7f3148d537db877e9b4b63308d61f52eaa253bc",
"reference": "c7f3148d537db877e9b4b63308d61f52eaa253bc",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e083a4dae6f460df1d02dcd827cf547592f5dc29",
"reference": "e083a4dae6f460df1d02dcd827cf547592f5dc29",
"shasum": ""
},
"require": {
@ -84,7 +84,7 @@
"s3",
"sdk"
],
"time": "2018-01-11T20:40:29+00:00"
"time": "2018-01-12T21:20:32+00:00"
},
{
"name": "barnabywalters/mf-cleaner",
@ -182,16 +182,16 @@
},
{
"name": "cakephp/chronos",
"version": "1.1.3",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/cakephp/chronos.git",
"reference": "56d98330d366a469745848b07540373846c40561"
"reference": "85bcaea6a832684b32ef54b2487b0c14a172e9e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cakephp/chronos/zipball/56d98330d366a469745848b07540373846c40561",
"reference": "56d98330d366a469745848b07540373846c40561",
"url": "https://api.github.com/repos/cakephp/chronos/zipball/85bcaea6a832684b32ef54b2487b0c14a172e9e6",
"reference": "85bcaea6a832684b32ef54b2487b0c14a172e9e6",
"shasum": ""
},
"require": {
@ -235,7 +235,7 @@
"datetime",
"time"
],
"time": "2017-12-25T22:42:18+00:00"
"time": "2018-01-13T12:19:50+00:00"
},
{
"name": "cocur/slugify",
@ -1891,16 +1891,16 @@
},
{
"name": "laravel/framework",
"version": "v5.5.28",
"version": "v5.5.29",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "cfafae1f2043208390a7c984e3070696f4969605"
"reference": "c58b6ff96fb56cc92e24545870be13cdcfcbc43a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/cfafae1f2043208390a7c984e3070696f4969605",
"reference": "cfafae1f2043208390a7c984e3070696f4969605",
"url": "https://api.github.com/repos/laravel/framework/zipball/c58b6ff96fb56cc92e24545870be13cdcfcbc43a",
"reference": "c58b6ff96fb56cc92e24545870be13cdcfcbc43a",
"shasum": ""
},
"require": {
@ -2021,7 +2021,7 @@
"framework",
"laravel"
],
"time": "2017-12-26T16:24:40+00:00"
"time": "2018-01-15T14:07:47+00:00"
},
{
"name": "laravel/horizon",
@ -3358,16 +3358,16 @@
},
{
"name": "ramsey/uuid",
"version": "3.7.1",
"version": "3.7.2",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "45cffe822057a09e05f7bd09ec5fb88eeecd2334"
"reference": "bba83ad77bb9deb6d3c352a7361b818e415b221d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/45cffe822057a09e05f7bd09ec5fb88eeecd2334",
"reference": "45cffe822057a09e05f7bd09ec5fb88eeecd2334",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/bba83ad77bb9deb6d3c352a7361b818e415b221d",
"reference": "bba83ad77bb9deb6d3c352a7361b818e415b221d",
"shasum": ""
},
"require": {
@ -3379,7 +3379,7 @@
},
"require-dev": {
"apigen/apigen": "^4.1",
"codeception/aspect-mock": "^1.0 | ^2.0",
"codeception/aspect-mock": "^1.0 | ~2.0.0",
"doctrine/annotations": "~1.2.0",
"goaop/framework": "1.0.0-alpha.2 | ^1.0 | ^2.1",
"ircmaxell/random-lib": "^1.1",
@ -3387,7 +3387,7 @@
"mockery/mockery": "^0.9.4",
"moontoast/math": "^1.1",
"php-mock/php-mock-phpunit": "^0.3|^1.1",
"phpunit/phpunit": "^4.7|>=5.0 <5.4",
"phpunit/phpunit": "^4.7|^5.0",
"satooshi/php-coveralls": "^0.6.1",
"squizlabs/php_codesniffer": "^2.3"
},
@ -3436,7 +3436,7 @@
"identifier",
"uuid"
],
"time": "2017-09-22T20:46:04+00:00"
"time": "2018-01-13T22:22:03+00:00"
},
{
"name": "sensiolabs/security-checker",

48
package-lock.json generated
View file

@ -306,16 +306,16 @@
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"autoprefixer": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.4.tgz",
"integrity": "sha512-am8jJ7Rbh1sy7FvLvNxxQScWvhv2FwLAS3bIhvrZpx9HbX5PEcc/7v6ecgpWuiu0Dwlj+p/z/1boHd8x60JFwA==",
"version": "7.2.5",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.5.tgz",
"integrity": "sha512-XqHfo8Ht0VU+T5P+eWEVoXza456KJ4l62BPewu3vpNf3LP9s2+zYXkXBznzYby4XeECXgG3N4i+hGvOhXErZmA==",
"dev": true,
"requires": {
"browserslist": "2.11.0",
"caniuse-lite": "1.0.30000787",
"browserslist": "2.11.3",
"caniuse-lite": "1.0.30000792",
"normalize-range": "0.1.2",
"num2fraction": "1.2.2",
"postcss": "6.0.15",
"postcss": "6.0.16",
"postcss-value-parser": "3.3.0"
},
"dependencies": {
@ -329,19 +329,19 @@
}
},
"browserslist": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.0.tgz",
"integrity": "sha512-mNYp0RNeu1xueGuJFSXkU+K0nH+dBE/gcjtyhtNKfU8hwdrVIfoA7i5iFSjOmzkGdL2QaO7YX9ExiVPE7AY9JA==",
"version": "2.11.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz",
"integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==",
"dev": true,
"requires": {
"caniuse-lite": "1.0.30000787",
"caniuse-lite": "1.0.30000792",
"electron-to-chromium": "1.3.30"
}
},
"caniuse-lite": {
"version": "1.0.30000787",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000787.tgz",
"integrity": "sha1-p2xPodasAGQER+yDwefGsz3WFcU=",
"version": "1.0.30000792",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000792.tgz",
"integrity": "sha1-0M6pgfgRjzlhRxr7tDyaHlu/AzI=",
"dev": true
},
"chalk": {
@ -376,9 +376,9 @@
}
},
"postcss": {
"version": "6.0.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.15.tgz",
"integrity": "sha512-v/SpyMzLbtkmh45zUdaqLAaqXqzPdSrw8p4cQVO0/w6YiYfpj4k+Wkzhn68qk9br+H+0qfddhdPEVnbmBPfXVQ==",
"version": "6.0.16",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.16.tgz",
"integrity": "sha512-m758RWPmSjFH/2MyyG3UOW1fgYbR9rtdzz5UNJnlm7OLtu4B2h9C6gi+bE4qFKghsBRFfZT8NzoQBs6JhLotoA==",
"dev": true,
"requires": {
"chalk": "2.3.0",
@ -8696,7 +8696,7 @@
"integrity": "sha512-56hPH5mTFnk8LzlEuTWq0epa34fHuS54UFYQidBOFt563RJBNi1nz1F2HK2MoT1X1waq47milvRsRahFCCJs/Q==",
"dev": true,
"requires": {
"autoprefixer": "7.2.4",
"autoprefixer": "7.2.5",
"balanced-match": "1.0.0",
"chalk": "2.3.0",
"cosmiconfig": "3.1.0",
@ -9168,19 +9168,19 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"uglify-js": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.5.tgz",
"integrity": "sha512-ZebM2kgBL/UI9rKeAbsS2J0UPPv7SBy5hJNZml/YxB1zC6JK8IztcPs+cxilE4pu0li6vadVSFqiO7xFTKuSrg==",
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.7.tgz",
"integrity": "sha512-esJIpNQIC44EFSrbeFPhiXHy2HJ+dTcnn0Zdkn+5meuLsvoV0mFJffKlyezNIIHNfhF0NpgbifygCfEyAogIhQ==",
"dev": true,
"requires": {
"commander": "2.12.2",
"commander": "2.13.0",
"source-map": "0.6.1"
},
"dependencies": {
"commander": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz",
"integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==",
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
"integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
"dev": true
}
}

View file

@ -12,7 +12,7 @@
},
"devDependencies": {
"ajv": "^5.5.2",
"autoprefixer": "^7.2.4",
"autoprefixer": "^7.2.5",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
@ -35,7 +35,7 @@
"source-list-map": "^2.0.0",
"stylelint": "^8.4.0",
"stylelint-config-standard": "^18.0.0",
"uglify-js": "^3.3.5",
"uglify-js": "^3.3.7",
"webpack": "^3.10.0",
"webpack-sources": "^1.1.0"
},

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
{"version":3,"sources":["../../../resources/assets/sass/_border-box.scss","../../../resources/assets/sass/_base-font.scss","../../../resources/assets/sass/_header.scss","../../../resources/assets/sass/_variables.scss","../../../resources/assets/sass/_main.scss","../../../resources/assets/sass/_hovercard.scss","../../../resources/assets/sass/_notes.scss","../../../resources/assets/sass/_pagination.scss","../../../resources/assets/sass/_contacts-page.scss","../../../resources/assets/sass/_projects.scss","../../../resources/assets/sass/_footer.scss","../../../resources/assets/sass/_admin-form.scss","../../../resources/assets/sass/_form.scss","../../../resources/assets/sass/_likes.scss","../../../resources/assets/sass/_bridgy-links.scss","../../../resources/assets/sass/_emoji.scss","../../../resources/assets/sass/_mapbox.scss","../../../resources/assets/sass/_colors.scss","../../../resources/assets/sass/_styles.scss","../../../resources/assets/sass/_tags.scss"],"names":[],"mappings":"AAKA,KACI,8BAAsB,AAAtB,qBAAsB,CACzB,qBAKG,2BAAmB,AAAnB,kBAAmB,CACtB,KCVG,eACA,gCAAiC,CACpC,gBAGG,oBAAqB,CACxB,WCNG,oBACA,AADA,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,kBACA,AADA,cACA,yBACA,AADA,sBACA,AADA,mBACA,WACA,eCJgB,CDKnB,cAGG,eACA,cAAe,CAClB,eAGG,cAAe,CAClB,KEdG,oBACA,AADA,oBACA,AADA,aACA,4BACA,AADA,6BACA,AADA,0BACA,AADA,sBACA,0BACA,AADA,uBACA,AADA,oBACA,gBACA,cACA,iBACA,cAAe,CAClB,WAIG,gBAAiB,CACpB,aCZG,iBAAkB,CACrB,qBAGG,iBAAkB,CACrB,2BAGG,WAAY,CACf,WAGG,kBACA,8BACA,AADA,6BACA,AADA,uBACA,AADA,mBACA,yBACA,AADA,sBACA,AADA,8BACA,sBACA,AADA,mBACA,AADA,qBACA,iBACA,YACA,WACA,UACA,WACA,uBACA,kBACA,2CACA,AADA,mCACA,YAAa,CAChB,8BAGG,oBAAa,AAAb,oBAAa,AAAb,YAAa,CAChB,0BAGG,WACA,WAAY,CACf,sBAGG,YAAa,CCnCjB,MACI,oBACA,AADA,oBACA,AADA,aACA,4BACA,AADA,6BACA,AADA,0BACA,AADA,sBACA,cAAe,CAClB,UAGG,eACA,eAAgB,CACnB,eAGG,oBACA,AADA,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,uBACA,AADA,mBACA,yBAA8B,AAA9B,sBAA8B,AAA9B,6BAA8B,CACjC,MAGG,WACA,UAAW,CACd,YCtBG,oBACA,AADA,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,uBACA,AADA,mBACA,8BACA,AADA,2BACA,AADA,6BACA,eACA,oBAAqB,CACxB,cCLG,eACA,oBACA,AADA,oBACA,AADA,aACA,8BACA,AADA,8BACA,AADA,+BACA,AADA,2BACA,yBACA,AADA,sBACA,AADA,8BACA,eAAgB,CACnB,kBAGG,WACA,WAAY,CACf,UCVG,cAAe,CAClB,gBCDG,gBACA,cACA,gBAAiB,CACpB,OAGG,gBACA,cACA,oBACA,AADA,oBACA,AADA,aACA,4BACA,AADA,6BACA,AADA,0BACA,AADA,sBACA,yBAAmB,AAAnB,sBAAmB,AAAnB,kBAAmB,CACtB,YCXG,gBACA,kBAAmB,CACtB,MCFG,oBACA,AADA,oBACA,AADA,aACA,4BAAsB,AAAtB,6BAAsB,AAAtB,0BAAsB,AAAtB,qBAAsB,CACzB,UAGG,oBACA,AADA,oBACA,AADA,aACA,4BAAsB,AAAtB,6BAAsB,AAAtB,0BAAsB,AAAtB,qBAAsB,CACzB,aAGG,8BAAmB,AAAnB,6BAAmB,AAAnB,uBAAmB,AAAnB,kBAAmB,CACtB,WCXG,eAAgB,CACnB,qDCAG,YAAa,CAChB,2BCAG,iBAAkB,CACrB,gFAIG,kBACA,cACA,UACA,aACA,OACA,cACA,qBACA,yBACA,oBACA,4CACA,AADA,oCACA,yBACA,kCACA,WACA,cACA,0CAAkC,AAAlC,iCAAkC,CACrC,2BAGG,KACI,aACA,6BACA,wCACA,0BACA,8BAAkC,AAAlC,qBAAkC,CAGtC,GACI,aACA,kCACA,yBACA,WACA,4CAAgD,AAAhD,mCAAgD,CAAA,CAIxD,AApBC,mBAGG,KACI,aACA,6BACA,wCACA,0BACA,8BAAkC,AAAlC,qBAAkC,CAGtC,GACI,aACA,kCACA,yBACA,WACA,4CAAgD,AAAhD,mCAAgD,CAAA,CAIxD,aACI,kCACI,kCAAmC,CACtC,CC9CL,KACI,YAAa,CAChB,oBAGG,kBAAmB,CACtB,QAGG,y4HACA,wBACA,WACA,WAAY,CACf,UAGG,kBACA,MACA,OACA,iBACA,cAAe,CAClB,gBAGG,gBACA,gBAAiB,CACpB,KCzBG,gCACA,kBAAmB,CACtB,WAGG,8BACA,kBAAmB,CACtB,YAIG,iBAAkB,CACrB,KCZG,oBAAqB,CACxB,aAGG,oBAAqB,CACxB,MCHG,SACA,gBACA,SAAU,CACb,SAGG,WACA,oBAAqB,CACxB,kBAIG,wBACA,0BACA,mBACA,qBACA,cACA,mBACA,sBACA,kBACA,qBACA,qBACA,8BAAsB,AAAtB,qBAAsB,CACzB,YAGG,0BACA,uCACA,oCACA,oCACA,WACA,kBACA,QACA,KAAM,CACT,WAGG,4BACA,kBAAmB,CACtB,kBAGG,4BAA6B,CAChC","file":"app.css"}
{"version":3,"sources":["../../../resources/assets/sass/_border-box.scss","../../../resources/assets/sass/_base-font.scss","../../../resources/assets/sass/_header.scss","../../../resources/assets/sass/_variables.scss","../../../resources/assets/sass/_main.scss","../../../resources/assets/sass/_hovercard.scss","../../../resources/assets/sass/_notes.scss","../../../resources/assets/sass/_pagination.scss","../../../resources/assets/sass/_contacts-page.scss","../../../resources/assets/sass/_projects.scss","../../../resources/assets/sass/_footer.scss","../../../resources/assets/sass/_admin-form.scss","../../../resources/assets/sass/_form.scss","../../../resources/assets/sass/_likes.scss","../../../resources/assets/sass/_bridgy-links.scss","../../../resources/assets/sass/_emoji.scss","../../../resources/assets/sass/_mapbox.scss","../../../resources/assets/sass/_colors.scss","../../../resources/assets/sass/_styles.scss","../../../resources/assets/sass/_tags.scss"],"names":[],"mappings":"AAKA,KACI,8BAAsB,AAAtB,qBAAsB,CACzB,qBAKG,2BAAmB,AAAnB,kBAAmB,CACtB,KCVG,eACA,gCAAiC,CACpC,gBAGG,oBAAqB,CACxB,WCNG,oBACA,AADA,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,kBACA,AADA,cACA,yBACA,AADA,sBACA,AADA,mBACA,WACA,eCJgB,CDKnB,cAGG,eACA,cAAe,CAClB,eAGG,cAAe,CAClB,KEdG,oBACA,AADA,oBACA,AADA,aACA,4BACA,AADA,6BACA,AADA,0BACA,AADA,sBACA,0BACA,AADA,uBACA,AADA,oBACA,gBACA,cACA,iBACA,cAAe,CAClB,WAIG,gBAAiB,CACpB,aCZG,iBAAkB,CACrB,qBAGG,iBAAkB,CACrB,2BAGG,WAAY,CACf,WAGG,kBACA,8BACA,AADA,6BACA,AADA,uBACA,AADA,mBACA,yBACA,AADA,sBACA,AADA,8BACA,sBACA,AADA,mBACA,AADA,qBACA,iBACA,YACA,WACA,UACA,WACA,uBACA,kBACA,2CACA,AADA,mCACA,YAAa,CAChB,8BAGG,oBAAa,AAAb,oBAAa,AAAb,YAAa,CAChB,0BAGG,WACA,WAAY,CACf,sBAGG,YAAa,CCnCjB,MACI,oBACA,AADA,oBACA,AADA,aACA,4BACA,AADA,6BACA,AADA,0BACA,AADA,sBACA,cAAe,CAClB,UAGG,eACA,eAAgB,CACnB,eAGG,oBACA,AADA,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,uBACA,AADA,mBACA,yBAA8B,AAA9B,sBAA8B,AAA9B,6BAA8B,CACjC,MAGG,WACA,UAAW,CACd,YCtBG,oBACA,AADA,oBACA,AADA,aACA,8BACA,AADA,6BACA,AADA,uBACA,AADA,mBACA,8BACA,AADA,2BACA,AADA,6BACA,eACA,oBAAqB,CACxB,cCLG,eACA,oBACA,AADA,oBACA,AADA,aACA,8BACA,AADA,8BACA,AADA,+BACA,AADA,2BACA,yBACA,AADA,sBACA,AADA,8BACA,eAAgB,CACnB,kBAGG,WACA,WAAY,CACf,UCVG,cAAe,CAClB,gBCDG,gBACA,cACA,gBAAiB,CACpB,OAGG,gBACA,cACA,oBACA,AADA,oBACA,AADA,aACA,4BACA,AADA,6BACA,AADA,0BACA,AADA,sBACA,yBAAmB,AAAnB,sBAAmB,AAAnB,kBAAmB,CACtB,YCXG,gBACA,kBAAmB,CACtB,MCFG,oBACA,AADA,oBACA,AADA,aACA,4BAAsB,AAAtB,6BAAsB,AAAtB,0BAAsB,AAAtB,qBAAsB,CACzB,UAGG,oBACA,AADA,oBACA,AADA,aACA,4BAAsB,AAAtB,6BAAsB,AAAtB,0BAAsB,AAAtB,qBAAsB,CACzB,aAGG,8BAAmB,AAAnB,6BAAmB,AAAnB,uBAAmB,AAAnB,kBAAmB,CACtB,WCXG,eAAgB,CACnB,qDCAG,YAAa,CAChB,2BCAG,iBAAkB,CACrB,gFAIG,kBACA,cACA,UACA,aACA,OACA,cACA,qBACA,yBACA,oBACA,4CACA,AADA,oCACA,yBACA,kCACA,WACA,cACA,0CAAkC,AAAlC,iCAAkC,CACrC,2BAGG,KACI,aACA,6BACA,wCACA,0BACA,8BAAkC,AAAlC,qBAAkC,CAGtC,GACI,aACA,kCACA,yBACA,WACA,4CAAgD,AAAhD,mCAAgD,CAAA,CAIxD,AApBC,mBAGG,KACI,aACA,6BACA,wCACA,0BACA,8BAAkC,AAAlC,qBAAkC,CAGtC,GACI,aACA,kCACA,yBACA,WACA,4CAAgD,AAAhD,mCAAgD,CAAA,CAIxD,aACI,kCACI,kCAAmC,CACtC,CC9CL,KACI,YAAa,CAChB,oBAGG,kBAAmB,CACtB,QAGG,y4HACA,wBACA,WACA,WAAY,CACf,UAGG,kBACA,MACA,OACA,iBACA,cAAe,CAClB,gBAGG,gBACA,gBAAiB,CACpB,KCzBG,gCACA,kBAAmB,CACtB,WAGG,8BACA,kBAAmB,CACtB,YAIG,iBAAkB,CACrB,KCZG,kCAEA,yBACA,qBACA,4BAAoB,AAApB,mBAAoB,CACvB,KAGG,oBAAqB,CACxB,aAGG,oBAAqB,CACxB,MCXG,SACA,gBACA,SAAU,CACb,SAGG,WACA,oBAAqB,CACxB,kBAIG,wBACA,0BACA,mBACA,qBACA,cACA,mBACA,sBACA,kBACA,qBACA,qBACA,8BAAsB,AAAtB,qBAAsB,CACzB,YAGG,0BACA,uCACA,oCACA,oCACA,WACA,kBACA,QACA,KAAM,CACT,WAGG,4BACA,kBAAmB,CACtB,kBAGG,4BAA6B,CAChC","file":"app.css"}

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

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,5 +1,13 @@
//styles.scss
html {
text-rendering: optimizeLegibility;
//text-decoration-skip-ink: auto;
overflow-wrap: break-word;
word-break: break-all;
font-kerning: normal;
}
h1 a {
text-decoration: none;
}

View file

@ -887,7 +887,7 @@ class MicropubControllerTest extends TestCase
'h' => 'entry',
'content' => $note,
'published' => Carbon::now()->toW3CString(),
'access_token' => $this->getToken(),
'access_token' => (string) $this->getToken(),
]
);
$response->assertJson(['response' => 'created']);

View file

@ -146,7 +146,6 @@ class SwarmTest extends TestCase
$response
->assertStatus(201)
->assertJson(['response' => 'created']);
//dump($response->__get('headers')->get('location'));
$this->assertDatabaseHas('places', [
'external_urls' => '{"foursquare": "https://foursquare.com/v/654321"}'
]);

View file

@ -55,6 +55,5 @@ class TokenServiceTest extends TestCase
$service = new TokenService();
$token = $service->validateToken($token);
dump($token);
}
}