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 QUEUE_DRIVER=sync
SCOUT_DRIVER=pgsql SCOUT_DRIVER=pgsql
DISPLAY_NAME='Travis Test'
USER_NAME=travis

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,20 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use Illuminate\View\View;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
class HomeController extends 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')]); return view('admin.welcome', ['name' => config('admin.user')]);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,19 +1,33 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Bookmark; use App\Models\Bookmark;
use Illuminate\View\View;
class BookmarksController extends Controller 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); $bookmarks = Bookmark::latest()->with('tags')->withCount('tags')->paginate(10);
return view('bookmarks.index', compact('bookmarks')); 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'); $bookmark->loadMissing('tags');

View file

@ -1,8 +1,11 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Contact; use App\Models\Contact;
use Illuminate\View\View;
use Illuminate\Filesystem\Filesystem; use Illuminate\Filesystem\Filesystem;
class ContactsController extends Controller class ContactsController extends Controller
@ -10,9 +13,9 @@ class ContactsController extends Controller
/** /**
* Show all the contacts. * Show all the contacts.
* *
* @return \Illuminate\View\Factory view * @return \Illuminate\View\View
*/ */
public function index() public function index(): View
{ {
$filesystem = new Filesystem(); $filesystem = new Filesystem();
$contacts = Contact::all(); $contacts = Contact::all();
@ -31,9 +34,12 @@ class ContactsController extends Controller
/** /**
* Show a single contact. * 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(); $filesystem = new Filesystem();
$contact = Contact::where('nick', '=', $nick)->firstOrFail(); $contact = Contact::where('nick', '=', $nick)->firstOrFail();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,15 +1,22 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Note; use App\Models\Note;
use Illuminate\Http\Request; use Illuminate\View\View;
class SearchController extends Controller 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')); return view('search', compact('notes'));
} }

View file

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

View file

@ -1,7 +1,11 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
class ShortURLsController extends Controller class ShortURLsController extends Controller
{ {
/* /*
@ -16,9 +20,9 @@ class ShortURLsController extends Controller
/** /**
* Redirect from '/' to the long url. * 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')); return redirect(config('app.url'));
} }
@ -26,9 +30,9 @@ class ShortURLsController extends Controller
/** /**
* Redirect from '/@' to a twitter profile. * 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'); return redirect('https://twitter.com/jonnybarnes');
} }
@ -36,9 +40,9 @@ class ShortURLsController extends Controller
/** /**
* Redirect from '/+' to a Google+ profile. * 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'); 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 type
* @param string Post ID * @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') { if ($type == 't') {
$type = 'notes'; $type = 'notes';

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Jobs; namespace App\Jobs;
use App\Models\Like; use App\Models\Like;
@ -23,7 +25,7 @@ class ProcessLike implements ShouldQueue
/** /**
* Create a new job instance. * Create a new job instance.
* *
* @return void * @param \App\Models\Like $like
*/ */
public function __construct(Like $like) public function __construct(Like $like)
{ {
@ -33,9 +35,11 @@ class ProcessLike implements ShouldQueue
/** /**
* Execute the job. * 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)) { if ($this->isTweet($this->like->url)) {
$tweet = Twitter::getOembed(['url' => $this->like->url]); $tweet = Twitter::getOembed(['url' => $this->like->url]);
@ -83,8 +87,16 @@ class ProcessLike implements ShouldQueue
} }
$this->like->save(); $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 private function isTweet(string $url): bool
{ {
$host = parse_url($url, PHP_URL_HOST); $host = parse_url($url, PHP_URL_HOST);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Models; namespace App\Models;
use Mf2; use Mf2;
@ -11,17 +13,33 @@ class Like extends Model
{ {
protected $fillable = ['url']; 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); $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); $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) { if ($value === null) {
return null; return null;
@ -38,7 +56,13 @@ class Like extends Model
return $value; 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 = HTMLPurifier_Config::createDefault();
$config->set('Cache.SerializerPath', storage_path() . '/HTMLPurifier'); $config->set('Cache.SerializerPath', storage_path() . '/HTMLPurifier');

View file

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

View file

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

View file

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

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Models; namespace App\Models;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -36,7 +38,7 @@ class Place extends Model
* *
* @return array * @return array
*/ */
public function sluggable() public function sluggable(): array
{ {
return [ return [
'slug' => [ 'slug' => [
@ -49,7 +51,7 @@ class Place extends Model
/** /**
* Define the relationship with Notes. * Define the relationship with Notes.
* *
* @var array * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */
public function notes() public function notes()
{ {
@ -59,12 +61,12 @@ class Place extends Model
/** /**
* Select places near a given location. * Select places near a given location.
* *
* @param \Illuminate\Database\Eloquent\Builder $query * @param \Illuminate\Database\Eloquent\Builder $query
* @param Point $point * @param \Phaza\LaravelPostgis\Geometries\Point $point
* @param int Distance * @param int $distance
* @return \Illuminate\Database\Eloquent\Builder * @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( $field = DB::raw(
sprintf( sprintf(
@ -77,7 +79,14 @@ class Place extends Model
return $query->where($field, '<=', $distance)->orderBy($field); 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([ return $query->where('external_urls', '@>', json_encode([
$this->getType($url) => $url, $this->getType($url) => $url,
@ -87,21 +96,21 @@ class Place extends Model
/** /**
* Get the latitude from the `location` property. * 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. * 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 * @return string
*/ */
public function getLongurlAttribute() public function getLongurlAttribute(): string
{ {
return config('app.url') . '/places/' . $this->slug; return config('app.url') . '/places/' . $this->slug;
} }
@ -119,7 +128,7 @@ class Place extends Model
* *
* @return string * @return string
*/ */
public function getShorturlAttribute() public function getShorturlAttribute(): string
{ {
return config('app.shorturl') . '/places/' . $this->slug; return config('app.shorturl') . '/places/' . $this->slug;
} }
@ -129,12 +138,17 @@ class Place extends Model
* *
* @return string * @return string
*/ */
public function getUriAttribute() public function getUriAttribute(): string
{ {
return $this->longurl; 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) { if ($url === null) {
return; return;
@ -148,7 +162,13 @@ class Place extends Model
$this->attributes['external_urls'] = json_encode($already); $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); $host = parse_url($url, PHP_URL_HOST);
if (ends_with($host, 'foursquare.com') === true) { if (ends_with($host, 'foursquare.com') === true) {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@ -14,9 +16,9 @@ class Tag extends Model
protected $guarded = ['id']; protected $guarded = ['id'];
/** /**
* Define the relationship with tags. * Define the relationship with notes.
* *
* @var array * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/ */
public function notes() public function notes()
{ {
@ -25,6 +27,8 @@ class Tag extends Model
/** /**
* The bookmarks that belong to the tag. * The bookmarks that belong to the tag.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/ */
public function bookmarks() 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); $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 * This method actually normalizes a tag. That means lowercase-ing and
* removing fancy diatric characters. * 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( return mb_strtolower(
preg_replace( preg_replace(

View file

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

View file

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

View file

@ -1,8 +1,11 @@
<?php <?php
declare(strict_types=1);
namespace App\Observers; namespace App\Observers;
use App\Models\{Note, Tag}; use App\Models\{Note, Tag};
use Illuminate\Support\Collection;
class NoteObserver class NoteObserver
{ {
@ -10,11 +13,14 @@ class NoteObserver
* Listen to the Note created event. * Listen to the Note created event.
* *
* @param \App\Note $note * @param \App\Note $note
* @return void
*/ */
public function created(Note $note) 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) { if (count($tags) === 0) {
return; return;
@ -31,11 +37,15 @@ class NoteObserver
* Listen to the Note updated event. * Listen to the Note updated event.
* *
* @param \App\Note $Note * @param \App\Note $Note
* @return void
*/ */
public function updated(Note $note) 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) { if (count($tags) === 0) {
return; return;
} }
@ -53,14 +63,19 @@ class NoteObserver
* Listen to the Note deleting event. * Listen to the Note deleting event.
* *
* @param \App\Note $note * @param \App\Note $note
* @return void
*/ */
public function deleting(Note $note) public function deleting(Note $note)
{ {
$note->tags()->detach(); $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); preg_match_all('/#([^\s<>]+)\b/', $note, $tags);

View file

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

View file

@ -20,7 +20,7 @@ class BookmarkService
/** /**
* Create a new Bookmark. * Create a new Bookmark.
* *
* @param array $request * @param array $request Data from request()->all()
* @return Bookmark $bookmark * @return Bookmark $bookmark
*/ */
public function createBookmark(array $request): Bookmark public function createBookmark(array $request): Bookmark
@ -84,6 +84,12 @@ class BookmarkService
return $bookmark; 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 public function saveScreenshot(string $url): string
{ {
$browsershot = new Browsershot(); $browsershot = new Browsershot();
@ -99,6 +105,12 @@ class BookmarkService
return $uuid->toString(); 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 public function getArchiveLink(string $url): string
{ {
$client = resolve(Client::class); $client = resolve(Client::class);

View file

@ -1,12 +1,20 @@
<?php <?php
declare(strict_types=1);
namespace App\Services\Micropub; namespace App\Services\Micropub;
use App\Services\PlaceService; use App\Services\PlaceService;
class HCardService 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 = []; $data = [];
if (array_get($request, 'properties.name')) { if (array_get($request, 'properties.name')) {

View file

@ -1,12 +1,21 @@
<?php <?php
declare(strict_types=1);
namespace App\Services\Micropub; namespace App\Services\Micropub;
use App\Services\{BookmarkService, LikeService, NoteService}; use App\Services\{BookmarkService, LikeService, NoteService};
class HEntryService 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')) { if (array_get($request, 'properties.like-of') || array_get($request, 'like-of')) {
$like = resolve(LikeService::class)->createLike($request); $like = resolve(LikeService::class)->createLike($request);

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Services\Micropub; namespace App\Services\Micropub;
use App\Models\{Media, Note}; use App\Models\{Media, Note};
@ -7,6 +9,12 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
class UpdateService 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) public function process(array $request)
{ {
$urlPath = parse_url(array_get($request, 'url'), PHP_URL_PATH); $urlPath = parse_url(array_get($request, 'url'), PHP_URL_PATH);

View file

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

View file

@ -1,5 +1,9 @@
# Changelog # 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) ## Version 0.15.3 (2018-01-12)
- Improve `likes`, including adding a new section in the admin cp - Improve `likes`, including adding a new section in the admin cp
- Add the ability to POSSE the like of a Tweet - Add the ability to POSSE the like of a Tweet

44
composer.lock generated
View file

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

48
package-lock.json generated
View file

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

View file

@ -12,7 +12,7 @@
}, },
"devDependencies": { "devDependencies": {
"ajv": "^5.5.2", "ajv": "^5.5.2",
"autoprefixer": "^7.2.4", "autoprefixer": "^7.2.5",
"babel-cli": "^6.26.0", "babel-cli": "^6.26.0",
"babel-core": "^6.26.0", "babel-core": "^6.26.0",
"babel-loader": "^7.1.2", "babel-loader": "^7.1.2",
@ -35,7 +35,7 @@
"source-list-map": "^2.0.0", "source-list-map": "^2.0.0",
"stylelint": "^8.4.0", "stylelint": "^8.4.0",
"stylelint-config-standard": "^18.0.0", "stylelint-config-standard": "^18.0.0",
"uglify-js": "^3.3.5", "uglify-js": "^3.3.7",
"webpack": "^3.10.0", "webpack": "^3.10.0",
"webpack-sources": "^1.1.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 //styles.scss
html {
text-rendering: optimizeLegibility;
//text-decoration-skip-ink: auto;
overflow-wrap: break-word;
word-break: break-all;
font-kerning: normal;
}
h1 a { h1 a {
text-decoration: none; text-decoration: none;
} }

View file

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

View file

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

View file

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